ipxact-tools 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/config/detector.yml +63 -0
- data/lib/ipxact_tools.rb +287 -0
- data/lib/ipxact_tools/graph_pathfinder.rb +191 -0
- data/lib/ipxact_tools/interrupt_line_detector.rb +98 -0
- data/spec/integration/integration_spec.rb +254 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/pv_initiator_port.h +135 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/pv_initiator_port.tpp +247 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/pv_initiator_port_base.h +189 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/pv_router.h +205 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/pv_router.tpp +278 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/pv_slave_base.h +115 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/pv_slave_base.tpp +126 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/pv_target_port.h +168 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/pv_target_port_base.h +133 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/pv_tlm_if.h +280 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/PV/user_types.h +38 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM1/Leon2Platform/Leon2Platform.xml +77 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM1/Leon2Platform/design_Leon2Platform.xml +156 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM1/Leon2Platform/tlmsrc/Leon2Platform.h +149 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM1/apbSubSystem/apbSubSystem.xml +406 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM1/apbSubSystem/design_apbSubSystem.xml +135 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM1/apbSubSystem/tlmsrc/apbSubSystem.h +133 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM2/Leon2Platform/Leon2Platform.xml +77 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM2/Leon2Platform/design_Leon2Platform.xml +157 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM2/Leon2Platform/tlmsrc/Leon2Platform.h +150 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM2/apbSubSystem/apbSubSystem.xml +422 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM2/apbSubSystem/designConfig_apbSubSystem.xml +79 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM2/apbSubSystem/design_apbSubSystem.xml +184 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM2/apbSubSystem/tlmsrc/apbSubSystem.h +164 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM3/Leon2Platform/Leon2Platform.xml +105 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM3/Leon2Platform/design_Leon2Platform.xml +157 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM3/Leon2Platform/tlmsrc/Leon2Platform.h +152 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM3/apbSubSystem/apbSubSystem.xml +429 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM3/apbSubSystem/designConfig_apbSubSystem.xml +64 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM3/apbSubSystem/design_apbSubSystem.xml +150 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/SystemTLM3/apbSubSystem/tlmsrc/apbSubSystem.h +167 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/ahbbus/1.4/ahbbus22.xml +236 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/ahbbus/1.4/tlmsrc/ahbbus.h +52 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/ahbram/1.4/ahbram.xml +184 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/ahbram/1.4/tlmsrc/ahbram.cc +93 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/ahbram/1.4/tlmsrc/ahbram.h +77 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/apbbus/1.4/apbbus8.xml +544 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/apbbus/1.4/tlmsrc/apbbus.h +55 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/apbmst/1.4/apbmst.xml +209 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/apbmst/1.4/tlmsrc/apbmst.h +53 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/apbram/1.0/apbram.xml +395 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/apbram/1.0/hdlsrc/apbram.cc +89 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/apbram/1.0/hdlsrc/apbram.h +69 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/apbram/1.0/hdlsrc/apbram.v +82 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/apbram/1.0/hdlsrc/apbram.vhd +132 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/cgu/1.4/cgu.xml +686 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/cgu/1.4/tlmsrc/cgu.cc +121 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/cgu/1.4/tlmsrc/cgu.h +81 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/cgu/1.5/cgu.xml +707 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/cgu/1.5/tlmsrc/cgu.cc +121 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/cgu/1.5/tlmsrc/cgu.h +81 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/dma/1.4/dma.xml +272 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/dma/1.4/tlmsrc/dma.cc +230 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/dma/1.4/tlmsrc/dma.h +116 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/irqctrl/1.4/irqctrl.xml +530 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/irqctrl/1.4/tlmsrc/irqctrl.cc +211 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/irqctrl/1.4/tlmsrc/irqctrl.h +108 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/processor/1.4/processor.xml +423 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/processor/1.4/tlmsrc/processor.cc +331 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/processor/1.4/tlmsrc/processor.h +121 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/pv2apb/1.0/pv2apb.xml +240 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/pv2apb/1.0/tlmsrc/pv2apb.cc +153 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/pv2apb/1.0/tlmsrc/pv2apb.h +77 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/pv2tac/1.0/pv2tac.xml +154 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/pv2tac/1.0/tlmsrc/pv2tac.cc +114 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/pv2tac/1.0/tlmsrc/pv2tac.h +67 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/rgu/1.4/rgu.xml +766 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/rgu/1.4/tlmsrc/rgu.cc +126 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/rgu/1.4/tlmsrc/rgu.h +105 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/scmlAdaptor/1.0/scmlAdaptor.xml +154 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/scmlAdaptor/1.0/tlmsrc/scmladaptor.cc +100 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/scmlAdaptor/1.0/tlmsrc/scmladaptor.h +62 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/serial_device/1.0/serial_device.xml +151 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/serial_device/1.0/tlmsrc/serial_device.h +85 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/timers/1.4/timers.xml +460 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/timers/1.4/tlmsrc/timers.cc +201 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/timers/1.4/tlmsrc/timers.h +102 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_scml/1.0/tlmsrc/inc/uart.h +152 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_scml/1.0/tlmsrc/inc/uart_fifo.h +113 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_scml/1.0/tlmsrc/inc/uart_memory_map.h +96 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_scml/1.0/tlmsrc/inc/uart_types.h +60 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_scml/1.0/tlmsrc/src/uart.cc +125 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_scml/1.0/tlmsrc/src/uart_interrupt_handler.cc +136 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_scml/1.0/tlmsrc/src/uart_register_bank.cc +129 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_scml/1.0/tlmsrc/src/uart_serial_tx_rx.cc +145 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_scml/1.0/uart_scml.xml +372 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_tac/1.0/tlmsrc/include/Leon2_uart.h +216 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_tac/1.0/tlmsrc/include/def_Leon2_uart.h +34 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_tac/1.0/tlmsrc/include/tlm_field.h +72 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_tac/1.0/tlmsrc/include/tlm_register.h +129 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_tac/1.0/tlmsrc/include/tlmreg_Leon2_uart.h +133 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_tac/1.0/tlmsrc/src/Leon2_uart.cc +316 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_tac/1.0/tlmsrc/src/tlmreg_Leon2_uart.cc +197 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_tac/1.0/tlmsrc/src/user_specific_Leon2_uart.cc +146 -0
- data/spec/test_data/spiritconsortium.org/Leon2TLM/uart_tac/1.0/uart_tac.xml +222 -0
- data/spec/unit/graph_pathfinder_spec.rb +128 -0
- metadata +272 -0
data/config/detector.yml
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
---
|
2
|
+
:bus_name_regex:
|
3
|
+
-
|
4
|
+
- /INT_/i
|
5
|
+
- 0.7
|
6
|
+
-
|
7
|
+
- /INT/i
|
8
|
+
- 0.5
|
9
|
+
-
|
10
|
+
- /INTERRUPT/i
|
11
|
+
- 0.8
|
12
|
+
-
|
13
|
+
- /IRQ/i
|
14
|
+
- 0.8
|
15
|
+
-
|
16
|
+
- /FIQ/i
|
17
|
+
- 0.4
|
18
|
+
-
|
19
|
+
- /IRL_/i
|
20
|
+
- 0.4
|
21
|
+
-
|
22
|
+
- /IRL/i
|
23
|
+
- 0.3
|
24
|
+
|
25
|
+
:bus_library_regex:
|
26
|
+
-
|
27
|
+
- /INT_/i
|
28
|
+
- 0.7
|
29
|
+
-
|
30
|
+
- /INT/i
|
31
|
+
- 0.5
|
32
|
+
-
|
33
|
+
- /INTERRUPT/i
|
34
|
+
- 0.8
|
35
|
+
-
|
36
|
+
- /IRQ/i
|
37
|
+
- 0.8
|
38
|
+
-
|
39
|
+
- /FIQ/i
|
40
|
+
- 0.4
|
41
|
+
-
|
42
|
+
- /IRL_/i
|
43
|
+
- 0.4
|
44
|
+
-
|
45
|
+
- /IRL/i
|
46
|
+
- 0.3
|
47
|
+
|
48
|
+
:transition_matrix:
|
49
|
+
:not_int:
|
50
|
+
:not_int: 0.958904109589041
|
51
|
+
:int: 0.0410958904109589
|
52
|
+
:int:
|
53
|
+
:not_int: 0.5333333333333333
|
54
|
+
:int: 0.4666666666666667
|
55
|
+
|
56
|
+
:initial_state:
|
57
|
+
:not_int: 0.5042735042735043
|
58
|
+
:int: 0.49572649572649574
|
59
|
+
|
60
|
+
:states:
|
61
|
+
- :not_int
|
62
|
+
- :int
|
63
|
+
|
data/lib/ipxact_tools.rb
ADDED
@@ -0,0 +1,287 @@
|
|
1
|
+
# Copyright (C) 2011 TIMA Laboratory
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'ipxact'
|
18
|
+
|
19
|
+
# The {IPXACT::Tools} module provides analysis tools for extracting or
|
20
|
+
# deducing relevant data from IP-Xact platforms. Three functionality are
|
21
|
+
# currently handled by the module:
|
22
|
+
# - It computes the shortest path between two component instances of a
|
23
|
+
# valid {IPXACT::Platform} instance.
|
24
|
+
# - It validates the data path by verifying that it does not contain any
|
25
|
+
# interrupt line, and emits a warning if several CPUs may contend for
|
26
|
+
# an access to the target component.
|
27
|
+
# - It deduces the target component's base address from the data path
|
28
|
+
# (handling all address remaps and IP-Xact variables).
|
29
|
+
#
|
30
|
+
# Because the IP-Xact standard does not include (yet) any formal way to
|
31
|
+
# distinguish interrupt lines from other communication channels, we try
|
32
|
+
# to identify those interrupt lines based on the different terms used
|
33
|
+
# for describing them. As a result, if the platform uses standard
|
34
|
+
# conventions for naming the interrupt line elements (esp. ports), such
|
35
|
+
# as 'INT_xxx', 'INTERRUPT_xxx', 'IRQ', 'IRL' etc., there is a high
|
36
|
+
# probability that our algorithm will be able to detect it.
|
37
|
+
#
|
38
|
+
# *NOTE* Unfortunately, there is currently no way to select an alternate
|
39
|
+
# data path if the one provided by the pathfinding algorithm happens to
|
40
|
+
# be invalid. If this is a strong requirement, drop me a mail (guillaume
|
41
|
+
# dot godetbar at gmail dot com) and I will
|
42
|
+
# try to include it ASAP.
|
43
|
+
#
|
44
|
+
# @author Guillaume Godet-Bar
|
45
|
+
#
|
46
|
+
module IPXACT
|
47
|
+
module Tools
|
48
|
+
|
49
|
+
# A simple structure for handling diagnosis messages.
|
50
|
+
#
|
51
|
+
class ValidationMessage < Struct.new(:reason, :cause_element)
|
52
|
+
def initialize(hash)
|
53
|
+
entry = hash.entries[0]
|
54
|
+
self.reason = entry[0]
|
55
|
+
self.cause_element = entry[1]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Structure for handling validation errors.
|
60
|
+
#
|
61
|
+
class ValidationError < ValidationMessage ; end
|
62
|
+
|
63
|
+
# Structure for handling warning messages.
|
64
|
+
#
|
65
|
+
class ValidationWarning < ValidationMessage ; end
|
66
|
+
|
67
|
+
# Commodity class for handling errors and warnings produced by
|
68
|
+
# the {#validate_data_path method}.
|
69
|
+
#
|
70
|
+
class Diagnosis < Struct.new(:errors, :warnings)
|
71
|
+
def initialize
|
72
|
+
super
|
73
|
+
self.errors = []
|
74
|
+
self.warnings = []
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Validates the given data path. In particular, the method verifies
|
79
|
+
# that no interrupt line is present in the data path.
|
80
|
+
#
|
81
|
+
# @param [IPXACT::Platform] platform The reference platform
|
82
|
+
# @param [Array] data_path The data path to validate
|
83
|
+
#
|
84
|
+
# @return [Array<IPXACT::Tools::ValidationMessage>] a list of errors
|
85
|
+
# of errors and warnings, or an empty list if the data path is
|
86
|
+
# valid.
|
87
|
+
#
|
88
|
+
def self.validate_data_path(platform, data_path)
|
89
|
+
diagnosis = Diagnosis.new
|
90
|
+
path_ports = data_path.collect do |el|
|
91
|
+
platform.rec_get_subcomponent(el[0][:component_instance]).ports.select{|p|
|
92
|
+
p[:name] == el[0][:port_name]
|
93
|
+
}.first
|
94
|
+
end
|
95
|
+
|
96
|
+
if path_ports.first[:type] == :master
|
97
|
+
|
98
|
+
# Interrupt line detection
|
99
|
+
detector = IPXACT::Tools::InterruptLineDetector.new
|
100
|
+
stack = detector.viterbi(path_ports)
|
101
|
+
rendered_stack = detector.render_seq(stack)
|
102
|
+
|
103
|
+
interrupt_line_index = rendered_stack.find_index{|el| el == :int}
|
104
|
+
unless interrupt_line_index.nil?
|
105
|
+
diagnosis.errors << ValidationError.new(:probable_interrupt_line => data_path[interrupt_line_index])
|
106
|
+
end
|
107
|
+
|
108
|
+
# Concurrent access check
|
109
|
+
target_component_name = data_path.last[1][:component_instance]
|
110
|
+
target_component = platform.rec_get_subcomponent(target_component_name)
|
111
|
+
components = platform.rec_get_subcomponents.reject{|el| el == target_component}
|
112
|
+
|
113
|
+
cpu_instances = []
|
114
|
+
components.each do |instance_name, component|
|
115
|
+
if component.has_cpu?
|
116
|
+
cpu_instances << instance_name
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
if cpu_instances.size >= 2
|
121
|
+
diagnosis.warnings << ValidationWarning.new(:cpu_nb_causes_concurrent_access_risk => cpu_instances)
|
122
|
+
end
|
123
|
+
else
|
124
|
+
diagnosis.errors << ValidationError.new(:first_element_not_a_master => data_path.first)
|
125
|
+
end
|
126
|
+
|
127
|
+
return diagnosis
|
128
|
+
end
|
129
|
+
|
130
|
+
# Calculates an optimal path in the subcomponent graph between
|
131
|
+
# +subcomponent_start+ and +subcomponent_end+. This computation takes into
|
132
|
+
# account hierarchical subcomponents, and identifies optimal sub-paths in
|
133
|
+
# these elements as well, using hierconnections. This method is in fact the
|
134
|
+
# facade for the {IPXACT::GraphPathFinder} class. It handles the creation of
|
135
|
+
# the connection Hash, and translates the result in a IPXACT-specific format.
|
136
|
+
#
|
137
|
+
# @param [IPXACT::Platform] platform The reference platform
|
138
|
+
# @param [String] subcomponent_start The name of the component instance that
|
139
|
+
# should be the source of the data path.
|
140
|
+
# @param [String] subcomponent_end The name of the component instance that
|
141
|
+
# should be the target of the data path.
|
142
|
+
#
|
143
|
+
# @return [Array<Array<Hash>>] an Array of Arrays of Hashes that summarizes
|
144
|
+
# the component instances and the ports on the data path. Note that this
|
145
|
+
# data path may be used for calculating the target component's base
|
146
|
+
# address. The Arrays of Hashes are composed of 2 elements, the first
|
147
|
+
# element being the source of a connection step and the second element
|
148
|
+
# being the target of the connection step. Each element is a Hash, built as
|
149
|
+
# follows:
|
150
|
+
# +:component_instance+ the String name of the component instance that is
|
151
|
+
# traversed by the data path;
|
152
|
+
# +:port_name+ the String name of the port of the component instance that
|
153
|
+
# is being traversed.
|
154
|
+
# @raise several exceptions. Please refer to {IPXACT::GraphPathFinder} class
|
155
|
+
# for more details.
|
156
|
+
#
|
157
|
+
def self.resolve_data_path(platform, subcomponent_start, subcomponent_end)
|
158
|
+
nodes, connections = prepare_pathfinder_connections(platform)
|
159
|
+
|
160
|
+
pathfinder_connections = connections.collect do |connection|
|
161
|
+
{
|
162
|
+
:link => [nodes.index(connection[0][:component_instance]),
|
163
|
+
nodes.index(connection[1][:component_instance])],
|
164
|
+
:ports => [connection[0][:port_name], connection[1][:port_name]]
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
pathfinder_interconnect = {:nodes => nodes, :connections => pathfinder_connections}
|
169
|
+
pathfinder = IPXACT::Tools::GraphPathFinder.new(pathfinder_interconnect)
|
170
|
+
finder_result = pathfinder.resolve(subcomponent_start, subcomponent_end)
|
171
|
+
|
172
|
+
formatted_result = finder_result.collect do |res|
|
173
|
+
pathfinder_interconnect[:connections][res]
|
174
|
+
end.collect do |connection|
|
175
|
+
[
|
176
|
+
{
|
177
|
+
:component_instance => pathfinder_interconnect[:nodes][connection[:link][0]],
|
178
|
+
:port_name => connection[:ports][0]
|
179
|
+
},
|
180
|
+
{
|
181
|
+
:component_instance => pathfinder_interconnect[:nodes][connection[:link][1]],
|
182
|
+
:port_name => connection[:ports][1]
|
183
|
+
}
|
184
|
+
]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
# Computes a the base address of the last component instance mentionned in
|
190
|
+
# the +data_path+. The latter should be calculated using the
|
191
|
+
# {#resolve_data_path} method. Additionally, this method takes into account
|
192
|
+
# all subcomponents from the +data_path+, and basically adds up mirrored slave
|
193
|
+
# ports' remap addresses.
|
194
|
+
#
|
195
|
+
# @param [IPXACT::Platform] platform The reference platform
|
196
|
+
# @param [Arrray] data_path the data path from which the base address should be
|
197
|
+
# calculated. Please refer to the documentation of
|
198
|
+
# {#resolve_data_path} for more details on the structure of
|
199
|
+
# this argument.
|
200
|
+
# @param [Integer] initial_value the value that should be added to the base
|
201
|
+
# address computed by the method.
|
202
|
+
#
|
203
|
+
# Returns the base address of the target component, as an Integer.
|
204
|
+
#
|
205
|
+
def self.resolve_base_address(platform, data_path, initial_value = 0)
|
206
|
+
base_address = initial_value
|
207
|
+
data_path.each do |connection|
|
208
|
+
connection.each do |connection_step|
|
209
|
+
subcomponent = platform.rec_get_subcomponent(connection_step[:component_instance])
|
210
|
+
port = subcomponent.ports \
|
211
|
+
.select{|port| port[:name] == connection_step[:port_name]} \
|
212
|
+
.first
|
213
|
+
if port[:type] == :mirrored_slave && !port[:port_data].nil?
|
214
|
+
base_address += port[:port_data][:remap][:address].to_i(16)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
base_address
|
219
|
+
end
|
220
|
+
|
221
|
+
private
|
222
|
+
|
223
|
+
# Executes the actual replacement of an hierarchical component's outside port
|
224
|
+
# with the attached subcomponent port.
|
225
|
+
#
|
226
|
+
# @param [IPXACT::Platform] platform The reference platform
|
227
|
+
# @param [Hash] connector The connector on which the
|
228
|
+
# replacement should be performed.
|
229
|
+
# @return [Hash] the flattened connector, i.e., a Hash built as follows:
|
230
|
+
# +:component_instance+ the String name of the component instance that is
|
231
|
+
# traversed by the data path;
|
232
|
+
# +:port_name+ the String name of the port of the component
|
233
|
+
# instance that is being traversed.
|
234
|
+
#
|
235
|
+
def self.reformat_connector(platform, connector)
|
236
|
+
instance = platform.subcomponents[connector[:component_instance]]
|
237
|
+
if instance.subcomponents.empty?
|
238
|
+
new_connector = connector
|
239
|
+
else
|
240
|
+
port = connector[:port_name]
|
241
|
+
new_connector = instance.hierconnections[port]
|
242
|
+
end
|
243
|
+
new_connector
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
# Prepares the connection structure for the {IPXACT::GraphPathFinder} class.
|
248
|
+
# Traverses the hierarchy of subcomponents in order to create a flattened
|
249
|
+
# representation of the subcomponents graph. In particular, the method
|
250
|
+
# considers hierconnections, and replaces the wrapper's interface port with
|
251
|
+
# the subcomponent's port that it is attached to.
|
252
|
+
#
|
253
|
+
# @param [IPXACT::Component] component root component from which to
|
254
|
+
# to start the formatting.
|
255
|
+
#
|
256
|
+
# @return [Array<Array,Array<Hash>>] an Array of 2 elements, composed of a
|
257
|
+
# list of nodes and a list of connections. Please refer to the
|
258
|
+
# {IPXACT::Tools::GraphPathFinder#initialize} method for more details on
|
259
|
+
# the structure of these elements (both Hashes).
|
260
|
+
#
|
261
|
+
def self.prepare_pathfinder_connections(component)
|
262
|
+
rec_prepare_pathfinder_connections(component, [], [])
|
263
|
+
end
|
264
|
+
|
265
|
+
# Recursive function for {#prepare_pathfinder_connections}.
|
266
|
+
#
|
267
|
+
def self.rec_prepare_pathfinder_connections(component, current_nodes, current_interconnections)
|
268
|
+
if component.subcomponents.empty?
|
269
|
+
[current_nodes << component.instance_name, current_interconnections]
|
270
|
+
else
|
271
|
+
component.subcomponents.each do |sub_name, subcomponent|
|
272
|
+
formatted_interconnections = component.interconnections.values.collect do |interconnect|
|
273
|
+
[reformat_connector(component, interconnect[0]), reformat_connector(component, interconnect[1])]
|
274
|
+
end
|
275
|
+
current_nodes, current_interconnections = \
|
276
|
+
rec_prepare_pathfinder_connections(subcomponent, current_nodes,
|
277
|
+
current_interconnections +
|
278
|
+
formatted_interconnections)
|
279
|
+
end
|
280
|
+
[current_nodes, current_interconnections]
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
require File.join File.dirname(__FILE__), 'ipxact_tools/graph_pathfinder'
|
287
|
+
require File.join File.dirname(__FILE__), 'ipxact_tools/interrupt_line_detector'
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# Copyright (C) 2010 TIMA Laboratory
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
#
|
16
|
+
|
17
|
+
# {IPXACT::Tools::GraphPathFinder} instances handle graph-based
|
18
|
+
# pathfinding requests within a given IPXACT architecture. It is
|
19
|
+
# responsible for translating the IPXACT architecture into a list of
|
20
|
+
# connections and transition weights, which may then be queried for
|
21
|
+
# optimal paths between two nodes from the connection graph. As such,
|
22
|
+
# even though the path finder is pretty generic, it should not be used
|
23
|
+
# directly for IPXACT-related work.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# @pathfinder = IPXACT::Tools::GraphPathFinder.new({
|
27
|
+
# :nodes => ['component_a', 'component_b', 'component_c']
|
28
|
+
# :connections =>
|
29
|
+
# [
|
30
|
+
# {:link => [0,1]},
|
31
|
+
# {:link => [1,2], :cost => 2},
|
32
|
+
# {:link => [2,0]}
|
33
|
+
# ]
|
34
|
+
# })
|
35
|
+
# @data_path = @pathfinder.resolve('component_a', 'component_b')
|
36
|
+
#
|
37
|
+
# @author Guillaume Godet-Bar
|
38
|
+
#
|
39
|
+
class IPXACT::Tools::GraphPathFinder
|
40
|
+
|
41
|
+
# The initializer is essentially used for passing a structured connection
|
42
|
+
# Hash.
|
43
|
+
#
|
44
|
+
# @param [Hash] hash the connection Hash, which should be structured as follows:
|
45
|
+
#
|
46
|
+
# :nodes - An Array of node names (String).
|
47
|
+
#
|
48
|
+
# :connections - An Array of connection elements, which are defined as
|
49
|
+
# :link - A 2-element Array, which describes an oriented
|
50
|
+
# connection between two nodes. The nodes are
|
51
|
+
# expressed using their index in the :nodes
|
52
|
+
# Array.
|
53
|
+
# :cost - A positive Integer expressing the cost of
|
54
|
+
# the transition (in the sense of a
|
55
|
+
# Branch-And-Bound algorithm) (optional,
|
56
|
+
# default: 1).
|
57
|
+
#
|
58
|
+
def initialize(hash)
|
59
|
+
@hash = hash
|
60
|
+
end
|
61
|
+
|
62
|
+
# Searches an optimal path between the start and _end elements, which
|
63
|
+
# should be defined in the Hash structured used for instanciating the
|
64
|
+
# {IPXACT::GraphPathFinder} instance. Optimal should be understood as "the
|
65
|
+
# cheapest path given the cost of each connection". Additionally, validates
|
66
|
+
# the latter Hash structure before executing the search algorithm.
|
67
|
+
#
|
68
|
+
# @param [String] start a String that corresponds to an element that was
|
69
|
+
# registered when the pathfinder was created. The latter will be used
|
70
|
+
# as the start node by the pathfinder algorithm.
|
71
|
+
# @param [String] _end a String that corresponds to an element that was
|
72
|
+
# registered when the pathfinder was created. The latter will be used
|
73
|
+
# as the target node by the pathfinder algorithm.
|
74
|
+
#
|
75
|
+
# @return [Array<Integer>] an Array of Integers corresponding to connection
|
76
|
+
# indices for the optimal data path between the +start+ and +_end+
|
77
|
+
# elements, or an empty Array if the +start+ and +_end+ elements are
|
78
|
+
# identical.
|
79
|
+
#
|
80
|
+
# @raise an exception if the Hash structure for the connections is empty.
|
81
|
+
# @raise an exception unless all the node names are unique.
|
82
|
+
# @raise an exception if the start or end element does not exist.
|
83
|
+
# @raise an exception if no optimal data path could be found.
|
84
|
+
#
|
85
|
+
def resolve(start, _end)
|
86
|
+
raise "Empty connection table!" if @hash.empty?
|
87
|
+
return [] if start == _end
|
88
|
+
raise "Duplicate connection node found!" if @hash[:nodes].uniq.size != @hash[:nodes].size
|
89
|
+
raise "The source or target node does not exist!" unless @hash[:nodes].include?(start) && @hash[:nodes].include?(_end)
|
90
|
+
|
91
|
+
@interconnect_table = (0..@hash[:nodes].size - 1).collect do |i|
|
92
|
+
(0..@hash[:nodes].size - 1).collect do |j|
|
93
|
+
result_hash = {
|
94
|
+
:connection =>
|
95
|
+
@hash[:connections].any?{|connection| connection[:link] == [j,i]},
|
96
|
+
:total => Float::INFINITY,
|
97
|
+
:previous => nil
|
98
|
+
}
|
99
|
+
if @hash[:connections].any?{|connection| connection[:link] == [j,i]}
|
100
|
+
result = @hash[:connections].select{|connection| connection[:link] == [j,i]}.first[:cost] || 1
|
101
|
+
result_hash.merge!({:cost => result})
|
102
|
+
end
|
103
|
+
|
104
|
+
result_hash
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
start_index = @hash[:nodes].index(start)
|
109
|
+
end_index = @hash[:nodes].index(_end)
|
110
|
+
@interconnect_table[start_index][start_index][:total] = 0
|
111
|
+
|
112
|
+
|
113
|
+
rec_resolve([start_index, start_index], end_index, 0)
|
114
|
+
|
115
|
+
last_writer_index = nil
|
116
|
+
@interconnect_table[end_index].each_with_index do |el, i|
|
117
|
+
last_writer_index = i if el[:total] < Float::INFINITY
|
118
|
+
end
|
119
|
+
raise "Destination could not be reached!" if last_writer_index.nil?
|
120
|
+
|
121
|
+
|
122
|
+
dump_trajectory(end_index).reverse
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
# Recursive function for the pathfinder search algorithm.
|
129
|
+
#
|
130
|
+
# @param [Array] current the current node that is being traversed by the
|
131
|
+
# algorithm.
|
132
|
+
# @param [Integer] _end the index of the target node.
|
133
|
+
# @param [Integer] current_total current Integer cost of the trajectory that
|
134
|
+
# is being explored by this branch of the algorithm.
|
135
|
+
#
|
136
|
+
def rec_resolve(current, _end, current_total)
|
137
|
+
current_writer_index = current[0]
|
138
|
+
(0..@interconnect_table.size - 1).each do |i|
|
139
|
+
if @interconnect_table[i][current_writer_index][:connection]
|
140
|
+
new_total = current_total + (@interconnect_table[i][current_writer_index][:cost] || 1)
|
141
|
+
|
142
|
+
if @interconnect_table[i][current_writer_index][:total] > new_total
|
143
|
+
@interconnect_table[i][current_writer_index] = {:total => new_total, :connection => true, :previous => current.reverse}
|
144
|
+
|
145
|
+
unless @interconnect_table[_end].any?{|el| el[:total] < new_total}
|
146
|
+
rec_resolve([i, current_writer_index], _end, new_total)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Scans the interconnect table, starting with the end_index, and traces a
|
154
|
+
# trajectory back to its source, using the traceback references in each cell
|
155
|
+
# from the interconnect table. The source cell is considered as the cell for
|
156
|
+
# which there is no traceback reference.
|
157
|
+
#
|
158
|
+
# @param [Integer] end_index an Integer that corresponds to the last
|
159
|
+
# element (from the original Hash structure) for which there
|
160
|
+
# should be a trajectory calculated by the search algorithm.
|
161
|
+
#
|
162
|
+
# @return [Array<Integer>] the connection indices from the original Hash
|
163
|
+
# structure.
|
164
|
+
#
|
165
|
+
def dump_trajectory(end_index)
|
166
|
+
result = []
|
167
|
+
|
168
|
+
# Get the path with the cheapest result
|
169
|
+
min_element = @interconnect_table[end_index].min{|a,b| a[:total] <=> b[:total]}
|
170
|
+
min_index = @interconnect_table[end_index].index(min_element)
|
171
|
+
|
172
|
+
previous = [min_index, end_index]
|
173
|
+
begin
|
174
|
+
connection_index = @hash[:connections].index(@hash[:connections].detect{|el| el[:link] == previous})
|
175
|
+
result << connection_index unless connection_index.nil?
|
176
|
+
previous = @interconnect_table[previous[1]][previous[0]][:previous]
|
177
|
+
end while not previous.nil?
|
178
|
+
|
179
|
+
result
|
180
|
+
end
|
181
|
+
|
182
|
+
# Commodity method for printing the interconnect table during the execution of the search algorigthm.
|
183
|
+
#
|
184
|
+
def dump_table
|
185
|
+
@interconnect_table.inject('') do |row_acc, row|
|
186
|
+
row_acc + "\n" + row.inject('') do |col_acc, col|
|
187
|
+
col_acc + (col[:total] == Float::INFINITY ? 'I' : col[:total].to_s) + " | "
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|