├── .gitignore
├── LICENSE
├── README.md
├── bin
└── verilogviz
└── verilogviz
├── __init__.py
├── common
├── __init__.py
└── pvg
│ ├── __init__.py
│ ├── update_visual_graph.py
│ └── visual_graph
│ ├── __init__.py
│ ├── box.py
│ ├── default_graphics_scene.py
│ ├── default_graphics_widget.py
│ ├── defines.py
│ ├── graphics_scene.py
│ ├── graphics_utils.py
│ ├── graphics_view.py
│ ├── graphics_widget.py
│ ├── link.py
│ ├── module_box.py
│ ├── port.py
│ └── port_box.py
├── include_path_dialog.py
├── model
├── __init__.py
├── arbiter.py
├── module.py
├── module_list_model.py
├── preprocessor.py
├── utils.py
└── verilog_utils.py
├── module_graph.py
├── verilog_viz_actions.py
├── verilogviz.py
└── view
├── __init__.py
├── graph
├── __init__.py
├── graphical_defines.py
├── graphics_scene.py
├── graphics_view.py
├── module_box.py
└── verilog_graph.py
├── include_path_dialog.py
├── main_form.py
└── matplot_lib_widget.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[cod]
2 | .idea
3 |
4 | clean_nysa.py
5 |
6 | # VIM temporary files
7 | *~
8 | *.swp
9 | *.swo
10 |
11 | #Simulation Things
12 | .sconsign.dblite
13 | temp.txt
14 | *.sim
15 | *.vcd
16 | *.gtkw
17 |
18 | # C extensions
19 | *.so
20 |
21 | # Packages
22 | *.egg
23 | *.egg-info
24 | dist
25 | build
26 | eggs
27 | parts
28 | #bin
29 | var
30 | sdist
31 | develop-eggs
32 | .installed.cfg
33 | #lib
34 | lib64
35 |
36 | # Installer logs
37 | pip-log.txt
38 |
39 | # Unit test / coverage reports
40 | .coverage
41 | .tox
42 | nosetests.xml
43 |
44 | # Translations
45 | *.mo
46 |
47 | # Mr Developer
48 | .mr.developer.cfg
49 | .project
50 | .pydevproject
51 |
52 |
53 | # Documentation Output
54 | docs/html
55 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # verilog-visualizer
2 | A GUI to help users visualize the structure of a verilog HDL project
3 |
--------------------------------------------------------------------------------
/bin/verilogviz:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 |
3 | # Copyright (c) 2015 Dave McCoy (dave.mccoy@cospandesign.com)
4 | #
5 | # This file is part of Nysa.
6 | # (http://wiki.cospandesign.com/index.php?title=Nysa.org)
7 | #
8 | # Nysa is free software; you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation; either version 3 of the License, or
11 | # any later version.
12 | #
13 | # Nysa is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with Nysa; If not, see .
20 |
21 |
22 |
23 | import sys
24 | import os
25 | import argparse
26 | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)))
27 |
28 | from verilogviz.verilogviz import VerilogViz
29 |
30 | DESCRIPTION = "\n" \
31 | "\n" \
32 | "usage: nui [options]\n"
33 |
34 | EPILOG = "\n" \
35 | "\n" \
36 | "Examples:\n" \
37 | "\tSomething\n" \
38 | "\t\ttest_dionysus.py\n" \
39 | "\n"
40 |
41 |
42 | def main(argv):
43 | #Parse out command line options
44 | parser = argparse.ArgumentParser(
45 | formatter_class=argparse.RawDescriptionHelpFormatter,
46 | description=DESCRIPTION,
47 | epilog=EPILOG
48 | )
49 | parser.add_argument("-d", "--debug",
50 | action="store_true",
51 | help="Enable Debug Messages")
52 |
53 | args = parser.parse_args()
54 | vv = VerilogViz()
55 |
56 |
57 | if __name__ == "__main__":
58 | main(sys.argv)
59 |
--------------------------------------------------------------------------------
/verilogviz/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CospanDesign/verilog-visualizer/be9fb8b35b46bb0162963888f9a4e9461409623e/verilogviz/__init__.py
--------------------------------------------------------------------------------
/verilogviz/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CospanDesign/verilog-visualizer/be9fb8b35b46bb0162963888f9a4e9461409623e/verilogviz/common/__init__.py
--------------------------------------------------------------------------------
/verilogviz/common/pvg/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CospanDesign/verilog-visualizer/be9fb8b35b46bb0162963888f9a4e9461409623e/verilogviz/common/pvg/__init__.py
--------------------------------------------------------------------------------
/verilogviz/common/pvg/update_visual_graph.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 |
3 | import sys
4 | import os
5 | import argparse
6 | import shutil
7 |
8 | DESCRIPTION = "\n" \
9 | "Update pyqt4-visual-graph with the most recent version of the library\n"
10 |
11 | EPILOG = "\n" \
12 | "Examples:\n" + \
13 | "\n" + \
14 | "update_visual_graph.py\n" + \
15 | "\tupdates the xilinx build with the files in ~/Projects/visual_graph\n" + \
16 | "\n" + \
17 | "update_visual_graph.py \n" + \
18 | "\tupdates the xilinx build with the path specified"
19 |
20 |
21 | debug = False
22 |
23 | if __name__ == "__main__":
24 | parser = argparse.ArgumentParser(
25 | formatter_class=argparse.RawDescriptionHelpFormatter,
26 | description=DESCRIPTION,
27 | epilog=EPILOG
28 | )
29 |
30 | #Add an argument to the parser
31 | home = os.path.expanduser("~")
32 | default_path = os.path.join(home, "Projects", "pyqt4-visual-graph", "visual_graph")
33 | parser.add_argument("path",
34 | type = str,
35 | nargs='?',
36 | default=default_path,
37 | help="Specify the path to visual_grapher (leave blank for %s" % default_path)
38 | parser.parse_args()
39 | arg = parser.parse_args()
40 |
41 | if not os.path.exists(arg.path):
42 | print "Path: %s Doesn't exists!" % arg.path
43 | sys.exit(1)
44 |
45 | out_dir = os.path.join(os.path.dirname(__file__), "visual_graph")
46 |
47 | arg.path
48 |
49 | #Remove any local version of the files
50 | if os.path.exists(out_dir):
51 | shutil.rmtree(out_dir)
52 |
53 | #Copy over site_scons
54 | shutil.copytree(arg.path, out_dir)
55 |
56 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CospanDesign/verilog-visualizer/be9fb8b35b46bb0162963888f9a4e9461409623e/verilogviz/common/pvg/visual_graph/__init__.py
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/box.py:
--------------------------------------------------------------------------------
1 | # Distributed under the MIT licesnse.
2 | # Copyright (c) 2013 Dave McCoy (dave.mccoy@cospandesign.com)
3 |
4 | #Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | #this software and associated documentation files (the "Software"), to deal in
6 | #the Software without restriction, including without limitation the rights to
7 | #use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | #of the Software, and to permit persons to whom the Software is furnished to do
9 | #so, subject to the following conditions:
10 | #
11 | #The above copyright notice and this permission notice shall be included in all
12 | #copies or substantial portions of the Software.
13 | #
14 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | #SOFTWARE.
21 |
22 |
23 | #A huge thanks to 'Rapid GUI Programming with Python and Qt' by Mark Summerfield
24 |
25 | '''
26 | Log
27 | 6/05/2013: Initial commit
28 | 6/12/2013: Adding support for links
29 | '''
30 |
31 | __author__ = "Dave McCoy dave.mccoy@cospandesign.com"
32 |
33 | from PyQt4.QtCore import *
34 | from PyQt4.QtGui import *
35 |
36 | from link import Link
37 | from link import side_type as st
38 |
39 | Dirty = False
40 |
41 | DEFAULT_BOX_SIZE = (100, 50)
42 |
43 | PADDING = 20
44 |
45 | def string_to_side(side_string):
46 | if side_string == "right":
47 | return st.right
48 |
49 | if side_string == "left":
50 | return st.left
51 |
52 | if side_string == "top":
53 | return st.top
54 |
55 | if side_string == "bottom":
56 | return st.bottom
57 |
58 |
59 | class Box (QGraphicsItem):
60 | """Generic box used for flow charts"""
61 |
62 | def __init__(self,
63 | position,
64 | scene,
65 | name="Test",
66 | color="green",
67 | rect=None,
68 | user_data=None):
69 |
70 | super(Box, self).__init__()
71 | #Box Properties
72 | self.box_name = name
73 | self.color = QColor(color)
74 | self.user_data = user_data
75 |
76 | if rect is None:
77 | rect = QRectF(0, 0, DEFAULT_BOX_SIZE[0], DEFAULT_BOX_SIZE[1])
78 |
79 | self.rect = rect
80 | self.start_rect = QRectF(rect)
81 |
82 | self.style = Qt.SolidLine
83 | self.setPos(position)
84 | self.setMatrix(QMatrix())
85 | scene.clearSelection()
86 | scene.addItem(self)
87 | self.s = scene
88 |
89 | #self.setSelected(True)
90 | #self.setFocus()
91 |
92 | #Tooltip
93 | self.setToolTip(
94 | "Name: %s" % self.box_name
95 | )
96 |
97 | #Font
98 | self.text_font = QFont('White Rabbit')
99 | self.text_font.setPointSize(16)
100 |
101 | #if self.select_func is not None:
102 | # self.select_func(self.user_data)
103 |
104 | global Dirty
105 | Dirty = True
106 | self.links = {}
107 |
108 | self.setFlags(QGraphicsItem.ItemIsSelectable |
109 | QGraphicsItem.ItemIsMovable |
110 | QGraphicsItem.ItemIsFocusable)
111 | self.dbg = False
112 |
113 | def movable(self, enable):
114 | self.setFlag(QGraphicsItem.ItemIsMovable, enable)
115 |
116 | def is_movable(self):
117 | #print "Type: %s, Self.flags(): %s" % (int(self.flags()), str(dir(self.flags())))
118 | #print "\tTest: %s" % str(self.flags() and QGraphicsItem.ItemIsMovable)
119 | #print "\tTest: %s" % str((int(self.flags()) & QGraphicsItem.ItemIsMovable) > 0)
120 | return (int(self.flags()) & QGraphicsItem.ItemIsMovable) > 0
121 |
122 | def selectable(self, enable):
123 | if self.dbg: print "BOX: %s selectable %s" % (self.box_name, str(enable))
124 | self.setFlag(QGraphicsItem.ItemIsSelectable, enable)
125 |
126 | def side_coordinates(self, side):
127 | if side == st.top:
128 | return QPointF(self.rect.width() / 2, self.rect.top())
129 | if side == st.bottom:
130 | return QPointF(self.rect.width() / 2, self.rect.bottom())
131 | if side == st.left:
132 | return QPointF(self.rect.left(), self.rect.height() / 2)
133 | if side == st.right:
134 | return QPointF(self.rect.right(), self.rect.height() / 2)
135 |
136 | def set_size(self, width, height):
137 | self.rect.setWidth(width)
138 | self.rect.setHeight(height)
139 |
140 | def contextMenuEvent(self, event):
141 | #wrapped = []
142 | menu = QMenu(self.parentWidget())
143 | for text, func in (("&Demo Function", self.demo_function),):
144 | menu.addAction(text, func)
145 | menu.exec_(event.screenPos())
146 |
147 | def demo_function(self):
148 | print ("Demo function!")
149 |
150 | def itemChange(self, a, b):
151 | if QGraphicsItem.ItemSelectedHasChanged == a:
152 | if b.toBool():
153 | if self.s is not None:
154 | self.s.box_selected(self.user_data)
155 | else:
156 | if self.s is not None:
157 | self.s.box_deselected(self.user_data)
158 |
159 | return QGraphicsItem.itemChange(self, a, b)
160 |
161 | def update(self):
162 | self.update_links()
163 |
164 | def set_pen_border(self, option, pen):
165 | highlight_width = 8
166 | pen.setWidth(1)
167 | if option.state & QStyle.State_Selected:
168 | #Selected
169 | pen.setColor(QColor("black"))
170 | pen.setWidth(highlight_width)
171 | return pen
172 |
173 | #Paint
174 | def paint(self, painter, option, widget):
175 |
176 |
177 | pen = QPen(self.style)
178 | pen.setColor(Qt.black)
179 | pen.setWidth(1)
180 | self.set_pen_border(option, pen)
181 | '''
182 | if option.state & QStyle.State_Selected:
183 | #Selected
184 | pen.setColor(QColor("black"))
185 | pen.setWidth(highlight_width)
186 | '''
187 |
188 | painter.setPen(pen)
189 | painter.drawRect(self.rect)
190 | painter.fillRect(self.rect, QColor(self.color))
191 | painter.setFont(self.text_font)
192 |
193 | #draw text
194 | pen.setColor(Qt.black)
195 | painter.setPen(pen)
196 |
197 | self.add_label_to_rect(painter, self.rect, self.box_name)
198 |
199 | def boundingRect(self):
200 | return self.rect.adjusted(-2, -2, 2, 2)
201 |
202 | def parentWidget(self):
203 | return self.s.views()[0]
204 |
205 | def add_label_to_rect(self, painter, rect, label):
206 | painter.save()
207 | r = QRectF(rect)
208 | br = painter.fontMetrics().boundingRect(label)
209 | scale = r.width() / br.width()
210 |
211 | if scale >= 1.0:
212 | painter.drawText(r, Qt.AlignCenter, label)
213 | else:
214 | font_height = br.height()
215 | box_height = r.height()
216 |
217 | painter.scale(scale, scale)
218 | br.translate(r.left() - br.left(), r.top() - br.top())
219 | br.translate(0.0, box_height * (0.5 / scale) - font_height / 2)
220 | painter.drawText(br, Qt.TextSingleLine, label)
221 |
222 | painter.restore()
223 |
224 | def add_link(self, to_box, from_side = "right", to_side="left"):
225 | #print "link"
226 | from_side = string_to_side(from_side)
227 | to_side = string_to_side(to_side)
228 | name = "%s_%s" % (self.box_name, to_box.box_name)
229 | self.links[name] = Link(self, to_box)
230 | self.links[name].from_box_side(from_side)
231 | self.links[name].to_box_side(to_side)
232 | self.update_links()
233 | self.s.invalidate(self.s.sceneRect())
234 | return self.links[name]
235 |
236 | def update_links(self):
237 | for link in self.links:
238 | if self.links[link].is_center_track():
239 | self.links[link].auto_update_center()
240 |
241 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/default_graphics_scene.py:
--------------------------------------------------------------------------------
1 | # Distributed under the MIT licesnse.
2 | # Copyright (c) 2013 Dave McCoy (dave.mccoy@cospandesign.com)
3 |
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | # this software and associated documentation files (the "Software"), to deal in
6 | # the Software without restriction, including without limitation the rights to
7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | # of the Software, and to permit persons to whom the Software is furnished to do
9 | # so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in all
12 | # copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | # SOFTWARE.
21 |
22 |
23 | #A huge thanks to 'Rapid GUI Programming with Python and Qt' by Mark Summerfield
24 |
25 | '''
26 | Log
27 | 6/05/2013: Initial commit
28 | '''
29 |
30 | __author__ = "Dave McCoy dave.mccoy@cospandesign.com"
31 |
32 |
33 | import os
34 | import sys
35 |
36 | from PyQt4.QtCore import *
37 | from PyQt4.QtGui import *
38 |
39 |
40 | class GraphicsScene(QGraphicsScene):
41 | def __init__(self, view, app):
42 | super(GraphicsScene, self).__init__()
43 | self.view = view
44 | self.app = app
45 | self.links = []
46 | self.dbg = True
47 | if self.dbg: print "GS: Set state for normal"
48 | #self.setAcceptDrops(True)
49 |
50 | def get_view(self):
51 | return self.view
52 |
53 | def fit_view(self):
54 | self.app.fit_view()
55 |
56 | def box_selected(self, data):
57 | #raise AssertionError ("box_selected() is not defined")
58 | pass
59 |
60 | def box_deselected(self, data):
61 | #raise AssertionError ("box_deselected() is not defined")
62 | pass
63 |
64 | def remove_selected(self, reference):
65 | #raise AssertionError ("remove_selected() is not defined")
66 | pass
67 |
68 | #Links
69 | def set_link_ref(self, lref):
70 | if self.dbg: print "\tlink_ref: %s - %s" % (lref.from_box.box_name, lref.to_box.box_name)
71 | self.links.append(lref)
72 |
73 | def clear_links(self):
74 | for i in range (len(self.links)):
75 | self.removeItem(self.links[i])
76 |
77 | def auto_update_all_links(self):
78 | for l in self.links:
79 | if l.is_center_track():
80 | l.auto_update_center()
81 |
82 |
83 | #The user should override the following methods:
84 | def mouseMoveEvent(self, event):
85 | super (GraphicsScene, self).mouseMoveEvent(event)
86 | self.auto_update_all_links()
87 |
88 | def mousePressEvent(self, event):
89 | super (GraphicsScene, self).mousePressEvent(event)
90 | self.auto_update_all_links()
91 |
92 | def dropEvent(self, event):
93 | super (GraphicsScene, self).dropEvent(event)
94 |
95 | def startDrag(self, event):
96 | pass
97 |
98 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/default_graphics_widget.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from PyQt4.Qt import *
4 | from PyQt4.QtCore import *
5 | from PyQt4.QtGui import *
6 |
7 | from graphics_view import GraphicsView as GV
8 | from default_graphics_scene import GraphicsScene as GS
9 | from graphics_widget import GraphicsWidget as GW
10 | from box import Box
11 |
12 | class GraphicsWidget (GW):
13 |
14 | def __init__(self):
15 | QWidget.__init__(self, None)
16 | self.view = GV(self)
17 | self.scene = GS(self.view, self)
18 | self.view.setScene(self.scene)
19 | self.prevPoint = QPoint()
20 | layout = QHBoxLayout()
21 | layout.addWidget(self.view)
22 | self.setLayout(layout)
23 | self.controller = None
24 |
25 | def fit_view(self):
26 | self.view._scale_fit()
27 |
28 | def add_box(self, name, color = "black", position = QPointF(0, 0)):
29 | b = Box(position = position,
30 | scene = self.scene,
31 | name = name,
32 | color = color)
33 | return b
34 |
35 | #Controller Functions
36 | def set_controller(self, controller):
37 | self.controller = controller
38 |
39 | def get_controller(self):
40 | return self.controller
41 |
42 | def drop_event(self, event):
43 | #Send an event to a controller
44 | print "Demo Graphics View: Drop Event"
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/defines.py:
--------------------------------------------------------------------------------
1 | # Distributed under the MIT licesnse.
2 | # Copyright (c) 2013 Dave McCoy (dave.mccoy@cospandesign.com)
3 |
4 | #Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | #this software and associated documentation files (the "Software"), to deal in
6 | #the Software without restriction, including without limitation the rights to
7 | #use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | #of the Software, and to permit persons to whom the Software is furnished to do
9 | #so, subject to the following conditions:
10 | #
11 | #The above copyright notice and this permission notice shall be included in all
12 | #copies or substantial portions of the Software.
13 | #
14 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | #SOFTWARE.
21 |
22 | '''
23 | Log
24 | 6/12/2013: Initial commit
25 | '''
26 |
27 | from PyQt4.QtGui import *
28 | from PyQt4.QtCore import *
29 |
30 | def enum(*sequential, **named):
31 | enums = dict(zip(sequential, range(len(sequential))), **named)
32 | return type('Enum', (), enums)
33 |
34 | direc = enum("input",
35 | "output",
36 | "inout")
37 |
38 |
39 |
40 |
41 | #Note this cannot have a QColor for these two because the values are passed
42 | #through MIME types
43 | DEMO_BOX_COLOR = "blue"
44 | DEMO_BOX_RECT = QRectF(0, 0, 100, 50)
45 | DEMO_BOX_POS = QPointF(0, 0)
46 | DEMO_BOX_ID = "Demo Box"
47 |
48 | LINK_DEMO_COLOR = QColor("black")
49 |
50 | BEZIER_CONNECTION = False
51 |
52 |
53 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/graphics_scene.py:
--------------------------------------------------------------------------------
1 | # Distributed under the MIT licesnse.
2 | # Copyright (c) 2013 Dave McCoy (dave.mccoy@cospandesign.com)
3 |
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | # this software and associated documentation files (the "Software"), to deal in
6 | # the Software without restriction, including without limitation the rights to
7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | # of the Software, and to permit persons to whom the Software is furnished to do
9 | # so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in all
12 | # copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | # SOFTWARE.
21 |
22 |
23 | #A huge thanks to 'Rapid GUI Programming with Python and Qt' by Mark Summerfield
24 |
25 | '''
26 | Log
27 | 6/05/2013: Initial commit
28 | '''
29 |
30 | __author__ = "Dave McCoy dave.mccoy@cospandesign.com"
31 |
32 |
33 | import os
34 | import sys
35 |
36 | from PyQt4.QtCore import *
37 | from PyQt4.QtGui import *
38 |
39 |
40 | class GraphicsScene(QGraphicsScene):
41 | def __init__(self, view, app):
42 | super(GraphicsScene, self).__init__()
43 | self.view = view
44 | self.controller = None
45 | self.app = app
46 | self.links = []
47 | self.dbg = False
48 | if self.dbg: print "GS: Set state for normal"
49 | self.view.set_scene(self)
50 | #self.setAcceptDrops(True)
51 |
52 | def set_controller(self, controller):
53 | self.controller = controller
54 |
55 | def get_view(self):
56 | return self.view
57 |
58 | def box_selected(self, data):
59 | #raise AssertionError ("box_selected() is not defined")
60 | pass
61 |
62 | def fit_in_view(self):
63 | self.app.fit_in_view()
64 |
65 | def box_deselected(self, data):
66 | #raise AssertionError ("box_deselected() is not defined")
67 | pass
68 |
69 | def remove_selected(self, reference):
70 | #raise AssertionError ("remove_selected() is not defined")
71 | pass
72 |
73 | #Links
74 | def set_link_ref(self, lref):
75 | if self.dbg: print "\tlink_ref: %s - %s" % (lref.from_box.box_name, lref.to_box.box_name)
76 | self.links.append(lref)
77 |
78 | def clear_links(self):
79 | #items = list(self.links)
80 | #for i in range (len(self.links)):
81 | #for l in self.links:
82 | # #print "Removing: %s" % str(l)
83 | # self.removeItem(self.links[0])
84 |
85 | self.links = []
86 | #while len(self.links) > 0:
87 | # self.removeItem(self.links[items])
88 |
89 |
90 |
91 | def auto_update_all_links(self):
92 | #print "updating links!"
93 | for l in self.links:
94 | #print "updating: %s" % l
95 | if l.is_center_track():
96 | l.auto_update_center()
97 |
98 |
99 | '''
100 | #The user should override the following methods:
101 | def mouseMoveEvent(self, event):
102 | super (GraphicsScene, self).mouseMoveEvent(event)
103 | self.auto_update_all_links()
104 |
105 | def mousePressEvent(self, event):
106 | super (GraphicsScene, self).mousePressEvent(event)
107 | self.auto_update_all_links()
108 |
109 | def dropEvent(self, event):
110 | super (GraphicsScene, self).dropEvent(event)
111 |
112 | def startDrag(self, event):
113 | pass
114 | '''
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/graphics_utils.py:
--------------------------------------------------------------------------------
1 | # Distributed under the MIT licesnse.
2 | # Copyright (c) 2013 Dave McCoy (dave.mccoy@cospandesign.com)
3 |
4 | #Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | #this software and associated documentation files (the "Software"), to deal in
6 | #the Software without restriction, including without limitation the rights to
7 | #use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | #of the Software, and to permit persons to whom the Software is furnished to do
9 | #so, subject to the following conditions:
10 | #
11 | #The above copyright notice and this permission notice shall be included in all
12 | #copies or substantial portions of the Software.
13 | #
14 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | #SOFTWARE.
21 |
22 | '''
23 | Log
24 | 6/17/2013: Initial commit
25 | '''
26 |
27 | import os
28 | import sys
29 |
30 | from PyQt4.QtCore import *
31 | from PyQt4.QtGui import *
32 |
33 |
34 | def add_label_to_rect(painter, rect, label):
35 | painter.save()
36 | r = QRectF(rect)
37 | br = painter.fontMetrics().boundingRect(label)
38 | scale = r.width() / br.width()
39 |
40 | if scale >= 1.0:
41 | painter.drawText(r, Qt.AlignCenter, label)
42 | else:
43 | font_height = br.height()
44 | box_height = r.height()
45 |
46 | painter.scale(scale, scale)
47 | br.translate(r.left() - br.left(), r.top() - br.top())
48 | br.translate(0.0, box_height * (0.5 / scale) - font_height / 2)
49 | painter.drawText(br, Qt.TextSingleLine, label)
50 |
51 | painter.restore()
52 |
53 |
54 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/graphics_view.py:
--------------------------------------------------------------------------------
1 | # Distributed under the MIT licesnse.
2 | # Copyright (c) 2013 Dave McCoy (dave.mccoy@cospandesign.com)
3 |
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | # this software and associated documentation files (the "Software"), to deal in
6 | # the Software without restriction, including without limitation the rights to
7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | # of the Software, and to permit persons to whom the Software is furnished to do
9 | # so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in all
12 | # copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | # SOFTWARE.
21 |
22 |
23 | #A huge thanks to 'Rapid GUI Programming with Python and Qt' by Mark Summerfield
24 |
25 | '''
26 | Log
27 | 6/05/2013: Initial commit
28 | '''
29 |
30 | __author__ = "Dave McCoy dave.mccoy@cospandesign.com"
31 |
32 | import sys
33 | import random
34 | import json
35 |
36 | from PyQt4.QtCore import *
37 | from PyQt4.QtGui import *
38 |
39 | from link import Link
40 | from box import Box
41 |
42 | class GraphicsView(QGraphicsView):
43 | def __init__(self, parent = None):
44 | super(GraphicsView, self).__init__(None)
45 | self.parent = parent
46 | self.setDragMode(QGraphicsView.RubberBandDrag)
47 | self.setRenderHint(QPainter.Antialiasing)
48 | self.setRenderHint(QPainter.TextAntialiasing)
49 | self.setAcceptDrops(True)
50 | self.icon = QIcon()
51 | self.controller = parent
52 | #self.setAcceptDrops(True)
53 | self.scale_min = .01
54 | self.scale_max = 1000
55 | self.dbg = False
56 | self.s = None
57 | #self.dbg = True
58 |
59 | def set_scene(self, scene):
60 | self.s = scene
61 |
62 | def set_controller(self, controller):
63 | self.controller = controller
64 |
65 | def wheelEvent(self, event):
66 | factor = 1.41 ** (-event.delta() / 240.0)
67 | self.scale(factor, factor)
68 |
69 | def dragEnterEvent(self, event):
70 | self.controller.drag_enter(event)
71 |
72 | def dragLeaveEvent(self, event):
73 | self.controller.drag_leave(event)
74 |
75 | def dragMoveEvent(self, event):
76 | if self.dbg: print "GV: dragMoveEvent"
77 | self.controller.drag_move(event)
78 |
79 | def mousePressEvent(self, event):
80 | if self.dbg: print "GV: mousePressEvent"
81 | super(GraphicsView, self).mousePressEvent(event)
82 |
83 | def mouseReleaseEvent(self, event):
84 | if self.dbg: print "GV: mouseReleaseEvent"
85 | super(GraphicsView, self).mouseReleaseEvent(event)
86 |
87 | def dropEvent(self, event):
88 | if self.dbg: print "GV: dropEvent"
89 | self.controller.drop_event(event)
90 |
91 | def keyPressEvent(self, event):
92 | if self.dbg: print "GV: Key press event"
93 | task_list = {
94 | Qt.Key_Plus: lambda: self._scale_view(1.2),
95 | Qt.Key_Minus: lambda: self._scale_view(1 / 1.2),
96 | #Qt.Key_Equal: lambda: self._scale_normal()
97 | Qt.Key_Equal: lambda: self._scale_fit()
98 | #TODO: Add more key interfaces here
99 | }
100 | if event.key() in task_list:
101 | task_list[event.key()]()
102 |
103 | else:
104 | #Pass the key event to the system
105 | QWidget.keyPressEvent(self, event)
106 |
107 | def _scale_view(self, scale_factor):
108 | """Scale the view"""
109 | if self.dbg: print "Canvas: Scale view by: %f" % scale_factor
110 |
111 | #check if the scale factor is alright
112 | factor = self.transform().scale(scale_factor, scale_factor).mapRect(QRectF(0, 0, 1, 1)).width()
113 |
114 | if factor > self.scale_min and factor < self.scale_max:
115 | #Scale factor is within limits
116 | self.scale(scale_factor, scale_factor)
117 | elif factor < self.scale_min:
118 | if self.dbg: print "GV: Scale too small: %f" % factor
119 | elif factor > self.scale_max:
120 | if self.dbg: print "GV: Scale too large: %f" % factor
121 |
122 | def _scale_normal(self):
123 | scale_factor = 1.0
124 | factor = self.transform().scale(scale_factor, scale_factor).mapRect(QRectF(0, 0, 1, 1)).width()
125 | scale_factor = 1.0 / factor
126 | if self.dbg: print "GV: Set scale back to 1.0"
127 | self.scale(scale_factor, scale_factor)
128 |
129 | def _scale_fit(self):
130 | if self.dbg: print "GV: Set scale to fit all items"
131 | self.fitInView(self.scene().sceneRect(), Qt.KeepAspectRatio)
132 |
133 | def paintEvent(self, event):
134 | #print 'paint event'
135 | #self.s.auto_update_all_links()
136 | super(GraphicsView, self).paintEvent(event)
137 |
138 | def resizeEvent(self, event):
139 | super(GraphicsView, self).resizeEvent(event)
140 | #print "resize event"
141 | if self.s is not None:
142 | self.s.auto_update_all_links()
143 | #self.fit_in_view()
144 | #print "fit in view"
145 |
146 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/graphics_widget.py:
--------------------------------------------------------------------------------
1 | import PyQt4.QtCore
2 | from PyQt4.QtGui import *
3 |
4 | from graphics_view import GraphicsView as GV
5 | from graphics_scene import GraphicsScene as GS
6 | from box import Box
7 |
8 | class GraphicsWidget (QWidget):
9 |
10 |
11 | def __init__(self, view = None, scene = None):
12 | QWidget.__init__(self, None)
13 | if view is None:
14 | self.view = GV(self)
15 | else:
16 | self.view = view
17 |
18 | if scene is None:
19 | self.scene = GS(self.view, self)
20 | else:
21 | self.scene = scene
22 | self.view.setScene(self.scene)
23 | self.prevPoint = PyQt4.QtCore.QPoint()
24 | layout = QHBoxLayout()
25 | layout.addWidget(self.view)
26 | self.setLayout(layout)
27 | self.controller = None
28 |
29 | def fit_view(self):
30 | self.view._scale_fit()
31 |
32 | def add_box(self, name, color = "black", position = PyQt4.QtCore.QPointF(0, 0)):
33 | b = Box(position = position,
34 | scene = self.scene,
35 | name = name,
36 | color = color)
37 | return b
38 |
39 | #Controller Functions
40 | def set_controller(self, controller):
41 | self.controller = controller
42 |
43 | def get_controller(self):
44 | return self.controller
45 |
46 | def drop_event(self, event):
47 | #Send an event to a controller
48 | print "Demo Graphics View: Drop Event"
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/link.py:
--------------------------------------------------------------------------------
1 | # Distributed under the MIT licesnse.
2 | # Copyright (c) 2013 Dave McCoy (dave.mccoy@cospandesign.com)
3 |
4 | #Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | #this software and associated documentation files (the "Software"), to deal in
6 | #the Software without restriction, including without limitation the rights to
7 | #use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | #of the Software, and to permit persons to whom the Software is furnished to do
9 | #so, subject to the following conditions:
10 | #
11 | #The above copyright notice and this permission notice shall be included in all
12 | #copies or substantial portions of the Software.
13 | #
14 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | #SOFTWARE.
21 |
22 |
23 | #A huge thanks to 'Rapid GUI Programming with Python and Qt' by Mark Summerfield
24 |
25 | #Thanks to http://github.com/woopeex edd repository... I love his picture too
26 |
27 | '''
28 | Log
29 | 6/05/2013: Initial commit
30 | '''
31 |
32 | __author__ = "Dave McCoy dave.mccoy@cospandesign.com"
33 |
34 |
35 | import PyQt4.QtCore
36 | from PyQt4.QtGui import *
37 |
38 |
39 | def enum(*sequential, **named):
40 | enums = dict(zip(sequential, range(len(sequential))), **named)
41 | return type('Enum', (), enums)
42 |
43 | side_type = enum( "top",
44 | "bottom",
45 | "right",
46 | "left")
47 |
48 | from defines import BEZIER_CONNECTION
49 | from defines import LINK_DEMO_COLOR
50 |
51 | padding = 20
52 |
53 |
54 | class BoxLinkError(Exception):
55 | """
56 | Errors associated with Links between boxes
57 |
58 | Error associated with:
59 | -invalid side
60 | -invalid link type
61 | """
62 | def __init__(self, value):
63 | self.value = value
64 | def __str__(self):
65 | return repr(self.value)
66 |
67 |
68 | class Link (QGraphicsItem):
69 |
70 | def __init__(self, from_box, to_box):
71 | super(Link, self).__init__(parent = None, scene = from_box.scene())
72 | self.rect = PyQt4.QtCore.QRectF(0, 0, 0, 0)
73 | self.from_box = from_box
74 | self.to_box = to_box
75 | self.scene().set_link_ref(self)
76 | self.setFlags
77 | self.setZValue(-1)
78 | #self.setFlags(QGraphicsItem.ItemIsSelectable |
79 | # QGraphicsItem.ItemIsFocusable)
80 | self.from_side = side_type.right
81 | self.to_side = side_type.left
82 |
83 | style = PyQt4.QtCore.Qt.SolidLine
84 | pen = QPen(style)
85 | pen.setColor(QColor("black"))
86 | self.pen = pen
87 | self.path = QPainterPath()
88 | self.track_nodes()
89 | self.bezier_en = BEZIER_CONNECTION
90 | self.start = PyQt4.QtCore.QPointF(0.0, 0.0)
91 | self.end = PyQt4.QtCore.QPointF(0.0, 0.0)
92 | self.start_offset = PyQt4.QtCore.QLineF(0.0, 0.0, 0.0, 0.0)
93 | self.end_offset = PyQt4.QtCore.QLineF(0.0, 0.0, 0.0, 0.0)
94 | self.line = PyQt4.QtCore.QLineF(self.start, self.end)
95 | self.dbg = False
96 | self.center_track = True
97 | self.directed = False
98 | self.padding = padding
99 |
100 | def en_center_track(self, enable):
101 | self.center_track = True
102 |
103 | def set_padding(self, p = 20):
104 | self.padding = padding
105 |
106 | def is_center_track(self):
107 | return self.center_track
108 |
109 | def en_bezier_connections(self, enable):
110 | if self.dbg: print "Enable Bezier Connections: %s" % str(enable)
111 | self.bezier_en = enable
112 |
113 | def bezier_connections(self):
114 | return self.bezier_en
115 |
116 | def from_box_side(self, side):
117 | self.from_side = side
118 |
119 | def to_box_side(self, side):
120 | self.to_side = side
121 |
122 | def set_directed(self, enable):
123 | self.directed = enable
124 |
125 | def track_nodes(self):
126 | self.update()
127 |
128 | def get_min_max_to(self):
129 | return self.end_offset.y()
130 |
131 | def set_start_end(self, start, end):
132 | self.prepareGeometryChange()
133 |
134 | self.start = start
135 | self.end = end
136 |
137 | s_offset_point = PyQt4.QtCore.QPointF(self.start.x() + 15, self.start.y())
138 | e_offset_point = PyQt4.QtCore.QPointF(self.end.x() - 15, self.end.y())
139 |
140 |
141 | self.start_offset = PyQt4.QtCore.QLineF(self.start, s_offset_point)
142 | self.end_offset = PyQt4.QtCore.QLineF(self.end, e_offset_point)
143 |
144 | self.line = PyQt4.QtCore.QLineF(self.start, self.end)
145 |
146 | def auto_update_center(self):
147 | #print "Auto update!"
148 | self.prepareGeometryChange()
149 |
150 | self.start = self.mapFromItem(self.from_box, self.from_box.side_coordinates(self.from_side))
151 | self.end = self.mapFromItem(self.to_box, self.from_box.side_coordinates(self.to_side))
152 |
153 |
154 | self.start_offset = PyQt4.QtCore.QLineF(self.start, PyQt4.QtCore.QPointF(self.start.x() + 15, self.start.y()))
155 | self.end_offset = PyQt4.QtCore.QLineF(self.end, PyQt4.QtCore.QPointF(self.end.x() - 15, self.end.y()))
156 |
157 | self.line = PyQt4.QtCore.QLineF(self.start, self.end)
158 |
159 | def update_direct(self, from_coordinate, to_coordinate):
160 | self.prepareGeometryChange()
161 |
162 | self.start = self.mapFromItem(self.from_box, from_coordinate)
163 | self.end = self.mapFromItem(self.to_box, to_coordinate)
164 |
165 |
166 | self.start_offset = PyQt4.QtCore.QLineF(self.start, PyQt4.QtCore.QPointF(self.start.x() + 15, self.start.y()))
167 | self.end_offset = PyQt4.QtCore.QLineF(self.end, PyQt4.QtCore.QPointF(self.end.x() - 15, self.end.y()))
168 |
169 |
170 | self.line = PyQt4.QtCore.QLineF(self.start, self.end)
171 |
172 | def boundingRect(self):
173 | extra = (self.pen.width() * 64) / 2
174 | return PyQt4.QtCore.QRectF(self.line.p1(),
175 | PyQt4.QtCore.QSizeF( self.line.p2().x() - self.line.p1().x(),
176 | self.line.p2().y() - self.line.p1().y())).normalized().adjusted(-extra,
177 | -extra,
178 | extra,
179 | extra)
180 |
181 | def shape(self):
182 | return QPainterPath(self.path)
183 |
184 | def paint_direct_connect(self, painter, width, color):
185 | center_point = PyQt4.QtCore.QLineF(self.start, self.end).pointAt(0.5)
186 | pen = self.pen
187 | pen.setJoinStyle(PyQt4.QtCore.Qt.RoundJoin)
188 | pen.setColor(color)
189 | pen.setWidth(width)
190 | painter.setPen(pen)
191 |
192 | path = QPainterPath()
193 |
194 | pstart = self.start_offset.p1()
195 | pend = self.end_offset.p1()
196 |
197 | pstart = PyQt4.QtCore.QPointF(pstart.x(), pend.y())
198 | path.moveTo(pstart)
199 | path.lineTo(pend)
200 |
201 | self.path = path
202 | painter.drawPath(path)
203 | if self.directed:
204 |
205 | height = 2 * pen.width()
206 | pa = None
207 | pb = None
208 | if self.to_side == side_type.left:
209 | pa = PyQt4.QtCore.QPointF(end_pad.x(), end_pad.y() + (height / 2))
210 | pb = PyQt4.QtCore.QPointF(end_pad.x(), end_pad.y() - (height / 2))
211 | if self.to_side == side_type.right:
212 | pa = PyQt4.QtCore.QPointF(end_pad.x(), end_pad.y() + (height / 2))
213 | pb = PyQt4.QtCore.QPointF(end_pad.x(), end_pad.y() - (height / 2))
214 |
215 | if self.to_side == side_type.top:
216 | pa = PyQt4.QtCore.QPointF(end_pad.x() + height / 2, end_pad.y())
217 | pb = PyQt4.QtCore.QPointF(end_pad.x() - height / 2, end_pad.y())
218 |
219 | if self.to_side == side_type.bottom:
220 | pa = PyQt4.QtCore.QPointF(end_pad.x() + height / 2, end_pad.y())
221 | pb = PyQt4.QtCore.QPointF(end_pad.x() - height / 2, end_pad.y())
222 |
223 | arrow_head = QPolygonF([pa, pb, pend])
224 | painter.drawPolygon(arrow_head)
225 |
226 |
227 |
228 | def paint_normal_connect(self, painter, width, color):
229 | center_point = PyQt4.QtCore.QLineF(self.start, self.end).pointAt(0.5)
230 | pen = self.pen
231 | pen.setJoinStyle(PyQt4.QtCore.Qt.RoundJoin)
232 | pen.setColor(color)
233 | pen.setWidth(width)
234 | painter.setPen(pen)
235 |
236 | path = QPainterPath()
237 |
238 | pstart = self.start_offset.p1()
239 | pend = self.end_offset.p1()
240 |
241 | start_pad = PyQt4.QtCore.QPointF(pstart.x() + self.padding, pstart.y())
242 | end_pad = PyQt4.QtCore.QPointF(pend.x() - self.padding, pend.y())
243 | center = PyQt4.QtCore.QLineF(pstart, pend).pointAt(0.5)
244 |
245 | one = (PyQt4.QtCore.QPointF(pend.x(), pstart.y()) + pstart) / 2
246 | two = (PyQt4.QtCore.QPointF(pstart.x(), pend.y()) + pend) / 2
247 |
248 | path.moveTo(pstart)
249 |
250 | if self.bezier_en:
251 | path.cubicTo(one, two, pend)
252 |
253 | else:
254 | if (pstart.x() + self.padding) < pend.x():
255 | path.lineTo(one)
256 | path.lineTo(two)
257 | path.lineTo(pend)
258 |
259 | else:
260 | start_pad = 0
261 | end_pad = 0
262 | if self.from_side == side_type.left:
263 | start_pad = PyQt4.QtCore.QPointF(pstart.x() - self.padding, pstart.y())
264 | if self.from_side == side_type.right:
265 | start_pad = PyQt4.QtCore.QPointF(pstart.x() + self.padding, pstart.y())
266 | if self.from_side == side_type.top:
267 | start_pad = PyQt4.QtCore.QPointF(pstart.x(), pstart.y() - self.padding)
268 | if self.from_side == side_type.bottom:
269 | start_pad = PyQt4.QtCore.QPointF(pstart.x(), pstart.y() + self.padding)
270 |
271 |
272 | if self.to_side == side_type.left:
273 | end_pad = PyQt4.QtCore.QPointF(pend.x() - self.padding, pend.y())
274 | if self.to_side == side_type.right:
275 | end_pad = PyQt4.QtCore.QPointF(pend.x() + self.padding, pend.y())
276 | if self.to_side == side_type.top:
277 | end_pad = PyQt4.QtCore.QPointF(pend.x(), pend.y() - self.padding)
278 | if self.to_side == side_type.bottom:
279 | end_pad = PyQt4.QtCore.QPointF(pend.x(), pend.y() + self.padding)
280 |
281 |
282 | center = PyQt4.QtCore.QLineF(pstart, pend).pointAt(0.5)
283 | one = (PyQt4.QtCore.QPointF(start_pad.x(), center.y()))
284 | two = (PyQt4.QtCore.QPointF(end_pad.x(), center.y()))
285 | path.lineTo(start_pad)
286 | path.lineTo(one)
287 | path.lineTo(two)
288 | path.lineTo(end_pad)
289 | path.lineTo(pend)
290 |
291 | self.path = path
292 | painter.drawPath(path)
293 | if self.directed:
294 |
295 | height = 2 * pen.width()
296 | pa = None
297 | pb = None
298 | if self.to_side == side_type.left:
299 | pa = PyQt4.QtCore.QPointF(end_pad.x(), end_pad.y() + (height / 2))
300 | pb = PyQt4.QtCore.QPointF(end_pad.x(), end_pad.y() - (height / 2))
301 | if self.to_side == side_type.right:
302 | pa = PyQt4.QtCore.QPointF(end_pad.x(), end_pad.y() + (height / 2))
303 | pb = PyQt4.QtCore.QPointF(end_pad.x(), end_pad.y() - (height / 2))
304 |
305 | if self.to_side == side_type.top:
306 | pa = PyQt4.QtCore.QPointF(end_pad.x() + height / 2, end_pad.y())
307 | pb = PyQt4.QtCore.QPointF(end_pad.x() - height / 2, end_pad.y())
308 |
309 | if self.to_side == side_type.bottom:
310 | pa = PyQt4.QtCore.QPointF(end_pad.x() + height / 2, end_pad.y())
311 | pb = PyQt4.QtCore.QPointF(end_pad.x() - height / 2, end_pad.y())
312 |
313 | arrow_head = QPolygonF([pa, pb, pend])
314 | painter.drawPolygon(arrow_head)
315 |
316 |
317 | def paint(self, painter, option, widget):
318 | self.paint_normal_connect(painter, 4, LINK_DEMO_COLOR)
319 | #self.paint_direct_connect(painter, 4, LINK_DEMO_COLOR)
320 |
321 | def get_inverted_side(side):
322 | if side == side_type.top:
323 | return side_type.bottom
324 | if side == side_type.bottom:
325 | return side_type.top
326 | if side == side_type.right:
327 | return side_type.left
328 | if side == side_type.left:
329 | return side_type.right
330 | #This should be an error
331 | raise BoxLinkError("Invalid side: %s" % str(side))
332 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/module_box.py:
--------------------------------------------------------------------------------
1 | __author__ = "Dave McCoy dave.mccoy@cospandesign.com"
2 |
3 | import PyQt4.QtCore
4 |
5 | from box import Box
6 | from port import Port
7 |
8 |
9 | class Port(object):
10 | def __init__(self, name, direction, group=None, port_range=1):
11 | self.name = name
12 | self.direction = direction
13 | self.group = group
14 |
15 |
16 | class Signal(object):
17 | def __init__(self, name, cnct_module, cnct_module_port_name, signal_range=[1], start_range=0):
18 | self.name = name
19 | self.module_
20 | self.port_name = name
21 |
22 |
23 | class ModuleBox(object, Box):
24 | def __init__(self,
25 | name,
26 | color,
27 | scene,
28 | graphics_widget,
29 | position=PyQt4.QtCore.QPointF(0, 0),
30 | user_data=None):
31 | super(ModuleBox, self).__init__(position=position,
32 | scene=scene,
33 | name=name,
34 | color=color,
35 | user_data=user_data)
36 | self.ports = {}
37 | self.signals = {}
38 | self.connections = {}
39 | self.removed = False
40 |
41 | def add_port(self, port_name, direction, group=None, port_range=1):
42 | self.ports[port_name] = Port(port_name, direction, group, port_range)
43 |
44 | def remove_port(self, port_name):
45 | if port_name in self.ports:
46 | del (self.ports[port_name])
47 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/port.py:
--------------------------------------------------------------------------------
1 | # Distributed under the MIT licesnse.
2 | # Copyright (c) 2013 Dave McCoy (dave.mccoy@cospandesign.com)
3 |
4 | #Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | #this software and associated documentation files (the "Software"), to deal in
6 | #the Software without restriction, including without limitation the rights to
7 | #use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | #of the Software, and to permit persons to whom the Software is furnished to do
9 | #so, subject to the following conditions:
10 | #
11 | #The above copyright notice and this permission notice shall be included in all
12 | #copies or substantial portions of the Software.
13 | #
14 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | #SOFTWARE.
21 |
22 |
23 | from box import Box
24 |
25 | from link import side_type as st
26 | from link import Link
27 |
28 | from PyQt4.Qt import *
29 | from PyQt4.QtCore import *
30 | from PyQt4.QtGui import *
31 |
32 | from defines import direc
33 |
34 | import graphics_utils as gu
35 |
36 | input_color = "black"
37 | output_color = "black"
38 | inout_color = "black"
39 |
40 | highlight_width = 8
41 |
42 | class Port (Box):
43 |
44 | def __init__(self, name, position, y_pos, direction, scene, parent):
45 | QGraphicsItem.__init__(self)
46 | self.color = input_color
47 | self.direction = direction
48 | if direction == direc.input:
49 | self.color = input_color
50 | elif direction == direc.output:
51 | self.color = output_color
52 | elif direction == direc.inout:
53 | self.color = inout_color
54 |
55 | self.box_name = name
56 | self.activate = False
57 |
58 | self.user_data = 'port'
59 | self.rect = QRectF(0, 0, 100, 50)
60 | self.s = scene
61 | self.link = None
62 | self.slave = parent
63 |
64 | #User Cannot Move Directly
65 | self.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable)
66 | scene.addItem(self)
67 |
68 | #Tool Tip
69 | self.setToolTip("Name: %s" % self.box_name)
70 |
71 | self.style = Qt.SolidLine
72 | self.setPos(position)
73 |
74 | #Setup The Drawing Rectangles
75 | self.label_rect = QRectF(0, 0, 100, 50)
76 | self.label_rect.setHeight(self.label_rect.height() / 2)
77 |
78 | self.y_pos = y_pos
79 | self.dbg = False
80 |
81 | def mousePressEvent(self, event):
82 | if self.dbg: print "Port: mousePressEvent()"
83 |
84 | return QGraphicsItem.mousePressEvent(self, event)
85 |
86 |
87 |
88 | #def connect_port(self, port):
89 | # self.link = Link(self, port)
90 | # self.s.set_link_ref(self.link)
91 | # self.link.from_box_side(st.right)
92 | # self.link.to_box_size(st.left)
93 | # self.link.en_bezier_connections(False)
94 | # self.update_link()
95 |
96 | def paint(self, painter, option, widget):
97 | if self.activate:
98 | self.paint_selected(painter, option, widget)
99 | else:
100 | self.paint_not_selected(painter, option, widget)
101 |
102 | def paint_selected(self, painter, option, widget):
103 | m = QMatrix(painter.matrix())
104 |
105 | pen = QPen(self.style)
106 | pen.setColor(Qt.black)
107 | pen.setWidth(1)
108 |
109 | if option.state & QStyle.State.Selected:
110 | #Selected
111 | pen.setColor(QColor("black"))
112 | pen.setWidth(highlight_width)
113 |
114 | painter.setPen(pen)
115 |
116 | painter.drawRect(self.rect)
117 | painter.fillRect(self.rect, QColor(self.color))
118 |
119 | #Draw Text
120 | pen.setColor(Qt.black)
121 | painter.setPen(pen)
122 | gu.add_label_to_rect(painter, self.label_rect, self.box_name)
123 |
124 |
125 | def paint_not_selected(self, painter, option, widget):
126 | pen = QPen(self.style)
127 | pen.setColor(Qt.black)
128 | pen.setWidth(1)
129 | if option.state & QStyle.State_Selected:
130 | #Selected
131 | pen.SetColor(QColor("black"))
132 | pen.setWidth(highlight_width)
133 |
134 | painter.setPen(pen)
135 | #Draw all the boxes
136 | painter.drawRect(self.rect)
137 | painter.fillRect(self.rect, QColor(self.color))
138 |
139 | pen.setColor(Qt.black)
140 | painter.setPen(pen)
141 |
142 | gu.add_label_to_rect(painter, self.rect, self.box_name)
143 |
144 |
145 |
--------------------------------------------------------------------------------
/verilogviz/common/pvg/visual_graph/port_box.py:
--------------------------------------------------------------------------------
1 | '''
2 | Log
3 | 1/09/2014: Initial commit
4 | '''
5 |
6 | __author__ = "Dave McCoy dave.mccoy@cospandesign.com"
7 |
8 | from PyQt4.QtCore import *
9 | from PyQt4.QtGui import *
10 | from box import Box
11 |
12 | class PortBox(Box):
13 |
14 | def __init__(self,
15 | name,
16 | color,
17 | scene = None,
18 | graphics_widget = None,
19 | position = QPointF(0, 0),
20 | user_data = None):
21 | if scene is None:
22 | if graphics_widget is None:
23 | raise Exception("Need a better exception")
24 | scene = graphics_widget.scene
25 | super(PortBox, self).__init__(position = position,
26 | scene = scene,
27 | name = name,
28 | color = color,
29 | user_data = user_data)
30 |
31 | self.ports = {}
32 | self.ports["input"] = {}
33 | self.ports["output"] = {}
34 | self.ports["inout"] = {}
35 |
36 | self.ports_group = ["ngroup"]
37 |
38 | self.connections = {}
39 | self.removed = False
40 | #Generate a random number so cores of the same name can exist
41 |
42 | def add_port_group(self, name):
43 | if name not in self.ports_group:
44 | self.ports_group.append(name)
45 |
46 | def remove_port_group(self, name):
47 | if name in self.ports_group:
48 | self.ports.remove(name)
49 |
50 | def add_port(self, direction, name, bus_range = 1, port_group = None):
51 | if((direction != "input") or
52 | (direction != "output") or
53 | (direction != "inout")):
54 | raise Exception("%s is not direction!" % direction)
55 |
56 | self.ports[direction][name] = {}
57 | self.ports[direction][name]["range"] = {}
58 |
59 | if self.port_group is None:
60 | #self.ports_group["ngroup"] =
61 | pass
62 |
63 | def remove_port(self, direction, name):
64 | if ((direction != "input") or
65 | (direction != "output") or
66 | (direction != "inout")):
67 | raise Exception ("%s is not direction!" % direction)
68 |
69 | if name not in self.ports[direction]:
70 | raise Exception ("%s is not inside ports" % name)
71 |
72 | del (self.ports[direction][name])
73 |
74 | def add_connection(self, output_port, port_box, input_port):
75 | if ((output_port not in self.ports["output"]) or
76 | (output_port not in self.ports["inout"])):
77 | if output_port not in self.connections:
78 | self.connections[output_port] = {}
79 |
80 | if port_box not in self.connections[output_port]:
81 | self.connections[output_port][port_box] = []
82 |
83 | if input_port not in self.connections[output_port][port_box]:
84 | self.connections[output_port][port_box].append(input_port)
85 |
86 | def remove_connection(self, port_box, input_port = None):
87 | if ((port_box not in self.connections["output"]) and
88 | (port_box not in self.connections["inout"])):
89 | return
90 | if input_port is None:
91 | #The user is deleting all connections to the specified box
92 | if port_box in self.connections["output"]:
93 | del(self.connections["output"][port_box])
94 | if port_box in self.connections["inout"]:
95 | del(self.connections["inout"][port_box])
96 | return
97 |
98 | if ((input_port not in self.connections["output"][port_box]) and
99 | (input_port not in self.connections["inout"][port_box])):
100 | #Couldn't find the port in the list of connections
101 | return
102 |
103 | if port_box not in self.connections["output"]:
104 | #Remove the specified connection
105 | self.connections[output_port][port_box].remove(input_port)
106 | if len(self.connections[output_port][port_box]) == 0:
107 | #No more connections remove the rest
108 | del(self.connections[output_port][port_box])
109 |
110 | if port_box not in self.connections["inout"]:
111 | #Remove the specified connection
112 | self.connections[output_port][port_box].remove(input_port)
113 | if len(self.connections[output_port][port_box]) == 0:
114 | #No more connections remove the rest
115 | del(self.connections[output_port][port_box])
116 |
117 | def remove(self):
118 | self.removed = True
119 |
120 | def is_removed(self):
121 | #We need this because during an update the graph may still have a reference to this connection
122 | #if so we need to remove it from our own graph
123 | return self.removed()
124 |
125 | def get_global_port_pos(self, direction, port_name):
126 | p = self.ports[direction][port_name]
127 |
128 | #Paint
129 | def paint(self, painter, option, widget):
130 |
131 | #Draw Rectangle
132 | pen = QPen(self.style)
133 | pen.setColor(Qt.black)
134 | self.set_pen_border(option, pen)
135 |
136 | painter.setPen(pen)
137 | painter.drawRect(self.rect)
138 | painter.fillRect(self.rect, QColor(self.color))
139 | painter.setFont(self.text_font)
140 |
141 | #draw text
142 | pen.setColor(Qt.black)
143 | painter.setPen(pen)
144 |
145 | self.add_label_to_rect(painter, self.rect, self.box_name)
146 |
147 | for group in self.ports_group:
148 | print "Port Group: %s" % group
149 | #Create a sub box for everything
150 | #Get the size of this port box
151 | #First find the number of input and output ports
152 |
153 |
154 | '''
155 | for i in range(0, len(self.arbiter_masters)):
156 | #print "Add Arbiter %s" % self.arbiter_masters[i]
157 | arb_rect = QRectF(ARB_MASTER_RECT)
158 |
159 | am = ArbiterMaster(name = self.arbiter_masters[i],
160 | position = arb_pos,
161 | y_pos = position.y(),
162 | scene = self.scene(),
163 | slave = self)
164 |
165 | #am.movable(False)
166 |
167 | self.arbiter_boxes.append(am)
168 | al = Link(self, am, self.scene(), lt.arbiter_master)
169 | al.from_box_side(st.right)
170 | al.to_box_side(st.left)
171 |
172 | al.en_bezier_connections(True)
173 | self.links[am] = al
174 |
175 | #Progress the position
176 | arb_pos = QPointF(arb_pos.x(), arb_pos.y() + arb_rect.height() + ARB_MASTER_VERTICAL_SPACING)
177 | '''
178 |
179 |
180 |
--------------------------------------------------------------------------------
/verilogviz/include_path_dialog.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import logging
4 |
5 | from PyQt4.Qt import *
6 | from PyQt4.QtCore import *
7 | from PyQt4.QtGui import *
8 |
9 |
10 | class IncludePathDialog(QDialog):
11 |
12 |
13 |
14 | def __init__(self, parent):
15 | super IncludePathDialog(self).__init__(parent)
16 | self.logger = logging.getLogger("verilogviz")
17 | layout = QVBoxLayout()
18 |
19 | path_layout = QHBoxLayout()
20 | self.tree_view = QLabel("HI")
21 | add_remove_button_layout = QVBoxLayout()
22 | self.add_button = QPushButton("+")
23 | self.remove_button = QPushButton("-")
24 | self.clear_button = QPushButton("Clear")
25 |
26 | add_remove_button_layout.addWidget(self.add_button)
27 | add_remove_button_layout.addWidget(self.remove_button)
28 | add_remove_button_layout.addWidget(self.clear_button)
29 |
30 | self.include_dir_list = QListView()
31 |
32 | self.priority_layout = QVerticalLayout()
33 | self.up_button = QPushButton("Move Up")
34 | self.up_button.setEnabled(False)
35 | self.down_button = QPushButton("Move Down")
36 | self.down_button.setEnabled(False)
37 | self.priority_layout.addWidget(self.up_button)
38 | self.priority_layout.addWidget(self.down_button)
39 |
40 | path_layout.addWidget(tree_view)
41 | path_layout.addLayout(add_remove_button_layout)
42 | path_layout.addWidget(include_dir_list)
43 | path_layout.addLayout(priority_layout)
44 |
45 | layout.addLayout(path_layout)
46 | self.setLayout(layout)
47 | self.include_paths = []
48 | self.path = os.path.curdir()
49 | self.logger.debug("Initial Path: %s" % self.path)
50 |
51 | def set_start_path(self, path):
52 | self.path = path
53 |
54 | def set_path_list(self, include_paths):
55 | self.include_paths = include_paths
56 |
57 | def get_path_list(self):
58 | return self.include_paths
59 |
60 |
61 |
--------------------------------------------------------------------------------
/verilogviz/model/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CospanDesign/verilog-visualizer/be9fb8b35b46bb0162963888f9a4e9461409623e/verilogviz/model/__init__.py
--------------------------------------------------------------------------------
/verilogviz/model/arbiter.py:
--------------------------------------------------------------------------------
1 | #Distributed under the MIT licesnse.
2 | #Copyright (c) 2012 Cospan Design (dave.mccoy@cospandesign.com)
3 |
4 | #Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | #this software and associated documentation files (the "Software"), to deal in
6 | #the Software without restriction, including without limitation the rights to
7 | #use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | #of the Software, and to permit persons to whom the Software is furnished to do
9 | #so, subject to the following conditions:
10 | #
11 | #The above copyright notice and this permission notice shall be included in all
12 | #copies or substantial portions of the Software.
13 | #
14 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | #SOFTWARE.
21 |
22 | import os
23 | import string
24 | from string import Template
25 |
26 | """Arbiter Factory
27 |
28 | Analyzes the project tags and determine if one or many arbiters are required
29 | """
30 |
31 | __author__ = 'dave.mccoy@cospandesign.com (Dave McCoy)'
32 |
33 | """Changes:
34 | 06/12/2012
35 | -Added Documentation and licsense
36 | """
37 |
38 |
39 | def get_number_of_arbiter_hosts(module_tags = {}, debug = False):
40 | """get_number_of_arbiter_hosts
41 |
42 | returns the number of arbiter hosts found inside the module
43 |
44 | Args:
45 | module_tags: the tags for this module
46 | can be obtained with vutils.get_module_tags
47 |
48 | Return:
49 | the number of arbiter hosts associated with this module
50 |
51 | Raises:
52 | Nothing
53 | """
54 |
55 | #go through all the ports and verify that after the first
56 | #'_' there is a a wbm and the wbm has all the arbiter
57 | #host components
58 | #debug = False
59 |
60 | if debug:
61 | print "Module Name: %s" % (module_tags["module"])
62 | print "ports: "
63 |
64 | wb_bus = [ "o_we",
65 | "i_dat",
66 | "i_int",
67 | "i_ack",
68 | "o_adr",
69 | "o_stb",
70 | "o_cyc",
71 | "o_dat",
72 | "o_sel"
73 | ]
74 | possible_prefix = {}
75 | prefixes = []
76 | #debug = True
77 | for io_ports in module_tags["ports"]:
78 | if debug:
79 | print "\tio_ports: " + io_ports
80 | for name in module_tags["ports"][io_ports]:
81 | if debug:
82 | print "\t\t: " + str(name)
83 | #throw out obvious false
84 | if "_" not in name:
85 | continue
86 |
87 | for wbm_wire in wb_bus:
88 | if wbm_wire in name:
89 | #io = name.partition("_")[0]
90 | #if io not in wbm_wire[0]:
91 | # continue
92 |
93 | #prefix = name.partition("_")[2]
94 | prefix = name.partition("_")[0]
95 | #wbm_post = prefix.partition("_")[1] + prefix.partition("_")[2]
96 | wbm_post = wbm_wire
97 | prefix = prefix.partition(wbm_wire)[0]
98 | if prefix not in possible_prefix.keys():
99 | possible_prefix[prefix] = list(wb_bus)
100 | if debug:
101 | print "found a possible arbiter: %s" % (prefix)
102 |
103 | #wbm_post = name.partition("_")[2]
104 | if wbm_post in possible_prefix[prefix]:
105 | possible_prefix[prefix].remove(wbm_post)
106 |
107 | for prefix in possible_prefix.keys():
108 | if debug:
109 | print "examining: %s" % (prefix)
110 | print "\tlength of prefix list: %s" % (str(possible_prefix[prefix]))
111 | if len (possible_prefix[prefix]) == 0:
112 | if debug:
113 | print "%s is an arbiter host" % (prefix)
114 | prefixes.append(prefix)
115 |
116 | #debug = True
117 | return prefixes
118 |
119 |
120 | def is_arbiter_host(module_tags = {}, debug = False):
121 | """is_arbiter_host
122 |
123 | Determins if a slave can be an arbiter host
124 |
125 | Args:
126 | module_tags: The tags that are associated with this modue
127 | can be obtained with vutils.get_module_tags
128 |
129 | Return:
130 | True: Slave is an arbiter host
131 | False: Slave is not an arbiter host
132 |
133 | Raises:
134 | Nothing
135 | """
136 | return (len(get_number_of_arbiter_hosts(module_tags, debug)) > 0)
137 |
138 | def is_arbiter_required(tags = {}, debug = False):
139 | """is_arbiter_required
140 |
141 | Analyzes the project tags and determines if an arbiter is requried
142 |
143 | Args:
144 | tags: Project tags
145 |
146 | Return:
147 | True: An arbiter is required
148 | False: An arbiter is not required
149 |
150 | Raises:
151 | Nothing
152 | """
153 | if debug:
154 | print "in is_arbiter_required()"
155 | #count the number of times a device is referenced
156 |
157 | #SLAVES
158 | slave_tags = tags["SLAVES"]
159 | for slave in slave_tags:
160 | if debug: print "found slave " + str(slave)
161 | if ("BUS" in slave_tags[slave]):
162 | if (len(slave_tags[slave]["BUS"]) > 0):
163 | return True
164 | #XXX: FOR THIS FIRST ONE YOU MUST SPECIFIY THE PARTICULAR MEMORY SLAVE AS APPOSED TO JUST MEMORY WHICH IS THAT ACTUAL MEMORY INTERCONNECT
165 |
166 | return False
167 |
168 | def generate_arbiter_tags(tags = {}, debug = False):
169 | """generate_arbiter_tags
170 |
171 | generate a dictionary (tags) that is required to generate all the
172 | arbiters and how and where to connect all the arbiters
173 |
174 | Args:
175 | tags: Project tags
176 |
177 | Return:
178 | dictionary of arbiters to generating
179 |
180 | Raises:
181 | Nothing
182 | """
183 | arb_tags = {}
184 | if (not is_arbiter_required(tags)):
185 | return {}
186 |
187 | if debug:
188 | print "arbitration is required"
189 |
190 | slave_tags = tags["SLAVES"]
191 | for slave in slave_tags:
192 | if ("BUS" in slave_tags[slave]):
193 | if (len(slave_tags[slave]["BUS"]) == 0):
194 | continue
195 | if debug:
196 | print "slave: " + slave + " is an arbtrator master"
197 | for bus in slave_tags[slave]["BUS"].keys():
198 | if debug:
199 | print "bus for " + slave + " is " + bus
200 | arb_slave = slave_tags[slave]["BUS"][bus]
201 | if debug:
202 | print "adding: " + arb_slave + " to the arb_tags for " + bus
203 |
204 | if (not already_existing_arb_bus(arb_tags, arb_slave)):
205 | #create a new list
206 | arb_tags[arb_slave] = {}
207 |
208 | arb_tags[arb_slave][slave] = bus
209 |
210 | return arb_tags
211 |
212 | def already_existing_arb_bus(arb_tags = {}, arb_slave = "", debug = False):
213 | """already_existing_arb_bus
214 |
215 | Check if the arbitrated slave already exists in the arbiter tags
216 |
217 | Args:
218 | arb_tags: arbiter tags
219 | arb_slave: possible arbiter slave
220 |
221 | Return:
222 | True: There is already an arbiter bus associated with this slave
223 | False: There is not already an arbiter bus associated with this slave
224 |
225 | Raises:
226 | Nothing
227 | """
228 | for arb_item in arb_tags.keys():
229 | if (arb_item == arb_slave):
230 | return True
231 | return False
232 |
233 |
--------------------------------------------------------------------------------
/verilogviz/model/module.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import logging
4 |
5 | import verilog_utils as vutils
6 | import preprocessor
7 | import utils
8 | from utils import ModuleError
9 | import networkx as nx
10 |
11 |
12 | def find_module(path, user_paths, instance_name = None):
13 | module_tags = vutils.get_module_tags(filename = path, user_paths = user_paths)
14 | return Module(module_tags, path, user_paths, instance_name, is_include_file = False, depth = 0)
15 |
16 | class Module (object):
17 |
18 | def __init__(self, module_tags, path, user_paths, instance_name = None, is_include_file = False, depth = 0):
19 | super (Module, self).__init__()
20 | self.depth = depth
21 | self.logger = logging.getLogger("verilogviz")
22 | self.instance_name = instance_name
23 | self.vpos = 0
24 |
25 | self.module_tags = module_tags
26 | self.path = path
27 | if self.path is not None:
28 | self.path = str(self.path)
29 | self.user_paths = user_paths
30 | self.include_file = is_include_file
31 | self.module_exists = True
32 | if path is None:
33 | self.module_exists = False
34 | self.graph = None
35 | self.refresh()
36 |
37 | def is_include_file(self):
38 | return self.include_file
39 |
40 | def exists(self):
41 | return self.module_exists
42 |
43 | def set_user_paths(self, user_paths):
44 | self.user_paths = user_paths
45 | self.refresh()
46 |
47 | def name(self):
48 | if self.instance_name is None:
49 | return self.module_tags["module"]
50 | return self.instance_name
51 |
52 | def get_type(self):
53 | return self.module_tags["module"]
54 |
55 | def get_module_tags(self):
56 | return self.module_tags
57 |
58 | def refresh(self):
59 | self.graph = nx.DiGraph()
60 | self.generate_dependency_graph()
61 |
62 | def get_path(self):
63 | return self.path
64 |
65 | def get_depth(self):
66 | return self.depth
67 |
68 | def set_vpos(self, pos):
69 | self.vpos = pos
70 |
71 | def get_vpos(self):
72 | return self.vpos
73 |
74 | def generate_dependency_graph(self):
75 | self.graph.add_node(id(self))
76 | self.graph.node[id(self)] = self
77 | if self.path is not None:
78 | self._resolve_dependency_for_module(self.path)
79 |
80 | def get_module_graph(self):
81 | return self.graph
82 |
83 | def _resolve_dependency_for_module(self, module_path):
84 | vpos = 0
85 | buf = ""
86 | try:
87 | filein = open(module_path)
88 | buf = str(filein.read())
89 | filein.close()
90 | except IOError as e:
91 | raise ModuleError("File %s not found" % module_path)
92 |
93 | buf = utils.remove_comments(buf)
94 | include_buf = buf
95 | while len(include_buf.partition("`include")[2]) > 0:
96 | ifname = include_buf.partition("`include")[2]
97 | ifname = ifname.splitlines()[0]
98 | ifname = ifname.strip()
99 | ifname = ifname.strip("\"")
100 | #self.logger.debug("Found ifname: %s" % ifname)
101 | module = None
102 | try:
103 | filename = utils.find_module_filename(ifname, self.user_paths)
104 | module_tags = vutils.get_module_tags(filename, self.user_paths)
105 | module = Module(module_tags, path = filename, user_paths = self.user_paths, instance_name = None, is_include_file = True, depth = self.depth + 1)
106 | except ModuleError:
107 | self.logger.debug("Didn't find verilog module with filename: %s" % ifname)
108 | module_tags = {}
109 | module_tags["module"] = ifname
110 | module = Module(module_tags, path = None, user_paths = self.user_paths, instance_name = None, is_include_file = True, depth = self.depth + 1)
111 |
112 | module.set_vpos(vpos)
113 | vpos + 1
114 | include_buf = include_buf.partition("`include")[2]
115 | for n in module.get_module_graph().nodes():
116 | #m = module.get_module_graph().node[n]
117 | #self.graph.add_node(n)
118 | #self.graph.node[n] = m
119 | #if n not in self.graph.nodes():
120 | m = module.get_module_graph().node[n]
121 | self.graph.add_node(id(m))
122 | self.graph.node[id(m)] = m
123 |
124 |
125 |
126 | self.graph.add_edges_from(module.get_module_graph().edges())
127 | self.graph.add_edge(id(self), id(module))
128 |
129 | self.logger.debug("Looking for actual modules...")
130 | module_dict = self.find_modules_within_buffer(buf.partition(")")[2])
131 |
132 | for instance in module_dict:
133 | module_type = module_dict[instance]
134 | self.logger.info("module_type: %s" % module_type)
135 | module = None
136 | #print "Module Type: %s" % module_type
137 | try:
138 | filename = utils.find_module_filename(module_type, self.user_paths)
139 | #print "Filename: %s" % filename
140 | module_tags = vutils.get_module_tags(filename, user_paths = self.user_paths)
141 | #print "got tags..."
142 | module = Module(module_tags, path = filename, user_paths = self.user_paths, instance_name = instance, is_include_file = False, depth = self.depth + 1)
143 | except ModuleError:
144 | #self.logger.debug("Didn't find verilog module with filename :%s" % module_type)
145 | module_tags = {}
146 | module_tags["module"] = module_type
147 | module = Module(module_tags, path = None, user_paths = self.user_paths, instance_name = instance, is_include_file = False, depth = self.depth + 1)
148 |
149 | for n in module.get_module_graph().nodes():
150 | m = module.get_module_graph().node[n]
151 | self.graph.add_node(id(m))
152 | self.graph.node[id(m)] = m
153 |
154 | module.set_vpos(vpos)
155 | vpos + 1
156 | #self.graph.add_nodes_from(module.get_module_graph().nodes())
157 | self.graph.add_edges_from(module.get_module_graph().edges())
158 | self.graph.add_edge(id(self), id(module))
159 |
160 | def find_modules_within_buffer(self, buf):
161 | buf = buf.strip()
162 | buf = buf.partition(";")[2]
163 | done = False
164 | module_dict = {}
165 |
166 | while not done:
167 | lines = buf.splitlines()
168 | module_token = ""
169 | parameter_found = False
170 | parameter_flag = False
171 | parameter_debt = None
172 | for line in lines:
173 | line = line.strip()
174 | if "#" in line:
175 | if "<=" in line:
176 | continue;
177 | #print "Found #: %s" % line
178 | line = line.partition("#")[2]
179 | parameter_found = True
180 |
181 | if parameter_found:
182 | #print "line: %s" % line
183 | for c in line:
184 | if c == "(":
185 | #print "+1"
186 | if parameter_debt is None:
187 | parameter_debt = 1
188 | else:
189 | parameter_debt += 1
190 | if c == ")":
191 | #print "-1"
192 | parameter_debt -= 1
193 |
194 | if parameter_debt == 0:
195 | parameter_found = False
196 | parameter_flag = True
197 | break
198 |
199 | continue
200 |
201 |
202 | if line.startswith(".") and line.endswith(","):
203 | module_token = line
204 | break
205 |
206 | if len(module_token) == 0:
207 | done = True
208 |
209 | else:
210 | #Found a possible occurance of a module
211 | print "MODULE TOKEN: %s" % module_token
212 | module_buf = buf.partition(module_token)[0].strip()
213 | buf = buf.partition(module_token)[2]
214 | buf = buf.partition(";")[2]
215 |
216 | module_buf = module_buf[:module_buf.rfind("(")].strip()
217 | #print "module_buffer: %s" % module_buf
218 | if module_buf.startswith("`"):
219 | continue
220 |
221 | module_type = ""
222 | module_instance = ""
223 |
224 | if parameter_flag:
225 | #print "Parameter type: %s" % str(module_buf)
226 | module_type = module_buf.partition ("#")[0].strip("() ")
227 | module_instance = module_buf.split()[-1].strip("() ")
228 | #print "parameter type: %s" % module_type
229 | #print "parameter instance: %s" % module_instance
230 | else:
231 | module_buf = module_buf.splitlines()[-1]
232 | module_type = module_buf.partition(" ")[0].strip("() ")
233 | module_instance = module_buf.partition(" ")[2].strip("() ")
234 | #print "non parameter type: %s" % module_type
235 | #print "non parameter instance: %s" % module_instance
236 | if len(module_type) > 50:
237 | print "*** module type: %s" % module_type
238 | print "*** module instance: %s" % module_instance
239 |
240 |
241 | self.logger.debug("Adding: %s to dictionary with instance name: %s" % (module_type, module_instance))
242 | module_dict[module_instance] = module_type
243 |
244 | return module_dict
245 |
246 | def _find_module_dependency(self, module_buffer):
247 | pass
248 |
249 |
--------------------------------------------------------------------------------
/verilogviz/model/module_list_model.py:
--------------------------------------------------------------------------------
1 | from PyQt4.Qt import *
2 | from PyQt4.QtCore import *
3 | from PyQt4.QtGui import *
4 |
5 |
6 | class ModuleListModel(QAbstractListModel):
7 |
8 | def __init__(self, parent = None):
9 | super (ModuleListModel, self).__init__(parent)
10 | self.modules = []
11 |
12 | def rowCount(self, parent):
13 | return len(self.modules)
14 |
15 | def data(self, index, role = Qt.DisplayRole):
16 | data = self.modules[index.row()]
17 | if role != Qt.DisplayRole:
18 | return QVariant()
19 |
20 | return QVariant(data.name())
21 |
22 | def flags(self, index):
23 | f = super (ModuleListModel, self).flags(index)
24 | return f
25 |
26 | def headerData(self, sections, orientation):
27 | if orientation == Qt.Vertical:
28 | return QVariant()
29 | else:
30 | return QVariant(QString("Project Name"))
31 |
32 | def removeRows(self, row, count, parent = None):
33 | self.beginRemoveRows()
34 | del(self.module[row])
35 | self.endRemoveRows()
36 | return True
37 |
38 | def addItem(self, item):
39 | index = self.createIndex(0, 0)
40 |
41 | self.beginInsertRows(index, len(self.modules), len(self.modules))
42 | self.modules.append(item)
43 | self.endInsertRows()
44 |
45 | def remove_module_by_name(self, name):
46 | module = self.get_module_by_name(name)
47 | row = self.modules.index(module)
48 | self.removeRows(row, 1)
49 |
50 | def get_module_by_name(self, name):
51 | if not in_list(name):
52 | raise LookupError("Module: %s is not in list" % name)
53 |
54 | for module in self.modules:
55 | if str(name).lower() == str(module.name()).lower():
56 | return module
57 |
58 | def get_module_tags(index):
59 | return self.modules(index)
60 |
61 | def in_list(self, name):
62 | for module in self.modules:
63 | if str(name).lower() == str(module.name()).lower():
64 | return True
65 | return False
66 |
67 | def update_modules_user_paths(self, paths):
68 | for module in self.modules:
69 | module.set_user_paths(paths)
70 |
--------------------------------------------------------------------------------
/verilogviz/model/preprocessor.py:
--------------------------------------------------------------------------------
1 | #Distributed under the MIT licesnse.
2 | #Copyright (c) 2012 Cospan Design (dave.mccoy@cospandesign.com)
3 |
4 | #Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | #this software and associated documentation files (the "Software"), to deal in
6 | #the Software without restriction, including without limitation the rights to
7 | #use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | #of the Software, and to permit persons to whom the Software is furnished to do
9 | #so, subject to the following conditions:
10 | #
11 | #The above copyright notice and this permission notice shall be included in all
12 | #copies or substantial portions of the Software.
13 | #
14 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | #SOFTWARE.
21 |
22 |
23 | """Resolves defines from verilog files much like a pre-processor for c
24 |
25 | Defines in verilog can be dependent on verilog include file. The ibuilder
26 | script will sometimes need the evaluated values to generate all the files
27 | """
28 |
29 | __author__ = 'dave.mccoy@cospandesign.com (Dave McCoy)'
30 |
31 | """Changes:
32 | 06/07/2012
33 | -Added Documentation and license
34 | """
35 |
36 | import os
37 | import sys
38 | import string
39 | import utils
40 |
41 |
42 |
43 | def generate_define_table(filestring="", user_paths = [], debug = False):
44 | """Reads in a module as a buffer and returns a dictionary of defines
45 |
46 | Generates a table of defines that can be used to resolve values.
47 | If all the defines cannot be evaluated directly by the
48 | current module then this will search all the included modules
49 |
50 | Args:
51 | filestring: A buffer from the module's file
52 |
53 | Returns:
54 | A dictionary of defines
55 |
56 | Raises:
57 | PreProcessorError
58 | """
59 | define_dict = {}
60 | #from a file string find all the defines and generate an entry into a
61 | #dictionary
62 | filestring = utils.remove_comments(filestring)
63 | str_list = filestring.splitlines()
64 |
65 | for item in str_list:
66 | if debug: print "Working on: %s" % item
67 | item = item.strip()
68 | #look for include files
69 | if item.startswith("`include"):
70 | if debug: print "found an include: " + item
71 | #read int the include file, strip away the comments
72 | #then append everything to the end
73 | item = item.partition("`include")[2]
74 | item = item.strip()
75 | item = item.strip("\"")
76 | inc_file = utils.find_rtl_file_location(item, user_paths)
77 | if debug: print "include file location: " + inc_file
78 |
79 | #try and open the include file
80 | try:
81 | ifile = open(inc_file)
82 | fs = ifile.read()
83 | ifile.close()
84 | except:
85 | if item != "project_defines.v":
86 | raise PreProcessorError("Error while attempting to the include file: %s" % inc_file)
87 |
88 | try:
89 | if debug:
90 | print "got the new file string"
91 | include_defines = generate_define_table(fs, user_paths)
92 | if debug:
93 | print "after include_define"
94 | print "length of include defines: " + str(len(include_defines.keys()))
95 | for key in include_defines.keys():
96 | #append the values found in the include back in the local dictionary
97 | if debug: print "working on: " + key
98 | if (not define_dict.has_key(key)):
99 | define_dict[key] = include_defines[key]
100 |
101 |
102 | if debug: print "added new items onto the list"
103 | ## except TypeError as terr:
104 | ## print "Type Error: " + str(terr)
105 | except:
106 | if item != "project_defines.v":
107 | raise PreProcessorError("Error while processing: %s: %s" %(item, sys.exc_info()[0]))
108 | #print "error while processing : ", item, ": ", sys.exc_info()[0]
109 | continue
110 |
111 | if item.startswith("`define"):
112 | #if the string starts with `define split the name and value into the dictionary
113 | ## if debug:
114 | ## print "found a define: " + item
115 | item = item.partition("`define")[2]
116 | item = item.strip()
117 | if (len(item.partition(" ")[2]) > 0):
118 | name = item.partition(" ")[0].strip()
119 | value = item.partition(" ")[2].strip()
120 | if debug:
121 | print "added " + name + "\n\tWith value: " + value
122 | define_dict[name] = value
123 | continue
124 | if (len(item.partition("\t")[2]) > 0):
125 | name = item.partition("\t")[0].strip()
126 | value = item.partition("\t")[2].strip()
127 | if debug:
128 | print "added " + name + "\n\tWith value: " + value
129 | define_dict[name] = value
130 | continue
131 | if debug:
132 | print "found a define without a value: " + item
133 |
134 | return define_dict
135 |
136 |
137 | def resolve_defines(work_string="", define_dict={}, parameter_dict = {}, debug = False):
138 | """Evauate define
139 |
140 | Reads a string expression and returns a string with all expressions evaluated
141 |
142 | Args:
143 | work_string: string to be evaluated
144 | define_dict: the dictionary of defines used for evaluation
145 |
146 | Returns:
147 | A string with no define references only complete values
148 |
149 | Raises:
150 | PreProcessorError
151 |
152 | """
153 | #loop through the string until all the defines are resolved
154 | #there could be nested defines so the string might go through the same loop
155 |
156 | #a few times
157 | if debug: print "starting string: " + work_string
158 | work_string = work_string.strip()
159 | #while there is still a tick mark in the string
160 | while "`" in work_string:
161 | if debug: print "found debug marker"
162 | #look through the filedict
163 | #only need to look after the ` portion
164 | def_string = work_string.partition("`")[2]
165 | #if there are any white spaces in the line we only want the first one
166 | def_string = def_string.split()[0]
167 | #if (len(def_string.split()) > 0)
168 | # def_string = def_string.split()[0]
169 |
170 | if debug: print "found the first occurance of a define: " + def_string
171 | #now I'm working with only the definition and any characters afterwards
172 | #attempt to match up this entire string to one wihtin the keys
173 | def_len = len(def_string)
174 | while ( (def_len > 0) and not define_dict.has_key(def_string[0:def_len])):
175 | if debug: print "def_string: " + def_string[0:def_len]
176 | #didn't find the string yet
177 | def_len = def_len - 1
178 | #check to see if the item found is unique
179 | #actually the solution must be unique because dictionaries cannot contain multiple keys with the same name
180 | if (def_len > 0):
181 | key = def_string[0:def_len]
182 | value = str(define_dict[key])
183 | if debug:
184 | print "found define! " + key
185 | print "replacement value: " + value
186 | work_string = work_string.replace("`" + key, value, 1)
187 | if debug: print "final string: " + work_string
188 |
189 | else:
190 | if debug:
191 | print "Error in resolve_define(): didn't find define status in %s" % work_string
192 | raise PreProcessorError("Unable to resolve the defines for %s, are all \
193 | the defined variables declared?" % work_string)
194 | return ""
195 |
196 | for param in parameter_dict:
197 | #print "param: %s" % str(param)
198 | if param in work_string:
199 | work_string = work_string.replace(param, parameter_dict[param])
200 |
201 | #print "work string: %s" % work_string
202 | return work_string
203 |
204 | def evaluate_define_region(in_string, define_name):
205 | """
206 | Given a define name and an input string, determine if we
207 | need to remove a portion of the code,
208 |
209 | This handles the following statements:
210 | `ifdef DEFINE_NAME
211 | ADD IF DEFINE_NAME IS DEFINES
212 | `else
213 | ADD IF DEFINE_NAME IS NOT DEFINED
214 | `endif
215 | """
216 |
217 | pass
218 |
219 |
220 | def evaluate_range(in_string = "", define_dict = {}, debug = False):
221 | """Resolve the range of a statement
222 |
223 | There are times when registers, wires and ports do not have the true value
224 | of a range. The ibuilder needs to evaluate the defines. this function will
225 | evaluate all the non-numeric characters to their numeric values
226 |
227 | Args:
228 | in_string: The string to be evaluated
229 | define_dict: dictionary that has all the define values
230 |
231 | Returns:
232 | an output string with all the define's evaluated
233 |
234 | Raises:
235 | PreProcessorError
236 |
237 | Example:
238 | `define SIZE 32
239 | `define MIN 0
240 |
241 | reg bus[(`SIZE - 1):`MIN]
242 |
243 | output = evaluate_range(\"reg bus[(`SIZE - 1): `MIN]\", {SIZE:32, MIN:0})
244 |
245 | outputs: bus[31:0]
246 | """
247 |
248 | #resolve all the defines
249 | #work_string = resolve_defines(in_string, define_dict)
250 | if ("[" in in_string):
251 | pre = str(eval(in_string[in_string.index("[") + 1: in_string.index(":")]))
252 | if debug: print "pre: " + pre
253 | post = str(eval(in_string[in_string.index(":") + 1: in_string.index("]")]))
254 | if debug: print "post: " + post
255 | in_string = in_string[:in_string.index("[") + 1] + pre + ":" + post + \
256 | in_string[in_string.index("]")]
257 |
258 | if debug: print in_string
259 | return in_string
260 |
261 |
--------------------------------------------------------------------------------
/verilogviz/model/utils.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 |
4 | class ModuleError(Exception):
5 | """
6 | - Module File not found
7 | """
8 | pass
9 |
10 | def find_rtl_file_location(filename="", user_paths = [], debug=False):
11 | """Finds a RTL file in the cbuilder rtl directory.
12 |
13 | Args:
14 | filename: the name of a verilog file to search for
15 | user_paths: list of paths to search for cbuilder projects
16 |
17 | Returns:
18 | If found, The absolute path of the verilog module file,
19 | Otherwise an empty string
20 |
21 | Raises:
22 | Nothing
23 | """
24 | for path in user_paths:
25 | path = str(path)
26 | for root, dirs, names in os.walk(path):
27 | if filename in names:
28 | return os.path.join(root, filename)
29 |
30 | raise ModuleError("File: %s not found, looked in %s" % (filename, str(user_paths)))
31 | #XXX: This should probably return none, and not an empty string upon failure
32 | #XXX: perhaps even raise an error
33 |
34 |
35 | def remove_comments(buf="", debug=False):
36 | """Remove comments from a buffer.
37 |
38 | Args:
39 | buf = Buffer to remove the comments from
40 |
41 | Returns:
42 | A buffer with no verilog comments in it
43 |
44 | Raises:
45 | Nothing
46 | """
47 | #first pass remove the '//' comments
48 | lines = buf.splitlines()
49 | if debug:
50 | print "buf:\n" + buf
51 | bufx = ""
52 | for line in lines:
53 | line = line.partition("//")[0]
54 | bufx = bufx + line + "\n"
55 | if debug:
56 | print "bufx:\n" + bufx
57 |
58 | if debug:
59 | print "working on /* */ comments\n\n\n"
60 | #get rid of /*, */ comments
61 | buf_part = bufx.partition("/*")
62 | pre_comment = ""
63 | post_comment = ""
64 | bufy = bufx
65 | while (len(buf_part[1]) != 0):
66 | pre_comment = buf_part[0]
67 | post_comment = buf_part[2].partition("*/")[2]
68 | #print "pre_comment: " + pre_comment
69 | #print "post comment: " + post_comment
70 | bufy = pre_comment + post_comment
71 | buf_part = bufy.partition("/*")
72 | pre_comment = ""
73 | post_comment = ""
74 |
75 | bufx = bufy
76 | buf_part = bufx.partition("\(*")
77 | pre_comment = ""
78 | post_comment = ""
79 | bufy = bufx
80 | while (len(buf_part[1]) != 0):
81 | pre_comment = buf_part[0]
82 | post_comment = buf_part[2].partition("*\)")[2]
83 | bufy = pre_comment + post_comment
84 | buf_part = bufy.partition("(*")
85 | pre_comment = ""
86 | post_comment = ""
87 |
88 |
89 | if debug:
90 | print "buf:\n" + bufy
91 |
92 | return bufy
93 |
94 |
95 |
96 |
97 | def is_module_in_file(filename, module_name, debug = False):
98 | fbuf = ""
99 |
100 | try:
101 | filein = open(filename)
102 | fbuf = filein.read()
103 | filein.close()
104 | except IOError as err:
105 | if debug:
106 | print "the file is not a full path, searching RTL"
107 |
108 | try:
109 | filepath = find_rtl_file_location(filename)
110 | filein = open(filepath)
111 | fbuf = filein.read()
112 | filein.close()
113 | except IOError as err_int:
114 | if debug:
115 | print "%s is not in %s" % (module_name, filename)
116 | return False
117 |
118 | if debug:
119 | print "Openning file: %s" % filename
120 |
121 | fbuf = remove_comments(fbuf)
122 | done = False
123 | module_string = fbuf.partition("module")[2]
124 |
125 | while (not done):
126 | #remove the parameter and ports list from this possible module
127 | module_string = module_string.partition("(")[0]
128 | module_string = module_string.strip("#")
129 | module_string = module_string.strip()
130 |
131 | if debug:
132 | print "Searching through: %s" % module_string
133 |
134 | if len(module_string) == 0:
135 | if debug:
136 | print "length of module string == 0"
137 | done = True
138 |
139 | if module_string.endswith("("):
140 | if debug:
141 | print "module_string endswith \"(\""
142 | module_string = module_string.strip("(")
143 |
144 | if debug:
145 | print "Looking at: %s" % module_string
146 |
147 | if module_string == module_name:
148 | #Success!
149 | if debug:
150 | print "Found %s in %s" % (module_string, filename)
151 | return True
152 |
153 | elif len(module_string.partition("module")[2]) > 0:
154 | if debug:
155 | print "Found another module in the file"
156 | module_string = module_string.partition("module")[2]
157 |
158 | else:
159 | done = True
160 |
161 | return False
162 |
163 | def _find_module_filename (directory, module_name, debug = False):
164 | filename = ""
165 |
166 | verilog_files = []
167 | #get all the verilog files
168 | for root, dirs, files in os.walk(directory):
169 | filelist = [os.path.join(root, fi) for fi in files if fi.endswith(".v")]
170 | for f in filelist:
171 | verilog_files.append(f)
172 |
173 | for f in verilog_files:
174 | if debug:
175 | print "serching through %s" % f
176 |
177 | if is_module_in_file(f, module_name):
178 | return f
179 |
180 | raise ModuleError("Searched in standard hdl/rtl location for file \
181 | containing the module %s" % module_name)
182 |
183 | def find_module_filename (module_name, user_paths = [], debug = False):
184 | filename = ""
185 | paths = [os.getcwd()]
186 | paths.extend(user_paths)
187 |
188 | if debug: print "Search directory: %s" % str(paths)
189 | for p in paths:
190 | p = str(p)
191 | try:
192 | return _find_module_filename(p, module_name, debug = debug)
193 | except ModuleError as mnf:
194 | continue
195 |
196 | raise ModuleError ("Module %s not found: %s" % (module_name, str(paths)))
197 |
198 |
199 | def _get_file_recursively(directory):
200 | file_dir_list = glob.glob(directory + "/*")
201 | file_list = []
202 | for f in file_dir_list:
203 | if (os.path.isdir(f)):
204 | if(f.split("/")[-1] != "sim"):
205 | file_list += _get_file_recursively(f)
206 | elif (os.path.isfile(f)):
207 | if f.endswith(".v"):
208 | file_list.append(f)
209 |
210 | return file_list
211 |
212 |
213 |
--------------------------------------------------------------------------------
/verilogviz/model/verilog_utils.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2013 Dave McCoy (dave.mccoy@cospandesign.com)
2 | #
3 | # This file is part of Nysa.
4 | #
5 | # (http://wiki.cospandesign.com/index.php?title=Nysa.org)
6 | #
7 | # Nysa is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 3 of the License, or
10 | # any later version.
11 | #
12 | # Nysa is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with Nysa; If not, see .
19 |
20 | """Utilities used to generate and extract information from Verilog cores"""
21 |
22 | __author__ = 'dave.mccoy@cospandesign.com (Dave McCoy)'
23 | import os
24 | import sys
25 | import string
26 | import json
27 | import arbiter
28 | import re
29 |
30 | import utils
31 | from utils import ModuleError
32 | import preprocessor
33 |
34 | def get_eol(total, index):
35 | if index < total:
36 | return ","
37 | return ""
38 |
39 |
40 | def get_module_buffer_tags(buf, bus = "", keywords = [], user_paths = [], project_tags = {}, debug = False):
41 | raw_buf = buf
42 | tags = {}
43 | tags["keywords"] = {}
44 | tags["ports"] = {}
45 | tags["module"] = ""
46 | tags["parameters"] = {}
47 | tags["arbiter_masters"] = []
48 |
49 | in_task = False
50 | end_module = False
51 |
52 | ports = [
53 | "input",
54 | "output",
55 | "inout"
56 | ]
57 |
58 |
59 | #XXX only working with verilog at this time, need to extend to VHDL
60 | #print "filename: %s" % filename
61 |
62 | #find all the metadata
63 | for key in keywords:
64 | index = buf.find (key)
65 | if (index == -1):
66 | if debug:
67 | print "didn't find substring for " + key
68 | continue
69 | if debug:
70 | print "found substring for " + key
71 |
72 | substring = buf.__getslice__(index, len(buf)).splitlines()[0]
73 | if debug:
74 | print "substring: " + substring
75 |
76 |
77 | if debug:
78 | print "found " + key + " substring: " + substring
79 |
80 | substring = substring.strip()
81 | substring = substring.strip("//")
82 | substring = substring.strip("/*")
83 | tags["keywords"][key] = substring.partition(":")[2]
84 |
85 |
86 |
87 | #remove all the comments from the code
88 | buf = utils.remove_comments(buf)
89 | #print "no comments: \n\n" + buf
90 |
91 | for substring in buf.splitlines():
92 | if (len(substring.partition("module")[1]) == 0):
93 | continue
94 | module_string = substring.partition("module")[2]
95 | module_string = module_string.strip(" ")
96 | module_string = module_string.strip("(")
97 | module_string = module_string.strip("#")
98 | index = module_string.find(" ")
99 |
100 | if (index != -1):
101 | tags["module"] = module_string.__getslice__(0, index)
102 | else:
103 | tags["module"] = module_string
104 |
105 | if debug:
106 | print "module name: " + module_string
107 | print tags["module"]
108 | break
109 |
110 | #find all the ports
111 | #find the index of all the processing block
112 | substrings = buf.splitlines()
113 |
114 | input_count = buf.count("input")
115 | output_count = buf.count("output")
116 | inout_count = buf.count("inout")
117 |
118 | ldebug = debug
119 | define_dict = preprocessor.generate_define_table(raw_buf, user_paths, ldebug)
120 | if 'defines' in project_tags:
121 | for d in project_tags["defines"]:
122 | define_dict[d] = project_tags["defines"][d]
123 |
124 | #find all the USER_PARAMETER declarations
125 | user_parameters = []
126 | substrings = raw_buf.splitlines()
127 | for substring in substrings:
128 | substring = substring.strip()
129 | if "USER_PARAMETER" in substring:
130 | name = substring.partition(":")[2].strip()
131 | user_parameters.append(name)
132 |
133 |
134 | param_dict = {}
135 | #find all the parameters
136 | substrings = buf.splitlines()
137 | for substring in substrings:
138 | substring = substring.strip()
139 | if ("parameter" in substring):
140 | if debug:
141 | print "found parameter!"
142 | substring = substring.partition("parameter")[2].strip()
143 | parameter_name = substring.partition("=")[0].strip()
144 | parameter_value = substring.partition("=")[2].strip()
145 | parameter_value = parameter_value.partition(";")[0].strip()
146 | parameter_value = parameter_value.strip(',')
147 | if debug:
148 | print "parameter name: " + parameter_name
149 | print "parameter value: " + parameter_value
150 | if parameter_name not in user_parameters:
151 | tags["parameters"][parameter_name] = parameter_value
152 | param_dict[parameter_name] = parameter_value
153 |
154 |
155 |
156 | #find all the IO's
157 | for io in ports:
158 | end_module = False
159 | in_task = False
160 |
161 | tags["ports"][io] = {}
162 | substrings = buf.splitlines()
163 | for substring in substrings:
164 | substring = substring.strip()
165 | if substring.startswith("endmodule"):
166 | end_module = True
167 | continue
168 | #Only count one module per buffer
169 | if end_module:
170 | continue
171 |
172 | if substring.startswith("task"):
173 | #Sub tasks and functions declare inputs and outputs, don't count these
174 | in_task = True
175 | continue
176 | if substring.startswith("function"):
177 | in_task = True
178 | continue
179 |
180 | if substring.startswith("endtask"):
181 | in_task = False
182 | continue
183 |
184 | if substring.startswith("endfunction"):
185 | in_task = False
186 | continue
187 |
188 | if in_task:
189 | continue
190 |
191 | #if line doesn't start with an input/output or inout
192 | if (not substring.startswith(io)):
193 | continue
194 | #if the line does start with input/output or inout but is used in a name then bail
195 | if (not substring.partition(io)[2][0].isspace()):
196 | continue
197 | #one style will declare the port names after the ports list
198 | if (substring.endswith(";")):
199 | substring = substring.rstrip(";")
200 | #the other stile will include the entire port definition within the port declaration
201 | if (substring.endswith(",")):
202 | substring = substring.rstrip(",")
203 | if debug:
204 | print "substring: " + substring
205 | substring = substring.partition(io)[2]
206 | if (len(substring.partition("reg")[1]) != 0):
207 | if (len(substring.partition("reg")[0]) > 0) and \
208 | (substring.partition("reg")[0][-1].isspace()):
209 | #print "Substring: %s" % substring
210 | substring = substring.partition("reg")[2]
211 | substring = substring.strip()
212 | max_val = -1
213 | min_val = -1
214 | if (len(substring.partition("]")[2]) != 0):
215 | #we have a range to work with?
216 | length_string = substring.partition("]")[0] + "]"
217 | substring = substring.partition("]")[2]
218 | substring = substring.strip()
219 | length_string = length_string.strip()
220 | if "wire" in length_string:
221 | length_string = length_string.partition("wire")[2].strip()
222 | length_string = length_string.strip()
223 |
224 | #if debug:
225 | #print "length string: " + length_string
226 |
227 | ldebug = debug
228 |
229 | #print "module name: %s" % tags["module"]
230 | #print "parameters: %s" % str(param_dict)
231 |
232 | length_string = preprocessor.resolve_defines(length_string, define_dict, param_dict, debug=ldebug)
233 | length_string = preprocessor.evaluate_range(length_string)
234 |
235 | length_string = length_string.partition("]")[0]
236 | length_string = length_string.strip("[")
237 | if debug:
238 | print "length string: " + length_string
239 | max_val = string.atoi(length_string.partition(":")[0])
240 | min_val = string.atoi(length_string.partition(":")[2])
241 |
242 | tags["ports"][io][substring] = {}
243 |
244 | if (max_val != -1):
245 | tags["ports"][io][substring]["max_val"] = max_val
246 | tags["ports"][io][substring]["min_val"] = min_val
247 | tags["ports"][io][substring]["size"] = (max_val + 1) - min_val
248 | else:
249 | tags["ports"][io][substring]["size"] = 1
250 |
251 |
252 | tags["arbiter_masters"] = arbiter.get_number_of_arbiter_hosts(tags)
253 |
254 |
255 | if debug:
256 | print "input count: " + str(input_count)
257 | print "output count: " + str(output_count)
258 | print "inout count: " + str(inout_count)
259 | print "\n"
260 |
261 | if debug:
262 | print "module name: " + tags["module"]
263 | for key in tags["keywords"].keys():
264 | print "key: " + key + ":" + tags["keywords"][key]
265 | for io in ports:
266 | for item in tags["ports"][io].keys():
267 | print io + ": " + item
268 | for key in tags["ports"][io][item].keys():
269 | value = tags["ports"][io][item][key]
270 | if (isinstance( value, int)):
271 | value = str(value)
272 | print "\t" + key + ":" + value
273 |
274 | return tags
275 |
276 |
277 |
278 |
279 | def get_module_tags(filename="", bus="", keywords = [], user_paths = [], project_tags = {}, debug=False):
280 | """Gets the tags for the module within the specified filename
281 |
282 | Given a module within a filename search through the module and
283 | find:
284 | metadata
285 | \"SDB_CORE_ID\"
286 | ports: Inputs/Outputs of this module
287 | module: Name of the module
288 | parameters: Configuration parameters within the module
289 | arbiter_masters: Any arbiter masters within the module
290 |
291 | Args:
292 | filename: Name of the module to interrogate
293 | bus: A string declaring the bus type, this can be
294 | \"wishbone\" or \"axie\"
295 | keywords:
296 | Besides the standard metadata any additional values to search for
297 |
298 | Returns:
299 | A dictionary of module tags
300 |
301 | Raises
302 | Nothing
303 | """
304 | buf = ""
305 | with open(filename) as slave_file:
306 | buf = slave_file.read()
307 |
308 | return get_module_buffer_tags(buf = buf,
309 | keywords = keywords,
310 | user_paths = user_paths,
311 | project_tags = project_tags,
312 | debug = debug)
313 |
314 |
315 | def generate_module_port_signals(invert_reset,
316 | name = "",
317 | prename = "",
318 | slave_tags = {},
319 | module_tags = {},
320 | wishbone_slave = False,
321 | debug = False):
322 |
323 | buf = ""
324 | if ("parameters" in module_tags) and \
325 | len(module_tags["parameters"].keys()) > 0:
326 | buf = "%s #(\n" % module_tags["module"]
327 | num_params = len(module_tags["parameters"])
328 | param_count = 1
329 | for param in module_tags["parameters"]:
330 | buf += "\t.{0:<20}({1:<18}){2}\n".format(param,
331 | module_tags["parameters"][param],
332 | get_eol(num_params, param_count))
333 | param_count += 1
334 |
335 | buf += ")%s (\n" % name
336 |
337 | else:
338 | buf = "%s %s(\n" % (module_tags["module"], name)
339 |
340 | if not wishbone_slave:
341 | IF_WIRES = []
342 |
343 | #Keep track of the port count so the last one won't have a comma
344 | port_max = get_port_count(module_tags)
345 | port_count = 0
346 |
347 | input_ports = []
348 | output_ports = []
349 | inout_ports = []
350 | if "input" in module_tags["ports"]:
351 | input_ports = module_tags["ports"]["input"].keys()
352 | if "output" in module_tags["ports"]:
353 | output_ports = module_tags["ports"]["output"].keys()
354 | if "inout" in module_tags["ports"]:
355 | inout_ports = module_tags["ports"]["inout"].keys()
356 |
357 | #Add the port declarations
358 | if "clk" in input_ports:
359 | buf += "\t.{0:<20}({1:<20}),\n".format("clk", "clk")
360 | if "rst" in input_ports:
361 | if invert_reset:
362 | buf += "\t.{0:<20}({1:<20}),\n".format("rst", "rst_n")
363 | else:
364 | buf += "\t.{0:<20}({1:<20}),\n".format("rst", "rst")
365 |
366 |
367 |
368 | ports = sorted(input_ports, cmp = port_cmp)
369 | buf += "\n"
370 | buf += "\t//inputs\n"
371 |
372 | for port in ports:
373 | port_count += 1
374 | line = ""
375 | if port == "rst":
376 | continue
377 | if port == "clk":
378 | continue
379 |
380 | #Check to see if this is one of the pre-defined wires
381 | wire = ""
382 | if wishbone_slave:
383 | for w in IF_WIRES:
384 | if w.endswith(port[2:]):
385 | wire = "%s" % w[2:]
386 | break
387 |
388 | #Not Pre-defines
389 | if len(wire) == 0:
390 | if len(prename) > 0:
391 | wire = "%s_%s" % (prename, port)
392 | else:
393 | wire = "%s" % port
394 |
395 | line = "\t.{0:<20}({1:<20})".format(port, wire)
396 | if port_count == port_max:
397 | buf += "%s\n" % line
398 | else:
399 | buf += "%s,\n" % line
400 |
401 |
402 | ports = sorted(output_ports, cmp = port_cmp)
403 | buf += "\n"
404 | buf += "\t//outputs\n"
405 |
406 | for port in ports:
407 | port_count += 1
408 | line = ""
409 | #Check to see if this is one of the pre-defined wires
410 | wire = ""
411 | if wishbone_slave:
412 | for w in IF_WIRES:
413 | if w.endswith(port[2:]):
414 | wire = "%s" % w[2:]
415 | break
416 |
417 | #Not Pre-defines
418 | if len(wire) == 0:
419 | if len(prename) > 0:
420 | wire = "%s_%s" % (prename, port)
421 | else:
422 | wire = "%s" % port
423 |
424 | line = "\t.{0:<20}({1:<20})".format(port, wire)
425 | if port_count == port_max:
426 | buf += "%s\n" % line
427 | else:
428 | buf += "%s,\n" % line
429 |
430 | ports = sorted(inout_ports, cmp = port_cmp)
431 |
432 | if len(ports) > 0:
433 | buf += "\n"
434 | buf += "\t//inouts\n"
435 |
436 |
437 | for port in ports:
438 | port_count += 1
439 | line = ""
440 | found = False
441 | #Special Case, we need to tie the specific signal directly to this port
442 | for key in sorted(slave_tags["bind"], cmp = port_cmp):
443 | bname = key.partition("[")[0]
444 | bname.strip()
445 | if bname == port:
446 | found = True
447 | loc = slave_tags["bind"][key]["loc"]
448 | if port_count == port_max:
449 | buf += "\t.{0:<20}({1:<20})\n".format(port, loc)
450 | else:
451 | buf += "\t.{0:<20}({1:<20}),\n".format(port, loc)
452 |
453 | if not found:
454 | buf += "\t.{0:<20}({1:<20}){2}\n".format(port, port, get_eol(port_max, port_count))
455 |
456 |
457 | buf += ");"
458 | return string.expandtabs(buf, 2)
459 |
460 | def get_port_count(module_tags = {}):
461 | port_count = 0
462 | if "inout" in module_tags["ports"]:
463 | port_count += len(module_tags["ports"]["inout"])
464 | if "output" in module_tags["ports"]:
465 | port_count += len(module_tags["ports"]["output"])
466 | if "input" in module_tags["ports"]:
467 | port_count += len(module_tags["ports"]["input"])
468 | return port_count
469 |
470 |
471 |
472 | def create_reg_buf_from_dict(name, d):
473 | size = d["size"]
474 | if size == 1:
475 | return create_reg_buf(name, 1, 0, 0)
476 | else:
477 | return create_reg_buf(name, size, d["max_val"], d["min_val"])
478 |
479 | def create_reg_buf(name, size, max_val, min_val):
480 | line = ""
481 | if size > 1:
482 | size_range = "[%d:%d]" % (max_val, min_val)
483 | line = "reg\t{0:20}{1};\n".format(size_range, name)
484 | else:
485 | line = "reg\t{0:20}{1};\n".format("", name)
486 | return string.expandtabs(line, 2)
487 |
488 |
489 |
490 | def create_wire_buf_from_dict(name, d):
491 | size = d["size"]
492 | if size == 1:
493 | return create_wire_buf(name, 1, 0, 0)
494 | else:
495 | return create_wire_buf(name, size, d["max_val"], d["min_val"])
496 |
497 | def create_wire_buf(name, size, max_val, min_val):
498 | line = ""
499 | if size > 1:
500 | size_range = "[%d:%d]" % (max_val, min_val)
501 | line = "wire\t{0:18}{1};\n".format(size_range, name)
502 | else:
503 | line = "wire\t{0:18}{1};\n".format("", name)
504 | return string.expandtabs(line, 2)
505 |
506 | def generate_assigns_buffer(invert_reset, bindings, internal_bindings, debug=False):
507 | buf = ""
508 | if len(internal_bindings) > 0:
509 | buf += "//Internal Bindings\n"
510 | for key in internal_bindings:
511 | if key == "clk":
512 | continue
513 | if key == "rst":
514 | continue
515 | if key == internal_bindings[key]["signal"]:
516 | continue
517 |
518 | buf += "assign\t{0:<20}=\t{1};\n".format(key, internal_bindings[key]["signal"])
519 |
520 | buf += "\n\n"
521 | if len(bindings) > 0:
522 | buf += "//Bindings\n"
523 | for key in bindings:
524 | if key == "clk":
525 | continue
526 | if key == "rst":
527 | continue
528 | if key == bindings[key]["loc"]:
529 | continue
530 |
531 | if bindings[key]["direction"] == "input":
532 | buf += "assign\t{0:<20}=\t{1};\n".format(key, bindings[key]["loc"])
533 | elif bindings[key]["direction"] == "output":
534 | buf += "assign\t{0:<20}=\t{1};\n".format(bindings[key]["loc"], key)
535 |
536 | if invert_reset:
537 | buf += "\n"
538 | buf += "//Invert Reset for this board\n"
539 | buf += "assign\t{0:<20}=\t{1};\n".format("rst_n", "~rst")
540 |
541 | return string.expandtabs(buf, 2)
542 |
543 | def port_cmp(x, y):
544 | if re.search("[0-9]", x) and re.search("[0-9]", y):
545 | x_name = x.strip(string.digits)
546 | y_name = y.strip(string.digits)
547 | if x_name == y_name:
548 | #print "%s == %s" % (x_name, y_name)
549 | x_temp = x.strip(string.letters)
550 | x_temp = x_temp.strip("[")
551 | x_temp = x_temp.strip("]")
552 |
553 | y_temp = y.strip(string.letters)
554 | y_temp = y_temp.strip("[")
555 | y_temp = y_temp.strip("]")
556 |
557 |
558 |
559 | x_num = int(x_temp, 10)
560 | y_num = int(y_temp, 10)
561 | #print "x:%s, y:%s, x_num:%d, y_num:%d" % (x, y, x_num, y_num)
562 | if x_num < y_num:
563 | #print "\tx < y"
564 | return -1
565 | if x_num == y_num:
566 | #print "\tx == y"
567 | return 0
568 | if x_num > y_num:
569 | #print "\tx > y"
570 | return 1
571 |
572 | #print "normal search: %s:%s" % (x, y)
573 | if x < y:
574 | return -1
575 | if x == y:
576 | return 0
577 | else:
578 | return 1
579 |
580 | def has_dependencies(self, filename, debug = False):
581 | """has_dependencies
582 |
583 | returns true if the file specified has dependencies
584 |
585 | Args:
586 | filename: search for dependencies with this filename
587 |
588 | Return:
589 | True: The file has dependencies.
590 | False: The file doesn't have dependencies
591 |
592 | Raises:
593 | IOError
594 | """
595 |
596 | if debug:
597 | print "input file: " + filename
598 | #filename needs to be a verilog file
599 | if (filename.partition(".")[2] != "v"):
600 | if debug:
601 | print "File is not a recognized verilog source"
602 | return False
603 |
604 | fbuf = ""
605 |
606 | #the name is a verilog file, try and open is
607 | try:
608 | filein = open(filename)
609 | fbuf = filein.read()
610 | filein.close()
611 | except IOError as err:
612 | if debug:
613 | print "the file is not a full path, searching RTL... ",
614 | #didn't find with full path, search for it
615 | try:
616 | #print "self.user_paths: %s" % (self.user_paths)
617 | filepath = utils.find_rtl_file_location(filename, self.user_paths)
618 |
619 | filein = open(filepath)
620 | fbuf = filein.read()
621 | filein.close()
622 | except ModuleError as err:
623 | fbuf = ""
624 | except IOError as err_int:
625 | if debug:
626 | print "couldn't find file in the RTL directory"
627 | ModuleFactoryError("Couldn't find file %s in the RTL directory" % filename)
628 |
629 |
630 | #we have an open file!
631 | if debug:
632 | print "found file!"
633 |
634 | #strip out everything we can't use
635 | fbuf = utils.remove_comments(fbuf)
636 |
637 | #modules have lines that start with a '.'
638 | str_list = fbuf.splitlines()
639 |
640 | for item in str_list:
641 | item = item.strip()
642 | if (item.startswith(".")):
643 | if debug:
644 | print "found a module!"
645 | return True
646 | return False
647 |
648 |
649 | def resolve_dependencies(filename, debug = True):
650 | """resolve_dependencies
651 |
652 | given a filename determine if there are any modules it depends on,
653 | recursively search for any files found in order to extrapolate all
654 | dependencies
655 |
656 | Args:
657 | filename: The filename to resolve dependencies for
658 |
659 | Return:
660 | Nothing
661 |
662 | Raises:
663 | ModuleFactoryError
664 | """
665 |
666 | result = True
667 | ldebug = debug
668 | if debug:
669 | print "in resolve dependencies"
670 | local_file_list = []
671 | if debug:
672 | print "working on filename: " + filename
673 | if (has_dependencies(filename, debug = ldebug)):
674 | if debug:
675 | print "found dependencies!"
676 | deps = get_list_of_dependencies(filename, debug = ldebug)
677 | for d in deps:
678 | try:
679 | dep_filename = utils.find_module_filename(d, user_paths, debug = ldebug)
680 | except ModuleError as ex:
681 | print "Dependency Warning: %s" % (str(ex))
682 | print "Module Name: %s" % (d)
683 | print "This warning may be due to:"
684 | print "\tIncluding a simulation only module"
685 | print "\tIncluding a vendor specific module"
686 | print "\tA module that was not found"
687 | continue
688 |
689 | if debug:
690 | print "found the filename: " + dep_filename
691 | #check this file out for dependecies, then append that on to the local list
692 | resolve_dependencies(dep_filename, debug = ldebug)
693 | if debug:
694 | print "found all sub dependencies for: " + dep_filename
695 | local_file_list.append(dep_filename)
696 |
697 | #go through the local file list and add anything found to the list of dependencies or verilog files
698 | for f in local_file_list:
699 | if f not in verilog_dependency_list and f not in verilog_file_list:
700 |
701 | if debug:
702 | print "found dependency: " + f
703 | verilog_dependency_list.append(f)
704 | return
705 |
706 | def get_list_of_dependencies(self, filename, debug=False):
707 | """get_list_of_dependencies
708 |
709 | return a list of the files that this file depends on
710 |
711 | Args:
712 | filename: the name of the file to analyze
713 |
714 | Return:
715 | A list of files that specify the dependenies
716 |
717 | Raises:
718 | IOError
719 | """
720 | deps = []
721 | if debug:
722 | print "input file: " + filename
723 | #filename needs to be a verilog file
724 | if (filename.partition(".")[2] != "v"):
725 | if debug:
726 | print "File is not a recognized verilog source"
727 | return False
728 |
729 | fbuf = ""
730 | #the name is a verilog file, try and open is
731 | try:
732 | filein = open(filename)
733 | fbuf = filein.read()
734 | filein.close()
735 | except IOError as err:
736 | #if debug:
737 | # print "the file is not a full path... searching RTL"
738 | #didn't find with full path, search for it
739 | try:
740 | filepath = utils.find_rtl_file_location(filename, self.user_paths)
741 |
742 | filein = open(filepath)
743 | fbuf = filein.read()
744 | filein.close()
745 | except IOError as err_int:
746 | ModuleFactoryError("Couldn't find file %s in the RTL directory" % filename)
747 |
748 |
749 | #we have an open file!
750 | if debug:
751 | print "found file!"
752 |
753 | #strip out everything we can't use
754 | fbuf = utils.remove_comments(fbuf)
755 |
756 | include_fbuf = fbuf
757 | #search for `include
758 | while (not len(include_fbuf.partition("`include")[2]) == 0):
759 | ifile_name = include_fbuf.partition("`include")[2]
760 | ifile_name = ifile_name.splitlines()[0]
761 | ifile_name = ifile_name.strip()
762 | ifile_name = ifile_name.strip("\"")
763 | if debug:
764 | print "found an include " + ifile_name + " ",
765 | if (not self.verilog_dependency_list.__contains__(ifile_name) and
766 | not self.verilog_file_list.__contains__(ifile_name)):
767 | self.verilog_dependency_list.append(ifile_name)
768 | if debug:
769 | print "adding " + ifile_name + " to the dependency list"
770 | else:
771 | if debug:
772 | print "... already in have it"
773 | include_fbuf = include_fbuf.partition("`include")[2]
774 |
775 | #remove the ports list and the module name
776 | fbuf = fbuf.partition(")")[2]
777 |
778 | #modules have lines that start with a '.'
779 | str_list = fbuf.splitlines()
780 |
781 | module_token = ""
782 | done = False
783 | while (not done):
784 | module_token = ""
785 | parameter_found = False
786 | parameter_flag = False
787 | parameter_debt = None
788 | for i in range (0, len(str_list)):
789 | line = str_list[i]
790 | #remove white spaces
791 | line = line.strip()
792 | if "#" in line:
793 | line = line.partition("#")[2]
794 | parameter_found = True
795 |
796 | if parameter_found:
797 | if parameter_debt == 0:
798 | parameter_found = False
799 | parameter_flag = True
800 | while ("(" in line) or (")" in line):
801 | if "(" in line:
802 | line = line.partition("(")[2]
803 | if parameter_debt is None:
804 | parameter_debt = 1
805 | else:
806 | parameter_debt += 1
807 | else:
808 | line = line.partition("(")[2]
809 | parameter_debt -= 1
810 |
811 | if (line.startswith(".") and line.endswith(",")):
812 | #if debug:
813 | # print "found a possible module... with token: " + line
814 | module_token = line
815 | continue
816 | if ";" in line and len(module_token) > 0:
817 | break
818 | #check if we reached the last line
819 | if (i >= len(str_list) - 1):
820 | done = True
821 |
822 | if (not done):
823 | module_string = fbuf.partition(module_token)[0]
824 | fbuf = fbuf.partition(module_token)[2]
825 | fbuf = fbuf.partition(";")[2]
826 | str_list = fbuf.splitlines()
827 |
828 | #get rid of everything before the possible module
829 | while (len(module_string.partition(";")[2]) > 0):
830 | module_string = module_string.partition(";")[2].strip()
831 |
832 | #Now we have a string that contains the module_type and name
833 | module_string = module_string.partition("(")[0].strip()
834 | m_name = ""
835 | if parameter_found:
836 | m_name = module_string.partition("#")[0].strip()
837 | else:
838 | m_name = module_string.partition(" ")[0].strip()
839 |
840 | if m_name not in deps:
841 | if debug:
842 | print "adding it to the deps list"
843 | deps.append(m_name)
844 |
845 | return deps
846 |
847 |
--------------------------------------------------------------------------------
/verilogviz/module_graph.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CospanDesign/verilog-visualizer/be9fb8b35b46bb0162963888f9a4e9461409623e/verilogviz/module_graph.py
--------------------------------------------------------------------------------
/verilogviz/verilog_viz_actions.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from PyQt4 import QtGui
3 | from PyQt4 import QtCore
4 |
5 | _action_instance = None
6 |
7 | #Singleton Magic
8 | def Actions(*args, **kw):
9 | global _action_instance
10 | if _action_instance is None:
11 | _action_instance = _Actions(*args, **kw)
12 | return _action_instance
13 |
14 | class _Actions(QtCore.QObject):
15 |
16 | #Host Actions
17 | #Control Signals
18 | test = QtCore.pyqtSignal(name = "test_signal")
19 | add_verilog_module = QtCore.pyqtSignal(int, str, name = "add_verilog_module")
20 | configure_include_paths = QtCore.pyqtSignal(name = "configure_include_paths")
21 |
22 |
--------------------------------------------------------------------------------
/verilogviz/verilogviz.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 |
3 | import os
4 | import sys
5 | import logging
6 | from PyQt4.Qt import *
7 | from PyQt4.QtCore import *
8 | from PyQt4.QtGui import *
9 |
10 | from view.main_form import MainForm
11 |
12 | from verilog_viz_actions import Actions
13 | from model.module import find_module
14 |
15 | class VerilogViz(QObject):
16 |
17 | def __init__(self):
18 | super (VerilogViz, self).__init__()
19 |
20 | #Create a logger
21 | self.actions = Actions()
22 | self.logger = logging.getLogger('verilogviz')
23 | self.logger.setLevel(logging.DEBUG)
24 | formatter = logging.Formatter('%(filename)s:%(module)s:%(funcName)s: %(message)s')
25 | #formatter = logging.Formatter('%(pathname)s:%(module)s:%(funcName)s: %(message)s')
26 |
27 | #Create a Console Handler
28 | ch = logging.StreamHandler(sys.stdout)
29 | ch.setFormatter(formatter)
30 | ch.setLevel(logging.DEBUG)
31 | self.logger.addHandler(ch)
32 |
33 | QThread.currentThread().setObjectName("main")
34 |
35 | self.actions.add_verilog_module.connect(self.add_verilog_module)
36 | self.actions.configure_include_paths.connect(self.include_paths_dialog)
37 |
38 | app = QApplication(sys.argv)
39 | #Setup the view
40 | self.main_form = MainForm(self, self.actions)
41 | sys.exit(app.exec_())
42 | self.focused_module = None
43 |
44 | def add_verilog_module(self, index, path):
45 | module = find_module(path, self.main_form.get_include_paths())
46 | self.main_form.clear_graph()
47 | self.main_form.add_verilog_project_list_item(module)
48 | self.main_form.draw_module(module)
49 |
50 | def include_paths_dialog(self):
51 | if self.main_form.configure_include_paths_dialog():
52 | self.main_form.update_modules_user_paths(self.main_form.get_include_paths())
53 |
54 | def set_focused_module(self, module_name):
55 | self.focused_module = self.main_form.get_module(module_name)
56 |
57 | def create_module_graph(self):
58 | self.focused_module
59 |
60 |
--------------------------------------------------------------------------------
/verilogviz/view/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CospanDesign/verilog-visualizer/be9fb8b35b46bb0162963888f9a4e9461409623e/verilogviz/view/__init__.py
--------------------------------------------------------------------------------
/verilogviz/view/graph/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CospanDesign/verilog-visualizer/be9fb8b35b46bb0162963888f9a4e9461409623e/verilogviz/view/graph/__init__.py
--------------------------------------------------------------------------------
/verilogviz/view/graph/graphical_defines.py:
--------------------------------------------------------------------------------
1 | from PyQt4.QtGui import *
2 | from PyQt4.QtCore import *
3 |
4 | MODULE_RECT = QRectF(0, 0, 100, 50)
5 | MODULE_COLOR = QColor("blue")
6 | MODULE_POS = QPointF(0, -50)
7 |
--------------------------------------------------------------------------------
/verilogviz/view/graph/graphics_scene.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2015 Dave McCoy (dave.mccoy@cospandesign.com)
2 |
3 | # This file is part of Nysa (wiki.cospandesign.com/index.php?title=Nysa).
4 | #
5 | # Nysa is free software; you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation; either version 3 of the License, or
8 | # any later version.
9 | #
10 | # Nysa is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Nysa; If not, see .
17 |
18 |
19 | """ nysa interface
20 | """
21 |
22 | __author__ = 'dave.mccoy@cospandesign.com (Dave McCoy)'
23 |
24 | import sys
25 | import os
26 | import time
27 | import logging
28 |
29 | from PyQt4.Qt import *
30 | from PyQt4.QtCore import *
31 | from PyQt4.QtGui import *
32 |
33 | p = os.path.abspath(os.path.join(os.path.dirname(__file__),
34 | os.pardir,
35 | os.pardir,
36 | os.pardir))
37 |
38 | p = os.path.abspath(p)
39 | #print "Visual Graph Path: %s" % p
40 | sys.path.append(p)
41 | from common.pvg.visual_graph.graphics_scene import GraphicsScene as gs
42 |
43 | def enum(*sequential, **named):
44 | enums = dict(zip(sequential, range(len(sequential))), **named)
45 | return type('Enum', (), enums)
46 |
47 | view_state = enum( "normal",
48 | "core_selected")
49 |
50 |
51 |
52 |
53 |
54 | class GraphicsScene(gs):
55 |
56 | def __init__(self, view, actions, app):
57 | super (GraphicsScene, self).__init__(view, app)
58 | self.logger = logging.getLogger("verilogviz")
59 | self.actions = actions
60 | self.state = view_state.normal
61 |
62 | self.links = []
63 |
64 | #Parent Graphics Scene Overriden Functions
65 | def box_selected(self, data):
66 | self.logger.debug("Box Selected Not Implemented yet!")
67 |
68 | def box_deselected(self, data):
69 | self.logger.debug("Box Deselected Not Implemented yet!")
70 |
71 | def remove_selected(self, reference):
72 | self.logger.debug("Remove not implemented yet!")
73 |
74 | #Overriden PyQT4 Methods
75 | def mouseMoveEvent(self, event):
76 | super (GraphicsScene, self).mouseMoveEvent(event)
77 | self.auto_update_all_links()
78 |
79 | def mousePressEvent(self, event):
80 | super (GraphicsScene, self).mousePressEvent(event)
81 | self.auto_update_all_links()
82 |
83 | def dropEvent(self, event):
84 | self.logger("Drop Event: %s" % str(event))
85 | super (GraphicsScene, self).dropEvent(event)
86 |
87 | def startDrag(self, event):
88 | self.logger("start Drag Event: %s" % str(event))
89 | if self.dbg: print "GS: Drag start event"
90 |
91 | #States
92 | def get_state(self):
93 | return self.state
94 |
95 | def auto_update_all_links(self):
96 | for l in self.links:
97 | if l.is_center_track():
98 | l.auto_update_center()
99 |
100 |
101 |
--------------------------------------------------------------------------------
/verilogviz/view/graph/graphics_view.py:
--------------------------------------------------------------------------------
1 | # Distributed under the MIT licesnse.
2 | # Copyright (c) 2013 Dave McCoy (dave.mccoy@cospandesign.com)
3 |
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | # this software and associated documentation files (the "Software"), to deal in
6 | # the Software without restriction, including without limitation the rights to
7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | # of the Software, and to permit persons to whom the Software is furnished to do
9 | # so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in all
12 | # copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | # SOFTWARE.
21 |
22 |
23 | #A huge thanks to 'Rapid GUI Programming with Python and Qt' by Mark Summerfield
24 |
25 | '''
26 | Log
27 | 5/19/2015: Initial commit
28 | '''
29 |
30 | __author__ = "Dave McCoy dave.mccoy@cospandesign.com"
31 |
32 | import sys
33 | import os
34 |
35 | from PyQt4.QtCore import *
36 | from PyQt4.QtGui import *
37 |
38 |
39 | p = os.path.abspath(os.path.join(os.path.dirname(__file__),
40 | os.pardir,
41 | os.pardir,
42 | os.pardir))
43 |
44 | p = os.path.abspath(p)
45 | sys.path.append(p)
46 | from common.pvg.visual_graph.graphics_view import GraphicsView as gv
47 |
48 |
49 | class GraphicsView(gv):
50 | def __init__(self, parent):
51 | super(GraphicsView, self).__init__(parent)
52 | self.initialize = True
53 |
54 | def fit_in_view(self):
55 | self._scale_fit()
56 |
57 | def update(self):
58 | self._scale_fit()
59 | self.initialize = False
60 | super (GraphicsView, self).update()
61 |
62 | def paint(self, painter, option, widget):
63 | super(GraphicsView, self).paint(painter, option, widget)
64 | if self.initialize:
65 | self.update()
66 |
67 | def showEvent(self, sevent):
68 | super (GraphicsView, self).showEvent(sevent)
69 |
70 | def resizeEvent(self, event):
71 | super(GraphicsView, self).resizeEvent(event)
72 | #print "resize event"
73 | self.s.auto_update_all_links()
74 | #self.fit_in_view()
75 |
76 |
77 |
--------------------------------------------------------------------------------
/verilogviz/view/graph/module_box.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2015 Dave McCoy (dave.mccoy@cospandesign.com)
2 |
3 | # This file is part of Nysa (wiki.cospandesign.com/index.php?title=Nysa).
4 | #
5 | # Nysa is free software; you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation; either version 3 of the License, or
8 | # any later version.
9 | #
10 | # Nysa is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Nysa; If not, see .
17 |
18 |
19 | """ nysa interface
20 | """
21 |
22 | __author__ = 'dave.mccoy@cospandesign.com (Dave McCoy)'
23 |
24 | import sys
25 | import os
26 | import time
27 | import logging
28 | import json
29 |
30 | from PyQt4.Qt import *
31 | from PyQt4.QtCore import *
32 | from PyQt4.QtGui import *
33 |
34 | p = os.path.abspath(os.path.join(os.path.dirname(__file__),
35 | os.pardir,
36 | os.pardir,
37 | os.pardir))
38 |
39 | p = os.path.abspath(p)
40 | #print "Visual Graph Path: %s" % p
41 | sys.path.append(p)
42 |
43 |
44 | from common.pvg.visual_graph.box import Box
45 | from common.pvg.visual_graph import graphics_utils as gu
46 |
47 |
48 | class ModuleBox(Box):
49 | def __init__(self,
50 | scene,
51 | position,
52 | module_name,
53 | color,
54 | data,
55 | rect):
56 |
57 | super(ModuleBox, self).__init__(position = position,
58 | scene = scene,
59 | name = module_name,
60 | color = color,
61 | rect = rect,
62 | user_data = data)
63 | self.module_name = module_name
64 | self.logger = logging.getLogger("verilogviz")
65 | self.dragging = False
66 |
67 | md = {}
68 | md["name"] = module_name
69 | md["color"] = "color"
70 | md["data"] = data.get_module_tags()
71 | md["path"] = data.get_path()
72 | md["instance"] = data.name()
73 | md["move_type"] = "move"
74 | self.mime_data = json.dumps(md)
75 | self.setAcceptDrops(False)
76 | #self.movable(False)
77 |
78 | def contextMenuEvent(self, event):
79 |
80 | menu_items = (("&Remove", self.view_module),)
81 |
82 | menu = QMenu(self.parentWidget())
83 | for text, func in menu_items:
84 | menu.addAction(text, func)
85 | menu.exec_(event.screenPos())
86 |
87 | def view_module(self):
88 | self.logger.debug("View Module!")
89 |
90 | def itemChange(self, a, b):
91 | if QGraphicsItem.ItemSelectedHasChanged == a:
92 | if b.toBool():
93 | #Tell the scene that we are selected
94 | #self.s.
95 | self.logger.debug("Item Selected")
96 | pass
97 | else:
98 | #Tell the scene that we are no longer selected
99 | self.logger.debug("Item Deselected")
100 | pass
101 |
102 | return super(ModuleBox, self).itemChange(a, b)
103 |
104 | def mouseMoveEvent(self, event):
105 | if not self.is_movable():
106 | return super(ModuleBox, self).mouseMoveEvent(event)
107 |
108 | if (Qt.LeftButton & event.buttons()) > 0:
109 | pos = event.pos()
110 | epos = event.buttonDownPos(Qt.LeftButton)
111 | l = QLineF(pos, epos)
112 | if (l.length() < QApplication.startDragDistance()):
113 | event.accept
114 | return
115 |
116 | elif not self.dragging:
117 | self.dragging = True
118 | self.hide()
119 | mime_data = QMimeData()
120 | mime_data.setData("application/flowchart-data", self.mime_data)
121 | #Create and dispatch a move event
122 | drag = QDrag(event.widget())
123 | drag.start(Qt.MoveAction)
124 | drag.setMimeData(mime_data)
125 |
126 | #create an image for the drag
127 | size = QSize(self.start_rect.width(), self.start_rect.height())
128 | pixmap = QPixmap(size)
129 | pixmap.fill(QColor(self.color))
130 | painter = QPainter(pixmap)
131 | pen = QPen(self.style)
132 | pen.setColor(Qt.black)
133 | painter.setPen(pen)
134 | painter.setFont(self.text_font)
135 | gu.add_label_to_rect(painter, self.rect, self.box_name)
136 | painter.end()
137 | drag.setPixmap(pixmap)
138 | prev_pos = self.pos()
139 | drag.setHotSpot(epos.toPoint())
140 | value = drag.exec_(Qt.MoveAction)
141 | self.show()
142 | if value == 0:
143 | event.accept
144 | else:
145 | event.accept
146 | self.dragging = False
147 |
148 | super(ModuleBox, self).mouseMoveEvent(event)
149 |
150 |
151 | def mouseReleaseEvent(self, event):
152 | super (ModuleBox, self).mouseReleaseEvent(event)
153 |
154 |
155 |
--------------------------------------------------------------------------------
/verilogviz/view/graph/verilog_graph.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import logging
4 |
5 | p = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
6 | p = os.path.abspath(p)
7 | sys.path.append(p)
8 |
9 | from PyQt4.QtCore import *
10 | from PyQt4.QtGui import *
11 |
12 | from graphics_view import GraphicsView
13 | from graphics_scene import GraphicsScene
14 |
15 | from common.pvg.visual_graph.graphics_widget import GraphicsWidget
16 |
17 | from module_box import ModuleBox
18 |
19 | from graphical_defines import MODULE_RECT
20 | from graphical_defines import MODULE_COLOR
21 | from graphical_defines import MODULE_POS
22 |
23 | import networkx as nx
24 |
25 | SCALE_X = 200.0
26 | SCALE_Y = 100.0
27 |
28 |
29 | class VerilogGraph(GraphicsWidget):
30 |
31 | def __init__(self, app, actions):
32 | self.actions = actions
33 | self.logger = logging.getLogger("verilogviz")
34 | self.view = GraphicsView(self)
35 | self.scene = GraphicsScene(self.view, self.actions, app)
36 | super (VerilogGraph, self).__init__(self.view, self.scene)
37 | self.boxes = {}
38 | self.app = app
39 | self.vpos = 0
40 |
41 | def fit_in_view(self):
42 | self.view.fit_in_view()
43 | self.logger.debug("fit in view")
44 |
45 | def clear(self):
46 | self.scene.clear_links()
47 | self.scene.clear()
48 | self.boxes = {}
49 |
50 | def update(self):
51 | super (VerilogGraph, self).update()
52 | self.scene.auto_update_all_links()
53 | self.view._scale_fit()
54 |
55 | def sizeHint (self):
56 | size = QSize()
57 | size.setWidth(600)
58 | return size
59 |
60 | def add_verilog_module(self, module, position):
61 | mb = ModuleBox( self.scene,
62 | MODULE_POS,
63 | module.name(),
64 | MODULE_COLOR,
65 | module,
66 | MODULE_RECT)
67 |
68 | self.boxes[id(module)] = mb
69 |
70 | mb.setPos(position)
71 | return mb
72 |
73 | def set_verilog_module_position(name, position):
74 | box = self.boxes[name]
75 | box.setPos(position)
76 |
77 | def clear(self):
78 | self.boxes = {}
79 | self.scene.clear()
80 | self.scene.clear_links()
81 |
82 | def get_box_data(self, name):
83 | return self.boxes[name]
84 |
85 | def is_box_in_graph(self, name):
86 | return name in self.boxes.keys()
87 |
88 | def drag_enter(self, event):
89 | self.logger.debug("Module Drag Enter")
90 |
91 | def drag_leave(self, event):
92 | self.logger.debug("Module Drag Leave")
93 |
94 | def drag_move(self, event):
95 | self.logger.debug("Module Drag Move")
96 |
97 | def drop_event(self, event):
98 | self.logger.debug("Drop Event")
99 |
100 |
101 | def _tree_layout_creator(self, node_id, tree, graph, depth = 0, position_depth_list = [0], layout = {}, vpos = 0):
102 |
103 | self.vpos = vpos
104 | #layout[node_id] = [depth * SCALE_X, position_depth_list[depth] * SCALE_Y]
105 | layout[node_id] = [depth * SCALE_X, vpos * SCALE_Y]
106 | position_depth_list[depth] += 1
107 | successors = tree.successors(node_id)
108 | '''
109 | for e in tree.edges():
110 | if e[0] == node_name:
111 | if e[1] not in successors:
112 | successors.append(e[1])
113 | '''
114 |
115 | sub_position = 0
116 |
117 | if len(successors) > 0 and len(position_depth_list) <= depth + 1:
118 | position_depth_list.append(0)
119 |
120 | for s in successors:
121 | #print "successor :%d" % s
122 | self._tree_layout_creator(s, tree, graph, depth + 1, position_depth_list, layout, self.vpos)
123 | if successors.index(s) > 0:
124 | self.vpos += 1
125 |
126 | return layout
127 |
128 |
129 | def draw_module(self, module):
130 | graph = module.get_module_graph()
131 | #Find the depth of the modules
132 | #layout = nx.layout.fruchterman_reingold_layout(graph, scale = 1000.0)
133 | #layout = nx.layout.spring_layout(graph, scale = 1000.0)
134 | #import matplotlib.pyplot as plt
135 | bfs_tree = nx.bfs_tree(graph, source = id(module))
136 | #print "BFS Tree: %s" % str(dir(bfs_tree))
137 | #print "nodes: %s" % str(bfs_tree.successors(module.name()))
138 | #print "edges: %s" % str(bfs_tree.edges())
139 | layout = self._tree_layout_creator(id(module), bfs_tree, graph)
140 |
141 | #nx.draw(layout)
142 | #plt.show()
143 | #print "layout: %s" % str(layout)
144 | for n in graph.nodes():
145 | #print "N: %d" % n
146 | module = graph.node[n]
147 | p = QPointF(layout[n][0], layout[n][1])
148 | self.add_verilog_module(module, p)
149 |
150 | for e in graph.edges():
151 | #print "e: %s" % str(e)
152 | #print "\t%s %s" % (e[0], e[1])
153 | x1 = layout[e[0]][0]
154 | x2 = layout[e[1]][0]
155 | self.boxes[e[0]].add_link(self.boxes[e[1]], from_side = "right", to_side = "left")
156 | #self.boxes[e[0]].add_link(self.boxes[e[1]], from_side = "left", to_side = "right")
157 | #if x2 > x1:
158 | #else:
159 |
160 |
161 |
--------------------------------------------------------------------------------
/verilogviz/view/include_path_dialog.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import logging
4 |
5 | from PyQt4.Qt import *
6 | from PyQt4.QtCore import *
7 | from PyQt4.QtGui import *
8 |
9 |
10 | class IncludePathDialog(QDialog):
11 |
12 | def __init__(self, parent = None):
13 | super (IncludePathDialog, self).__init__(parent)
14 | self.logger = logging.getLogger("verilogviz")
15 | layout = QVBoxLayout()
16 |
17 | path_layout = QHBoxLayout()
18 | self.path_tree_view = QTreeView()
19 | self.set_dir_path_model()
20 | self.path_tree_view.clicked.connect(self.path_tree_clicked)
21 |
22 | add_remove_button_layout = QVBoxLayout()
23 | self.add_button = QPushButton("+")
24 | self.add_button.setEnabled(False)
25 | self.add_button.clicked.connect(self.add_button_clicked)
26 |
27 | self.remove_button = QPushButton("-")
28 | self.remove_button.setEnabled(False)
29 | self.remove_button.clicked.connect(self.remove_button_clicked)
30 |
31 | self.clear_button = QPushButton("Clear")
32 | self.clear_button.setEnabled(False)
33 | self.clear_button.clicked.connect(self.clear_button_clicked)
34 |
35 | add_remove_button_layout.addWidget(self.add_button)
36 | add_remove_button_layout.addWidget(self.remove_button)
37 | add_remove_button_layout.addWidget(self.clear_button)
38 |
39 | self.include_dir_list = QListWidget()
40 | self.include_dir_list.currentRowChanged.connect(self.path_item_changed)
41 |
42 | self.priority_layout = QVBoxLayout()
43 | self.up_button = QPushButton("Move Up")
44 | self.up_button.setEnabled(False)
45 | self.down_button = QPushButton("Move Down")
46 | self.down_button.setEnabled(False)
47 | self.priority_layout.addWidget(self.up_button)
48 | self.priority_layout.addWidget(self.down_button)
49 |
50 | path_layout.addWidget(self.path_tree_view)
51 | path_layout.addLayout(add_remove_button_layout)
52 | path_layout.addWidget(self.include_dir_list)
53 | path_layout.addLayout(self.priority_layout)
54 |
55 |
56 | ok_button = QPushButton("OK")
57 | #ok_button.clicked.connect(self.accepted)
58 | #ok_button.clicked.connect(self.finished)
59 | ok_button.clicked.connect(self.ok_button_clicked)
60 | cancel_button = QPushButton("Cancel")
61 | #cancel_button.clicked.connect(self.rejected)
62 | #cancel_button.clicked.connect(self.finished)
63 | cancel_button.clicked.connect(self.cancel_button_clicked)
64 |
65 | layout.addLayout(path_layout)
66 | layout.addWidget(ok_button)
67 | layout.addWidget(cancel_button)
68 |
69 | self.setLayout(layout)
70 | self.path = os.path.curdir
71 | self.logger.debug("Initial Path: %s" % self.path)
72 |
73 | def set_dir_path_model(self):
74 | file_model = QFileSystemModel()
75 | user_path = os.path.expanduser("~")
76 | file_model.setRootPath(user_path)
77 | self.path_tree_view.setModel(file_model)
78 |
79 | def set_start_path(self, path):
80 | self.logger.debug("Start Path: %s" % path)
81 | index = self.path_tree_view.model().index(path)
82 | self.path_tree_view.setCurrentIndex(index)
83 |
84 | def set_path_list(self, include_paths):
85 | self.include_dir_list.clear()
86 | self.include_dir_list.addItems(include_paths)
87 |
88 | def get_path_list(self):
89 | paths = []
90 | for index in xrange(self.include_dir_list.count()):
91 | item = self.include_dir_list.item(index)
92 | paths.append(str(item.text()))
93 | return paths
94 |
95 | def get_start_path(self):
96 | index = self.path_tree_view.currentIndex()
97 | path = self.path_tree_view.model().filePath(index)
98 | self.logger.info("Setting new path: %s" % str(path))
99 | return path
100 |
101 | def ok_button_clicked(self):
102 | self.setResult(QDialog.Accepted)
103 | self.accepted.emit()
104 | self.finished.emit(QDialog.Accepted)
105 | #self.close()
106 | self.done(QDialog.Accepted)
107 |
108 | def cancel_button_clicked(self):
109 | self.setResult(QDialog.Rejected)
110 | self.rejected.emit()
111 | self.finished.emit(QDialog.Rejected)
112 | #self.close()
113 | self.done(QDialog.Rejected)
114 |
115 | def path_tree_clicked(self):
116 | index = self.path_tree_view.currentIndex()
117 | path = str(self.path_tree_view.model().filePath(index))
118 | if os.path.isdir(path):
119 | self.logger.debug("Enable Add")
120 | self.add_button.setEnabled(True)
121 | else:
122 | self.logger.debug("Not a Path")
123 | self.add_button.setEnabled(False)
124 |
125 | def path_item_changed(self, index):
126 | if index == None:
127 | self.remove_button.setEnabled(False)
128 | self.up_button.setEnabled(False)
129 | self.down_button.setEnabled(False)
130 | return
131 |
132 |
133 | if self.include_dir_list.count() == 0:
134 | self.remove_button.setEnabled(False)
135 | return
136 |
137 | self.remove_button.setEnabled(True)
138 |
139 | if self.include_dir_list.count() <= 1:
140 | self.up_button.setEnabled(False)
141 | self.down_button.setEnabled(False)
142 | return
143 |
144 | if index == 0:
145 | self.up_button.setEnabled(False)
146 | self.down_button.setEnabled(True)
147 |
148 | elif index == self.include_dir_list.count() - 1:
149 | self.up_button.setEnabled(True)
150 | self.down_button.setEnabled(False)
151 |
152 | else:
153 | self.up_button.setEnabled(True)
154 | self.down_button.setEnabled(True)
155 |
156 |
157 | def add_button_clicked(self):
158 | index = self.path_tree_view.currentIndex()
159 | path = str(self.path_tree_view.model().filePath(index))
160 | self.logger.info("Adding path: %s" % path)
161 | self.include_dir_list.addItem(path)
162 | self.include_dir_list.update()
163 |
164 | def remove_button_clicked(self):
165 | self.logger.info("Remove")
166 | index = self.include_dir_list.currentRow()
167 | item = self.include_dir_list.takeItem(index)
168 | self.logger.info("removed: %s" % str(item.text()))
169 | index = self.include_dir_list.currentRow()
170 | self.path_item_changed(index)
171 |
172 | def clear_button_clicked(self):
173 | self.logger.info("Cleared")
174 |
175 |
--------------------------------------------------------------------------------
/verilogviz/view/main_form.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import logging
4 |
5 | from PyQt4.Qt import *
6 | from PyQt4.QtCore import *
7 | from PyQt4.QtGui import *
8 |
9 | from graph.verilog_graph import VerilogGraph
10 |
11 | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)))
12 | from model.module_list_model import ModuleListModel
13 |
14 | from include_path_dialog import IncludePathDialog
15 | from matplot_lib_widget import MatplotLibWidget
16 |
17 |
18 | FORMAT = '%(asctime)-15s %(message)s'
19 |
20 | VERILOG_EXTENSIONS = ["v"]
21 | VERILOG_NAME_EXTENSION = [("Verilog (*.v)")]
22 | PREVIOUS_DIR_KEY = "prev_dir"
23 | INCLUDE_PATH_START_DIR = "include_path_start_dir"
24 | INCLUDE_PATH_DIRS = "include_paths_dirs"
25 |
26 |
27 | def is_verilog_file(path):
28 | if not os.path.isfile(str(path)):
29 | return False
30 | fname, ext = os.path.splitext(path)
31 | ext = ext.strip(".")
32 | return ext in VERILOG_EXTENSIONS
33 |
34 |
35 | class MainForm (QMainWindow):
36 |
37 | def __init__(self, app, actions):
38 | super (MainForm, self).__init__()
39 | self.settings = QSettings("Cospan Design", "verilog-visualizer")
40 | if not self.settings.contains(INCLUDE_PATH_DIRS):
41 | self.settings.setValue(INCLUDE_PATH_DIRS, [os.curdir])
42 |
43 |
44 |
45 | #Configure Settings
46 | self.actions = actions
47 | self.logger = logging.getLogger("verilogviz")
48 | self.setWindowTitle("Verilog Visualizer")
49 | self.show()
50 |
51 | ## Actions
52 | exit_action = QAction('&Exit', self)
53 | exit_action.setShortcut('Ctrl+Q')
54 | exit_action.triggered.connect(quit)
55 |
56 | save_action = QAction("&Save", self)
57 | save_action.setShortcut('Ctrl+S')
58 |
59 | open_action = QAction("&Open", self)
60 | open_action.setShortcut('Ctrl+O')
61 |
62 | demo_action = QAction("&Demo", self)
63 | demo_action.setShortcut('Ctrl+D')
64 |
65 | configure_include_path_action = QAction("Configure &Include Paths", self)
66 | configure_include_path_action.setShortcut('Ctrl+I')
67 |
68 | save_action.triggered.connect(self.save_clicked)
69 | open_action.triggered.connect(self.open_clicked)
70 | demo_action.triggered.connect(self.demo_action)
71 | configure_include_path_action.triggered.connect(self.actions.configure_include_paths)
72 |
73 | #Toolbar
74 | self.toolbar = self.addToolBar("main")
75 | self.toolbar.addAction(exit_action)
76 | self.toolbar.addAction(demo_action)
77 | self.toolbar.addAction(configure_include_path_action)
78 |
79 | #Menubar
80 | menubar = self.menuBar()
81 | file_menu = menubar.addMenu('&File')
82 | file_menu.addAction(exit_action)
83 | file_menu.addAction(open_action)
84 | file_menu.addAction(save_action)
85 |
86 | #Custom Views
87 | self.verilog_graph = VerilogGraph(app, self.actions)
88 | #self.verilog_graph = MatplotLibWidget()
89 |
90 | #Project/File Pane
91 | project_file_view = QSplitter(Qt.Vertical)
92 | #self.project_list = QListWidget()
93 | self.project_list = QListView()
94 | self.project_list.setModel(ModuleListModel(self))
95 |
96 | #self.user_path = "."
97 | self.user_path = os.path.expanduser("~")
98 | if self.settings.contains(PREVIOUS_DIR_KEY):
99 | self.user_path = self.settings.value(PREVIOUS_DIR_KEY, type=str)
100 | self.logger.info("Loading previous path: %s" % self.user_path)
101 |
102 | self.file_model = QFileSystemModel()
103 | self.file_model.setRootPath(self.user_path)
104 | index = self.file_model.index(self.user_path)
105 | vext = []
106 | for v in VERILOG_EXTENSIONS:
107 | vext.append("*.%s" % v)
108 | self.file_model.setNameFilters(vext)
109 | self.file_model.setNameFilterDisables(False)
110 | self.file_view = QTreeView()
111 | self.file_view.setModel(self.file_model)
112 | self.file_view.setCurrentIndex(index)
113 |
114 | self.file_view.clicked.connect(self.tree_clicked)
115 | self.file_model.directoryLoaded.connect(self.tree_directory_loaded)
116 |
117 | self.main_splitter = QSplitter(Qt.Horizontal)
118 | #self.main_splitter.addWidget(self.file_view)
119 | project_file_view.addWidget(self.project_list)
120 | project_file_view.addWidget(self.file_view)
121 | self.main_splitter.addWidget(project_file_view)
122 | self.main_splitter.addWidget(self.verilog_graph)
123 |
124 | self.setCentralWidget(self.main_splitter)
125 |
126 | self.show()
127 | self.logger.debug("Showing")
128 |
129 | def save_clicked(self):
130 | self.logger.debug("Save Clicked")
131 |
132 | def open_clicked(self):
133 | self.logger.debug("Open Clicked")
134 |
135 | def demo_action(self):
136 | self.logger.debug("Demo Action!")
137 | #self.verilog_graph.add_verilog_module("test", {"test_data":"data"})
138 |
139 | def clear_graph(self):
140 | self.verilog_graph.clear()
141 |
142 | def draw_module(self, module):
143 | self.verilog_graph.draw_module(module)
144 |
145 | def add_verilog_project_list_item(self, module):
146 | if self.project_list.model().in_list(module.name()):
147 | raise LookupError("Project name in list")
148 |
149 | self.project_list.model().addItem(module)
150 |
151 | def remove_verilog_project_list_item(self, module_name):
152 | if not self.project_list.model().in_list(module_name):
153 | raise LookupError("Project not found in list")
154 | self.project_list.removeItemWidget(items[0])
155 |
156 | def get_module(self, module_name):
157 | return self.project_list.model().get_module_by_name(module_name)
158 |
159 | def get_graph(self):
160 | return self.verilog_graph
161 |
162 | def tree_clicked(self):
163 | index = self.file_view.currentIndex()
164 | path = self.file_model.filePath(index)
165 | if os.path.isdir(str(path)):
166 | self.user_path = path
167 | self.logger.info("Start Path changed to: %s" % self.user_path)
168 | elif is_verilog_file(str(path)):
169 | self.logger.info("verilog path: %s" % path)
170 | self.actions.add_verilog_module.emit(0, path)
171 |
172 | for i in range(self.file_model.columnCount()):
173 | self.file_view.resizeColumnToContents(i)
174 |
175 | def tree_directory_loaded(self):
176 | for i in range(self.file_model.columnCount()):
177 | self.file_view.resizeColumnToContents(i)
178 |
179 | def closeEvent(self, event):
180 | self.logger.debug("Close Event")
181 | self.settings.setValue(PREVIOUS_DIR_KEY, self.user_path)
182 | del(self.settings)
183 | quit()
184 |
185 | def configure_include_paths_dialog(self):
186 | self.logger.debug("Paths dialog clicked")
187 | start_path = os.path.expanduser("~")
188 |
189 | if self.settings.contains(INCLUDE_PATH_START_DIR):
190 | start_path = self.settings.value(INCLUDE_PATH_START_DIR, type = str)
191 |
192 | include_paths = self.settings.value(INCLUDE_PATH_DIRS, type = str)
193 | self.logger.debug("Loading Paths: %s " % str(include_paths))
194 |
195 | ipd = IncludePathDialog(self)
196 | ipd.set_path_list(include_paths)
197 | ipd.set_start_path(start_path)
198 | result = ipd.exec_()
199 | paths = ipd.get_path_list()
200 |
201 | if result:
202 | self.logger.debug("Accepted, new paths: %s" % str(ipd.get_path_list()))
203 | print "Paths: %s " % str(ipd.get_path_list())
204 | self.settings.setValue(INCLUDE_PATH_DIRS, ipd.get_path_list())
205 | self.settings.setValue(INCLUDE_PATH_START_DIR, ipd.get_start_path())
206 | return True
207 | else:
208 | self.logger.debug("Rejected")
209 | return False
210 |
211 | def get_include_paths(self):
212 | return self.settings.value(INCLUDE_PATH_DIRS, type = str)
213 |
214 | def update_modules_user_paths(self, paths):
215 | self.project_list.model().update_modules_user_paths(paths)
216 |
217 |
218 |
--------------------------------------------------------------------------------
/verilogviz/view/matplot_lib_widget.py:
--------------------------------------------------------------------------------
1 | from PyQt4.Qt import *
2 | from PyQt4.QtCore import *
3 | from PyQt4.QtGui import *
4 |
5 | from matplotlib.backends import qt4_compat
6 | from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
7 | from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg
8 | from matplotlib.figure import Figure
9 | #from matplotlib.pyplot import figure
10 |
11 | import networkx as nx
12 |
13 |
14 |
15 |
16 |
17 | class MatplotLibWidget(FigureCanvas):
18 |
19 | def __init__(self, parent = None):
20 |
21 | fig = Figure()
22 | self.sp = fig.add_subplot(111)
23 | FigureCanvas.__init__(self, fig)
24 | self.sp.hold(False)
25 |
26 | #layout = QVBoxLayout()
27 |
28 | #self.figure = figure()
29 | '''
30 | self.canvas = FigureCanvas(self.figure)
31 |
32 | self.toolbar = NavigationToolbar2QTAgg(self.canvas, self)
33 | '''
34 |
35 | #layout.addWidget(self.toolbar)
36 | #layout.addWidget(self.canvas)
37 | #self.setLayout(layout)
38 |
39 |
40 | '''
41 | fig = Figure(figsize=(width, height), dpi=dpi)
42 | self.axes = fig.add_subplot(111)
43 | # We want the axes cleared every time plot() is called
44 | self.axes.hold(False)
45 |
46 | self.compute_initial_figure()
47 |
48 | FigureCanvas.__init__(self, fig)
49 | '''
50 |
51 | self.setParent(parent)
52 | self.setSizePolicy( QSizePolicy.Expanding,
53 | QSizePolicy.Expanding)
54 | self.updateGeometry()
55 |
56 | def draw_graph(self, graph):
57 | value = nx.draw(graph, ax=self.sp)
58 | self.sp.draw(value)
59 |
60 |
61 | def compute_initial_figure(self):
62 | pass
63 |
64 |
--------------------------------------------------------------------------------