Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
B-ASIC - Better ASIC Toolbox
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Computer Engineering
B-ASIC - Better ASIC Toolbox
Commits
0a9120da
Commit
0a9120da
authored
2 months ago
by
Simon Bjurek
Browse files
Options
Downloads
Patches
Plain Diff
Add output and total mux minimizing memory allocation/assignment
parent
19ac7b91
No related branches found
No related tags found
1 merge request
!511
Add output and total mux minimizing memory allocation/assignment
Pipeline
#160769
passed
2 months ago
Stage: test
Stage: deploy
Changes
3
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
b_asic/resources.py
+222
-10
222 additions, 10 deletions
b_asic/resources.py
test/integration/test_sfg_to_architecture.py
+48
-0
48 additions, 0 deletions
test/integration/test_sfg_to_architecture.py
test/unit/test_resources.py
+36
-0
36 additions, 0 deletions
test/unit/test_resources.py
with
306 additions
and
10 deletions
b_asic/resources.py
+
222
−
10
View file @
0a9120da
...
...
@@ -99,6 +99,30 @@ def _sanitize_port_option(
return
read_ports
,
write_ports
,
total_ports
def
_get_source
(
var
:
MemoryVariable
,
pes
:
list
[
"
ProcessingElement
"
]
)
->
"
ProcessingElement
"
:
name
=
var
.
name
.
split
(
"
.
"
)[
0
]
for
pe
in
pes
:
pe_names
=
[
proc
.
name
for
proc
in
pe
.
collection
]
if
name
in
pe_names
:
return
pe
raise
ValueError
(
"
Source could not be found for the given variable.
"
)
def
_get_destination
(
var
:
MemoryVariable
,
pes
:
list
[
"
ProcessingElement
"
]
)
->
"
ProcessingElement
"
:
name
=
var
.
name
.
split
(
"
.
"
)[
0
]
for
pe
in
pes
:
for
process
in
pe
.
processes
:
for
input
in
process
.
operation
.
inputs
:
input_op
=
input
.
connected_source
.
operation
if
input_op
.
graph_id
==
name
:
return
pe
raise
ValueError
(
"
Destination could not be found for the given variable.
"
)
def
draw_exclusion_graph_coloring
(
exclusion_graph
:
nx
.
Graph
,
color_dict
:
dict
[
Process
,
int
],
...
...
@@ -984,6 +1008,38 @@ class ProcessCollection:
processing_elements
,
amount_of_sets
,
)
elif
heuristic
==
"
ilp_min_output_mux
"
:
if
processing_elements
is
None
:
raise
ValueError
(
"
processing_elements must be provided if heuristic =
'
ilp_min_output_mux
'"
)
if
amount_of_sets
is
None
:
raise
ValueError
(
"
amount_of_sets must be provided if heuristic =
'
ilp_min_output_mux
'"
)
return
self
.
_split_ports_ilp_min_output_mux_graph_color
(
read_ports
,
write_ports
,
total_ports
,
processing_elements
,
amount_of_sets
,
)
elif
heuristic
==
"
ilp_min_total_mux
"
:
if
processing_elements
is
None
:
raise
ValueError
(
"
processing_elements must be provided if heuristic =
'
ilp_min_total_mux
'"
)
if
amount_of_sets
is
None
:
raise
ValueError
(
"
amount_of_sets must be provided if heuristic =
'
ilp_min_total_mux
'"
)
return
self
.
_split_ports_ilp_min_total_mux_graph_color
(
read_ports
,
write_ports
,
total_ports
,
processing_elements
,
amount_of_sets
,
)
elif
heuristic
==
"
greedy_graph_color
"
:
return
self
.
_split_ports_greedy_graph_color
(
read_ports
,
write_ports
,
total_ports
...
...
@@ -1455,28 +1511,180 @@ class ProcessCollection:
# x[node, color] - whether node is colored in a certain color
# c[color] - whether color is used
# y[pe, color] - whether a color has nodes generated from a certain pe
x
=
LpVariable
.
dicts
(
"
x
"
,
(
nodes
,
colors
),
cat
=
LpBinary
)
c
=
LpVariable
.
dicts
(
"
c
"
,
colors
,
cat
=
LpBinary
)
y
=
LpVariable
.
dicts
(
"
y
"
,
(
processing_elements
,
colors
),
cat
=
LpBinary
)
problem
=
LpProblem
()
problem
+=
lpSum
(
y
[
pe
][
i
]
for
pe
in
processing_elements
for
i
in
colors
)
# constraints:
# 1 - nodes have exactly one color
# 2 - adjacent nodes cannot have the same color
# 3 - only permit assignments if color is used
# 4 - if node is colored then enable the PE which generates that node (variable)
for
node
in
nodes
:
problem
+=
lpSum
(
x
[
node
][
i
]
for
i
in
colors
)
==
1
for
u
,
v
in
edges
:
for
color
in
colors
:
problem
+=
x
[
u
][
color
]
+
x
[
v
][
color
]
<=
1
for
node
in
nodes
:
for
color
in
colors
:
problem
+=
x
[
node
][
color
]
<=
c
[
color
]
for
node
in
nodes
:
pe
=
_get_source
(
node
,
processing_elements
)
for
color
in
colors
:
problem
+=
x
[
node
][
color
]
<=
y
[
pe
][
color
]
status
=
problem
.
solve
()
if
status
!=
LpStatusOptimal
:
raise
ValueError
(
"
Optimal solution could not be found via ILP, use another method.
"
)
node_colors
=
{}
for
node
in
nodes
:
for
i
in
colors
:
if
value
(
x
[
node
][
i
])
==
1
:
node_colors
[
node
]
=
i
# reduce the solution by removing unused colors
sorted_unique_values
=
sorted
(
set
(
node_colors
.
values
()))
coloring_mapping
=
{
val
:
i
for
i
,
val
in
enumerate
(
sorted_unique_values
)}
minimal_coloring
=
{
key
:
coloring_mapping
[
node_colors
[
key
]]
for
key
in
node_colors
}
return
self
.
_split_from_graph_coloring
(
minimal_coloring
)
def
_split_ports_ilp_min_output_mux_graph_color
(
self
,
read_ports
:
int
,
write_ports
:
int
,
total_ports
:
int
,
processing_elements
:
list
[
"
ProcessingElement
"
],
amount_of_colors
:
int
,
)
->
list
[
"
ProcessCollection
"
]:
from
pulp
import
(
LpBinary
,
LpProblem
,
LpStatusOptimal
,
LpVariable
,
lpSum
,
value
,
)
# create new exclusion graph. Nodes are Processes
exclusion_graph
=
self
.
create_exclusion_graph_from_ports
(
read_ports
,
write_ports
,
total_ports
)
nodes
=
list
(
exclusion_graph
.
nodes
())
edges
=
list
(
exclusion_graph
.
edges
())
colors
=
range
(
amount_of_colors
)
# minimize the amount of output muxes connecting PEs to memories
# by minimizing the amount of PEs connected to each memory
# binary variables:
# x[node, color] - whether node is colored in a certain color
# c[color] - whether color is used
# y[pe, color] - whether a color has nodes writing to a certain PE
x
=
LpVariable
.
dicts
(
"
x
"
,
(
nodes
,
colors
),
cat
=
LpBinary
)
c
=
LpVariable
.
dicts
(
"
c
"
,
colors
,
cat
=
LpBinary
)
y
=
LpVariable
.
dicts
(
"
y
"
,
(
processing_elements
,
colors
),
cat
=
LpBinary
)
problem
=
LpProblem
()
problem
+=
lpSum
(
y
[
pe
][
i
]
for
pe
in
processing_elements
for
i
in
colors
)
def
_get_source
(
var
:
MemoryVariable
,
pes
:
list
[
"
ProcessingElement
"
]
)
->
"
ProcessingElement
"
:
name
=
var
.
name
.
split
(
"
.
"
)[
0
]
for
pe
in
pes
:
pe_names
=
[
proc
.
name
for
proc
in
pe
.
collection
]
if
name
in
pe_names
:
return
pe
raise
ValueError
(
"
Source could not be found for the given variable.
"
)
# constraints:
# 1 - nodes have exactly one color
# 2 - adjacent nodes cannot have the same color
# 3 - only permit assignments if color is used
# 4 - if node is colored then enable the PE reads from that node (variable)
for
node
in
nodes
:
problem
+=
lpSum
(
x
[
node
][
i
]
for
i
in
colors
)
==
1
for
u
,
v
in
edges
:
for
color
in
colors
:
problem
+=
x
[
u
][
color
]
+
x
[
v
][
color
]
<=
1
for
node
in
nodes
:
for
color
in
colors
:
problem
+=
x
[
node
][
color
]
<=
c
[
color
]
for
node
in
nodes
:
pe
=
_get_destination
(
node
,
processing_elements
)
for
color
in
colors
:
problem
+=
x
[
node
][
color
]
<=
y
[
pe
][
color
]
status
=
problem
.
solve
()
if
status
!=
LpStatusOptimal
:
raise
ValueError
(
"
Optimal solution could not be found via ILP, use another method.
"
)
node_colors
=
{}
for
node
in
nodes
:
for
i
in
colors
:
if
value
(
x
[
node
][
i
])
==
1
:
node_colors
[
node
]
=
i
# reduce the solution by removing unused colors
sorted_unique_values
=
sorted
(
set
(
node_colors
.
values
()))
coloring_mapping
=
{
val
:
i
for
i
,
val
in
enumerate
(
sorted_unique_values
)}
minimal_coloring
=
{
key
:
coloring_mapping
[
node_colors
[
key
]]
for
key
in
node_colors
}
return
self
.
_split_from_graph_coloring
(
minimal_coloring
)
def
_split_ports_ilp_min_total_mux_graph_color
(
self
,
read_ports
:
int
,
write_ports
:
int
,
total_ports
:
int
,
processing_elements
:
list
[
"
ProcessingElement
"
],
amount_of_colors
:
int
,
)
->
list
[
"
ProcessCollection
"
]:
from
pulp
import
(
LpBinary
,
LpProblem
,
LpStatusOptimal
,
LpVariable
,
lpSum
,
value
,
)
# create new exclusion graph. Nodes are Processes
exclusion_graph
=
self
.
create_exclusion_graph_from_ports
(
read_ports
,
write_ports
,
total_ports
)
nodes
=
list
(
exclusion_graph
.
nodes
())
edges
=
list
(
exclusion_graph
.
edges
())
colors
=
range
(
amount_of_colors
)
# minimize the amount of total muxes connecting PEs to memories
# by minimizing the amount of PEs connected to each memory (input & output)
# binary variables:
# x[node, color] - whether node is colored in a certain color
# c[color] - whether color is used
# y[pe, color] - whether a color has nodes generated from a certain pe
# z[pe, color] - whether a color has nodes writing to a certain PE
x
=
LpVariable
.
dicts
(
"
x
"
,
(
nodes
,
colors
),
cat
=
LpBinary
)
c
=
LpVariable
.
dicts
(
"
c
"
,
colors
,
cat
=
LpBinary
)
y
=
LpVariable
.
dicts
(
"
y
"
,
(
processing_elements
,
colors
),
cat
=
LpBinary
)
z
=
LpVariable
.
dicts
(
"
z
"
,
(
processing_elements
,
colors
),
cat
=
LpBinary
)
problem
=
LpProblem
()
problem
+=
lpSum
(
y
[
pe
][
i
]
+
z
[
pe
][
i
]
for
pe
in
processing_elements
for
i
in
colors
)
# constraints:
# 1 - nodes have exactly one color
# 2 - adjacent nodes cannot have the same color
# 3 - only permit assignments if color is used
# 4 - if node is colored then enable the PE which generates that node (variable)
# 5 - if node is colored then enable the PE reads from that node (variable)
for
node
in
nodes
:
problem
+=
lpSum
(
x
[
node
][
i
]
for
i
in
colors
)
==
1
for
u
,
v
in
edges
:
...
...
@@ -1489,6 +1697,10 @@ class ProcessCollection:
pe
=
_get_source
(
node
,
processing_elements
)
for
color
in
colors
:
problem
+=
x
[
node
][
color
]
<=
y
[
pe
][
color
]
for
node
in
nodes
:
pe
=
_get_destination
(
node
,
processing_elements
)
for
color
in
colors
:
problem
+=
x
[
node
][
color
]
<=
z
[
pe
][
color
]
status
=
problem
.
solve
()
...
...
@@ -1571,7 +1783,7 @@ class ProcessCollection:
coloring
:
dict
[
Process
,
int
]
|
None
=
None
,
)
->
list
[
"
ProcessCollection
"
]:
"""
Perform assignment of the processes in this collection using graph coloring.
Perform assignment of the processes in this collection using
greedy
graph coloring.
Two or more processes can share a single resource if, and only if, they have no
overlapping execution time.
...
...
This diff is collapsed.
Click to expand it.
test/integration/test_sfg_to_architecture.py
+
48
−
0
View file @
0a9120da
...
...
@@ -395,3 +395,51 @@ def test_different_resource_algorithms():
)
assert
len
(
arch
.
processing_elements
)
==
5
assert
len
(
arch
.
memories
)
==
4
# ILP COLOR MIN OUTPUT MUX
mem_vars_set
=
mem_vars
.
split_on_ports
(
read_ports
=
1
,
write_ports
=
1
,
total_ports
=
2
,
heuristic
=
"
ilp_min_output_mux
"
,
processing_elements
=
processing_elements
,
amount_of_sets
=
4
,
)
memories
=
[]
for
i
,
mem
in
enumerate
(
mem_vars_set
):
memory
=
Memory
(
mem
,
memory_type
=
"
RAM
"
,
entity_name
=
f
"
memory
{
i
}
"
)
memories
.
append
(
memory
)
memory
.
assign
(
"
graph_color
"
)
arch
=
Architecture
(
processing_elements
,
memories
,
direct_interconnects
=
direct
,
)
assert
len
(
arch
.
processing_elements
)
==
5
assert
len
(
arch
.
memories
)
==
4
# ILP COLOR MIN TOTAL MUX
mem_vars_set
=
mem_vars
.
split_on_ports
(
read_ports
=
1
,
write_ports
=
1
,
total_ports
=
2
,
heuristic
=
"
ilp_min_total_mux
"
,
processing_elements
=
processing_elements
,
amount_of_sets
=
4
,
)
memories
=
[]
for
i
,
mem
in
enumerate
(
mem_vars_set
):
memory
=
Memory
(
mem
,
memory_type
=
"
RAM
"
,
entity_name
=
f
"
memory
{
i
}
"
)
memories
.
append
(
memory
)
memory
.
assign
(
"
graph_color
"
)
arch
=
Architecture
(
processing_elements
,
memories
,
direct_interconnects
=
direct
,
)
assert
len
(
arch
.
processing_elements
)
==
5
assert
len
(
arch
.
memories
)
==
4
This diff is collapsed.
Click to expand it.
test/unit/test_resources.py
+
36
−
0
View file @
0a9120da
...
...
@@ -90,6 +90,42 @@ class TestProcessCollectionPlainMemoryVariable:
processing_elements
=
[],
)
with
pytest
.
raises
(
ValueError
,
match
=
"
processing_elements must be provided if heuristic =
'
ilp_min_output_mux
'"
,
):
simple_collection
.
split_on_ports
(
heuristic
=
"
ilp_min_output_mux
"
,
total_ports
=
1
)
with
pytest
.
raises
(
ValueError
,
match
=
"
amount_of_sets must be provided if heuristic =
'
ilp_min_output_mux
'"
,
):
simple_collection
.
split_on_ports
(
heuristic
=
"
ilp_min_output_mux
"
,
total_ports
=
1
,
processing_elements
=
[],
)
with
pytest
.
raises
(
ValueError
,
match
=
"
processing_elements must be provided if heuristic =
'
ilp_min_total_mux
'"
,
):
simple_collection
.
split_on_ports
(
heuristic
=
"
ilp_min_total_mux
"
,
total_ports
=
1
)
with
pytest
.
raises
(
ValueError
,
match
=
"
amount_of_sets must be provided if heuristic =
'
ilp_min_total_mux
'"
,
):
simple_collection
.
split_on_ports
(
heuristic
=
"
ilp_min_total_mux
"
,
total_ports
=
1
,
processing_elements
=
[],
)
with
pytest
.
raises
(
ValueError
,
match
=
"
processing_elements must be provided if heuristic =
'
left_edge_min_pe_to_mem
'"
,
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment