Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
macroplacement
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
macroplacement
Commits
3c1362f8
Commit
3c1362f8
authored
Sep 04, 2022
by
Dinple
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
placement_util test done + updated plc_client_os
parent
7474f481
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
475 additions
and
83 deletions
+475
-83
CodeElements/Plc_client/placement_util_os.py
+70
-26
CodeElements/Plc_client/plc_client_os.py
+276
-36
CodeElements/Plc_client/plc_client_os_test.py
+129
-21
No files found.
CodeElements/Plc_client/placement_util_os.py
View file @
3c1362f8
...
...
@@ -44,7 +44,7 @@ def nodes_of_types(plc: plc_client.PlacementCost,
yield
i
i
+=
1
# done
def
get_node_xy_coordinates
(
plc
:
plc_client
.
PlacementCost
)
->
Dict
[
int
,
Tuple
[
float
,
float
]]:
"""Returns all node x,y coordinates (canvas) in a dict."""
...
...
@@ -54,7 +54,7 @@ def get_node_xy_coordinates(
node_coords
[
node_index
]
=
plc
.
get_node_location
(
node_index
)
return
node_coords
# done
def
get_macro_orientations
(
plc
:
plc_client
.
PlacementCost
)
->
Dict
[
int
,
int
]:
"""Returns all macros' orientations in a dict."""
macro_orientations
=
dict
()
...
...
@@ -62,7 +62,7 @@ def get_macro_orientations(plc: plc_client.PlacementCost) -> Dict[int, int]:
macro_orientations
[
node_index
]
=
plc
.
get_macro_orientation
(
node_index
)
return
macro_orientations
# done
def
restore_node_xy_coordinates
(
plc
:
plc_client
.
PlacementCost
,
node_coords
:
Dict
[
int
,
Tuple
[
float
,
float
]])
->
None
:
...
...
@@ -70,13 +70,13 @@ def restore_node_xy_coordinates(
if
not
plc
.
is_node_fixed
(
node_index
):
plc
.
update_node_coords
(
node_index
,
coords
[
0
],
coords
[
1
])
# done
def
restore_macro_orientations
(
plc
:
plc_client
.
PlacementCost
,
macro_orientations
:
Dict
[
int
,
int
])
->
None
:
for
node_index
,
orientation
in
macro_orientations
.
items
():
plc
.
update_macro_orientation
(
node_index
,
orientation
)
#
#
done
def
extract_attribute_from_comments
(
attribute
:
str
,
filenames
:
List
[
str
])
->
Optional
[
str
]:
"""Parses the files' comments section, tries to extract the attribute.
...
...
@@ -104,7 +104,7 @@ def extract_attribute_from_comments(attribute: str,
break
return
None
#done
#
done
def
get_blockages_from_comments
(
filenames
:
Union
[
str
,
List
[
str
]])
->
Optional
[
List
[
List
[
float
]]]:
"""Returns list of blockages if they exist in the file's comments section."""
...
...
@@ -171,7 +171,7 @@ def extract_sizes_from_comments(
if
canvas_width
and
canvas_height
and
grid_cols
and
grid_rows
:
return
canvas_width
,
canvas_height
,
grid_cols
,
grid_rows
# done
def
fix_port_coordinates
(
plc
:
plc_client
.
PlacementCost
)
->
None
:
"""Find all ports and fix their coordinates.
...
...
@@ -182,12 +182,13 @@ def fix_port_coordinates(plc: plc_client.PlacementCost) -> None:
# print("node to fix:", node)
plc
.
fix_node_coord
(
node
)
# done
# The routing capacities are calculated based on the public information about
# 7nm technology (https://en.wikichip.org/wiki/7_nm_lithography_process)
# with an arbitary, yet reasonable, assumption of 18% of the tracks for
# the power grids.
def
create_placement_cost
(
plc_client
:
None
,
netlist_file
:
str
,
init_placement
:
Optional
[
str
]
=
None
,
overlap_threshold
:
float
=
4e-3
,
...
...
@@ -300,20 +301,21 @@ def get_node_type_counts(plc: plc_client.PlacementCost) -> Dict[str, int]:
counts
[
'HARD_MACRO'
]
+=
1
if
node_type
==
'MACRO_PIN'
:
ref_id
=
plc
.
get_ref_node_id
(
node_index
)
# print("ref_id: ", ref_id)
if
plc
.
is_node_soft_macro
(
ref_id
):
counts
[
'SOFT_MACRO_PIN'
]
+=
1
else
:
counts
[
'HARD_MACRO_PIN'
]
+=
1
return
counts
# done
def
make_blockage_text
(
plc
:
plc_client
.
PlacementCost
)
->
str
:
ret
=
''
for
blockage
in
plc
.
get_blockages
():
ret
+=
'Blockage : {}
\n
'
.
format
(
' '
.
join
([
str
(
b
)
for
b
in
blockage
]))
return
ret
# done
def
save_placement
(
plc
:
plc_client
.
PlacementCost
,
filename
:
str
,
user_comments
:
str
=
''
)
->
None
:
...
...
@@ -372,9 +374,12 @@ def save_placement(plc: plc_client.PlacementCost,
if
user_comments
:
info
+=
'
\n
User comments:
\n
'
+
user_comments
+
'
\n
'
info
+=
'
\n
node_index x y orientation fixed'
# print(info)
return
plc
.
save_placement
(
filename
,
info
)
# TODO: plc.optimize_stdcells
def
fd_placement_schedule
(
plc
:
plc_client
.
PlacementCost
,
num_steps
:
Tuple
[
int
,
...
]
=
(
100
,
100
,
100
),
io_factor
:
float
=
1.0
,
...
...
@@ -415,7 +420,7 @@ def fd_placement_schedule(plc: plc_client.PlacementCost,
log_scale_conns
,
use_sizes
,
io_factor
,
num_steps
,
max_move_distance
,
attract_factor
,
repel_factor
)
# not tested
def
get_ordered_node_indices
(
mode
:
str
,
plc
:
plc_client
.
PlacementCost
,
exclude_fixed_nodes
:
bool
=
True
)
->
List
[
int
]:
...
...
@@ -456,7 +461,7 @@ def get_ordered_node_indices(mode: str,
ordered_indices
=
[
m
for
m
in
ordered_indices
if
not
plc
.
is_node_fixed
(
m
)]
return
ordered_indices
# done
def
extract_parameters_from_comments
(
filename
:
str
)
->
Tuple
[
float
,
float
,
int
,
int
]:
"""Parses the file's comments section, tries to extract canvas/grid sizes.
...
...
@@ -476,6 +481,7 @@ def extract_parameters_from_comments(
fp_re
=
re
.
search
(
r'FP bbox: \{([\d\.]+) ([\d\.]+)\} \{([\d\.]+) ([\d\.]+)\}'
,
line
)
if
fp_re
:
# NOTE: first two argument contains origin coord but not used
canvas_width
=
float
(
fp_re
.
group
(
3
))
canvas_height
=
float
(
fp_re
.
group
(
4
))
continue
...
...
@@ -494,7 +500,7 @@ def extract_parameters_from_comments(
break
return
canvas_width
,
canvas_height
,
grid_cols
,
grid_rows
# done
def
get_routing_resources
()
->
Dict
[
str
,
float
]:
"""Currently we only use default parameter settings.
...
...
@@ -512,6 +518,7 @@ def get_routing_resources() -> Dict[str, float]:
}
# done
def
nodes_of_types
(
plc
:
plc_client
.
PlacementCost
,
type_list
:
List
[
str
]):
"""Yields the index of a node of certain types."""
i
=
0
...
...
@@ -523,7 +530,7 @@ def nodes_of_types(plc: plc_client.PlacementCost, type_list: List[str]):
yield
i
i
+=
1
# done
def
num_nodes_of_type
(
plc
,
node_type
):
"""Returns number of node of a particular type."""
count
=
0
...
...
@@ -532,6 +539,7 @@ def num_nodes_of_type(plc, node_type):
return
count
# not tested
def
extract_blockages_from_tcl
(
filename
:
str
,
block_name
:
str
,
canvas_width
:
float
,
...
...
@@ -606,7 +614,7 @@ def extract_blockages_from_tcl(filename: str,
index
+=
1
return
blockages
# done
def
get_ascii_picture
(
vect
:
List
[
Any
],
cols
:
int
,
rows
:
int
,
...
...
@@ -636,7 +644,7 @@ def get_ascii_picture(vect: List[Any],
ret_str
+=
' -'
+
'-'
*
2
*
cols
+
'
\n
'
return
ret_str
# done
def
get_hard_macro_density_map
(
plc
:
plc_client
.
PlacementCost
)
->
List
[
float
]:
"""Returns the placement density map for hard macros only."""
# Unplaces all standard cells and soft macros, so that grid cell density
...
...
@@ -660,7 +668,7 @@ def get_hard_macro_density_map(plc: plc_client.PlacementCost) -> List[float]:
plc
.
set_canvas_boundary_check
(
check_boundary
)
return
hard_macro_density
# done
def
save_placement_with_info
(
plc
:
plc_client
.
PlacementCost
,
filename
:
str
,
user_comments
:
str
=
''
)
->
None
:
...
...
@@ -755,8 +763,9 @@ def save_placement_with_info(plc: plc_client.PlacementCost,
info
+=
'
\n
node_index x y orientation fixed'
return
plc
.
save_placement
(
filename
,
info
)
# done
def
create_placement_cost_using_common_arguments
(
plc_client
:
None
,
netlist_file
:
str
,
init_placement
:
Optional
[
str
]
=
None
,
canvas_width
:
Optional
[
float
]
=
None
,
...
...
@@ -847,7 +856,7 @@ def create_placement_cost_using_common_arguments(
return
plc
# done, but need verify
def
get_node_locations
(
plc
:
plc_client
.
PlacementCost
)
->
Dict
[
int
,
int
]:
"""Returns all node grid locations (macros and stdcells) in a dict."""
node_locations
=
dict
()
...
...
@@ -866,7 +875,7 @@ def get_node_ordering_by_size(plc: plc_client.PlacementCost) -> List[int]:
node_areas
[
i
]
=
w
*
h
return
sorted
(
node_areas
,
key
=
node_areas
.
get
,
reverse
=
True
)
# not tested
def
grid_locations_near
(
plc
:
plc_client
.
PlacementCost
,
start_grid_index
:
int
)
->
Iterator
[
int
]:
"""Yields node indices closest to the start_grid_index."""
...
...
@@ -894,7 +903,7 @@ def grid_locations_near(plc: plc_client.PlacementCost,
continue
yield
int
(
new_col
+
new_row
*
cols
)
# not tested
def
place_near
(
plc
:
plc_client
.
PlacementCost
,
node_index
:
int
,
location
:
int
)
->
bool
:
"""Places a node (legally) closest to the given location.
...
...
@@ -913,7 +922,7 @@ def place_near(plc: plc_client.PlacementCost, node_index: int,
return
True
return
False
# not tested
def
disconnect_high_fanout_nets
(
plc
:
plc_client
.
PlacementCost
,
max_allowed_fanouts
:
int
=
500
)
->
None
:
high_fanout_nets
=
[]
...
...
@@ -925,7 +934,7 @@ def disconnect_high_fanout_nets(plc: plc_client.PlacementCost,
high_fanout_nets
.
append
(
i
)
plc
.
disconnect_nets
(
high_fanout_nets
)
# not tested
def
legalize_placement
(
plc
:
plc_client
.
PlacementCost
)
->
bool
:
"""Places the nodes to legal positions snapping to grid cells."""
# Unplace all except i/o's.
...
...
@@ -963,10 +972,44 @@ def main():
test_netlist_dir
=
'./Plc_client/test/'
+
'ariane'
netlist_file
=
os
.
path
.
join
(
test_netlist_dir
,
'netlist.pb.txt'
)
init_placement
=
os
.
path
.
join
(
test_netlist_dir
,
'initial.plc'
)
plc
=
create_placement_cost
(
netlist_file
=
netlist_file
,
init_placement
=
init_placement
)
plc
=
create_placement_cost_using_common_arguments
(
netlist_file
=
netlist_file
,
init_placement
=
init_placement
,
grid_cols
=
10
,
grid_rows
=
10
,
congestion_smooth_range
=
2.0
,
overlap_threshold
=
0.004
,
use_incremental_cost
=
False
)
plc
=
create_placement_cost
(
plc_client
=
plc_client
,
netlist_file
=
netlist_file
,
init_placement
=
init_placement
)
# plc = create_placement_cost_using_common_arguments(netlist_file=netlist_file, init_placement=init_placement,
# grid_cols=10, grid_rows=10, congestion_smooth_range=2.0, overlap_threshold=0.004, use_incremental_cost=False)
print
(
make_blockage_text
(
plc
))
# save_placement(plc, "save_test", 'this is a comment')
# plc.nodes_of_types()
# node_xy_coordinates
NODE_XY_DICT
=
{}
for
i
in
nodes_of_types
(
plc
,
[
'MACRO'
,
'macro'
,
'STDCELL'
,
'PORT'
]):
NODE_XY_DICT
[
i
]
=
(
100
,
100
)
restore_node_xy_coordinates
(
plc
,
NODE_XY_DICT
)
# print(get_node_xy_coordinates(plc))
# macro_orientation
MACRO_ORIENTATION
=
{}
for
i
in
nodes_of_types
(
plc
,
[
'MACRO'
,
'macro'
]):
MACRO_ORIENTATION
[
i
]
=
"S"
restore_macro_orientations
(
plc
,
MACRO_ORIENTATION
)
# print(get_macro_orientations(plc))
fix_port_coordinates
(
plc
)
# write out new plc
save_placement
(
plc
,
"save_test"
,
'this is a comment'
)
# needs more testing
print
(
get_node_locations
(
plc
))
# num_nodes_of_type
print
(
"num_nodes_of_type 'MACRO':"
,
num_nodes_of_type
(
plc
,
"MACRO"
))
# get_hard_macro_density_map
print
(
"get_hard_macro_density_map:
\n
"
,
get_hard_macro_density_map
(
plc
))
print
(
"get_hard_macro_density_map in ASCII:
\n
"
,
get_ascii_picture
(
get_hard_macro_density_map
(
plc
),
*
plc
.
get_grid_num_columns_rows
()))
print
()
if
__name__
==
'__main__'
:
main
()
\ No newline at end of file
CodeElements/Plc_client/plc_client_os.py
View file @
3c1362f8
"""Open-Sourced PlacementCost client class."""
from
ast
import
Assert
import
os
,
io
from
platform
import
node
import
re
...
...
@@ -238,7 +239,7 @@ class PlacementCost(object):
# [MACRO_NAME]/[PIN_NAME]
soft_macro_name
=
node_name
.
rsplit
(
'/'
,
1
)[
0
]
# soft macro pin
soft_macro_pin
=
self
.
SoftMacroPin
(
name
=
node_name
,
soft_macro_pin
=
self
.
SoftMacroPin
(
name
=
node_name
,
ref_id
=
None
,
x
=
attr_dict
[
'x'
][
1
],
y
=
attr_dict
[
'y'
][
1
],
macro_name
=
attr_dict
[
'macro_name'
][
1
])
...
...
@@ -290,7 +291,7 @@ class PlacementCost(object):
# [MACRO_NAME]/[PIN_NAME]
hard_macro_name
=
node_name
.
rsplit
(
'/'
,
1
)[
0
]
# hard macro pin
hard_macro_pin
=
self
.
HardMacroPin
(
name
=
node_name
,
hard_macro_pin
=
self
.
HardMacroPin
(
name
=
node_name
,
ref_id
=
None
,
x
=
attr_dict
[
'x'
][
1
],
y
=
attr_dict
[
'y'
][
1
],
x_offset
=
attr_dict
[
'x_offset'
][
1
],
...
...
@@ -562,18 +563,20 @@ class PlacementCost(object):
for
macro_idx
in
(
self
.
hard_macro_indices
+
self
.
soft_macro_indices
):
macro
=
self
.
modules_w_pins
[
macro_idx
]
macro_name
=
macro
.
get_name
()
macro_type
=
macro
.
get_type
()
if
macro_type
==
"MACRO"
:
if
not
self
.
is_node_soft_macro
(
macro_idx
)
:
if
macro_name
in
self
.
hard_macros_to_inpins
.
keys
():
pin_names
=
self
.
hard_macros_to_inpins
[
macro_name
]
else
:
return
elif
macro_type
==
"macro"
:
print
(
"[ERROR UPDATE CONNECTION] MACRO not found"
)
exit
(
1
)
# use is_node_soft_macro()
elif
self
.
is_node_soft_macro
(
macro_idx
):
if
macro_name
in
self
.
soft_macros_to_inpins
.
keys
():
pin_names
=
self
.
soft_macros_to_inpins
[
macro_name
]
else
:
return
print
(
"[ERROR UPDATE CONNECTION] MACRO not found"
)
exit
(
1
)
for
pin_name
in
pin_names
:
pin
=
self
.
modules_w_pins
[
self
.
mod_name_to_indices
[
pin_name
]]
...
...
@@ -581,7 +584,7 @@ class PlacementCost(object):
if
inputs
:
for
k
in
inputs
.
keys
():
if
macro_type
==
"MACRO"
or
macro_type
==
"macro
"
:
if
self
.
get_node_type
(
macro_idx
)
==
"MACRO
"
:
weight
=
pin
.
get_weight
()
macro
.
add_connections
(
inputs
[
k
],
weight
)
...
...
@@ -647,7 +650,7 @@ class PlacementCost(object):
# retrieve location
x_coord
.
append
(
sink
.
get_pos
()[
0
])
y_coord
.
append
(
sink
.
get_pos
()[
1
])
elif
curr_type
==
"
macro_pin"
or
curr_type
==
"
MACRO_PIN"
:
elif
curr_type
==
"MACRO_PIN"
:
# add source position
x_coord
.
append
(
mod
.
get_pos
()[
0
])
y_coord
.
append
(
mod
.
get_pos
()[
1
])
...
...
@@ -733,6 +736,9 @@ class PlacementCost(object):
def
get_congestion_cost
(
self
):
#return max(self.get_H_congestion_cost(), self.get_V_congestion_cost())
# TODO need to test if cong is smaller than 5
if
self
.
FLAG_UPDATE_CONGESTION
:
self
.
get_routing
()
return
self
.
abu
(
self
.
V_routing_cong
+
self
.
H_routing_cong
,
0.05
)
def
__get_grid_cell_location
(
self
,
x_pos
,
y_pos
):
...
...
@@ -1292,7 +1298,6 @@ class PlacementCost(object):
self
.
FLAG_UPDATE_CONGESTION
=
False
for
mod
in
self
.
modules_w_pins
:
norm_fact
=
1.0
curr_type
=
mod
.
get_type
()
# bounding box data structure
node_gcells
=
set
()
...
...
@@ -1314,7 +1319,7 @@ class PlacementCost(object):
# retrieve grid location
node_gcells
.
add
(
self
.
__get_grid_cell_location
(
*
(
sink
.
get_pos
())))
elif
(
curr_type
==
"macro_pin"
or
curr_type
==
"MACRO_PIN"
)
and
mod
.
get_sink
():
elif
curr_type
==
"MACRO_PIN"
and
mod
.
get_sink
():
# add source position
node_gcells
.
add
(
self
.
__get_grid_cell_location
(
*
(
mod
.
get_pos
())))
source_gcell
=
self
.
__get_grid_cell_location
(
*
(
mod
.
get_pos
()))
...
...
@@ -1331,7 +1336,7 @@ class PlacementCost(object):
# retrieve grid location
node_gcells
.
add
(
self
.
__get_grid_cell_location
(
*
(
sink
.
get_pos
())))
elif
curr_type
==
"MACRO"
:
elif
curr_type
==
"MACRO"
and
self
.
is_node_hard_macro
(
self
.
mod_name_to_indices
[
mod
.
get_name
()])
:
module_h
=
mod
.
get_height
()
module_w
=
mod
.
get_width
()
module_x
,
module_y
=
mod
.
get_pos
()
...
...
@@ -1412,10 +1417,24 @@ class PlacementCost(object):
self
.
H_routing_cong
=
temp_H_routing_cong
def
is_node_soft_macro
(
self
,
node_idx
)
->
bool
:
return
self
.
get_node_type
(
node_idx
)
==
"soft_macro"
"""
Return None or return ref_id
"""
try
:
return
node_idx
in
self
.
soft_macro_indices
except
IndexError
:
print
(
"[ERROR INDEX OUT OF RANGE] Can not process index at {}"
.
format
(
node_idx
))
exit
(
0
)
def
is_node_hard_macro
(
self
,
node_idx
)
->
bool
:
return
self
.
get_node_type
(
node_idx
)
==
"hard_macro"
"""
Return None or return ref_id
"""
try
:
return
node_idx
in
self
.
hard_macro_indices
except
IndexError
:
print
(
"[ERROR INDEX OUT OF RANGE] Can not process index at {}"
.
format
(
node_idx
))
exit
(
0
)
def
get_node_name
(
self
,
node_idx
:
int
)
->
str
:
return
self
.
indices_to_mod_name
[
node_idx
]
...
...
@@ -1440,7 +1459,7 @@ class PlacementCost(object):
def
get_node_type
(
self
,
node_idx
:
int
)
->
str
:
"""
Return
Vertical/Horizontal Macro Allocation
Return
node type
"""
try
:
return
self
.
modules_w_pins
[
node_idx
]
.
get_type
()
...
...
@@ -1449,6 +1468,25 @@ class PlacementCost(object):
print
(
"[INDEX OUT OF RANGE WARNING] Can not process index at {}"
.
format
(
node_idx
))
return
None
def
get_node_width_height
(
self
,
node_idx
:
int
):
"""
Return node dimension
"""
mod
=
None
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
assert
mod
.
get_type
()
in
[
'MACRO'
,
'macro'
,
'STDCELL'
,
'PORT'
]
except
AssertionError
:
print
(
"[ERROR NODE FIXED] Found {}. Only 'MACRO', 'macro', 'STDCELL'"
.
format
(
mod
.
get_type
())
+
"'PORT' are considered to be fixable nodes"
)
exit
(
1
)
except
Exception
:
print
(
"[ERROR NODE FIXED] Could not find module by node index"
)
exit
(
1
)
return
mod
.
get_width
(),
mod
.
get_height
()
def
make_soft_macros_square
(
self
):
pass
...
...
@@ -1599,14 +1637,62 @@ class PlacementCost(object):
return
macro_adj
,
sorted
(
cell_location
)
def
is_node_fixed
(
self
):
pass
def
is_node_fixed
(
self
,
node_idx
:
int
):
mod
=
None
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
assert
mod
.
get_type
()
in
[
'MACRO'
,
'macro'
,
'STDCELL'
,
'PORT'
]
except
AssertionError
:
print
(
"[ERROR NODE FIXED] Found {}. Only 'MACRO', 'macro', 'STDCELL'"
.
format
(
mod
.
get_type
())
+
"'PORT' are considered to be fixable nodes"
)
exit
(
1
)
except
Exception
:
print
(
"[ERROR NODE FIXED] Could not find module by node index"
)
exit
(
1
)
return
mod
.
get_fix_flag
()
def
optimize_stdcells
(
self
):
pass
def
update_node_coords
(
self
):
pass
def
update_node_coords
(
self
,
node_idx
,
x_pos
,
y_pos
):
"""
Update Node location if node is 'MACRO', 'macro', 'STDCELL', 'PORT'
"""
mod
=
None
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
assert
mod
.
get_type
()
in
[
'MACRO'
,
'macro'
,
'STDCELL'
,
'PORT'
]
except
AssertionError
:
print
(
"[ERROR NODE LOCATION] Found {}. Only 'MACRO', 'macro', 'STDCELL'"
.
format
(
mod
.
get_type
())
+
"'PORT' are considered to be placable nodes"
)
exit
(
1
)
except
Exception
:
print
(
"[ERROR NODE LOCATION] Could not find module by node index"
)
exit
(
1
)
mod
.
set_pos
(
x_pos
,
y_pos
)
def
update_macro_orientation
(
self
,
node_idx
,
orientation
):
"""
Update macro orientation if node is 'MACRO', 'macro'
"""
mod
=
None
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
assert
mod
.
get_type
()
in
[
'MACRO'
,
'macro'
]
except
AssertionError
:
print
(
"[ERROR MACRO ORIENTATION] Found {}. Only 'MACRO', 'macro'"
.
format
(
mod
.
get_type
())
+
" are considered to be ORIENTED"
)
exit
(
1
)
except
Exception
:
print
(
"[ERROR MACRO ORIENTATION] Could not find module by node index"
)
exit
(
1
)
mod
.
set_orientation
(
orientation
)
def
update_port_sides
(
self
):
pass
...
...
@@ -1615,31 +1701,110 @@ class PlacementCost(object):
pass
def
get_node_location
(
self
,
node_idx
):
pass
"""
Return Node location if node is 'MACRO', 'macro', 'STDCELL', 'PORT'
"""
mod
=
None
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
assert
mod
.
get_type
()
in
[
'MACRO'
,
'macro'
,
'STDCELL'
,
'PORT'
]
except
AssertionError
:
print
(
"[ERROR NODE LOCATION] Found {}. Only 'MACRO', 'macro', 'STDCELL'"
.
format
(
mod
.
get_type
())
+
"'PORT' are considered to be placable nodes"
)
exit
(
1
)
except
Exception
:
print
(
"[ERROR NODE PLACED] Could not find module by node index"
)
exit
(
1
)
return
mod
.
get_pos
()
def
get_grid_cell_of_node
(
self
,
node_idx
):
""" if grid_cell at grid crossing, break-tie to upper right
"""
return
self
.
modules_w_pins
(
node_idx
)
.
get_location
()
mod
=
None
def
update_macro_orientation
(
self
,
node_idx
,
orientation
):
pass
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
assert
mod
.
get_type
()
in
[
'MACRO'
,
'macro'
]
except
AssertionError
:
print
(
"[ERROR NODE LOCATION] Found {}. Only 'MACRO', 'macro'"
.
format
(
mod
.
get_type
())
+
" can be called"
)
exit
(
1
)
except
Exception
:
print
(
"[ERROR NODE LOCATION] Could not find module by node index"
)
exit
(
1
)
def
get_macro_orientation
(
self
):
pass
row
,
col
=
self
.
__get_grid_cell_location
(
*
mod
.
get_pos
())
return
row
*
self
.
grid_col
+
col
def
get_macro_orientation
(
self
,
node_idx
):
mod
=
None
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
assert
mod
.
get_type
()
in
[
'MACRO'
,
'macro'
]
except
AssertionError
:
print
(
"[ERROR MACRO ORIENTATION] Found {}. Only 'MACRO', 'macro'"
.
format
(
mod
.
get_type
())
+
" are considered to be ORIENTED"
)
exit
(
1
)
except
Exception
:
print
(
"[ERROR MACRO ORIENTATION] Could not find module by node index"
)
exit
(
1
)
def
unfix_node_coord
(
self
):
"""In case plc is loaded with fixed macros
return
mod
.
get_orientation
()
def
unfix_node_coord
(
self
,
node_idx
):
"""
pass
Unfix a module
"""
mod
=
None
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
assert
mod
.
get_type
()
in
[
'MACRO'
,
'macro'
,
'STDCELL'
,
'PORT'
]
except
AssertionError
:
print
(
"[ERROR UNFIX NODE] Found {}. Only 'MACRO', 'macro', 'STDCELL'"
.
format
(
mod
.
get_type
())
+
"'PORT' are considered to be fixable nodes"
)
exit
(
1
)
except
Exception
:
print
(
"[ERROR UNFIX NODE] Could not find module by node index"
)
exit
(
1
)
self
.
modules_w_pins
[
node_idx
]
.
set_fix_flag
(
False
)
def
fix_node_coord
(
self
,
node_idx
):
"""
Fix a module
"""
mod
=
None
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
assert
mod
.
get_type
()
in
[
'MACRO'
,
'macro'
,
'STDCELL'
,
'PORT'
]
except
AssertionError
:
print
(
"[ERROR FIX NODE] Found {}. Only 'MACRO', 'macro', 'STDCELL'"
.
format
(
mod
.
get_type
())
+
"'PORT' are considered to be fixable nodes"
)
exit
(
1
)
except
Exception
:
print
(
"[ERROR FIX NODE] Could not find module by node index"
)
exit
(
1
)
self
.
modules_w_pins
[
node_idx
]
.
set_fix_flag
(
True
)
def
unplace_all_nodes
(
self
):
pass
def
place_node
(
self
,
node_idx
,
grid_cell_idx
):
mod
=
None
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
except
AssertionError
:
pass
except
Exception
:
pass
pass
def
can_place_node
(
self
,
node_idx
,
grid_cell_idx
):
...
...
@@ -1650,7 +1815,21 @@ class PlacementCost(object):
pass
def
is_node_placed
(
self
,
node_idx
):
pass
mod
=
None
try
:
mod
=
self
.
modules_w_pins
[
node_idx
]
assert
mod
.
get_type
()
in
[
'MACRO'
,
'macro'
,
'STDCELL'
,
'PORT'
]
except
AssertionError
:
print
(
"[ERROR NODE PLACED] Found {}. Only 'MACRO', 'STDCELL',"
.
format
(
mod
.
get_type
())
+
"'PORT' are considered to be placable nodes"
)
exit
(
1
)
except
Exception
:
print
(
"[ERROR NODE PLACED] Could not find module by node index"
)
exit
(
1
)
mod
=
self
.
modules_w_pins
[
node_idx
]
return
mod
.
get_placed_flag
()
def
disconnect_nets
(
self
):
pass
...
...
@@ -1661,7 +1840,7 @@ class PlacementCost(object):
return
self
.
netlist_file
def
get_blockages
(
self
):
pas
s
return
self
.
blockage
s
def
create_blockage
(
self
,
minx
,
miny
,
maxx
,
maxy
,
blockage_rate
):
self
.
blockages
.
append
([
minx
,
miny
,
maxx
,
maxy
,
blockage_rate
])
...
...
@@ -1678,8 +1857,32 @@ class PlacementCost(object):
return
self
.
mod_name_to_indices
[
pin
.
get_macro_name
()]
return
-
1
def
save_placement
(
self
):
pass
def
save_placement
(
self
,
filename
,
info
):
"""
When writing out info line-by-line, add a "#" at front
"""
with
open
(
filename
,
'w+'
)
as
f
:
for
line
in
info
.
split
(
'
\n
'
):
f
.
write
(
"# "
+
line
+
'
\n
'
)
# if first, no \newline
HEADER
=
True
for
mod_idx
in
sorted
(
self
.
hard_macro_indices
+
self
.
soft_macro_indices
+
self
.
port_indices
):
# [node_index] [x] [y] [orientation] [fixed]
mod
=
self
.
modules_w_pins
[
mod_idx
]
if
HEADER
:
f
.
write
(
"{} {:g} {:g} {} {}"
.
format
(
mod_idx
,
*
mod
.
get_pos
(),
mod
.
get_orientation
()
if
mod
.
get_orientation
()
else
"-"
,
"1"
if
mod
.
get_fix_flag
()
else
"0"
))
HEADER
=
False
else
:
f
.
write
(
"
\n
{} {:g} {:g} {} {}"
.
format
(
mod_idx
,
*
mod
.
get_pos
(),
mod
.
get_orientation
()
if
mod
.
get_orientation
()
else
"-"
,
"1"
if
mod
.
get_fix_flag
()
else
"0"
))
def
display_canvas
(
self
,
annotate
=
True
,
...
...
@@ -1740,10 +1943,15 @@ class PlacementCost(object):
self
.
connection
=
{}
# [module_name] => edge degree
self
.
fix_flag
=
True
self
.
placement
=
0
# needs to be updated
self
.
orientation
=
None
self
.
ifPlaced
=
True
def
get_name
(
self
):
return
self
.
name
def
get_orientation
(
self
):
return
self
.
orientation
def
add_connection
(
self
,
module_name
):
# NOTE: assume PORT names does not contain slash
ifPORT
=
False
...
...
@@ -1813,6 +2021,12 @@ class PlacementCost(object):
def
get_fix_flag
(
self
):
return
self
.
fix_flag
def
set_placed_flag
(
self
,
ifPlaced
):
self
.
ifPlaced
=
ifPlaced
def
get_placed_flag
(
self
):
return
self
.
ifPlaced
class
SoftMacro
:
def
__init__
(
self
,
name
,
width
,
height
,
x
=
0.0
,
y
=
0.0
):
self
.
name
=
name
...
...
@@ -1860,7 +2074,7 @@ class PlacementCost(object):
return
self
.
x
,
self
.
y
def
get_type
(
self
):
return
"
macro
"
return
"
MACRO
"
def
get_connection
(
self
):
return
self
.
connection
...
...
@@ -1892,11 +2106,18 @@ class PlacementCost(object):
def
get_fix_flag
(
self
):
return
self
.
fix_flag
def
set_placed_flag
(
self
,
ifPlaced
):
self
.
ifPlaced
=
ifPlaced
def
get_placed_flag
(
self
):
return
self
.
ifPlaced
class
SoftMacroPin
:
def
__init__
(
self
,
name
,
def
__init__
(
self
,
name
,
ref_id
,
x
=
0.0
,
y
=
0.0
,
macro_name
=
""
,
weight
=
1.0
):
self
.
name
=
name
self
.
ref_id
=
ref_id
self
.
x
=
float
(
x
)
self
.
y
=
float
(
y
)
self
.
x_offset
=
0.0
# not used
...
...
@@ -1908,6 +2129,12 @@ class PlacementCost(object):
def
set_weight
(
self
,
weight
):
self
.
weight
=
weight
def
set_ref_id
(
self
,
ref_id
):
self
.
ref_id
=
ref_id
def
get_ref_id
(
self
):
return
self
.
ref_id
def
get_weight
(
self
):
return
self
.
weight
...
...
@@ -1956,7 +2183,7 @@ class PlacementCost(object):
return
self
.
weight
def
get_type
(
self
):
return
"
macro_pin
"
return
"
MACRO_PIN
"
class
HardMacro
:
def
__init__
(
self
,
name
,
width
,
height
,
...
...
@@ -2038,12 +2265,19 @@ class PlacementCost(object):
def
get_fix_flag
(
self
):
return
self
.
fix_flag
def
set_placed_flag
(
self
,
ifPlaced
):
self
.
ifPlaced
=
ifPlaced
def
get_placed_flag
(
self
):
return
self
.
ifPlaced
class
HardMacroPin
:
def
__init__
(
self
,
name
,
def
__init__
(
self
,
name
,
ref_id
,
x
=
0.0
,
y
=
0.0
,
x_offset
=
0.0
,
y_offset
=
0.0
,
macro_name
=
""
,
weight
=
1.0
):
self
.
name
=
name
self
.
ref_id
=
ref_id
self
.
x
=
float
(
x
)
self
.
y
=
float
(
y
)
self
.
x_offset
=
float
(
x_offset
)
...
...
@@ -2053,6 +2287,12 @@ class PlacementCost(object):
self
.
sink
=
{}
self
.
ifPlaced
=
True
def
set_ref_id
(
self
,
ref_id
):
self
.
ref_id
=
ref_id
def
get_ref_id
(
self
):
return
self
.
ref_id
def
set_weight
(
self
,
weight
):
self
.
weight
=
weight
...
...
CodeElements/Plc_client/plc_client_os_test.py
View file @
3c1362f8
import
numpy
as
np
import
sys
,
os
,
traceback
import
argparse
import
math
import
math
,
re
from
absl
import
flags
from
absl.flags
import
argparse_flags
from
absl
import
app
from
Plc_client
import
plc_client_os
as
plc_client_os
from
Plc_client
import
placement_util_os
as
placement_util
try
:
from
Plc_client
import
plc_client
as
plc_client
except
ImportError
:
...
...
@@ -28,10 +30,10 @@ Example:
--height 356.640
\
--col 35
\
--row 33
\
--rpmh
1
0
\
--rpmv 10
\
--marh 5
\
--marv 5
\
--rpmh
70.33
0
\
--rpmv
74.5
10
\
--marh 5
1.790
\
--marv 5
1.790
\
--smooth 2
$ python3 -m Plc_client.plc_client_os_test --netlist ./Plc_client/test/ariane133/netlist.pb.txt
\
...
...
@@ -100,7 +102,6 @@ class PlacementCostTest():
self
.
MARV
=
marv
self
.
SMOOTH
=
smooth
def
test_metadata
(
self
):
print
(
"############################ TEST METADATA ############################"
)
# Google's Binary Executable
...
...
@@ -212,6 +213,9 @@ class PlacementCostTest():
macro_macro_x_spacing
=
50
,
macro_macro_y_spacing
=
50
)
self
.
plc
.
get_overlap_threshold
()
print
(
"overlap_threshold default"
,
self
.
plc
.
get_overlap_threshold
())
if
self
.
PLC_PATH
:
print
(
"[PLC FILE FOUND] Loading info from .plc file"
)
self
.
plc_os
.
set_canvas_boundary_check
(
False
)
...
...
@@ -238,10 +242,10 @@ class PlacementCostTest():
self
.
plc_os
.
set_canvas_size
(
self
.
CANVAS_WIDTH
,
self
.
CANVAS_HEIGHT
)
self
.
plc_os
.
set_placement_grid
(
self
.
GRID_COL
,
self
.
GRID_ROW
)
# TODO: [IGNORE]
Setting blockage has no effect on proxy cost computation
# TODO: [IGNORE]
create_blockage must be defined BEFORE set_canvas_size and set_placement_grid in order to be considered on the canvas
if
False
:
self
.
plc
.
create_blockage
(
0
,
0
,
400
,
400
,
1
)
self
.
plc
.
create_blockage
(
0
,
0
,
200
,
200
,
1
)
self
.
plc
.
create_blockage
(
0
.0
,
100.0
,
300.0
,
300.0
,
1.0
)
self
.
plc
.
create_blockage
(
300
,
0
,
500
,
200
,
1
)
print
(
self
.
plc
.
get_blockages
())
print
(
self
.
plc
.
make_soft_macros_square
())
print
(
self
.
plc
.
set_use_incremental_cost
(
True
))
...
...
@@ -257,7 +261,9 @@ class PlacementCostTest():
# Density
try
:
print
(
self
.
plc_os
.
get_density_cost
())
assert
int
(
sum
(
self
.
plc_os
.
get_grid_cells_density
()))
==
int
(
sum
(
self
.
plc
.
get_grid_cells_density
()))
print
(
self
.
plc_os
.
get_density_cost
())
assert
int
(
self
.
plc_os
.
get_density_cost
())
==
int
(
self
.
plc
.
get_density_cost
())
except
Exception
as
e
:
print
(
"[DENSITY ERROR] Discrepancies found when computing density -- {}, {}"
.
format
(
str
(
self
.
plc
.
get_density_cost
()),
self
.
plc_os
.
get_density_cost
()))
...
...
@@ -265,8 +271,10 @@ class PlacementCostTest():
# Congestion
try
:
print
(
self
.
plc_os
.
get_congestion_cost
())
assert
abs
(
sum
(
self
.
plc_os
.
get_horizontal_routing_congestion
())
-
sum
(
self
.
plc
.
get_horizontal_routing_congestion
()))
<
1e-3
assert
abs
(
sum
(
self
.
plc_os
.
get_vertical_routing_congestion
())
-
sum
(
self
.
plc
.
get_vertical_routing_congestion
()))
<
1e-3
print
(
self
.
plc_os
.
get_congestion_cost
())
assert
abs
(
self
.
plc
.
get_congestion_cost
()
-
self
.
plc_os
.
get_congestion_cost
())
<
1e-3
except
Exception
as
e
:
print
(
"[CONGESTION ERROR] Discrepancies found when computing congestion -- {}"
.
format
(
str
(
e
)))
...
...
@@ -287,19 +295,19 @@ class PlacementCostTest():
self
.
plc
.
set_placement_grid
(
self
.
GRID_COL
,
self
.
GRID_ROW
)
self
.
plc_os
.
set_canvas_size
(
self
.
CANVAS_WIDTH
,
self
.
CANVAS_HEIGHT
)
self
.
plc_os
.
set_placement_grid
(
self
.
GRID_COL
,
self
.
GRID_ROW
)
NODE_IDX
=
0
print
(
"get_macro_indices"
,
self
.
plc
.
get_macro_indices
()
,
self
.
plc_os
.
get_macro_indices
()
)
NODE_IDX
=
22853
print
(
"get_macro_indices"
,
self
.
plc
.
get_macro_indices
())
print
(
"get_node_name"
,
self
.
plc
.
get_node_name
(
NODE_IDX
))
print
(
"get_node_type"
,
self
.
plc
.
get_node_type
(
NODE_IDX
))
print
(
"get_node_location"
,
self
.
plc
.
get_node_location
(
NODE_IDX
))
print
(
"get_grid_cell_of_node"
,
self
.
plc
.
get_grid_cell_of_node
(
NODE_IDX
))
print
(
"get_node_location"
,
self
.
plc
.
get_node_location
(
NODE_IDX
))
print
(
"get_macro_orientation"
,
self
.
plc
.
get_macro_orientation
(
NODE_IDX
))
print
(
"is_node_placed"
,
self
.
plc
.
is_node_placed
(
NODE_IDX
))
print
(
"get_source_filename"
,
self
.
plc
.
get_source_filename
())
print
(
"get_blockages"
,
self
.
plc
.
get_blockages
())
print
(
"get_ref_node_id"
,
self
.
plc
.
get_ref_node_id
(
NODE_IDX
),
self
.
plc
.
get_ref_node_id
(
NODE_IDX
))
print
(
"get_node_mask
\n
"
,
np
.
array
(
self
.
plc
.
get_node_mask
(
NODE_IDX
))
.
reshape
((
4
,
4
)))
print
(
"can_place_node"
,
self
.
plc
.
can_place_node
(
0
,
1
))
#
print("get_node_mask\n", np.array(self.plc.get_node_mask(NODE_IDX)).reshape((4,4)))
#
print("can_place_node", self.plc.can_place_node(0, 1))
print
(
"***************************************************"
)
def
test_proxy_congestion
(
self
):
...
...
@@ -375,6 +383,87 @@ class PlacementCostTest():
print
(
"**************BY ENTRY DIFF"
)
print
(
temp_gl_h_mc
[
0
][
6
],
temp_os_h_mc
[
0
][
6
])
def
test_placement_util
(
self
,
keep_save_file
=
False
):
"""
* Read same input, perturb placement and orientation, write to new .plc
"""
print
(
"############################ TEST PLACEMENT UTIL ############################"
)
try
:
assert
self
.
PLC_PATH
except
AssertionError
:
print
(
"[ERROR PLACEMENT UTIL TEST] Facilitate required .plc file"
)
self
.
plc_util
=
placement_util
.
create_placement_cost
(
plc_client
=
plc_client
,
netlist_file
=
self
.
NETLIST_PATH
,
init_placement
=
self
.
PLC_PATH
)
self
.
plc_util_os
=
placement_util
.
create_placement_cost
(
plc_client
=
plc_client_os
,
netlist_file
=
self
.
NETLIST_PATH
,
init_placement
=
self
.
PLC_PATH
)
# ********************** plc_client_os **********************
# node_xy_coordinates
# NODE_XY_DICT = {}
# for i in placement_util.nodes_of_types(self.plc_util_os, ['MACRO', 'STDCELL', 'PORT']):
# NODE_XY_DICT[i] = (100, 100)
# placement_util.restore_node_xy_coordinates(self.plc_util_os, NODE_XY_DICT)
# macro_orientation
MACRO_ORIENTATION
=
{}
for
i
in
placement_util
.
nodes_of_types
(
self
.
plc_util_os
,
[
'MACRO'
]):
MACRO_ORIENTATION
[
i
]
=
"S"
placement_util
.
restore_macro_orientations
(
self
.
plc_util_os
,
MACRO_ORIENTATION
)
# fix ports
placement_util
.
fix_port_coordinates
(
self
.
plc_util_os
)
# write out new plc
placement_util
.
save_placement
(
self
.
plc_util_os
,
"save_test_os.plc"
,
'this is a comment'
)
# ********************** plc_client **********************
# # node_xy_coordinates
# NODE_XY_DICT = {}
# for i in placement_util.nodes_of_types(self.plc_util, ['MACRO', 'STDCELL', 'PORT']):
# NODE_XY_DICT[i] = (100, 100)
# placement_util.restore_node_xy_coordinates(self.plc_util, NODE_XY_DICT)
# macro_orientation
MACRO_ORIENTATION
=
{}
for
i
in
placement_util
.
nodes_of_types
(
self
.
plc_util
,
[
'MACRO'
]):
MACRO_ORIENTATION
[
i
]
=
"S"
placement_util
.
restore_macro_orientations
(
self
.
plc_util
,
MACRO_ORIENTATION
)
# fix ports
placement_util
.
fix_port_coordinates
(
self
.
plc_util
)
# write out new plc
placement_util
.
save_placement
(
self
.
plc_util
,
"save_test_gl.plc"
,
'this is a comment'
)
print
(
" +++++++++++++++++++++++++++++++++"
)
print
(
" +++ TEST PLACEMENT UTIL: PASS +++"
)
print
(
" +++++++++++++++++++++++++++++++++"
)
# This is only for node information, line-by-line test
try
:
with
open
(
'save_test_gl.plc'
)
as
f1
,
open
(
'save_test_os.plc'
)
as
f2
:
for
idx
,
(
line1
,
line2
)
in
enumerate
(
zip
(
f1
,
f2
)):
if
line1
.
strip
()
!=
line2
.
strip
():
if
not
re
.
match
(
r"(# )\w+"
,
line1
.
strip
()):
print
(
"PLC MISMATCH (GL, OS)
\n
"
,
line1
.
strip
(),
"
\n
"
,
line2
.
strip
())
raise
AssertionError
(
"false"
)
except
AssertionError
:
print
(
"[ERROR PLACEMENT UTIL] Saved PLC Discrepency found at line {}"
.
format
(
str
(
idx
)))
def
test_environment
(
self
):
pass
def
parse_flags
(
argv
):
parser
=
argparse_flags
.
ArgumentParser
(
description
=
'An argparse + app.run example'
)
parser
.
add_argument
(
"--netlist"
,
required
=
True
,
...
...
@@ -403,15 +492,33 @@ def parse_flags(argv):
def
main
(
args
):
if
args
.
plc
:
PCT
=
PlacementCostTest
(
args
.
netlist
,
args
.
plc
,
args
.
width
,
args
.
height
,
args
.
col
,
args
.
row
,
args
.
rpmv
,
args
.
rpmv
,
args
.
marh
,
args
.
marv
,
args
.
smooth
)
PCT
=
PlacementCostTest
(
NETLIST_PATH
=
args
.
netlist
,
PLC_PATH
=
args
.
plc
,
width
=
args
.
width
,
height
=
args
.
height
,
column
=
args
.
col
,
row
=
args
.
row
,
rpmv
=
args
.
rpmv
,
rpmh
=
args
.
rpmh
,
marh
=
args
.
marh
,
marv
=
args
.
marv
,
smooth
=
args
.
smooth
)
else
:
PCT
=
PlacementCostTest
(
args
.
netlist
,
args
.
width
,
args
.
height
,
args
.
col
,
args
.
row
,
args
.
rpmv
,
args
.
rpmv
,
args
.
marh
,
args
.
marv
,
args
.
smooth
)
PCT
.
test_metadata
()
PCT
=
PlacementCostTest
(
NETLIST_PATH
=
args
.
netlist
,
width
=
args
.
width
,
height
=
args
.
height
,
column
=
args
.
col
,
row
=
args
.
row
,
rpmv
=
args
.
rpmv
,
rpmh
=
args
.
rpmh
,
marh
=
args
.
marh
,
marv
=
args
.
marv
,
smooth
=
args
.
smooth
)
# PCT.test_metadata()
PCT
.
test_proxy_cost
()
PCT
.
test_placement_util
()
# PCT.test_miscellaneous()
if
__name__
==
'__main__'
:
app
.
run
(
main
,
flags_parser
=
parse_flags
)
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment