├── .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 | --------------------------------------------------------------------------------