Commit 90b74d7c authored by Jens Korinth's avatar Jens Korinth
Browse files

Make ILA debug feature generic for all platforms

* only get_debug_nets depends on the platform
* rest moved to platform/common/plugins
* supporting platforms implement wrapper plugin, see
  platform/zynq/plugins/debug.tcl
* fixed several other issues, e.g., clock selection
* closes #132 - no master compositions
parent cc8a2df2
Pipeline #325 passed with stage
in 3 minutes and 26 seconds
Debugging with TaPaSCo
======================
TaPaSCo designs don't have bugs, so no debugging facilities are required.
Right? Right. Well.
...
You're still reading? Goodness, why? ;-) Ok. so let's assume hypothetically that
your design would have a bug as a Gedankenexperiment. This document is about the
available facilities in TaPaSCo to hunt it down.
Table of Contents
-----------------
1. [Hunting hardware bugs](#hw-bugs)
a. [The debug feature](#debug-feature)
b. [Exercise an ILA](#use-ila)
2. [Hunting software bugs](#sw-bugs)
a. [tapasco-debug](#tapasco-debug)
b. [tapasco-benchmark](#tapasco-benchmark)
Hunting hardware bugs <a name="hw-bugs"/>
---------------------
To test the hardware interface of PE modules, use the peek and poke utilities in
[tapasco-debug](#tapasco-debug). It allows you to manually interact with the
registers of the module, peeking at the ISR and other registers, and even start
jobs.
If the bug does not concern the PE module interface, but something else in the
design, it is best to instantiate a (System) Integrated Logic Analyzer (ILA)
core in the design: ILA can record small traces in on-chip buffers, which can be
read out via Vivado to see actual signals in the running hardware. Recording can
be triggered by simple and complex conditions, making it feasible to debug on
real hardware. Since this is something that one needs to time and time again,
there's a TaPaSCo _feature_ that can automatically instantiate ILA cores in
compositions, which is shown in the next section.
The debug composition feature <a name="debug-feature"/>
-----------------------------
The `debug` feature can be added to any composition and can be configured in two
modes: `interfaces` mode and `nets` mode. The `interfaces` mode instantiates a
System ILA core during the generation of the Architecture. Its primary use is to
"listen" to transactions on AXI4 interfaces. To connect to an AXI4 interface,
you need to specify the (fully qualified) bus interface port and the
corresponding clock and reset pins. Example:
```
tapasco compose [precision_counter x 1] @ 100 Mhz --features 'Debug { interfaces: "{{/arch/target_ip_00_000/S00_AXI /arch/target_ip_00_000/s00_axi_aclk /arch/target_ip_00_000/s00_axi_aresetn }}" }'
```
This will attach a System ILA to the `S00_AXI` interface nets of the first
`precision_counter` instance.
The `nets` mode allows to connect to arbitrary nets (including nets within IP
cores which would not be visible in IP integrator). For this reason, it requires
two synthesis runs: First, the whole design without the ILA must be synthesized
into a netlist; then the ILA core is instantiated using a constraints file. This
requires a re-run of synthesis to take effect. Nets can be specified with
wildcards, patterns can be tested on a synthesized design using the Tcl console
in Vivado (use `get_bd_nets <pattern>` to test the pattern).
Example:
```
tapasco compose [precision_counter x 1] @ 100 Mhz --features 'Debug { nets: "{system_i/arch/target_ip_00_000/* system_i/arch/irq*}" }'
```
This would attach an ILA core to all nets attached to the first instance of the
first PE and all interrupt outputs of the entire Architecture.
Exercise an ILA <a name="use-ila"/>
---------------
You can connect to the generated ILAs by starting Vivado by opening the
`microarch.xpr` in the subdirectory of the composition (probably below `bd`).
Open "Hardware Manager", connect to the board and the ILAs should already be
visible. Consult the Xilinx user guides for more information on ILA debugging.
Hunting software bugs <a name="sw-bugs"/>
---------------------
Software is slightly easier to debug. First step should be to build all modules
and libraries, as well as the application, in _debug mode_. This can be achieved
by either
```
tapasco-build-libs --rebuild --mode debug
```
This will rebuild the TaPaSCo libraries with logging facilities enabled. Logging
is controlled via **environment variables** listed in the table below.
|:**Env Var** |:**Description** |
|-----------------------|------------------------------------------------------|
| `LIBTAPASCO_DEBUG` | Bitfield that enables/disables logging of subsystems |
| | in `libtapasco`, see below for details, -1 for all. |
| `LIBPLATFORM_DEBUG` | Bitfield that enables/disables logging of subsystems |
| | in `libplatform`, see below for details, -1 for all. |
| `LIBTAPASCO_LOGFILE` | Redirects logging output from `libtapasco` to the |
| | file specified here; can use absolute paths. |
| `LIBPLATFORM_LOGFILE` | Redirects logging output from `libplatform` to the |
| | file specified here; can use absolute paths. |
The `LIBTAPASCO_DEBUG` bitfield enables logging in specific subsystems. Current
implementation is defined in the `LIBTAPASCO_LOGLEVELS` macro in
[tapasco_logging.h](arch/common/include/tapasco_logging.h). For reference, the
following bits are defined as of the time of writing:
|:**Bit #**|:**Description** |
|---------:|-------------------------------------------------------------------|
| 0 | _Reserved_ |
| 1 | Initialization - startup messages |
| 2 | Device - interactions with the kernel module |
| 3 | Scheduler - TaPaSCo software scheduler |
| 4 | Interrupts |
| 5 | Memory |
| 6 | Function - hardware registers and enumeration |
| 7 | Status - TaPaSCo status core |
The `LIBPLATFORM_DEBUG` bitfield enables logging from specific `libplatform`
subsystems. Current implementation is defined in the `LIBPLATFORM_LOGLEVELS`
macro in
[platform_logging.h](platform/common/include/platform_logging.h). For reference,
the following bits are defined as of the time of writing:
|:**Bit #**|:**Description** |
|---------:|-------------------------------------------------------------------|
| 0 | _Reserved_ |
| 1 | Initialization - startup messages |
| 2 | Memory management |
| 3 | Memory allocator |
| 4 | Control Space interactions |
| 5 | Interrupts |
| 6 | DMA related |
As a safe bet, simply use `-1` to activate all logging subsystems. Note that the
logging system is designed to be as unintrusive as possible; string operations
are always costly and _will_ affect your runtime, but TaPaSCo attempts to
minimize the impact by moving logging to a separate thread and keeping buffers
that will only flush occasionally. So you shouldn't expect the log output to
appear immediately - by default, TaPaSCo installs a signal handler that will
catch a `KILL` signal and flush logs, so you can use that to if your application
is stuck.
The tapasco-debug tool
----------------------
There's a versatile debugging tool that comes with TaPaSCo and is automatically
build by `tapasco-build-libs` called `tapasco-debug`. It can be used to inspect
bitstreams, peek and poke registers and perform basic functionality tests. One
of its most common features is the _kernel map_, which displays the function ids
of PEs in all virtual slots in the currently loaded bitstream, as well as host,
design and memory clock frequencies.
The tapasco-benchmark tool
--------------------------
Another tool that can sometimes be useful is `tapasco-benchmark` (also
automatically build by `tapasco-build-libs`). It performs a basic performance
evaluation of the Platform and stores the results in a JSON file. This file can
in turn be used by the design space exploration. Each Platform comes with a
pre-computed results file, e.g., [pynq](platform/pynq.benchmark). However, to
calibrate the DSE it may be useful to update the benchmarks on your own system.
**WARNING**: Make sure you've built everything in _release_ mode before updating
the benchmark data!
`tapasco-benchmark` requires a bitstream with at least one PE for a kernel with
id 14 - the counter. Counters are simple: Arg#0 is the number of clock cycles to
wait before raising the interrupt - that's it. One example of such a kernel is
[precision_counter](common/ip/precision_counter_1.0). You can build and import
it like this:
```
cd $TAPASCO_HOME/common/ip && \
zip -r precision_counter.zip precision_counter_v1.0 && \
tapasco import $(TAPASCO_HOME)/common/ip/precision_counter.zip as 14
```
Alternative implementations are also available, e.g., [counter](kernel/counter),
which is an Vivado HLS implementation of the same functionality. It can be built
using:
```
tapasco hls counter
```
#
# Copyright (C) 2014 Jens Korinth, TU Darmstadt
#
# This file is part of Tapasco (TPC).
#
# Tapasco is free software: you can redistribute it and/or modify
# 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.
#
# Tapasco is distributed in the hope that it will be useful,
# 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
# along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
#
# @file debug.tcl
# @brief Plugin to add ILA cores to the design. GP0, HP0, HP2 and ACP are
# addded by default; other nets can be added as strings via feature.
# @author J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
#
namespace eval ::platform::debug {
namespace export debug_feature
proc get_debug_nets {} {
puts "Adding default signals to the ILA core ..."
set ret [list [get_nets -hier "*irq_out*"]]
set defsignals [list \
"_RDATA*" \
"_WDATA*" \
"_ARADDR*" \
"_AWADDR*" \
"_AWVALID" \
"_AWREADY" \
"_ARVALID" \
"_ARREADY" \
"_WVALID" \
"_WREADY" \
"_WSTRB*" \
"_RVALID" \
"_RREADY" \
"_ARBURST*" \
"_AWBURST*" \
"_ARLEN*" \
"_AWLEN*" \
"_WLAST" \
"_RLAST" \
]
foreach m [arch::get_masters] {
set name [get_property NAME $m]
foreach s $defsignals {
set net [get_nets -hier "*$name$s"]
puts " adding net $net for signal $s ..."
if {[llength $net] > 1} { lappend ret $net } { lappend ret [list $net] }
}
}
foreach m {"system_i/Host_M_AXI_GP0"} {
foreach s $defsignals {
set net [get_nets "$m$s"]
puts " adding net $net for signal $s ..."
if {[llength $net] > 1} { lappend ret $net} { lappend ret [list $net] }
}
}
puts " signal list: $ret"
return $ret
}
proc debug_feature {} {
if {[tapasco::is_feature_enabled "Debug"]} {
puts "Creating ILA debug core, will require re-run of synthesis."
# get config
set debug [tapasco::get_feature "Debug"]
puts " Debug = $debug"
# default values
set depth 4096
set stages 0
set use_defaults true
set nets {}
if {[dict exists $debug "depth"]} { set depth [dict get $debug "depth"] }
if {[dict exists $debug "stages"]} { set stages [dict get $debug "stages"] }
if {[dict exists $debug "use_defaults"]} { set use_defaults [dict get $debug "use_defaults"] }
if {[dict exists $debug "nets"]} { set nets [dict get $debug "nets"] }
set dnl {}
if {$use_defaults} { foreach n [get_debug_nets] { lappend dnl $n }}
if {[llength $nets] > 0} {
foreach n $nets {
set nnets [get_nets -hier $n]
puts " for '$n' found [llength $nnets] nets: $nnets"
foreach nn $nnets { lappend dnl [list $nn] }
}
}
# create ILA core
tapasco::ip::create_debug_core [system_i/clock_and_resets/design_clk] $dnl $depth $stages
reset_run synth
}
return {}
}
proc write_ltx {} {
global bitstreamname
if {[tapasco::is_feature_enabled "Debug"]} {
puts "Writing debug probes into file ${bitstreamname}.ltx ..."
write_debug_probes -force -verbose "${bitstreamname}.ltx"
}
return {}
}
}
tapasco::register_plugin "platform::vc709::debug::debug_feature" "post-synth"
tapasco::register_plugin "platform::vc709::debug::write_ltx" "post-impl"
......@@ -241,7 +241,17 @@ namespace eval platform {
puts " assuming internal connection, setting values as found in segment:"
set range [get_property RANGE $seg]
puts " range: $range"
if {$range eq ""} {
puts " found no range on segment $seg, skipping"
report_property $seg
continue
}
set offset [get_property OFFSET $seg]
if {$offset eq ""} {
puts " found no offset on segment $seg, skipping"
report_property $seg
continue
}
puts " offset: $offset"
set me [dict create "range" $range "offset" $offset "space" $space seg "$seg"]
} else {
......
#
# Copyright (C) 2014 Jens Korinth, TU Darmstadt
#
# This file is part of Tapasco (TPC).
#
# Tapasco is free software: you can redistribute it and/or modify
# 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.
#
# Tapasco is distributed in the hope that it will be useful,
# 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
# along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
#
# @file debug.tcl
# @brief Plugin to add ILA cores to the design. GP0, HP0, HP2 and ACP are
# addded by default; other nets can be added as strings via feature.
# @author J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
#
namespace eval ::platform::debug {
namespace export debug_feature
namespace export get_debug_nets
namespace export write_ltx
# This function should be overridden in Platform implementations with sensible
# default patterns for useful nets.
proc get_debug_nets {} {}
proc debug_feature {} {
if {[tapasco::is_feature_enabled "Debug"]} {
puts "Creating ILA debug core, will require re-run of synthesis."
# get config
set debug [tapasco::get_feature "Debug"]
puts " Debug = $debug"
# default values
set depth 4096
set stages 0
set use_defaults false
set nets {}
if {[dict exists $debug "depth"]} { set depth [dict get $debug "depth"] }
if {[dict exists $debug "stages"]} { set stages [dict get $debug "stages"] }
if {[dict exists $debug "use_defaults"]} { set use_defaults [dict get $debug "use_defaults"] }
if {[dict exists $debug "nets"]} { set nets [dict get $debug "nets"] }
set dnl {}
if {$use_defaults} { foreach n [get_debug_nets] { lappend dnl $n }}
if {[llength $nets] > 0} {
foreach n $nets {
set nnets [get_nets $n]
puts " for '$n' found [llength $nnets] nets: $nnets"
foreach nn $nnets { lappend dnl [list $nn] }
}
}
# create ILA core
set clk [lindex [get_nets system_i/* -filter {NAME =~ *clocks_and_resets_*clk}] 0]
puts " clk = $clk"
tapasco::create_debug_core $clk $dnl $depth $stages
reset_run synth_1
}
return {}
}
proc write_ltx {} {
global bitstreamname
if {[tapasco::is_feature_enabled "Debug"]} {
puts "Writing debug probes into file ${bitstreamname}.ltx ..."
write_debug_probes -force -verbose "${bitstreamname}.ltx"
}
return {}
}
}
tapasco::register_plugin "platform::debug::debug_feature" "post-synth"
tapasco::register_plugin "platform::debug::write_ltx" "post-impl"
......@@ -21,92 +21,12 @@
# addded by default; other nets can be added as strings via feature.
# @author J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
#
namespace eval ::platform::vc709::debug {
namespace export debug_feature
namespace eval ::platform::debug {
source -notrace "$::env(TAPASCO_HOME)/platform/common/plugins/debug.tcl"
# override empty debug nets
proc get_debug_nets {} {
puts "Adding default signals to the ILA core ..."
set ret [list [get_nets -hier "*irq_out*"]]
set defsignals [list \
"_RDATA*" \
"_WDATA*" \
"_ARADDR*" \
"_AWADDR*" \
"_AWVALID" \
"_AWREADY" \
"_ARVALID" \
"_ARREADY" \
"_WVALID" \
"_WREADY" \
"_WSTRB*" \
"_RVALID" \
"_RREADY" \
"_ARBURST*" \
"_AWBURST*" \
"_ARLEN*" \
"_AWLEN*" \
"_WLAST" \
"_RLAST" \
]
foreach m [arch::get_masters] {
set name [get_property NAME $m]
foreach s $defsignals {
set net [get_nets -hier "*$name$s"]
puts " adding net $net for signal $s ..."
if {[llength $net] > 1} { lappend ret $net } { lappend ret [list $net] }
}
}
foreach m {"system_i/Host_M_AXI_GP0"} {
foreach s $defsignals {
set net [get_nets "$m$s"]
puts " adding net $net for signal $s ..."
if {[llength $net] > 1} { lappend ret $net} { lappend ret [list $net] }
}
}
puts " signal list: $ret"
return $ret
}
proc debug_feature {} {
if {[tapasco::is_feature_enabled "Debug"]} {
puts "Creating ILA debug core, will require re-run of synthesis."
# get config
set debug [tapasco::get_feature "Debug"]
puts " Debug = $debug"
# default values
set depth 4096
set stages 0
set use_defaults true
set nets {}
if {[dict exists $debug "depth"]} { set depth [dict get $debug "depth"] }
if {[dict exists $debug "stages"]} { set stages [dict get $debug "stages"] }
if {[dict exists $debug "use_defaults"]} { set use_defaults [dict get $debug "use_defaults"] }
if {[dict exists $debug "nets"]} { set nets [dict get $debug "nets"] }
set dnl {}
if {$use_defaults} { foreach n [get_debug_nets] { lappend dnl $n }}
if {[llength $nets] > 0} {
foreach n $nets {
set nnets [get_nets -hier $n]
puts " for '$n' found [llength $nnets] nets: $nnets"
foreach nn $nnets { lappend dnl [list $nn] }
}
}
# create ILA core
tapasco::ip::create_debug_core [system_i/clock_and_resets/design_clk] $dnl $depth $stages
reset_run synth
}
return {}
}
proc write_ltx {} {
global bitstreamname
if {[tapasco::is_feature_enabled "Debug"]} {
puts "Writing debug probes into file ${bitstreamname}.ltx ..."
write_debug_probes -force -verbose "${bitstreamname}.ltx"
}
return {}
# TODO define defaults for VC709
return [list ]
}
}
tapasco::register_plugin "platform::vc709::debug::debug_feature" "post-synth"
tapasco::register_plugin "platform::vc709::debug::write_ltx" "post-impl"
......@@ -21,9 +21,10 @@
# addded by default; other nets can be added as strings via feature.
# @author J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
#
namespace eval ::platform::zynq::debug {
namespace export debug_feature
namespace eval ::platform::debug {
source -notrace "$::env(TAPASCO_HOME)/platform/common/plugins/debug.tcl"
# override empty debug nets
proc get_debug_nets {} {
puts "Adding default signals to the ILA core ..."
set ret [list [get_nets -hier "*irq_out*"]]
......@@ -56,7 +57,7 @@ namespace eval ::platform::zynq::debug {
if {[llength $net] > 1} { lappend ret $net } { lappend ret [list $net] }
}
}
foreach m {"system_i/Host_M_AXI_GP0"} {
foreach m {"system_i/host/Host_M_AXI_GP0"} {
foreach s $defsignals {
set net [get_nets "$m$s"]
puts " adding net $net for signal $s ..."
......@@ -66,47 +67,4 @@ namespace eval ::platform::zynq::debug {
puts " signal list: $ret"
return $ret
}
proc debug_feature {} {
if {[tapasco::is_feature_enabled "Debug"]} {
puts "Creating ILA debug core, will require re-run of synthesis."
# get config
set debug [tapasco::get_feature "Debug"]
puts " Debug = $debug"
# default values
set depth 4096
set stages 0
set use_defaults true
set nets {}
if {[dict exists $debug "depth"]} { set depth [dict get $debug "depth"] }
if {[dict exists $debug "stages"]} { set stages [dict get $debug "stages"] }
if {[dict exists $debug "use_defaults"]} { set use_defaults [dict get $debug "use_defaults"] }
if {[dict exists $debug "nets"]} { set nets [dict get $debug "nets"] }
set dnl {}
if {$use_defaults} { foreach n [get_debug_nets] { lappend dnl $n }}
if {[llength $nets] > 0} {
foreach n $nets {
set nnets [get_nets -hier $n]
puts " for '$n' found [llength $nnets] nets: $nnets"
foreach nn $nnets { lappend dnl [list $nn] }
}
}
# create ILA core
tapasco::create_debug_core [get_nets system_i/Host_fclk0_aclk] $dnl $depth $stages
reset_run synth
}
return {}
}
proc write_ltx {} {
global bitstreamname
if {[tapasco::is_feature_enabled "Debug"]} {
puts "Writing debug probes into file ${bitstreamname}.ltx ..."
write_debug_probes -force -verbose "${bitstreamname}.ltx"
}
return {}
}
}
tapasco::register_plugin "platform::zynq::debug::debug_feature" "post-synth"
tapasco::register_plugin "platform::zynq::debug::write_ltx" "post-impl"
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