-
Notifications
You must be signed in to change notification settings - Fork 12
/
parse_xdc.py
133 lines (108 loc) · 4.1 KB
/
parse_xdc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright 2021-2022 F4PGA Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
from collections import namedtuple
import re
""" Used to parse XDC files for pin constraints """
XdcIoConstraint = namedtuple("XdcIoConstraint",
"net pad line_str line_num params")
def to_int_float_or_string(s):
""" Convert string to int or float if possible
If s is an integer, return int(s)
>>> to_int_float_or_string("3")
3
>>> to_int_float_or_string("-7")
-7
If s is a float, return float(2)
>>> to_int_float_or_string("3.52")
3.52
>>> to_int_float_or_string("-10.0")
-10.0
>>> to_int_float_or_string("1.4e7")
14000000.0
Otherwise, just return the string s
>>> to_int_float_or_string("A3")
'A3'
"""
try:
s_int = int(s)
# int(s) will truncate. If user specified a '.', return a float instead
if "." not in s:
return s_int
except (TypeError, ValueError):
pass
try:
return float(s)
except (TypeError, ValueError):
return s
def parse_simple_xdc(fp):
""" Parse a simple XDC file object and return list of XdcIoConstraint objects. """
# For each port, maintain a dictionary of PROPERTIES
port_to_params = {}
# For each port, maintain XdcIoConstraint object to return
port_to_results = {}
for line_number, line in enumerate(fp):
m = re.match(r"^\s*set_property\s+(.*)\[\s*get_ports\s+(.*)\]", line,
re.I)
if not m:
continue
properties = m.group(1).strip()
port = m.group(2).strip()
# Check if port is surrounded by {} braces
m = re.match(r"{\s*(\S+)\s*}", port)
if m:
port = m.group(1).strip()
if port not in port_to_params:
# Default DRIVE value is 12.
port_to_params[port] = {'DRIVE': 12}
# Check for pin property as part of a dictionary, ie:
# -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 }
m = re.match(r"-dict\s+{(.*)}", properties)
if m:
# Convert tcl dict to python dict
dict_list = m.group(1).strip().split()
properties = dict(zip(dict_list[::2], dict_list[1::2]))
if "PACKAGE_PIN" in properties:
port_to_results[port] = XdcIoConstraint(
net=port,
pad=properties["PACKAGE_PIN"],
line_str=line.strip(),
line_num=line_number,
params=port_to_params[port],
)
port_to_params[port].update(properties)
else:
# Otherwise, must be a direct set_property, ie:
# PACKAGE_PIN N15
property_pair = properties.split()
assert len(property_pair) == 2, property_pair
port_to_params[port][property_pair[0]] = property_pair[1]
if property_pair[0] == "PACKAGE_PIN":
port_to_results[port] = XdcIoConstraint(
net=port,
pad=property_pair[1],
line_str=line.strip(),
line_num=line_number,
params=port_to_params[port],
)
# Convert all property values to int/float when possible
for port in port_to_results:
for k, v in port_to_results[port].params.items():
port_to_results[port].params[k] = to_int_float_or_string(v)
# Return list of XdcIoConstraint objects
return [port_to_results[port] for port in port_to_results]