Commit 15f96d39 authored by Jens Korinth's avatar Jens Korinth
Browse files

Implement automatic build process for new Status Core

* there is a subproject that is included as a subtree in
  common/ip/tapasco_status
* subproject is standalone and can be run from Tcl
* changed process in common.tcl: createTapascoStatus now generates a
  JSON configuration file and runs sbt in the subproject
* each Composition gets its unique status core
* status cores are cached in tapasco-status-cache in the main dir:
  when building the same composition a number of times, status cores can
  be reused; is removed automatically in sbt clean
* required more API extensions: platform::get_address_map must be
  implemented by the Platform to communicate the address mapping to the
  status core creation
* also added formal interface to capabilities in common.tcl:
  tapasco::add_capability_flag, tapasco::get_capability_flags and
  tapasco::set_capability_flags can be used by plugins to activate
  capability bits
* whole process should be transparent to the user, everything is
  supposed to work as before
* one exception: get_address_map is not yet implemented on VC709
parent decea9b9
Pipeline #80 passed with stage
in 3 minutes and 14 seconds
......@@ -15,8 +15,8 @@ before_script:
- curl -s "https://get.sdkman.io" | bash
- source "/root/.sdkman/bin/sdkman-init.sh"
- sdk install sbt
# Log the sbt version
- sbt version
# Log the sbt version and TaPaSCo version
- sbt sbtVersion version
test:
script:
......
......@@ -137,7 +137,8 @@ cleanFiles ++= Seq(
baseDirectory.value / "bin" / "tapasco",
baseDirectory.value / "bin" / "itapasco",
baseDirectory.value / "bin" / "tapasco-logviewer",
baseDirectory.value / "bin" / "tapasco-reportviewer"
baseDirectory.value / "bin" / "tapasco-reportviewer",
baseDirectory.value / "tapasco-status-cache"
)
lazy val root = (project in file("."))
......
......@@ -20,6 +20,9 @@
# @brief Common Vivado Tcl helper procs to create block designs.
# @authors J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
#
source -notrace "$::env(TAPASCO_HOME)/common/json_write.tcl"
package require json::write
namespace eval tapasco {
namespace export createBinaryCounter
namespace export createClockingWizard
......@@ -47,6 +50,9 @@ namespace eval tapasco {
namespace export get_number_of_processors
namespace export get_speed_grade
namespace export get_wns_from_timing_report
namespace export get_capabilities_flags
namespace export set_capabilities_flags
namespace export add_capabilities_flag
namespace export create_interconnect_tree
......@@ -353,62 +359,45 @@ namespace eval tapasco {
# @param ids List of kernel IDs.
proc createTapascoStatus {name {ids {}}} {
variable stdcomps
set json [make_status_config_json]
set json_file "[file normalize [pwd]]/tapasco_status.json"
puts "Creating TPC Status ..."
puts " VLNV: [dict get $stdcomps tapasco_status vlnv]"
puts " IDs : $ids"
# check if ids are given, otherwise fetch automatically
if {[llength $ids] > 0} {
set c $ids
} {
set c [list]
set composition [tapasco::get_composition]
set no_kinds [llength [dict keys $composition]]
for {set i 0} {$i < $no_kinds} {incr i} {
set no_inst [dict get $composition $i count]
for {set j 0} {$j < $no_inst} {incr j} {
lappend c [dict get $composition $i id]
}
}
puts " Status: $json_file $json"
if {[catch {open $json_file "w"} f]} {
error "could not open file $json_file!"
} else {
puts -nonewline $f $json
close $f
}
# make map of IDs -> number of slave interfaces
set composition [tapasco::get_composition]
set no_kinds [llength [dict keys $composition]]
set no_slaves [list]
for {set i 0} {$i < $no_kinds} {incr i} {
lappend no_slaves [dict get $composition $i id]
lappend no_slaves [llength [arch::get_aximm_interfaces $i 0 "Slave"]]
# generate core
set old_pwd [pwd]
cd $::env(TAPASCO_HOME)/common/ip/tapasco_status
if {[catch {exec -ignorestderr which sbt} sbt]} {
error "could not find sbt"
} else {
puts " SBT: $sbt"
}
if {[catch {exec -ignorestderr $sbt "run $::env(TAPASCO_HOME)/tapasco-status-cache $json_file" | tee ${json_file}.log >@stdout 2>@1}]} {
puts stderr "Building TaPaSCO status core failed, see ${json_file}.log:"
puts stderr [read [open ${json_file}.log r]]
error "Could not build status core."
}
puts " Slvs: $no_slaves"
cd $old_pwd
# parse log and add custom IP path to IP_REPO_PATHS
set log [read [open ${json_file}.log r]]
set ip_path [regsub {.*Finished, IP Core is located in ([^ \n\t]*).*} $log {\1}]
puts " Path to custom IP: $ip_path"
set ip_repo_paths [get_property IP_REPO_PATHS [current_project]]
lappend ip_repo_paths $ip_path
set_property IP_REPO_PATHS $ip_repo_paths [current_project]
update_ip_catalog
# create the IP core
set inst [create_bd_cell -type ip -vlnv [dict get $stdcomps tapasco_status vlnv] $name]
# make properties list
set props [list]
set slot 0
foreach i $c {
puts " slot #$slot = $i"
if {$slot < 128} {
lappend props "[format CONFIG.C_SLOT_KERNEL_ID_%d [expr $slot + 1]]" "$i"
}
incr slot [dict get $no_slaves $i]
}
# get version strings
set vversion [split [version -short] {.}]
set tversion [split [tapasco::get_tapasco_version] {.}]
lappend props "CONFIG.C_INTC_COUNT" "[expr [llength $c] > 96 ? 4 : ([llength $c] > 64 ? 3 : ([llength $c] > 32 ? 2 : 1))]"
lappend props "CONFIG.C_GEN_TS" "[clock seconds]"
lappend props "CONFIG.C_VIVADO_VERSION" [format "0x%04x%04x" [expr [lindex $vversion 0]] [expr [lindex $vversion 1]]]
lappend props "CONFIG.C_TAPASCO_VERSION" [format "0x%04x%04x" [expr [lindex $tversion 0]] [expr [lindex $tversion 1]]]
lappend props "CONFIG.C_HOST_CLK_MHZ" [format "%d" [tapasco::get_host_frequency]]
lappend props "CONFIG.C_MEM_CLK_MHZ" [format "%d" [tapasco::get_mem_frequency]]
lappend props "CONFIG.C_DESIGN_CLK_MHZ" [format "%d" [tapasco::get_design_frequency]]
puts " properties: $props"
set_property -dict $props $inst
return $inst
return [create_bd_cell -type ip -vlnv [dict get $stdcomps tapasco_status vlnv] $name]
}
# Instantiates an AXI protocol converter.
......@@ -983,4 +972,66 @@ namespace eval tapasco {
proc get_speed_grade {} {
return [get_property SPEED [get_parts [get_property PART [current_project]]]]
}
set capabilities_0 0
# Returns the value of the CAPABILITIES_0 bitfield as currently configured.
proc get_capabilities_flags {} {
variable capabilities_0
return $capabilities_0
}
# Sets the value of the CAPABILITIES_0 bitfield.
proc set_capabilities_flags {flags} {
variable capabilities_0
puts [format "Setting Capability bitfield to new value 0x%08x (%d)." $flags $flags]
set capabilities_0 $flags
}
# Adds a specific bit to the CAPABILITIES_0 bitfield.
proc add_capabilities_flag {bit} {
variable capabilities_0
if {$bit < 0 || $bit > 31} { error "Invalid bit index: $bit" }
set flags [get_capabilities_flags]
set nflags [expr "$flags | (1 << $bit)"]
puts [format "Adding bit #$bit to capability bitfield: 0x%08x (%d) -> 0x%08x (%d)." $flags $flags $nflags $nflags]
set capabilities_0 $nflags
}
# Generate JSON configuration for the status core.
proc make_status_config_json {} {
set addr [platform::get_address_map]
set slots [list]
set slot_id 0
foreach a $addr {
if {[dict get $a "kind"] != "memory"} {
set kind [format "%d" [regsub {.*target_ip_([0-9][0-9]).*} [dict get $a "interface"] {\1}]]
set kid [dict get [tapasco::get_composition] $kind id]
lappend slots [json::write object "Type" [json::write string "Kernel"] "SlotId" $slot_id "Kernel" $kid]
} else {
lappend slots [json::write object "Type" [json::write string "Memory"] "SlotId" $slot_id "Bytes" [format "%d" [dict get $a "range"]]]
}
incr slot_id
}
set regex {([0-9][0-9][0-9][0-9]).([0-9][0-9]*)}
set no_pes [llength [arch::get_processing_elements]]
set no_intc [expr "$no_pes > 96 ? 4 : ($no_pes > 64 ? 3 : ($no_pes > 32 ? 2 : 1))"]
return [json::write object \
"Composition" [json::write array {*}$slots] \
"Timestamp" [clock seconds] \
"Interrupt Controllers" $no_intc \
"Versions" [json::write array \
[json::write object "Software" [json::write string "Vivado"] "Year" [regsub $regex [version -short] {\1}] "Release" [regsub $regex [version -short] {\2}]] \
[json::write object "Software" [json::write string "TaPaSCo"] "Year" [regsub $regex [tapasco::get_tapasco_version] {\1}] "Release" [regsub $regex [tapasco::get_tapasco_version] {\2}]] \
] \
"Clocks" [json::write array \
[json::write object "Domain" [json::write string "Host"] "Frequency" [tapasco::get_host_frequency]] \
[json::write object "Domain" [json::write string "Design"] "Frequency" [tapasco::get_design_frequency]] \
[json::write object "Domain" [json::write string "Memory"] "Frequency" [tapasco::get_host_frequency]] \
] \
"Capabilities" [json::write object "Capabilities 0" [get_capabilities_flags]] \
]
}
}
......@@ -9,7 +9,7 @@ package object tapasco_status {
}
final case class KernelId(id: Int) {
require (id > 0, s"kernel id $id is invalid, must be > 0")
require (id >= 0, s"kernel id $id is invalid, must be >= 0")
}
final case class Size(size: Int) {
......
# json_write.tcl --
#
# Commands for the generation of JSON (Java Script Object Notation).
#
# Copyright (c) 2009-2011 Andreas Kupries <andreas_kupries@sourceforge.net>
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# RCS: @(#) $Id: json_write.tcl,v 1.2 2011/08/24 20:09:44 andreas_kupries Exp $
# ### ### ### ######### ######### #########
## Requisites
package require Tcl 8.5
namespace eval ::json::write {
namespace export \
string array object indented aligned
namespace ensemble create
}
# ### ### ### ######### ######### #########
## API.
proc ::json::write::indented {{bool {}}} {
variable indented
if {[llength [info level 0]] > 2} {
return -code error {wrong # args: should be "json::write indented ?bool?"}
} elseif {[llength [info level 0]] == 2} {
if {![::string is boolean -strict $bool]} {
return -code error "Expected boolean, got \"$bool\""
}
set indented $bool
if {!$indented} {
variable aligned 0
}
}
return $indented
}
proc ::json::write::aligned {{bool {}}} {
variable aligned
if {[llength [info level 0]] > 2} {
return -code error {wrong # args: should be "json::write aligned ?bool?"}
} elseif {[llength [info level 0]] == 2} {
if {![::string is boolean -strict $bool]} {
return -code error "Expected boolean, got \"$bool\""
}
set aligned $bool
if {$aligned} {
variable indented 1
}
}
return $aligned
}
proc ::json::write::string {s} {
variable quotes
return "\"[::string map $quotes $s]\""
}
proc ::json::write::array {args} {
# always compact form.
return "\[[join $args ,]\]"
}
proc ::json::write::object {args} {
# The dict in args maps string keys to json-formatted data. I.e.
# we have to quote the keys, but not the values, as the latter are
# already in the proper format.
variable aligned
variable indented
if {[llength $args] %2 == 1} {
return -code error {wrong # args, expected an even number of arguments}
}
set dict {}
foreach {k v} $args {
lappend dict [string $k] $v
}
if {$aligned} {
set max [MaxKeyLength $dict]
}
if {$indented} {
set content {}
foreach {k v} $dict {
if {$aligned} {
set k [AlignLeft $max $k]
}
if {[::string match *\n* $v]} {
# multi-line value
lappend content " $k : [Indent $v { } 1]"
} else {
# single line value.
lappend content " $k : $v"
}
}
if {[llength $content]} {
return "\{\n[join $content ,\n]\n\}"
} else {
return "\{\}"
}
} else {
# ultra compact form.
set tmp {}
foreach {k v} $dict {
lappend tmp "$k:$v"
}
return "\{[join $tmp ,]\}"
}
}
# ### ### ### ######### ######### #########
## Internals.
proc ::json::write::Indent {text prefix skip} {
set pfx ""
set result {}
foreach line [split $text \n] {
if {!$skip} { set pfx $prefix } else { incr skip -1 }
lappend result ${pfx}$line
}
return [join $result \n]
}
proc ::json::write::MaxKeyLength {dict} {
# Find the max length of the keys in the dictionary.
set lengths 0 ; # This will be the max if the dict is empty, and
# prevents the mathfunc from throwing errors for
# that case.
foreach str [dict keys $dict] {
lappend lengths [::string length $str]
}
return [tcl::mathfunc::max {*}$lengths]
}
proc ::json::write::AlignLeft {fieldlen str} {
return [format %-${fieldlen}s $str]
#return $str[::string repeat { } [expr {$fieldlen - [::string length $str]}]]
}
# ### ### ### ######### ######### #########
namespace eval ::json::write {
# Configuration of the layout to write.
# indented = boolean. objects are indented.
# aligned = boolean. object keys are aligned vertically.
# aligned => indented.
# Combinations of the format specific entries
# I A |
# - - + ---------------------
# 0 0 | Ultracompact (no whitespace, single line)
# 1 0 | Indented
# 0 1 | Not possible, per the implications above.
# 1 1 | Indented + vertically aligned keys
# - - + ---------------------
variable indented 1
variable aligned 1
variable quotes \
[list "\"" "\\\"" \\ \\\\ \b \\b \f \\f \n \\n \r \\r \t \\t \
\x00 \\u0000 \x01 \\u0001 \x02 \\u0002 \x03 \\u0003 \
\x04 \\u0004 \x05 \\u0005 \x06 \\u0006 \x07 \\u0007 \
\x0b \\u000b \x0e \\u000e \x0f \\u000f \x10 \\u0010 \
\x11 \\u0011 \x12 \\u0012 \x13 \\u0013 \x14 \\u0014 \
\x15 \\u0015 \x16 \\u0016 \x17 \\u0017 \x18 \\u0018 \
\x19 \\u0019 \x1a \\u001a \x1b \\u001b \x1c \\u001c \
\x1d \\u001d \x1e \\u001e \x1f \\u001f \x7f \\u007f \
\x80 \\u0080 \x81 \\u0081 \x82 \\u0082 \x83 \\u0083 \
\x84 \\u0084 \x85 \\u0085 \x86 \\u0086 \x87 \\u0087 \
\x88 \\u0088 \x89 \\u0089 \x8a \\u008a \x8b \\u008b \
\x8c \\u008c \x8d \\u008d \x8e \\u008e \x8f \\u008f \
\x90 \\u0090 \x91 \\u0091 \x92 \\u0092 \x93 \\u0093 \
\x94 \\u0094 \x95 \\u0095 \x96 \\u0096 \x97 \\u0097 \
\x98 \\u0098 \x99 \\u0099 \x9a \\u009a \x9b \\u009b \
\x9c \\u009c \x9d \\u009d \x9e \\u009e \x9f \\u009f ]
}
# ### ### ### ######### ######### #########
## Ready
package provide json::write 1.0.3
return
......@@ -22,6 +22,15 @@
#
namespace eval platform {
namespace export generate
namespace export get_address_map
# Returns the address map of the current composition.
# Format: <SLAVE INTF> <BASE ADDR> <RANGE> <KIND>
# Kind is either Mem or Register, depending on the usage.
# Must be implemented by Platforms.
proc get_address_map {} {
error "Platform does not implement get_address_map!"
}
# Checks all current runs at given step for errors, outputs their log files in case.
# @param synthesis Checks synthesis runs if true, implementation runs otherwise.
......
......@@ -21,6 +21,7 @@ source -notrace $::env(TAPASCO_HOME)/platform/zynq/zynq.tcl
namespace eval platform {
namespace export create
namespace export max_masters
namespace export get_address_map
namespace export create_clock_port
namespace export createZynqPS
......@@ -33,6 +34,10 @@ namespace eval platform {
return [zynq::max_masters]
}
proc get_address_map {} {
return [zynq::get_address_map]
}
proc create {} {
return [zynq::create]
}
......
......@@ -27,6 +27,7 @@ namespace eval platform {
namespace eval zynq {
namespace export create
namespace export max_masters
namespace export get_address_map
# check if TAPASCO_HOME env var is set
if {![info exists ::env(TAPASCO_HOME)]} {
......@@ -42,6 +43,29 @@ namespace eval platform {
return [list 64 64]
}
proc get_address_map {} {
set ret [list]
set pes [lsort [arch::get_processing_elements]]
set offset 0x43C00000
foreach pe $pes {
set usrs [lsort [get_bd_addr_segs $pe/* -filter { USAGE != memory }]]
for {set i 0} {$i < [llength $usrs]} {incr i; incr offset 0x10000} {
set seg [lindex $usrs $i]
set intf [get_bd_intf_pins -of_objects $seg]
set range [get_property RANGE $seg]
lappend ret "interface $intf [format "offset 0x%08x range 0x%08x" $offset $range] kind register"
}
set usrs [lsort [get_bd_addr_segs $pe/* -filter { USAGE == memory }]]
for {set i 0} {$i < [llength $usrs]} {incr i; incr offset 0x10000} {
set seg [lindex $usrs $i]
set intf [get_bd_intf_pins -of_objects $seg]
set range [get_property RANGE $seg]
lappend ret "interface $intf [format "offset 0x%08x range 0x%08x" $offset $range] kind memory"
}
}
return $ret
}
# Setup the clock network.
proc platform_connect_clock {ps} {
puts "Connecting clocks ..."
......@@ -396,13 +420,14 @@ namespace eval platform {
set gp0_out [tapasco::create_interconnect_tree "gp0_out" 2 false]
connect_bd_intf_net [get_bd_intf_pins "$ss_host/M_AXI_GP0"] [get_bd_intf_pins "$gp0_out/S000_AXI"]
connect_bd_intf_net [get_bd_intf_pins "$gp0_out/M000_AXI"] [get_bd_intf_pins "/uArch/S_AXI"]
connect_bd_intf_net [get_bd_intf_pins "$gp0_out/M001_AXI"] [get_bd_intf_pins "$tapasco_status/S00_AXI"]
connect_bd_intf_net [get_bd_intf_pins "$gp0_out/M001_AXI"] [get_bd_intf_pins "$tapasco_status/s_axi"]
connect_bd_net [get_bd_pins "$ss_cnr/host_aclk"] \
[get_bd_pins -filter {TYPE == clk && DIR == I} -of_objects $tapasco_status] \
[get_bd_pins -filter {TYPE == clk && DIR == I} -of_objects $gp0_out]
connect_bd_net [get_bd_pins "$ss_cnr/host_peripheral_aresetn"] \
[get_bd_pins -filter {TYPE == rst && DIR == I} -of_objects $tapasco_status] \
[get_bd_pins -filter {DIR == I && NAME =~ "*peripheral_aresetn"} -of_objects $gp0_out]
connect_bd_net [get_bd_pins -hier -of_objects $ss_cnr -filter {TYPE == rst && DIR == O && CONFIG.POLARITY == ACTIVE_HIGH && PATH =~ *host*peripheral* }] \
[get_bd_pins -filter {TYPE == rst && DIR == I} -of_objects $tapasco_status]
connect_bd_net [get_bd_pins "$ss_cnr/host_interconnect_aresetn"] \
[get_bd_pins -filter {DIR == I && NAME =~ "*interconnect_aresetn"} -of_objects $gp0_out]
......
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