├── .gitignore ├── .travis.yml ├── LICENSE ├── Readme.rst ├── pyverilog_toolbox ├── __init__.py ├── docs │ ├── cnt_analyzer.md │ ├── codeclone.md │ ├── combloop.md │ ├── gui.md │ ├── metrics.md │ ├── regmap.md │ ├── screenshot.PNG │ └── unreferenced.md ├── gui │ ├── __init__.py │ ├── gui_main.py │ ├── gui_main.spec │ └── gui_main_onefile.spec ├── testcode │ ├── case_in_func.v │ ├── combloop.v │ ├── combloop1.v │ ├── combloop2.v │ ├── combloop4.v │ ├── complex_partselect.v │ ├── config_metrics.txt │ ├── floating.v │ ├── floating2.v │ ├── fv_test.v │ ├── metrics_func.v │ ├── metrics_test.v │ ├── metrics_test2.v │ ├── norm_cnt.v │ ├── norm_cnt2.v │ ├── norm_cnt3.v │ ├── not_combloop.v │ ├── reg_clone.v │ ├── regmap.v │ ├── regmap2.v │ ├── regmap_split.v │ ├── setup.txt │ ├── test_ra.py │ └── unreferenced_variables.v └── verify_tool │ ├── __init__.py │ ├── bindlibrary.py │ ├── cnt_analyzer.py │ ├── codeclone_finder.py │ ├── combloop_finder.py │ ├── dataflow_facade.py │ ├── formal_verifier.py │ ├── metrics_calculator.py │ ├── regmap_analyzer.py │ └── unreferenced_finder.py ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | pyverilog_toolbox/verify_tool/parsetab.py 3 | *.output 4 | pyverilog_toolbox/testcode/parsetab.py 5 | *.out 6 | pyverilog_toolbox/verify_tool/out.csv 7 | *.csv 8 | pyverilog_toolbox/testcode/simple.v 9 | pyverilog_toolbox/testcode/test1.v 10 | pyverilog_toolbox/testcode/test2.v 11 | pyverilog_toolbox/verify_tool/refactor.py 12 | build/ 13 | dist/ 14 | pyverilog_toolbox.egg-info/ 15 | *.bat 16 | 17 | *.html 18 | pyverilog_toolbox/gui/parsetab.py 19 | *.ps1 20 | pyverilog_toolbox/gui/setup.txt 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 2.7 4 | - 3.4 5 | before_script: 6 | - sudo apt-get install iverilog 7 | - pip install jinja2 8 | - sudo apt-get install graphviz 9 | - pip install pygraphviz 10 | - pip install pyverilog 11 | - pip install sympy 12 | script: 13 | - cd pyverilog_toolbox/testcode/ 14 | - python test_ra.py 15 | - cd ../verify_tool/ 16 | - python codeclone_finder.py 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /Readme.rst: -------------------------------------------------------------------------------- 1 | |Build Status| 2 | 3 | Introduction 4 | ============ 5 | 6 | Pyverilog\_toolbox is Pyverilog-based verification/design tool including 7 | code clone finder, metrics calculator and so on. Pyverilog\_toolbox 8 | accerating your digital circuit design verification. Thanks to Pyverilog 9 | developer shtaxxx. 10 | 11 | Software Requirements 12 | ===================== 13 | 14 | - Python (2.7 or 3.4) 15 | - Pyverilog (you can download from 16 | https://github.com/shtaxxx/Pyverilog) Pyverilog requires Icarus 17 | verilog 18 | 19 | Installation 20 | ============ 21 | 22 | (If you want to use GUI stand alone version for windows, `Click here to 23 | get 24 | detail `__ 25 | 26 | If you want to use Pyverilog as a general library, you can install on 27 | your environment by using setup.py. 28 | 29 | :: 30 | 31 | python setup.py install 32 | 33 | Or you can use pip 34 | 35 | :: 36 | 37 | pip install pyverilog_toolbox 38 | 39 | Features 40 | ======== 41 | 42 | codeclone\_finder 43 | ----------------- 44 | 45 | codeclone\_finder can find pair of the register clone, which always hold 46 | same value. Also can find pair of the invert register, which always hold 47 | different value. 48 | 49 | `Click here to get 50 | detail `__ 51 | 52 | combloop\_finder 53 | ---------------- 54 | 55 | Combinational logic loop is sticky problem, but you can find it by 56 | combloop\_finder easily. 57 | 58 | `Click here to get 59 | detail `__ 60 | 61 | unreferenced\_finder 62 | -------------------- 63 | 64 | Unreferenced\_finder can find signals which isn't referenced by any 65 | signals. Also floating nodes can be found. By using this, you can delte 66 | unnecessary codes. 67 | 68 | `Click here to get 69 | detail `__ 70 | 71 | metrics\_calculator 72 | ------------------- 73 | 74 | metrics\_analyzer is metrics measurment tools for Verilog HDL. You can 75 | visualize complecity of module/register/function/. 76 | 77 | `Click here to get 78 | detail `__ 79 | 80 | regmap\_analyzer 81 | ---------------- 82 | 83 | regmap\_analyzer can analyze register map structure from RTL. 84 | 85 | `Click here to get 86 | detail `__ 87 | 88 | cnt\_analyzer 89 | ------------- 90 | 91 | cnt\_analyzer analyze counter property(up or down, max value, reset 92 | value and counter dependency). And extracting event which depends on 93 | counter value. This feature help you finding redundunt counter, deadlock 94 | loop, and other counter trouble. 95 | 96 | `Click here to get 97 | detail `__ 98 | 99 | License 100 | ======= 101 | 102 | Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) 103 | 104 | Copyright 105 | ========= 106 | 107 | Copyright (C) 2015, Ryosuke Fukatani 108 | 109 | Related Project and Site 110 | ======================== 111 | 112 | Pyverilog https://github.com/shtaxxx/Pyverilog 113 | 114 | Blog entry(in Japanese) 115 | http://segafreder.hatenablog.com/entry/2015/05/23/161000 116 | 117 | .. |Build Status| image:: https://travis-ci.org/fukatani/Pyverilog_toolbox.svg?branch=master 118 | :target: https://travis-ci.org/fukatani/Pyverilog_toolbox 119 | -------------------------------------------------------------------------------- /pyverilog_toolbox/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | if sys.version_info[0] < 3: 3 | import verify_tool 4 | #import gui 5 | #import testcode 6 | -------------------------------------------------------------------------------- /pyverilog_toolbox/docs/cnt_analyzer.md: -------------------------------------------------------------------------------- 1 | ## cnt_analyzer usage 2 | 3 | cnt_analyzer analyze counter property(up or down, max value, reset value and counter dependency). 4 | And extracting event which depends on counter value. 5 | This feature help you finding redundunt counter, deadlock loop, and other counter trouble. 6 | 7 | 8 | ``` 9 | python cnt_analyzer.py xxxx.v 10 | ``` 11 | 12 | ex. 13 | Input verilog file: 14 | ```verilog 15 | module TOP(CLK, RSTN, UP_ENABLE, UP_ENABLE2, CLEAR); 16 | input CLK,RSTN,UP_ENABLE,UP_ENABLE2,CLEAR; 17 | 18 | reg [2:0] up_cnt; 19 | wire is_count_max = up_cnt == 3'd6; 20 | 21 | always @(posedge CLK or negedge RSTN) begin 22 | if(!RSTN) begin 23 | up_cnt <= 0; 24 | end else if(is_count_max) begin 25 | up_cnt <= 0; 26 | end else if(up_cnt >= 3'd5) begin 27 | up_cnt <= 0; 28 | end else if(CLEAR) begin 29 | up_cnt <= 0; 30 | end else if(UP_ENABLE) begin 31 | up_cnt <= up_cnt + 3'd1; 32 | end else if(UP_ENABLE2) begin 33 | up_cnt <= up_cnt + 3'd1; 34 | end else begin 35 | up_cnt <= up_cnt; 36 | end 37 | end 38 | 39 | reg [2:0] up_cnt2; 40 | always @(posedge CLK or negedge RSTN) begin 41 | if(!RSTN) begin 42 | up_cnt2 <= 0; 43 | end else if(up_cnt2 != 3'd5 && up_cnt == 3'd5) begin 44 | up_cnt2 <= up_cnt2 + 3'd1; 45 | end else begin 46 | up_cnt2 <= 0; 47 | end 48 | end 49 | 50 | reg [2:0] down_cnt; 51 | always @(posedge CLK or negedge RSTN) begin 52 | if(!RSTN) begin 53 | down_cnt <= 0; 54 | end else if(down_cnt != 3'd0) begin 55 | down_cnt <= down_cnt - 3'd1; 56 | end else begin 57 | down_cnt <= 3'd5; 58 | end 59 | end 60 | 61 | reg now; 62 | always @(posedge CLK or negedge RSTN) begin 63 | if(!RSTN) begin 64 | now <= 0; 65 | end else if(up_cnt == 3'd4) begin 66 | now <= 0; 67 | end else if(up_cnt == 3'd2) begin 68 | now <= 1; 69 | end 70 | end 71 | 72 | endmodule 73 | 74 | 75 | 76 | ``` 77 | Output: 78 | ``` 79 | name: TOP.up_cnt 80 | category: up counter 81 | reset val: 0 82 | max_val: 6 83 | mother counter:set([]) 84 | 85 | name: TOP.down_cnt 86 | category: down counter 87 | reset val: 0 88 | max_val: 4 89 | mother counter:set([]) 90 | 91 | name: TOP.up_cnt2 92 | category: up counter 93 | reset val: 0 94 | max_val: 4 95 | mother counter:set(['TOP.up_cnt']) 96 | 97 | TOP.up_cnt {2: ["TOP.now='d1 @(TOP_up_cnt==3'd2)", "TOP.is_count_max='d1 @(TOP_up_cnt==3'd2)", "TOP.up_cnt2='d0 @(TOP_up_cnt==3'd2)"]} 98 | TOP.down_cnt {} 99 | TOP.up_cnt2 {} 100 | ``` 101 | -------------------------------------------------------------------------------- /pyverilog_toolbox/docs/codeclone.md: -------------------------------------------------------------------------------- 1 | ## CODECLONE_FINDER USAGE 2 | 3 | After install Pyverilog_toolbox, you can use codeclone_finder by this command. 4 | 5 | ``` 6 | python codeclone_finder.py xxxx.v 7 | ``` 8 | 9 | ex. 10 | Input verilog file: 11 | 12 | ```verilog 13 | always @(posedge CLK or negedge RST) begin 14 | if(RST) begin 15 | reg1 <= 1'b0; 16 | end else begin 17 | reg1 <= IN; 18 | end 19 | end 20 | 21 | assign in1 = IN; 22 | 23 | always @(posedge CLK or negedge RST) begin 24 | if(RST) begin 25 | reg2 <= 1'b0; 26 | end else begin 27 | reg2 <= in1; 28 | end 29 | end 30 | 31 | always @(posedge CLK or negedge RST) begin 32 | if(RST) begin 33 | reg3 <= 1'b1; 34 | end else begin 35 | reg3 <= !IN; 36 | end 37 | end 38 | ``` 39 | 40 | Output: 41 | 42 | ``` 43 | Invert reg pairs: [((TOP.reg1, 0), (TOP.reg3, 0)), ((TOP.reg2, 0), (TOP.reg3, 0))] 44 | Clone reg pairs: [((TOP.reg1, 0), (TOP.reg2, 0))] 45 | ``` 46 | 47 | You can refine this RTL as follows(by manual) for circuit size and maintainability. 48 | 49 | ``` 50 | always @(posedge CLK or negedge RST) begin 51 | if(RST) begin 52 | reg1 <= 1'b0; 53 | end else begin 54 | reg1 <= IN; 55 | end 56 | end 57 | 58 | wire reg2 = reg1; 59 | wire reg3 = !reg1; 60 | 61 | ``` 62 | 63 | -------------------------------------------------------------------------------- /pyverilog_toolbox/docs/combloop.md: -------------------------------------------------------------------------------- 1 | ## combloop_finder usage 2 | 3 | Combinational logic loop is sticky problem, but you can find it by combloop_finder easily. 4 | 5 | 6 | 7 | ``` 8 | python combloop_finder.py xxxx.v 9 | ``` 10 | 11 | if there is a combinational loop in your design, combloop_finder raise error and specify loop occurrence place. 12 | 13 | ex. 14 | 15 | ```verilog 16 | module TOP(CLK, RST); 17 | input CLK,RST; 18 | wire wire1,wire2,wire3; 19 | 20 | assign wire1 = wire2; 21 | assign wire2 = !wire3; 22 | assign wire3 = wire1; 23 | 24 | endmodule 25 | ``` 26 | 27 | 28 | Output: 29 | ``` 30 | CombLoopException: Combinational loop is found @TOP.wire3 31 | ``` -------------------------------------------------------------------------------- /pyverilog_toolbox/docs/gui.md: -------------------------------------------------------------------------------- 1 | ## ABOUT GUI STAND ALONE VERSION FOR WINDOWS 2 | 3 | Screenshots 4 | ============================== 5 | 6 | ![Screenshots](https://github.com/fukatani/Pyverilog_toolbox/blob/master/pyverilog_toolbox/docs/screenshot.PNG "Screenshots") 7 | 8 | Pyverilog_toolbox including gui stand alone version for windows (XP or later). 9 | Because this is stand alone version, you don't have to install Python, original Pyverilog, and other requirements. (Excluding Icarus verilog) 10 | 11 | 12 | Software Requirements 13 | ============================== 14 | Icarus verilog (get from http://bleyer.org/icarus/) 15 | 16 | 17 | Usage 18 | ============================== 19 | 20 | Download from 21 | https://github.com/fukatani/Pyverilog_toolbox/releases/tag/gui0.0.1 22 | 23 | And choose pyverilog_toolbox.exe. 24 | 25 | 26 | Click .exe file. 27 | (Don't place it in the directory which name include double-byte character.) 28 | 29 | 30 | License 31 | ============================== 32 | Pyverilog_toolbox gui stand alone version includes Pyverilog, that License is Apache License 2.0, 33 | and also includes PLY-3.4, That license of PLY is BSD. 34 | 35 | License of Pyverilog_toolbox is Apache License 2.0. 36 | 37 | Apache License 2.0 38 | (http://www.apache.org/licenses/LICENSE-2.0) 39 | 40 | 41 | Other 42 | ============================== 43 | 44 | if you need GUI stand alone version for other operational system, you can build it by using .spec file included in this project. You need wxpython, pyintaller, pyverilog, and other pyverilog's requirements. 45 | 46 | (You can get spec file from https://github.com/fukatani/Pyverilog_toolbox/tree/master/pyverilog_toolbox/gui) 47 | 48 | ``` 49 | Pyinstaller gui_main_onefile.spec 50 | ``` -------------------------------------------------------------------------------- /pyverilog_toolbox/docs/metrics.md: -------------------------------------------------------------------------------- 1 | ## metrics_analyzer usage 2 | 3 | metrics_analyzer is metrics measurment tools for Verilog HDL. 4 | You can visualize complexity of module/register/function/ for refactoring. 5 | Large score means complex module/register/function/. 6 | 7 | ``` 8 | python metrics_analyzer.py xxxx.v 9 | ``` 10 | 11 | Each metrics is calculated as described below. 12 | 13 | (COEF_FOR_XXXX * XXXX) ^ POW_OF_XXXX 14 | 15 | Parameters corresponding to XXXX is different by each of metrics (module/register/function). 16 | 17 | 18 | # module metrics arguments 19 | Number of input port in each module. 20 | 21 | Number of output port in each module. 22 | 23 | Number of register in each module. 24 | 25 | Number of clock in each module. 26 | 27 | Number of reset in each module. 28 | 29 | # register metrics arguments 30 | Number of condition branch in each register. 31 | 32 | Max nest of control syntax in each register. 33 | 34 | # function metrics arguments 35 | Number of condition branch in each function. 36 | 37 | Max nest of control syntax in each function. 38 | 39 | Number of arguments in each function. 40 | 41 | 42 | ex. 43 | Input verilog file: 44 | ``` 45 | 46 | module TOP(CLK, RST, IN, IN2); 47 | input CLK, RST, IN, IN2; 48 | reg reg2; 49 | 50 | always @(posedge CLK or negedge RST) begin 51 | reg2 <= func1(IN,IN2); 52 | end 53 | function func1; 54 | input bit; 55 | input bit2; 56 | 57 | if(bit2) 58 | func1 = !bit; 59 | else 60 | func1 = bit; 61 | 62 | endfunction 63 | 64 | endmodule 65 | 66 | 67 | ``` 68 | Output: 69 | ``` 70 | module metrics 71 | total: 19 72 | average: 19 73 | 74 | each score: 75 | TOP: 19 76 | Number of input ports: 4 77 | Number of output ports: 0 78 | Number of registers: 1 79 | Number of clocks: 1 80 | Number of resets: 1 81 | 82 | 83 | register metrics 84 | total: 1 85 | average: 1 86 | 87 | each score: 88 | ('TOP', 0): 1 89 | Number of branch: 0 90 | Max nest: 1 91 | 92 | 93 | function metrics 94 | total: 9 95 | average: 9 96 | 97 | each score: 98 | ('TOP.md_always0.al_block0.al_functioncall0', 0): 9 99 | Number of branch: 1 100 | Max nest: 2 101 | Number of variables: 2 102 | ``` 103 | 104 | 105 | You can use configure parameters for metrics calculation. 106 | 107 | ``` 108 | python metrics_analyzer.py xxxx.v -S yyy.txt 109 | ``` 110 | 111 | 112 | yyy.txt sample: 113 | ``` 114 | #config parameters for module metrics 115 | COEF_FOR_INPUT:1 116 | POW_FOR_INPUT:2 117 | COEF_FOR_OUTPUT:1 118 | POW_FOR_OUTPUT:2 119 | COEF_FOR_REG:1 120 | POW_FOR_REG:1 121 | COEF_FOR_CLK:1 122 | POW_FOR_CLK:1 123 | COEF_FOR_RST:1 124 | POW_FOR_RST:1 125 | 126 | #config parameters for module metrics 127 | COEF_FOR_BRANCH': 128 | POW_FOR_BRANCH': 129 | COEF_FOR_NEST': 130 | POW_FOR_NEST': 131 | 132 | #config parameters for function metrics 133 | COEF_FOR_VAR': 134 | NEST_FOR_VAR': 135 | 136 | #config parameters for display 137 | MODULE_DISP_LIMIT': 138 | REG_DISP_LIMIT': 139 | FUNC_DISP_LIMIT': 140 | ``` 141 | 142 | You may write only parameter which you want to change from default parameter in configuration file. -------------------------------------------------------------------------------- /pyverilog_toolbox/docs/regmap.md: -------------------------------------------------------------------------------- 1 | ## REGMAP_ANALYZER USAGE 2 | 3 | regmap_analyzer can analyze register map structure from RTL. 4 | After install Pyverilog_toolbox, you can use regmap analyzer by this command. 5 | 6 | ``` 7 | python regmap_analyzer.py xxxx.v -S config.txt 8 | ``` 9 | 10 | 11 | xxxx.v is regmap RTL file. 12 | To analyse register map, config file is needed. 13 | 14 | control flag is to be defined in config file. 15 | testcode/regmap.v is example of register map RTL file, 16 | and testcode/setup.txt is example of config file. 17 | 18 | Analysis result will be output as out.csv. 19 | 20 | ex. 21 | 22 | Input verilog file: 23 | ```verilog 24 | always @(posedge CLK) begin 25 | if(RST) begin 26 | reg0[1:0] <= 0; 27 | reg1 <= 0; 28 | end else if(WRITE) begin 29 | case(ADDR) 30 | 0:reg0[1:0] <= WRITE_DATA; 31 | 1:reg1 <= WRITE_DATA[0]; 32 | endcase 33 | end 34 | end 35 | 36 | always @* begin 37 | case(ADDR) 38 | 0:READ_DATA[1:0] = reg0[1:0]; 39 | 1:READ_DATA[1:0] = {1'b0,reg1}; 40 | endcase 41 | end 42 | ``` 43 | 44 | Output: 45 | 46 | ``` 47 | Write Map 48 | ADD 1 0 49 | 0 TOP.reg0[1] TOP.reg0[0] 50 | 1 TOP.reg1[0] 51 | Read Map 52 | ADD 1 0 53 | 0 TOP.reg0[1] TOP.reg0[0] 54 | 1 TOP.reg1[0] 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /pyverilog_toolbox/docs/screenshot.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fukatani/Pyverilog_toolbox/69726f9ba2a6c44cca98b6576a0cdb6a99f29f68/pyverilog_toolbox/docs/screenshot.PNG -------------------------------------------------------------------------------- /pyverilog_toolbox/docs/unreferenced.md: -------------------------------------------------------------------------------- 1 | ## unreferenced finder usage 2 | 3 | Unreferenced_finder can find signals which isn't referenced by any signals. 4 | And also find floating nodes. 5 | By using this, you can delte unnecessary codes. 6 | 7 | ``` 8 | python unrefereced_finder.py xxxx.v 9 | ``` 10 | 11 | 12 | xxxx.v is regmap RTL file. 13 | 14 | ex. 15 | 16 | Input verilog file: 17 | ```verilog 18 | module TOP(CLK, RST, IN, IN2, reg1, OUT); 19 | input CLK, RST, IN, IN2; 20 | reg reg1,reg2,reg3; 21 | output reg1,OUT; 22 | wire in1; 23 | 24 | always @(posedge CLK or negedge RST) begin 25 | if(RST) begin 26 | reg1 <= 1'b0; 27 | end else begin 28 | reg1 <= IN; 29 | end 30 | end 31 | 32 | always @(posedge CLK or negedge RST) begin 33 | if(RST) begin 34 | reg2 <= 1'b0; 35 | end else begin 36 | reg2 <= reg1; 37 | end 38 | end 39 | 40 | SUB sub(CLK,RST,in1,OUT); 41 | endmodule 42 | 43 | module SUB(CLK,RST,IN, OUT); 44 | input CLK, RST, IN; 45 | output OUT; 46 | reg reg1; 47 | wire OUT = reg1; 48 | 49 | always @(posedge CLK or negedge RST) begin 50 | if(RST) begin 51 | reg1 <= 1'b0; 52 | end else begin 53 | reg1 <= 1'b1; 54 | end 55 | end 56 | 57 | endmodule 58 | 59 | ``` 60 | 61 | Output: 62 | 63 | ``` 64 | finded unreferenced variables: ['TOP.reg2', 'TOP.IN2', 'TOP.reg3', 'TOP.sub.IN'] 65 | ``` 66 | 67 | You can delete these variables from design. 68 | 69 | 70 | Input verilog file: 71 | ```verilog 72 | module TOP(CLK, RST, IN, IN2, reg1, OUT); 73 | input CLK, RST, IN, IN2; 74 | reg reg1,reg2,reg3; 75 | output reg1,OUT; 76 | wire in1; 77 | 78 | always @(posedge CLK or negedge RST) begin 79 | if(RST) begin 80 | reg1 <= 1'b0; 81 | end else begin 82 | reg1 <= IN; 83 | end 84 | end 85 | 86 | always @(posedge CLK or negedge RST) begin 87 | if(RST) begin 88 | reg2 <= 1'b0; 89 | end else begin 90 | reg2 <= reg1; 91 | end 92 | end 93 | 94 | SUB sub(CLK,RST,in1,OUT); 95 | endmodule 96 | 97 | module SUB(CLK,RST,IN, OUT); 98 | input CLK, RST, IN; 99 | output OUT; 100 | reg reg1; 101 | wire OUT = reg1; 102 | 103 | always @(posedge CLK or negedge RST) begin 104 | if(RST) begin 105 | reg1 <= 1'b0; 106 | end else begin 107 | reg1 <= 1'b1; 108 | end 109 | end 110 | 111 | endmodule 112 | 113 | ``` 114 | 115 | Output: 116 | 117 | ``` 118 | finded unreferenced variables: ['TOP.reg2', 'TOP.IN2', 'TOP.reg3', 'TOP.sub.IN'] 119 | ``` 120 | 121 | And you can find floating nodes easily. 122 | 123 | ```verilog 124 | module TOP(CLK, RST); 125 | input CLK, RST; 126 | reg reg1; 127 | reg reg2; 128 | wire in1; //floating 129 | 130 | always @(posedge CLK or negedge RST) begin 131 | if(!RST) begin 132 | reg1 <= 1'b0; 133 | end else begin 134 | reg1 <= in1; 135 | end 136 | end 137 | 138 | endmodule 139 | ``` 140 | 141 | Output: 142 | 143 | ``` 144 | floating nodes: ['TOP.in1', 'TOP.reg2'] 145 | ``` -------------------------------------------------------------------------------- /pyverilog_toolbox/gui/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | if sys.version_info[0] < 3: 3 | import gui_main 4 | import output_display 5 | -------------------------------------------------------------------------------- /pyverilog_toolbox/gui/gui_main.py: -------------------------------------------------------------------------------- 1 | import wx 2 | import wx.html 3 | import os 4 | import sys 5 | import webbrowser 6 | import warnings 7 | import wx.lib.agw.persist as PM 8 | 9 | if getattr(sys, 'frozen', False): 10 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))))) 11 | elif __file__: 12 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 13 | 14 | from pyverilog.utils.verror import * 15 | from pyverilog_toolbox.verify_tool.regmap_analyzer import * 16 | from pyverilog_toolbox.verify_tool.combloop_finder import * 17 | from pyverilog_toolbox.verify_tool.bindlibrary import * 18 | from pyverilog_toolbox.verify_tool.cnt_analyzer import * 19 | from pyverilog_toolbox.verify_tool.codeclone_finder import CodeCloneFinder 20 | from pyverilog_toolbox.verify_tool.unreferenced_finder import UnreferencedFinder 21 | from pyverilog_toolbox.verify_tool.metrics_calculator import MetricsCalculator 22 | from pyverilog_toolbox.verify_tool.combloop_finder import CombLoopFinder 23 | from pyverilog_toolbox.verify_tool.bindlibrary import CombLoopException 24 | from pyverilog_toolbox.verify_tool.cnt_analyzer import CntAnalyzer 25 | 26 | #from pyverilog_toolbox.gui.output_display import OutputDisplay 27 | 28 | class GuiMain(wx.Frame): 29 | debug = False 30 | 31 | def OnClose(self, event): 32 | self._persistMgr.SaveAndUnregister() 33 | self.vfile_data.dump() 34 | event.Skip() 35 | 36 | def __init__(self): 37 | wx.Frame.__init__(self,None,wx.ID_ANY,"Pyv_guitools",size=(450,550)) 38 | 39 | # initialiuze status bar 40 | self.CreateStatusBar() 41 | self.SetStatusText("") 42 | self.GetStatusBar().SetBackgroundColour(None) 43 | 44 | # initialiuze menu bar 45 | self.Bind(wx.EVT_MENU, self.selectMenu) 46 | self.SetMenuBar(Menu()) 47 | 48 | # build body 49 | self.commands = ("exec dataflow analyzer", "exec controlflow analyzer", "calculate code metrics", 50 | "find combinational loop", "find unused variables", "find code clone", 51 | "analyze counter", "analyze register map") 52 | root_panel = wx.Panel(self,wx.ID_ANY) 53 | root_layout = wx.BoxSizer(wx.VERTICAL) 54 | 55 | root_layout.Add(wx.StaticText(root_panel, wx.ID_ANY, "TOP MODULE NAME:"), border=5) 56 | self.top_name_panel = TextPanel(root_panel) 57 | root_layout.Add(self.top_name_panel, 0, wx.GROW|wx.ALL, border=5) 58 | 59 | filebutton_panel = CommandButtonPanel(root_panel, "Verilog file select", self.click_fs_button) 60 | self.selected_file_panel = wx.StaticText(root_panel, wx.ID_ANY, "") 61 | root_layout.Add(filebutton_panel, 0, wx.GROW|wx.ALL,border=5) 62 | root_layout.Add(wx.StaticText(root_panel, wx.ID_ANY, "Selecting verilog file:"), border=5) 63 | root_layout.Add(self.selected_file_panel, border=5) 64 | 65 | self.radiobutton_panel = RadioPanel(root_panel, self.commands) 66 | root_layout.Add(self.radiobutton_panel, 0, wx.GROW|wx.ALL, border=10) 67 | 68 | exebutton_panel = CommandButtonPanel(root_panel, "EXECUTE!", self.click_exe_button) 69 | root_layout.Add(exebutton_panel, 0, wx.GROW|wx.LEFT|wx.RIGHT, border=5) 70 | 71 | root_panel.SetSizer(root_layout) 72 | root_layout.Fit(root_panel) 73 | 74 | self.dirname = '' 75 | 76 | #for persistence 77 | self.SetName('gui_main.dump') 78 | self.Bind(wx.EVT_CLOSE, self.OnClose) 79 | self._persistMgr = PM.PersistenceManager.Get() 80 | wx.CallAfter(self.RegisterControls) 81 | self.vfile_data = self.f_data() 82 | 83 | def RegisterControls(self): 84 | self.Freeze() 85 | self.Register() 86 | self.Thaw() 87 | 88 | def Register(self, children=None): 89 | if children is None: 90 | self._persistMgr.RegisterAndRestore(self) 91 | children = self.GetChildren() 92 | 93 | for child in children: 94 | with warnings.catch_warnings(): 95 | warnings.simplefilter("ignore") 96 | name = child.GetName() 97 | if name not in PM.BAD_DEFAULT_NAMES and 'widget' not in name and \ 98 | 'wxSpinButton' not in name: 99 | self._persistMgr.RegisterAndRestore(child) 100 | if child.GetChildren(): 101 | self.Register(child.GetChildren()) 102 | 103 | def click_fs_button(self, event): 104 | f_dlg = wx.FileDialog(self, "Select verilog file(s)", self.dirname, "", "*.*", wx.FD_MULTIPLE) 105 | self.SetStatusText("Selecting verilog file(s)...") 106 | if f_dlg.ShowModal() == wx.ID_OK: 107 | self.vfile_data.set_files(f_dlg.GetFilenames(), f_dlg.GetDirectory(), self.selected_file_panel) 108 | self.SetStatusText("") 109 | f_dlg.Destroy() 110 | 111 | def selectMenu(self, event): 112 | if event.GetId() == wx.ID_ABOUT: 113 | webbrowser.open('https://github.com/fukatani/Pyverilog_toolbox/blob/master/Readme.md') 114 | elif event.GetId() == wx.ID_EXIT: 115 | self.Destroy() 116 | 117 | def click_exe_button(self, event): 118 | now_command = self.radiobutton_panel.get_selected_item() 119 | if self.debug: 120 | print(now_command) 121 | 122 | if not hasattr(self.vfile_data, 'selected_vfiles'): 123 | self.ShowErrorMessage('Please select verilog files before execution.') 124 | return 125 | 126 | log_file_name = 'log.html' 127 | 128 | self.SetStatusText("Analyzing...") 129 | try: 130 | if now_command == 'exec dataflow analyzer': 131 | df = dataflow_facade(self.vfile_data.selected_full_path, topmodule=self.top_name_panel.get_text()) 132 | df.html_name = log_file_name 133 | df.print_dataflow() 134 | elif now_command == 'exec controlflow analyzer': 135 | df = dataflow_facade(self.vfile_data.selected_full_path, topmodule=self.top_name_panel.get_text()) 136 | df.html_name = log_file_name 137 | df.print_controlflow() 138 | elif now_command == 'calculate code metrics': 139 | mc = MetricsCalculator(self.vfile_data.selected_full_path, topmodule=self.top_name_panel.get_text()) 140 | mc.html_name = log_file_name 141 | mc.synth_profile() 142 | mc.show() 143 | elif now_command == 'find combinational loop': 144 | cf = CombLoopFinder(self.vfile_data.selected_full_path, topmodule=self.top_name_panel.get_text()) 145 | cf.html_name = log_file_name 146 | cf.search_combloop() 147 | elif now_command == 'find unused variables': 148 | uf = UnreferencedFinder(self.vfile_data.selected_full_path, topmodule=self.top_name_panel.get_text()) 149 | uf.html_name = log_file_name 150 | uf.search_unreferenced() 151 | elif now_command == 'find code clone': 152 | cf = CodeCloneFinder(self.vfile_data.selected_full_path, topmodule=self.top_name_panel.get_text()) 153 | cf.html_name = log_file_name 154 | cf.show() 155 | elif now_command == 'analyze counter': 156 | ca = CntAnalyzer(self.vfile_data.selected_full_path, topmodule=self.top_name_panel.get_text()) 157 | ca.html_name = log_file_name 158 | ca.show() 159 | elif now_command == "analyze register map": 160 | RegMapConfig(self.vfile_data.selected_full_path, topmodule=self.top_name_panel.get_text()).Show() 161 | return 162 | else: 163 | self.ShowErrorMessage('unimplemented function') 164 | return 165 | OutputDisplay(log_file_name).Show() 166 | self.SetStatusText("") 167 | except (DefinitionError, FormatError, ImplementationError, CombLoopException) as e: 168 | self.ShowErrorMessage(e.message) 169 | except IOError as e: 170 | if e.filename == 'preprocess.output': 171 | self.ShowErrorMessage(e.filename + 'is not found.' + '\n(Please make sure Icarus verilog is installed)') 172 | else: 173 | self.ShowErrorMessage(e.filename + 'is not found.') 174 | def ShowErrorMessage(self, message): 175 | wx.MessageBox(message, 'Error!', wx.ICON_ERROR) 176 | 177 | class f_data(object): 178 | """ [CLASSES] 179 | Selected verilog file data. 180 | Registerd by pickle. 181 | """ 182 | def __init__(self): 183 | self.dump_enable = False 184 | if self.dump_enable: 185 | try: 186 | with open("pyv.dump", "r") as f: 187 | (self.selected_vfiles, self.selected_full_path) = pickle.load(f) 188 | #if hasattr(self, 'selected_full_path'): 189 | except (IOError, EOFError): 190 | pass 191 | 192 | 193 | def __get_state__(self): 194 | return self.selected_vfiles, self.selected_full_path 195 | 196 | def set_label(self, file_panel): 197 | if len(self.selected_vfiles) > 1: 198 | file_panel.SetLabel(self.selected_vfiles[0] + ', ...') 199 | else: 200 | file_panel.SetLabel(self.selected_vfiles[0]) 201 | 202 | def set_files(self, filenames, directory, file_panel): 203 | self.selected_vfiles = filenames 204 | self.selected_full_path = [directory + "\\" + vfile for vfile in self.selected_vfiles] 205 | self.set_label(file_panel) 206 | 207 | def dump(self): 208 | if self.dump_enable: 209 | with open("pyv.dump", "w") as f: 210 | pickle.dump(self, f) 211 | 212 | class Menu(wx.MenuBar): 213 | 214 | def __init__(self): 215 | 216 | wx.MenuBar.__init__(self) 217 | 218 | menu_menu = wx.Menu() 219 | menu_menu.Append(wx.ID_ABOUT,"display usage(visit online github page)","https://github.com/fukatani/Pyverilog_toolbox") 220 | menu_menu.Append(wx.ID_EXIT,"exit","exit pyv_gui") 221 | self.Append(menu_menu,"menu") 222 | 223 | class TextPanel(wx.Panel): 224 | 225 | def __init__(self, parent, initial="TOP"): 226 | wx.Panel.__init__(self,parent, wx.ID_ANY) 227 | self.disp_text = wx.TextCtrl(self, wx.ID_ANY, initial, style=wx.TE_RIGHT) 228 | self.disp_text.SetName(initial + ".dump") 229 | layout = wx.BoxSizer(wx.HORIZONTAL) 230 | layout.Add(self.disp_text, 1) 231 | self.SetSizer(layout) 232 | 233 | def get_text(self): 234 | return self.disp_text.GetValue() 235 | 236 | class CommandButtonPanel(wx.Panel): 237 | 238 | def __init__(self, parent, disp_text, click_event): 239 | wx.Panel.__init__(self, parent, wx.ID_ANY) 240 | button = wx.Button(self, wx.ID_ANY, disp_text) 241 | button.Bind(wx.EVT_BUTTON, click_event) 242 | layout = wx.BoxSizer(wx.HORIZONTAL) 243 | layout.Add(button,flag=wx.GROW) 244 | self.SetSizer(layout) 245 | 246 | class RadioPanel(wx.Panel): 247 | def __init__(self, parent, button_array): 248 | wx.Panel.__init__(self,parent,wx.ID_ANY) 249 | 250 | self.radiobox = wx.RadioBox(self, wx.ID_ANY, choices=button_array, style=wx.RA_VERTICAL) 251 | self.radiobox.SetName("command_select.dump") 252 | layout = wx.BoxSizer(wx.VERTICAL) 253 | layout.Add(self.radiobox, 1, flag=wx.GROW) 254 | 255 | self.SetSizer(layout) 256 | 257 | def get_selected_item(self): 258 | return self.radiobox.GetStringSelection() 259 | 260 | class RegMapConfig(wx.Frame): 261 | def __init__(self, full_path, topmodule): 262 | wx.Frame.__init__(self,None,wx.ID_ANY,"Analyze register map",size=(300,400)) 263 | self.full_path = full_path 264 | self.topmodule = topmodule 265 | self.__persistMgr = PM.PersistenceManager.Get() 266 | 267 | root_panel = wx.Panel(self,wx.ID_ANY) 268 | root_layout = wx.BoxSizer(wx.VERTICAL) 269 | 270 | root_layout.Add(wx.StaticText(root_panel, wx.ID_ANY, "WRITE FLAG SIGNAL:"), border=5) 271 | self.write_flag_panel = TextPanel(root_panel, "TOP.WRITE") 272 | root_layout.Add(self.write_flag_panel, 0, wx.GROW|wx.ALL, border=5) 273 | 274 | ## root_layout.Add(wx.StaticText(root_panel, wx.ID_ANY, "READ FLAG SIGNAL:"), border=5) 275 | ## self.read_flag_panel = TextPanel(root_panel, "TOP.READ") 276 | ## root_layout.Add(self.read_flag_panel, 0, wx.GROW|wx.ALL, border=5) 277 | 278 | root_layout.Add(wx.StaticText(root_panel, wx.ID_ANY, "ADDRESS SIGNAL"), border=5) 279 | self.address_panel = TextPanel(root_panel, "TOP.ADR") 280 | root_layout.Add(self.address_panel, 0, wx.GROW|wx.ALL, border=5) 281 | 282 | root_layout.Add(wx.StaticText(root_panel, wx.ID_ANY, "WRITE DATA SIGNAL"), border=5) 283 | self.write_data_panel = TextPanel(root_panel, "TOP.W_DATA") 284 | root_layout.Add(self.write_data_panel, 0, wx.GROW|wx.ALL, border=5) 285 | 286 | root_layout.Add(wx.StaticText(root_panel, wx.ID_ANY, "READ DATA SIGNAL"), border=5) 287 | self.read_data_panel = TextPanel(root_panel, "TOP.R_DATA") 288 | root_layout.Add(self.read_data_panel, 0, wx.GROW|wx.ALL, border=5) 289 | 290 | exebutton_panel = CommandButtonPanel(root_panel, "EXECUTE!", self.click_exe_button) 291 | root_layout.Add(exebutton_panel, 0, wx.GROW|wx.LEFT|wx.RIGHT, border=5) 292 | 293 | root_panel.SetSizer(root_layout) 294 | root_layout.Fit(root_panel) 295 | 296 | #for persistence 297 | self.SetName('regmap_config.dump') 298 | self.Bind(wx.EVT_CLOSE, self.OnClose) 299 | self._persistMgr = PM.PersistenceManager.Get() 300 | wx.CallAfter(self.RegisterControls) 301 | 302 | def click_exe_button(self, event): 303 | with open("setup.txt", "w") as setup_file: 304 | setup_file.write("READ_FLAG:" + "None" + "\n") 305 | setup_file.write("WRITE_FLAG:" + self.write_flag_panel.get_text() + "\n") 306 | setup_file.write("ADDRESS:" + self.address_panel.get_text() + "\n") 307 | setup_file.write("WRITE_DATA:" + self.write_data_panel.get_text() + "\n") 308 | setup_file.write("READ_DATA:" + self.read_data_panel.get_text() + "\n") 309 | ra = RegMapAnalyzer(self.full_path, "setup.txt", self.topmodule, "out.csv") 310 | ra.getRegMaps() 311 | ra.csv2html("out.csv") 312 | OutputDisplay("log.html").Show() 313 | 314 | def RegisterControls(self): 315 | self.Freeze() 316 | self.Register() 317 | self.Thaw() 318 | 319 | def Register(self, children=None): 320 | if children is None: 321 | self._persistMgr.RegisterAndRestore(self) 322 | children = self.GetChildren() 323 | 324 | for child in children: 325 | with warnings.catch_warnings(): 326 | warnings.simplefilter("ignore") 327 | name = child.GetName() 328 | if name not in PM.BAD_DEFAULT_NAMES and 'widget' not in name and \ 329 | 'wxSpinButton' not in name: 330 | self._persistMgr.RegisterAndRestore(child) 331 | if child.GetChildren(): 332 | self.Register(child.GetChildren()) 333 | 334 | def OnClose(self, event): 335 | self._persistMgr.SaveAndUnregister() 336 | event.Skip() 337 | 338 | class OutputDisplay(wx.Frame): 339 | def __init__(self, log_file_name): 340 | wx.Frame.__init__(self,None,wx.ID_ANY,"Output report",size=(900,700)) 341 | log = open(log_file_name, 'r') 342 | log_disp_panel = wx.html.HtmlWindow(self) 343 | if "gtk2" in wx.PlatformInfo: 344 | log_disp_panel.SetStandardFonts() 345 | log_disp_panel.SetPage("".join(log.readlines())) 346 | 347 | 348 | if __name__ == "__main__": 349 | build_flag = False 350 | application = wx.App(redirect=build_flag)#false in debugging 351 | frame = GuiMain() 352 | frame.Show() 353 | application.MainLoop() 354 | 355 | 356 | -------------------------------------------------------------------------------- /pyverilog_toolbox/gui/gui_main.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | a = Analysis(['.\\gui_main.py'], 3 | pathex=['C:\\Users\\rf\\Documents\\github\\pyverilog_toolbox\\pyverilog_toolbox\\gui', 4 | 'C:\\Users\\rf\\Documents\\github\\pyverilog_toolbox', 5 | # 'C:\\Python27\\Lib\\site-packages\\wx-3.0-msw\\wx', 6 | 'C:\\Python27\\Lib\\site-packages', 7 | 'C:\\Python27\\Lib\\site-packages\\pyverilog'], 8 | hiddenimports=['pyverilog_toolbox', 9 | 'pyverilog_toolbox.verify_tool.regmap_analyzer', 10 | 'pyverilog', 11 | 'pyverilog.dataflow_analyzer'], 12 | hookspath=None, 13 | runtime_hooks=None) 14 | pyz = PYZ(a.pure) 15 | exe = EXE(pyz, 16 | a.scripts, 17 | exclude_binaries=True, 18 | name='gui_main.exe', 19 | debug=False, 20 | strip=None, 21 | upx=True, 22 | console=False ) 23 | coll = COLLECT(exe, 24 | a.binaries, 25 | a.zipfiles, 26 | a.datas, 27 | strip=None, 28 | upx=True, 29 | name='pyv_gui') 30 | -------------------------------------------------------------------------------- /pyverilog_toolbox/gui/gui_main_onefile.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | a = Analysis(['.\\gui_main.py'], 3 | pathex=['C:\\Users\\rf\\Documents\\github\\pyverilog_toolbox\\pyverilog_toolbox\\gui', 4 | 'C:\\Users\\rf\\Documents\\github\\pyverilog_toolbox', 5 | # 'C:\\Python27\\Lib\\site-packages\\wx-3.0-msw\\wx', 6 | 'C:\\Python27\\Lib\\site-packages', 7 | 'C:\\Python27\\Lib\\site-packages\\pyverilog'], 8 | hiddenimports=['pyverilog_toolbox', 9 | 'pyverilog_toolbox.verify_tool.regmap_analyzer', 10 | 'pyverilog', 11 | 'pyverilog.dataflow_analyzer'], 12 | hookspath=None, 13 | runtime_hooks=None) 14 | pyz = PYZ(a.pure) 15 | exe = EXE(pyz, 16 | a.scripts, 17 | a.binaries, 18 | a.zipfiles, 19 | a.datas, 20 | name='pyv_gui.exe', 21 | debug=False, 22 | strip=None, 23 | upx=True, 24 | console=False) -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/case_in_func.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST); 2 | input CLK, RST; 3 | reg bit; 4 | always @(posedge CLK or negedge RST) begin 5 | if(RST) begin 6 | bit <= bit; 7 | end else begin 8 | bit <= func1(bit); 9 | end 10 | end 11 | 12 | function func1; 13 | input bit; 14 | case(bit) 15 | 'h0: begin 16 | func1 = !bit; 17 | end 18 | default: begin 19 | func1 = !bit; 20 | end 21 | endcase 22 | endfunction 23 | endmodule 24 | 25 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/combloop.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST); 2 | input CLK,RST; 3 | wire wire1; 4 | 5 | assign wire1 = wire1; 6 | 7 | endmodule 8 | 9 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/combloop1.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST); 2 | input CLK,RST; 3 | wire wire1,wire2,wire3; 4 | 5 | assign wire1 = wire2; 6 | assign wire2 = !wire3; 7 | assign wire3 = wire1; 8 | 9 | endmodule 10 | 11 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/combloop2.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST); 2 | input CLK,RST; 3 | wire wire1,wire2,wire3; 4 | 5 | assign wire1 = wire3; 6 | 7 | SUB sub(wire1, !wire2, wire3); 8 | 9 | endmodule 10 | 11 | 12 | module SUB(wire1, wire2, wire3); 13 | 14 | input wire1, wire2; 15 | output wire3; 16 | assign wire3 = wire2; 17 | assign wire2 = wire1; 18 | 19 | endmodule -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/combloop4.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST); 2 | input CLK,RST; 3 | wire a1,wire1,wire2,wire3,z1; 4 | 5 | assign a1 = wire1; 6 | assign wire1 = wire2; 7 | assign wire2 = !wire3; 8 | assign wire3 = wire1; 9 | assign z1 = wire1; 10 | 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/complex_partselect.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST, WRITE, WRITE_DATA); 2 | input CLK,RST,WRITE; 3 | input [2:1] WRITE_DATA; 4 | reg [4:3] reg0; 5 | 6 | 7 | always @(posedge CLK) begin 8 | if(WRITE) begin 9 | reg0[4:3] <= WRITE_DATA[2:1]; 10 | end 11 | end 12 | 13 | endmodule 14 | 15 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/config_metrics.txt: -------------------------------------------------------------------------------- 1 | COEF_FOR_VAR:0 2 | 3 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/floating.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST); 2 | input CLK, RST; 3 | reg reg1; 4 | reg reg2; 5 | wire in1; //floating 6 | 7 | always @(posedge CLK or negedge RST) begin 8 | if(!RST) begin 9 | reg1 <= 1'b0; 10 | end else begin 11 | reg1 <= in1; 12 | end 13 | end 14 | 15 | endmodule 16 | 17 | 18 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/floating2.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST,IN); 2 | input CLK, RST,IN; 3 | reg [2:1] reg1; 4 | reg [2:1] reg2; 5 | reg [2:1] reg3; 6 | wire IN; //floating 7 | parameter one = 1; 8 | 9 | always @(posedge CLK or negedge RST) begin 10 | if(!RST) begin 11 | reg1[2] <= 1'b0; 12 | end else begin 13 | reg1[2] <= IN; 14 | end 15 | end 16 | 17 | always @(posedge CLK or negedge RST) begin 18 | if(!RST) begin 19 | reg2[2] <= 1'b0; 20 | end else begin 21 | reg2[2] <= IN; 22 | end 23 | end 24 | 25 | always @(posedge CLK or negedge RST) begin 26 | if(!RST) begin 27 | reg2[1] <= 1'b0; 28 | end else begin 29 | reg2[1] <= IN; 30 | end 31 | end 32 | 33 | always @(posedge CLK or negedge RST) begin 34 | if(!RST) begin 35 | reg3[one] <= 1'b0; 36 | end else begin 37 | reg3[one] <= IN; 38 | end 39 | end 40 | 41 | endmodule 42 | 43 | 44 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/fv_test.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK); 2 | input CLK; 3 | reg A,B,C,D,E,F,G,H; 4 | reg [2:0] multi,multi2,I; 5 | 6 | always @(posedge CLK) begin 7 | A <= A || B; 8 | B <= 1'b0; 9 | end 10 | 11 | always @(posedge CLK) begin 12 | C <= !B; 13 | end 14 | 15 | always @(posedge CLK) begin 16 | D <= B == A; 17 | end 18 | 19 | always @(posedge CLK) begin 20 | E <= B ? A : C; 21 | end 22 | 23 | always @(posedge CLK) begin 24 | F <= &multi; 25 | end 26 | 27 | always @(posedge CLK) begin 28 | G <= &multi[1:0]; 29 | end 30 | 31 | always @(posedge CLK) begin 32 | H <= F+G; 33 | end 34 | 35 | always @(posedge CLK) begin 36 | I <= multi & multi2; 37 | end 38 | 39 | endmodule 40 | 41 | 42 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/metrics_func.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST, IN, IN2); 2 | input CLK, RST, IN, IN2; 3 | reg reg2; 4 | 5 | /* 6 | always @(posedge CLK or negedge RST) begin 7 | if(RST) begin 8 | reg2 <= 1'b0; 9 | end else if(IN2) begin 10 | reg2 <= IN; 11 | end else if(IN2) begin 12 | if(IN2) begin 13 | reg2 <= IN; 14 | end 15 | end else begin 16 | if({IN2, IN2}) begin 17 | case(IN2) 18 | 2'b00: reg2 <= 1'b0; 19 | 2'b11: reg2 <= 1'b1; 20 | default: begin 21 | if(IN2) 22 | reg2 <= IN; 23 | else 24 | reg2 <= func1(IN,IN2); 25 | end 26 | endcase 27 | end 28 | end 29 | end 30 | */ 31 | 32 | always @(posedge CLK or negedge RST) begin 33 | reg2 <= func1(IN,IN2); 34 | end 35 | function func1; 36 | input bit; 37 | input bit2; 38 | 39 | if(bit2) 40 | func1 = !bit; 41 | else 42 | func1 = bit; 43 | 44 | endfunction 45 | 46 | /* 47 | function func1; 48 | input bit; 49 | input bit2; 50 | 51 | case(bit2) 52 | 'h0: begin 53 | func1 = !bit; 54 | end 55 | default: begin 56 | func1 = bit; 57 | end 58 | endcase 59 | endfunction 60 | */ 61 | 62 | 63 | endmodule 64 | 65 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/metrics_test.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST, IN, IN2, reg1, OUT); 2 | input CLK, RST, IN, IN2; 3 | reg reg1,reg2,reg3; 4 | output reg1,OUT; 5 | wire in1; 6 | 7 | always @(posedge CLK or negedge RST) begin 8 | if(RST) begin 9 | reg1 <= 1'b0; 10 | end else begin 11 | reg1 <= IN; 12 | end 13 | end 14 | 15 | always @(posedge CLK or negedge RST) begin 16 | if(RST) begin 17 | reg2 <= 1'b0; 18 | end else begin 19 | reg2 <= func1(reg1); 20 | end 21 | end 22 | 23 | SUB sub(CLK,RST,in1,OUT); 24 | SUB2 ccc(CLK,RST,in1); 25 | 26 | function func1; 27 | input bit; 28 | if(bit) 29 | func1 = !bit; 30 | else 31 | func1 = bit; 32 | endfunction 33 | 34 | endmodule 35 | 36 | module SUB(CLK,RST,IN, OUT); 37 | input CLK, RST, IN; 38 | output OUT; 39 | reg reg1; 40 | wire OUT = reg1; 41 | 42 | always @(posedge CLK or negedge RST) begin 43 | if(RST) begin 44 | reg1 <= 1'b0; 45 | end else begin 46 | reg1 <= 1'b1; 47 | end 48 | end 49 | 50 | endmodule 51 | 52 | module SUB2(CLK,RST,IN); 53 | input CLK, RST, IN; 54 | reg reg1; 55 | 56 | always @(posedge CLK or negedge RST) begin 57 | if(RST) begin 58 | reg1 <= 1'b0; 59 | end else if(IN) begin 60 | reg1 <= 1'b0; 61 | end else begin 62 | reg1 <= 1'b1; 63 | end 64 | end 65 | 66 | endmodule 67 | 68 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/metrics_test2.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST, IN, IN2); 2 | input CLK, RST, IN, IN2; 3 | reg reg2; 4 | 5 | always @(posedge CLK or negedge RST) begin 6 | if(RST) begin 7 | reg2 <= 1'b0; 8 | end else if(IN2) begin 9 | reg2 <= IN; 10 | end else if(IN2) begin 11 | if(IN2) begin 12 | reg2 <= IN; 13 | end 14 | end else begin 15 | if({IN2, IN2}) begin 16 | case(IN2) 17 | 2'b00: reg2 <= 1'b0; 18 | 2'b11: reg2 <= 1'b1; 19 | default: begin 20 | if(IN2) 21 | reg2 <= IN; 22 | else 23 | reg2 <= 1'b0; 24 | end 25 | endcase 26 | end 27 | end 28 | end 29 | 30 | 31 | endmodule 32 | 33 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/norm_cnt.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RSTN, UP_ENABLE); 2 | input CLK,RSTN,UP_ENABLE; 3 | 4 | reg [2:0] up_cnt; 5 | 6 | always @(posedge CLK or negedge RSTN) begin 7 | if(!RSTN) begin 8 | up_cnt <= 0; 9 | end else if(UP_ENABLE) begin 10 | up_cnt <= up_cnt + 2'd1; 11 | end else begin 12 | up_cnt <= up_cnt; 13 | end 14 | end 15 | 16 | endmodule 17 | 18 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/norm_cnt2.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RSTN, UP_ENABLE, UP_ENABLE2, CLEAR); 2 | input CLK,RSTN,UP_ENABLE,UP_ENABLE2,CLEAR; 3 | 4 | reg [2:0] up_cnt; 5 | wire is_cnt_four = up_cnt == 4; 6 | 7 | always @(posedge CLK or negedge RSTN) begin 8 | if(!RSTN) begin 9 | up_cnt <= 0; 10 | end else if(up_cnt >= 3'd5) begin 11 | up_cnt <= 0; 12 | end else if(CLEAR) begin 13 | up_cnt <= 0; 14 | end else if(UP_ENABLE) begin 15 | up_cnt <= up_cnt + 3'd1; 16 | end else if(UP_ENABLE2) begin 17 | up_cnt <= up_cnt + 3'd1; 18 | end else begin 19 | up_cnt <= up_cnt; 20 | end 21 | end 22 | 23 | reg [2:0] up_cnt2; 24 | always @(posedge CLK or negedge RSTN) begin 25 | if(!RSTN) begin 26 | up_cnt2 <= 0; 27 | end else if(up_cnt2 != 3'd5 && up_cnt == 3'd5) begin 28 | up_cnt2 <= up_cnt2 + 3'd1; 29 | end else begin 30 | up_cnt2 <= 0; 31 | end 32 | end 33 | 34 | reg [2:0] down_cnt; 35 | always @(posedge CLK or negedge RSTN) begin 36 | if(!RSTN) begin 37 | down_cnt <= 0; 38 | end else if(down_cnt != 3'd0) begin 39 | down_cnt <= down_cnt - 3'd1; 40 | end else begin 41 | down_cnt <= 3'd5; 42 | end 43 | end 44 | 45 | reg now; 46 | always @(posedge CLK or negedge RSTN) begin 47 | if(!RSTN) begin 48 | now <= 0; 49 | end else if(up_cnt == 3'd4) begin 50 | now <= 0; 51 | end else if(up_cnt == 3'd2) begin 52 | now <= 1; 53 | end else if((up_cnt == 2) && (up_cnt2 == 2)) begin 54 | now <= 1; 55 | end 56 | end 57 | 58 | endmodule 59 | 60 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/norm_cnt3.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RSTN); 2 | input CLK,RSTN; 3 | 4 | reg [2:0] up_cnt; 5 | 6 | always @(posedge CLK or negedge RSTN) begin 7 | if(!RSTN) begin 8 | up_cnt <= 0; 9 | end else if(up_cnt >= 3'd5) begin 10 | up_cnt <= 0; 11 | end else begin 12 | up_cnt <= up_cnt + 'd1; 13 | end 14 | end 15 | 16 | 17 | reg now; 18 | always @(posedge CLK or negedge RSTN) begin 19 | if(!RSTN) begin 20 | now <= 0; 21 | end else if(up_cnt[1] == 'd2) begin 22 | now <= 0; 23 | end else begin 24 | now <= 1; 25 | end 26 | end 27 | 28 | endmodule 29 | 30 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/not_combloop.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RSTN); 2 | input CLK; 3 | input [2:1] RSTN; 4 | reg reg1; 5 | 6 | always @(posedge CLK or negedge RSTN[1]) begin 7 | if(!RSTN[1]) begin 8 | reg1 <= 0; 9 | end else begin 10 | reg1 <= reg1; 11 | end 12 | end 13 | 14 | endmodule 15 | 16 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/reg_clone.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST,IN); 2 | input CLK, RST, IN; 3 | reg reg1,reg2,reg3,reg4; 4 | //reg [1:0] reg5; 5 | wire in1; 6 | 7 | always @(posedge CLK or negedge RST) begin 8 | if(RST) begin 9 | reg1 <= 1'b0; 10 | end else begin 11 | reg1 <= IN; 12 | end 13 | end 14 | 15 | always @(posedge CLK or negedge RST) begin 16 | if(RST) begin 17 | reg2 <= 1'b0; 18 | end else begin 19 | reg2 <= 1'b1; 20 | end 21 | end 22 | 23 | assign in1 = IN; 24 | 25 | always @(posedge CLK or negedge RST) begin 26 | if(RST) begin 27 | reg3 <= 1'b0; 28 | end else begin 29 | reg3 <= in1; 30 | end 31 | end 32 | 33 | always @(posedge CLK or negedge RST) begin 34 | if(RST) begin 35 | reg4 <= 1'b1; 36 | end else begin 37 | reg4 <= !in1; 38 | end 39 | end 40 | 41 | //not supported 42 | /* 43 | always @(posedge CLK or negedge RST) begin 44 | if(RST) begin 45 | reg5[1:0] <= 2'b10; 46 | end else begin 47 | reg5[1:0] <= {!in1,in1}; 48 | end 49 | end 50 | */ 51 | 52 | SUB sub(CLK,RST,in1); 53 | 54 | endmodule 55 | 56 | module SUB(CLK,RST,IN); 57 | input CLK, RST, IN; 58 | reg reg1; 59 | 60 | always @(posedge CLK or negedge RST) begin 61 | if(RST) begin 62 | reg1 <= 1'b0; 63 | end else begin 64 | reg1 <= IN; 65 | end 66 | end 67 | 68 | endmodule 69 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/regmap.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST, WRITE, READ, ADDR, WRITE_DATA, READ_DATA); 2 | input CLK,RST,WRITE,READ; 3 | input [2:0] ADDR; 4 | input [1:0] WRITE_DATA; 5 | output reg [1:0] READ_DATA; 6 | reg [1:0] reg0; 7 | reg reg1; 8 | 9 | always @(posedge CLK) begin 10 | if(RST) begin 11 | reg0[1:0] <= 0; 12 | reg1 <= 0; 13 | end else if(WRITE) begin 14 | case(ADDR) 15 | 0:reg0[1:0] <= WRITE_DATA; 16 | 1:reg1 <= WRITE_DATA[0]; 17 | endcase 18 | end 19 | end 20 | 21 | always @* begin 22 | case(ADDR) 23 | 0:READ_DATA[1:0] = reg0[1:0]; 24 | 1:READ_DATA[1:0] = {1'b0,reg1}; 25 | endcase 26 | end 27 | 28 | //pyverilog don't supported case in function now. 29 | 30 | /* 31 | assign READ_DATA = get_read_data(ADDR,reg0,reg1); 32 | function [1:0] get_read_data; 33 | input [2:0] ADDR; 34 | input [1:0] reg0; 35 | input [1:0] reg1; 36 | 37 | begin 38 | case(ADDR) 39 | 0:get_read_data = reg0; 40 | 1:get_read_data = reg1; 41 | endcase 42 | end 43 | endfunction 44 | */ 45 | 46 | endmodule 47 | 48 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/regmap2.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST, WRITE, READ, ADDR, WRITE_DATA, READ_DATA); 2 | input CLK,RST,WRITE,READ; 3 | input [2:0] ADDR; 4 | input [1:0] WRITE_DATA; 5 | output reg [1:0] READ_DATA; 6 | reg [4:3] reg0; 7 | 8 | 9 | always @(posedge CLK) begin 10 | if(RST) begin 11 | reg0[4:3] <= 0; 12 | end else if(WRITE) begin 13 | case(ADDR) 14 | 0:reg0[4:3] <= WRITE_DATA[1:0]; 15 | endcase 16 | end 17 | end 18 | 19 | always @* begin 20 | case(ADDR) 21 | 0:READ_DATA[1:0] = reg0[4:3]; 22 | endcase 23 | end 24 | 25 | 26 | 27 | endmodule 28 | 29 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/regmap_split.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST, WRITE, READ, ADDR, WRITE_DATA, READ_DATA); 2 | input CLK,RST,WRITE,READ; 3 | input [2:0] ADDR; 4 | input [3:0] WRITE_DATA; 5 | output [3:0] READ_DATA; 6 | 7 | reg [1:0] reg1,reg0; 8 | 9 | always @(posedge CLK) begin 10 | if(RST) begin 11 | reg0[1:0] <= 0; 12 | reg1[1:0] <= 0; 13 | end else if(WRITE) begin 14 | case(ADDR) 15 | 1:{reg1[1:0], reg0[1:0]} <= WRITE_DATA[3:0]; 16 | endcase 17 | end 18 | end 19 | 20 | always @* begin 21 | case(ADDR) 22 | 1:READ_DATA[3:0] = {reg1[1:0], reg0[1:0]}; 23 | endcase 24 | end 25 | endmodule 26 | 27 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/setup.txt: -------------------------------------------------------------------------------- 1 | READ_FLAG:None 2 | WRITE_FLAG:TOP.WRITE 3 | ADDRESS:TOP.ADDR 4 | READ_DATA:TOP.READ_DATA 5 | WRITE_DATA:TOP.WRITE_DATA -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/test_ra.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # test_ra.py 3 | # 4 | # the test for all pyverilog_toolbox function 5 | # 6 | # Copyright (C) 2015, Ryosuke Fukatani 7 | # License: Apache 2.0 8 | #------------------------------------------------------------------------------- 9 | 10 | 11 | import sys 12 | import os 13 | import unittest 14 | 15 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 16 | 17 | from pyverilog_toolbox.verify_tool.regmap_analyzer import * 18 | from pyverilog_toolbox.verify_tool.combloop_finder import * 19 | from pyverilog_toolbox.verify_tool.bindlibrary import * 20 | from pyverilog_toolbox.verify_tool.cnt_analyzer import * 21 | from pyverilog_toolbox.verify_tool.codeclone_finder import CodeCloneFinder 22 | from pyverilog_toolbox.verify_tool.unreferenced_finder import UnreferencedFinder 23 | from pyverilog_toolbox.verify_tool.metrics_calculator import MetricsCalculator 24 | from pyverilog_toolbox.verify_tool.formal_verifier import FormalVerifier 25 | 26 | class TestSequenceFunctions(unittest.TestCase): 27 | def setUp(self): 28 | pass 29 | 30 | def test_metrics(self): 31 | m_calculator = MetricsCalculator("metrics_test.v") 32 | m_metrics, _, _ = m_calculator.synth_profile() 33 | self.assertDictEqual(m_metrics.m_ordered, {'TOP': 27, 'TOP.sub': 19, 'TOP.ccc': 16}) 34 | 35 | def test_metrics2(self): 36 | m_calculator = MetricsCalculator("metrics_test2.v") 37 | m_metrics, r_metrics, _ = m_calculator.synth_profile() 38 | self.assertEqual(m_metrics.m_ordered['TOP'], 19) 39 | self.assertEqual(r_metrics.m_ordered[('TOP', 0)], 25) 40 | 41 | def test_metrics_func(self): 42 | m_calculator = MetricsCalculator("metrics_func.v") 43 | m_metrics, r_metrics, f_metrics = m_calculator.synth_profile() 44 | self.assertEqual(m_metrics.m_ordered['TOP'], 19) 45 | self.assertEqual(r_metrics.m_ordered[('TOP', 0)], 1) 46 | self.assertEqual(f_metrics.m_ordered[('TOP.md_always0.al_block0.al_functioncall0', 0)], 9) 47 | 48 | def test_reg_clone(self): 49 | cc_finder = CodeCloneFinder("reg_clone.v") 50 | clones = sorted(cc_finder.search_regclone(), key = lambda t: str(t[0])) 51 | ordered_clones = [] 52 | for clone in clones: 53 | ordered_clones.append(str(tuple(sorted(clone, key=lambda t: str(t))))) 54 | ordered_clones = sorted(ordered_clones, key=lambda t: str(t)) 55 | self.assertEqual(ordered_clones, 56 | ['((TOP.reg1, 0), (TOP.reg3, 0))', '((TOP.reg3, 0), (TOP.sub.reg1, 0))']) 57 | 58 | inv_reg_description = set([str(inv_pair) for inv_pair in cc_finder.search_invert_regs()]) 59 | self.assertEqual(set(['((TOP.reg1, 0), (TOP.reg4, 0))', 60 | '((TOP.reg3, 0), (TOP.reg4, 0))', 61 | '((TOP.reg4, 0), (TOP.sub.reg1, 0))']), inv_reg_description) 62 | 63 | def test_unreferenced(self): 64 | u_finder = UnreferencedFinder("unreferenced_variables.v") 65 | self.assertEqual(str(sorted(u_finder.search_unreferenced(), key=lambda x: str(x))), 66 | "['TOP.IN2', 'TOP.reg2', 'TOP.reg3', 'TOP.sub.IN']") 67 | 68 | def test_floating(self): 69 | u_finder = UnreferencedFinder("floating.v") 70 | self.assertEqual(str(sorted(u_finder.search_floating(), key=lambda x: str(x))), 71 | "['TOP.in1', 'TOP.reg2']") 72 | 73 | def test_floating2(self): 74 | u_finder = UnreferencedFinder("floating2.v") 75 | self.assertEqual(str(sorted(u_finder.search_floating(), key=lambda x: str(x))), 76 | "['TOP.IN', 'TOP.reg1[1]', 'TOP.reg3[2]']") 77 | 78 | def test_cnt_analyzer(self): 79 | c_analyzer = CntAnalyzer("norm_cnt2.v") 80 | cnt_dict = c_analyzer.analyze_cnt() 81 | self.assertEqual(cnt_dict['TOP.down_cnt'].tostr(), 82 | "name: TOP.down_cnt\ncategory: down counter\nreset val: 0" + 83 | "\nmax_val: 4\nmother counter:()") 84 | self.assertEqual(cnt_dict['TOP.up_cnt'].tostr(), 85 | 'name: TOP.up_cnt\ncategory: up counter\nreset val: 0' + 86 | '\nmax_val: 5\nmother counter:()') 87 | self.assertEqual(cnt_dict['TOP.up_cnt2'].tostr(), 88 | "name: TOP.up_cnt2\ncategory: up counter\nreset val: 0" + 89 | "\nmax_val: 4\nmother counter:('TOP.up_cnt',)") 90 | c_analyzer.make_cnt_event_all() 91 | #maybe depend on funcdict 92 | ok1 = (set(c_analyzer.cnt_dict['TOP.up_cnt'].cnt_event_dict[2]) == 93 | set(["TOP.now=TOP_now @(!((TOP_up_cnt=='d2)&&(TOP_up_cnt2=='d2)))", 94 | "TOP.now='d1 @((TOP_up_cnt=='d2)&&(TOP_up_cnt2=='d2))"])) 95 | ok2 = (set(c_analyzer.cnt_dict['TOP.up_cnt'].cnt_event_dict[2]) == 96 | set(["TOP.now=TOP_now @(!((TOP_up_cnt=='d2)&&(TOP_up_cnt2=='d2)))", 97 | "TOP.now='d1 @(TOP_up_cnt==3'd2)"])) 98 | self.assertTrue(ok1 or ok2) 99 | 100 | self.assertEqual(c_analyzer.cnt_dict['TOP.up_cnt'].cnt_event_dict[4], 101 | ["TOP.now='d0 @(TOP_up_cnt==3'd4)"]) 102 | self.assertEqual(set(c_analyzer.cnt_dict['TOP.up_cnt'].cnt_event_dict[5]), 103 | set(["TOP.up_cnt2='d0 @(!((TOP_up_cnt==3'd5)&&(TOP_up_cnt2!=3'd5)))", 104 | "TOP.up_cnt2=(TOP_up_cnt2+3'd1) @((TOP_up_cnt==3'd5)&&(TOP_up_cnt2!=3'd5))"]) 105 | ) 106 | 107 | def test_cnt_analyzer2(self): 108 | c_analyzer = CntAnalyzer("norm_cnt.v") 109 | cnt_dict = c_analyzer.analyze_cnt() 110 | self.assertEqual(cnt_dict['TOP.up_cnt'].tostr(), 111 | "name: TOP.up_cnt\ncategory: up counter\nreset val: 0" + 112 | "\nmax_val: 7\nmother counter:()") 113 | 114 | def test_cnt_analyzer3(self): 115 | c_analyzer = CntAnalyzer("norm_cnt3.v") 116 | cnt_dict = c_analyzer.analyze_cnt() 117 | c_analyzer.make_cnt_event_all() 118 | cnt_event_result = str(c_analyzer.cnt_dict['TOP.up_cnt'].cnt_event_dict).replace('"','') 119 | self.assertEqual(set(c_analyzer.cnt_dict['TOP.up_cnt'].cnt_event_dict[2]), 120 | set(["TOP.now='d1 @(!(TOP_up_cnt['d1]=='d2))", "TOP.now='d0 @(TOP_up_cnt['d1]=='d2)"])) 121 | 122 | def test_normal(self): 123 | ranalyzer = RegMapAnalyzer("regmap.v", "setup.txt") 124 | write_map, read_map = ranalyzer.getRegMaps() 125 | self.assertEqual(str(write_map.map), "{0: {0: ('TOP.reg0', 0), 1: ('TOP.reg0', 1)}, 1: {0: ('TOP.reg1', 0)}}") 126 | self.assertEqual(str(read_map.map), "{0: {0: ('TOP.reg0', 0), 1: ('TOP.reg0', 1)}, 1: {0: ('TOP.reg1', 0)}}") 127 | 128 | def test_split(self): 129 | ranalyzer = RegMapAnalyzer("regmap_split.v", "setup.txt") 130 | write_map, read_map = ranalyzer.getRegMaps() 131 | self.assertEqual(str(write_map.map), 132 | "{1: {0: ('TOP.reg0', 0), 1: ('TOP.reg0', 1), 2: ('TOP.reg1', 0), 3: ('TOP.reg1', 1)}}") 133 | self.assertEqual(str(read_map.map), 134 | "{1: {0: ('TOP.reg0', 0), 1: ('TOP.reg0', 1), 2: ('TOP.reg1', 0), 3: ('TOP.reg1', 1)}}") 135 | 136 | def test_partselect(self): 137 | df = dataflow_facade("complex_partselect.v") 138 | term_dict = df.make_extract_dfterm_dict() 139 | self.assertDictEqual(term_dict, 140 | {('TOP.reg0', 3): set(['(TOP.WRITE_DATA, 1)', '(TOP.WRITE, 0)', '(TOP.reg0, 3)']), 141 | ('TOP.reg0', 4): set(['(TOP.WRITE_DATA, 2)', '(TOP.WRITE, 0)', '(TOP.reg0, 4)'])}) 142 | 143 | def test_split2(self): 144 | ranalyzer = RegMapAnalyzer("regmap2.v", "setup.txt") 145 | write_map, read_map = ranalyzer.getRegMaps() 146 | self.assertEqual(str(write_map.map), 147 | "{0: {0: ('TOP.reg0', 3), 1: ('TOP.reg0', 4)}}") 148 | self.assertEqual(str(read_map.map), 149 | "{0: {0: ('TOP.reg0', 3), 1: ('TOP.reg0', 4)}}") 150 | 151 | def test_comb_loop(self): 152 | c_finder = CombLoopFinder("combloop.v") 153 | with self.assertRaises(CombLoopException): 154 | c_finder.search_combloop() 155 | c_finder = CombLoopFinder("not_combloop.v") 156 | c_finder.search_combloop() 157 | 158 | def test_comb_loop1(self): 159 | c_finder = CombLoopFinder("combloop1.v") 160 | with self.assertRaises(CombLoopException): 161 | c_finder.search_combloop() 162 | 163 | def test_comb_loop2(self): 164 | c_finder = CombLoopFinder("combloop2.v") 165 | with self.assertRaises(CombLoopException): 166 | c_finder.search_combloop() 167 | 168 | def test_comb_loop4(self): 169 | c_finder = CombLoopFinder("combloop4.v") 170 | with self.assertRaises(CombLoopException): 171 | c_finder.search_combloop() 172 | 173 | def test_formal_verifier(self): 174 | if sys.version_info[0] == 2: 175 | fv = FormalVerifier("../testcode/fv_test.v") 176 | t_table = fv._calc_truth_table('TOP.G') 177 | self.assertEqual(t_table["['TOP_multi__0__: False', 'TOP_multi__1__: False', 'TOP_multi__2__: False']"], 178 | False) 179 | self.assertEqual(t_table["['TOP_multi__0__: False', 'TOP_multi__1__: False', 'TOP_multi__2__: True']"], 180 | False) 181 | self.assertEqual(t_table["['TOP_multi__0__: False', 'TOP_multi__1__: True', 'TOP_multi__2__: False']"], 182 | False) 183 | self.assertEqual(t_table["['TOP_multi__0__: False', 'TOP_multi__1__: True', 'TOP_multi__2__: True']"], 184 | False) 185 | self.assertEqual(t_table["['TOP_multi__0__: True', 'TOP_multi__1__: False', 'TOP_multi__2__: False']"], 186 | False) 187 | self.assertEqual(t_table["['TOP_multi__0__: True', 'TOP_multi__1__: False', 'TOP_multi__2__: True']"], 188 | False) 189 | self.assertEqual(t_table["['TOP_multi__0__: True', 'TOP_multi__1__: True', 'TOP_multi__2__: False']"], 190 | True) 191 | self.assertEqual(t_table["['TOP_multi__0__: True', 'TOP_multi__1__: True', 'TOP_multi__2__: True']"], 192 | True) 193 | t_table = fv.calc_truth_table('TOP.H') 194 | self.assertEqual(str(t_table["['TOP_F: False', 'TOP_G: False']"]), 195 | "TOP_F_ + TOP_G_") 196 | 197 | if __name__ == '__main__': 198 | unittest.main() 199 | -------------------------------------------------------------------------------- /pyverilog_toolbox/testcode/unreferenced_variables.v: -------------------------------------------------------------------------------- 1 | module TOP(CLK, RST, IN, IN2, reg1, OUT); 2 | input CLK, RST, IN, IN2; 3 | reg reg1,reg2,reg3; 4 | output reg1,OUT; 5 | wire in1; 6 | 7 | always @(posedge CLK or negedge RST) begin 8 | if(RST) begin 9 | reg1 <= 1'b0; 10 | end else begin 11 | reg1 <= IN; 12 | end 13 | end 14 | 15 | always @(posedge CLK or negedge RST) begin 16 | if(RST) begin 17 | reg2 <= 1'b0; 18 | end else begin 19 | reg2 <= reg1; 20 | end 21 | end 22 | 23 | SUB sub(CLK,RST,in1,OUT); 24 | endmodule 25 | 26 | module SUB(CLK,RST,IN, OUT); 27 | input CLK, RST, IN; 28 | output OUT; 29 | reg reg1; 30 | wire OUT = reg1; 31 | 32 | always @(posedge CLK or posedge RST) begin 33 | if(RST) begin 34 | reg1 <= 1'b0; 35 | end else begin 36 | reg1 <= 1'b1; 37 | end 38 | end 39 | 40 | endmodule 41 | -------------------------------------------------------------------------------- /pyverilog_toolbox/verify_tool/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | if sys.version_info[0] < 3: 3 | import bindlibrary 4 | import dataflow_facade 5 | import regmap_analyzer 6 | import combloop_finder 7 | import codeclone_finder 8 | import cnt_analyzer 9 | import unreferenced_finder 10 | import metrics_calculator 11 | -------------------------------------------------------------------------------- /pyverilog_toolbox/verify_tool/bindlibrary.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # bindlibrary.py 3 | # 4 | # library for get bind information. 5 | # 6 | # 7 | # Copyright (C) 2015, Ryosuke Fukatani 8 | # License: Apache 2.0 9 | #------------------------------------------------------------------------------- 10 | 11 | import sys 12 | import os 13 | 14 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 15 | 16 | from pyverilog.dataflow.dataflow import * 17 | 18 | class BindLibrary(object): 19 | """ [CLASSES] 20 | Library for using dataflow information. 21 | """ 22 | 23 | def __init__(self, binddict, terms): 24 | def make_scope_dict(terms): 25 | """ [FUNCTIONS] for getScopeChaindict 26 | make {string: ScopeChain, ...} from binddict 27 | """ 28 | scope_dict = {} 29 | for scope in terms.keys(): 30 | scope_dict[str(scope)] = scope 31 | return scope_dict 32 | 33 | self._binddict = binddict 34 | self._terms = terms 35 | self.scope_dict = make_scope_dict(terms) 36 | self.cache = {} 37 | self.gnb_cache = {} 38 | 39 | def dfx_memoize(f): 40 | """ [FUNCTIONS] 41 | Memoize for extract_all_dfxxx. 42 | Using self.cache. 43 | """ 44 | def helper(self, target_tree, tree_list, bit, dftype): 45 | if dftype == DFTerminal: 46 | if (target_tree, bit) not in self.cache: 47 | self.cache[(target_tree, bit)] = f(self, target_tree, set([]), bit, dftype) 48 | return tree_list.union(self.cache[(target_tree, bit)]) 49 | else: 50 | return f(self, target_tree, tree_list, bit, dftype) 51 | return helper 52 | 53 | @dfx_memoize 54 | def extract_all_dfxxx(self, target_tree, tree_list, bit, dftype): 55 | """[FUNCTIONS] 56 | return set of DFXXX 57 | target_tree:DF*** 58 | tree_list:{(type, DF***, bit),(type, DF***, bit),...} 59 | bit: signal bit pointer 60 | dftype: DFOperator or DFIntConst or ,... 61 | """ 62 | if dftype == DFTerminal and isinstance(target_tree, DFTerminal): 63 | target_scope = self.get_scope(target_tree) 64 | if target_scope in self._binddict.keys(): 65 | target_bind, target_term_lsb = self.get_next_bind(target_scope, bit) 66 | if not target_bind.isCombination(): 67 | tree_list.add((target_tree, bit + target_term_lsb)) 68 | else: #TOP Input port 69 | tree_list.add((target_tree, bit + eval_value(self._terms[self.scope_dict[str(target_tree)]].lsb))) 70 | else: 71 | if isinstance(target_tree, dftype): 72 | tree_list.add((target_tree, bit)) 73 | 74 | if hasattr(target_tree, "nextnodes"): 75 | if isinstance(target_tree, DFConcat): 76 | now_max_bit = 0 77 | now_min_bit = 0 78 | for nextnode in reversed(target_tree.nextnodes): 79 | now_max_bit = now_min_bit + self.get_bit_width_from_tree(nextnode) - 1 80 | if now_min_bit <= bit <= now_max_bit: 81 | tree_list = self.extract_all_dfxxx(nextnode, tree_list, bit - now_min_bit, dftype) 82 | break 83 | now_min_bit = now_max_bit + 1 84 | else: 85 | for nextnode in target_tree.nextnodes: 86 | if isinstance(target_tree, DFBranch) and nextnode == target_tree.condnode: 87 | tree_list = self.extract_all_dfxxx(nextnode, tree_list, 0, dftype) 88 | else: 89 | tree_list = self.extract_all_dfxxx(nextnode, tree_list, bit, dftype) 90 | elif isinstance(target_tree, DFBranch): 91 | tree_list = self.extract_all_dfxxx(target_tree.condnode, tree_list, 0, dftype) 92 | tree_list = self.extract_all_dfxxx(target_tree.truenode, tree_list, bit, dftype) 93 | tree_list = self.extract_all_dfxxx(target_tree.falsenode, tree_list, bit, dftype) 94 | elif isinstance(target_tree, DFTerminal): 95 | target_scope = self.get_scope(target_tree) 96 | if target_scope in self._binddict.keys(): 97 | target_bind, target_term_lsb = self.get_next_bind(target_scope, bit) 98 | if target_bind.isCombination(): 99 | tree_list = self.extract_all_dfxxx(target_bind.tree, tree_list, bit, dftype) 100 | elif isinstance(target_tree, DFPartselect): 101 | ref_bit = eval_value(target_tree.lsb) + bit - eval_value(self._terms[self.scope_dict[str(target_tree.var)]].lsb) 102 | tree_list = self.extract_all_dfxxx(target_tree.var, tree_list, ref_bit, dftype) 103 | return tree_list 104 | 105 | def search_combloop(self, target_tree, bit, start_tree, start_bit, find_cnt=0, rec_call_cnt=0): 106 | """[FUNCTIONS] 107 | target_tree:DF*** 108 | bit: signal bit pointer 109 | start_tree:DF*** 110 | """ 111 | if (str(target_tree), bit) == (start_tree, start_bit): 112 | find_cnt += 1 113 | if find_cnt == 2: 114 | raise CombLoopException('Combinational loop is found @' + str(start_tree)) 115 | 116 | rec_call_cnt += 1 117 | if rec_call_cnt > 1000: 118 | raise CombLoopException(str(start_tree) + ' may be combinational loop, or too complex logic (over 1000 variable).') 119 | 120 | if hasattr(target_tree, "nextnodes"): 121 | if isinstance(target_tree, DFConcat): 122 | now_max_bit = 0 123 | now_min_bit = 0 124 | for nextnode in reversed(target_tree.nextnodes): 125 | now_max_bit = now_min_bit + self.get_bit_width_from_tree(nextnode) - 1 126 | if now_min_bit <= bit <= now_max_bit: 127 | self.search_combloop(nextnode, bit - now_min_bit, start_tree, start_bit, find_cnt, rec_call_cnt) 128 | break 129 | now_min_bit = now_max_bit + 1 130 | else: 131 | for nextnode in target_tree.nextnodes: 132 | if isinstance(target_tree, DFBranch) and nextnode == target_tree.condnode: 133 | self.search_combloop(nextnode, 0, start_tree, start_bit, find_cnt, rec_call_cnt) 134 | else: 135 | self.search_combloop(nextnode, bit, start_tree, start_bit, find_cnt, rec_call_cnt) 136 | elif isinstance(target_tree, DFBranch): 137 | self.search_combloop(target_tree.condnode, 0, start_tree, start_bit, find_cnt, rec_call_cnt) 138 | self.search_combloop(target_tree.truenode, bit, start_tree, start_bit, find_cnt, rec_call_cnt) 139 | self.search_combloop(target_tree.falsenode, bit, start_tree, start_bit, find_cnt, rec_call_cnt) 140 | elif isinstance(target_tree, DFTerminal): 141 | target_scope = self.get_scope(target_tree) 142 | if target_scope in self._binddict.keys(): 143 | target_bind, target_term_lsb = self.get_next_bind(target_scope, bit) 144 | if target_bind.isCombination(): 145 | self.search_combloop(target_bind.tree, bit, start_tree, start_bit, find_cnt, rec_call_cnt) 146 | elif isinstance(target_tree, DFPartselect): 147 | ref_bit = eval_value(target_tree.lsb) + bit - eval_value(self._terms[self.scope_dict[str(target_tree.var)]].lsb) 148 | self.search_combloop(target_tree.var, ref_bit, start_tree, start_bit, find_cnt, rec_call_cnt) 149 | return 150 | 151 | def delete_all_cache(self): 152 | self.cache = {} 153 | self.gnb_cache= {} 154 | 155 | def gnb_memoize(f): 156 | def helper(self, y, z): 157 | if (y, z) not in self.gnb_cache: 158 | self.gnb_cache[(y, z)] = f(self, y, z) 159 | return self.gnb_cache[(y, z)] 160 | return helper 161 | 162 | @gnb_memoize 163 | def get_next_bind(self, scope, bit): 164 | """[FUNCTIONS] get root bind.(mainly use at 'Rename' terminal.) 165 | """ 166 | if scope in self._binddict.keys(): 167 | target_binds = self._binddict[scope] 168 | target_bind_index = self.get_bind_index(target_binds, bit + eval_value(self._terms[scope].lsb), self._terms[scope]) 169 | target_bind = target_binds[target_bind_index] 170 | return target_bind, eval_value(self._terms[scope].lsb) 171 | else: 172 | return None, self._terms[scope].lsb 173 | 174 | def get_bind_index(self, binds=None, bit=None, term=None, scope=None): 175 | """[FUNCTIONS] get bind index in that target bit exists. 176 | """ 177 | if 'Rename' in term.termtype: 178 | return 0 179 | else: 180 | if scope is not None: 181 | binds = self._binddict[scope] 182 | term = self._terms[scope] 183 | for index, bind in enumerate(binds): 184 | if bind.lsb is None: 185 | return 0 186 | if self.get_bind_lsb(bind) <= bit <= self.get_bind_msb(bind): 187 | return index 188 | else: 189 | raise IRREGAL_CODE_FORM("unexpected bind @"+binds[0].tostr()) 190 | 191 | def get_bit_width_from_tree(self, tree): 192 | onebit_comb = ('Ulnot', 'Unot', 'Eq', 'Ne', 193 | 'Lor', 'Land', 'Unand', 'Uor', 'Unor', 'Uxor', 'Uxnor') 194 | if isinstance(tree, DFTerminal): 195 | term = self._terms[self.get_scope(tree)] 196 | return eval_value(term.msb) + 1 197 | elif isinstance(tree, DFPartselect): 198 | return eval_value(tree.msb) - eval_value(tree.lsb) + 1 199 | elif isinstance(tree, DFOperator): 200 | if tree.operator in onebit_comb: 201 | return 1 202 | else: 203 | each_sizes = (self.get_bit_width_from_tree(nextnode) for nextnode in tree.nextnodes) 204 | return min(each_sizes) 205 | elif isinstance(tree, DFIntConst): 206 | return tree.width() 207 | elif isinstance(tree, DFConcat): 208 | return sum([self.get_bit_width_from_tree(nextnode) for nextnode in tree.nextnodes]) 209 | elif isinstance(tree, DFEvalValue): 210 | return tree.width 211 | else: 212 | raise IRREGAL_CODE_FORM("unexpected concat node") 213 | 214 | def walk_reg_each_bit(self): 215 | for tk, tv in sorted(self._terms.items(), key=lambda x: len(x[0])): 216 | if tk in self._binddict.keys(): 217 | for bvi in self._binddict[tk]: #process for each always block 218 | bind_lsb = self.get_bind_lsb(bvi) 219 | bind_msb = self.get_bind_msb(bvi) 220 | for bit in range(bind_lsb, bind_msb + 1): 221 | yield tv, tk, bvi, bit, bind_lsb 222 | 223 | def walk_signal(self): 224 | for tk, tv in sorted(self._terms.items(), key=lambda x: len(x[0])): 225 | yield tv, tk 226 | 227 | def get_bind_lsb(self, bind): 228 | if bind.lsb: 229 | return bind.lsb.value 230 | else: 231 | return 0 232 | 233 | def get_bind_msb(self, bind): 234 | if bind.msb: 235 | return bind.msb.value 236 | else: 237 | return 0 238 | 239 | def get_scope(self, tree): 240 | name = str(tree) 241 | if name in self.scope_dict.keys(): 242 | return self.scope_dict[name] 243 | else: 244 | return None 245 | 246 | class CombLoopException(Exception): pass 247 | 248 | class MothernodeSetter(BindLibrary): 249 | """[CLASSES] 250 | set mother node for all nodes. 251 | need expressly call destructer. 252 | """ 253 | def __init__(self, bind_library): 254 | self._binddict = bind_library._binddict 255 | self._terms = bind_library._terms 256 | self.scope_dict = bind_library.scope_dict 257 | self.cache = bind_library.cache 258 | self.gnb_cache = bind_library.gnb_cache 259 | #self.disable_dfxxx_eq() 260 | 261 | def __del__(self): 262 | self.enable_dfxxx_eq() 263 | 264 | def set_mother_node(f): 265 | def helper(self, target_tree, tree_list, bit, dftype): 266 | tree_list = f(self, target_tree, tree_list, bit, dftype) 267 | if tree_list: 268 | for tree, bit in tree_list: 269 | #if hasattr(tree, 'mother_node'): continue 270 | #if str(tree) == str(target_tree): continue 271 | tree.mother_node = target_tree 272 | return tree_list 273 | return helper 274 | 275 | @set_mother_node 276 | def extract_all_dfxxx(self, target_tree, tree_list, bit, dftype): 277 | return BindLibrary.extract_all_dfxxx(self, target_tree, tree_list, bit, dftype) 278 | 279 | def disable_dfxxx_eq(self): 280 | """ [FUNCTIONS] 281 | Chenge df***.__eq__()method to identify each tree. 282 | """ 283 | 284 | self.DFConstant__eq__org = DFConstant.__eq__ 285 | self.DFEvalValue__eq__org = DFEvalValue.__eq__ 286 | self.DFUndefined__eq__org = DFUndefined.__eq__ 287 | self.DFHighImpedance__eq__org = DFHighImpedance.__eq__ 288 | self.DFTerminal__eq__org = DFTerminal.__eq__ 289 | self.DFBranch__eq__org = DFBranch.__eq__ 290 | self.DFOperator__eq__org = DFOperator.__eq__ 291 | self.DFPartselect__eq__org = DFPartselect.__eq__ 292 | self.DFPointer__eq__org = DFPointer.__eq__ 293 | self.DFConcat__eq__org = DFConcat.__eq__ 294 | 295 | DFConstant.__eq__ = return_false.__get__(DFConstant) 296 | DFEvalValue.__eq__ = return_false.__get__(DFEvalValue) 297 | DFUndefined.__eq__ = return_false.__get__(DFUndefined) 298 | DFHighImpedance.__eq__ = return_false.__get__(DFHighImpedance) 299 | DFTerminal.__eq__ = return_false.__get__(DFTerminal) 300 | DFBranch.__eq__ = return_false.__get__(DFBranch) 301 | DFOperator.__eq__ = return_false.__get__(DFOperator) 302 | DFPartselect.__eq__ = return_false.__get__(DFPartselect) 303 | DFPointer.__eq__ = return_false.__get__(DFPointer) 304 | DFConcat.__eq__ = return_false.__get__(DFConcat) 305 | #DFDelay.__eq__ = MethodType(return_false, None, DFDelay) 306 | #DFSyscall.__eq__ = MethodType(return_false, None, DFSyscall) 307 | 308 | def enable_dfxxx_eq(self): 309 | DFConstant.__eq__ = self.DFConstant__eq__org 310 | DFEvalValue.__eq__ = self.DFEvalValue__eq__org 311 | DFUndefined.__eq__ = self.DFUndefined__eq__org 312 | DFHighImpedance.__eq__ = self.DFHighImpedance__eq__org 313 | DFTerminal.__eq__ = self.DFTerminal__eq__org 314 | DFBranch.__eq__ = self.DFBranch__eq__org 315 | DFOperator.__eq__ = self.DFOperator__eq__org 316 | DFPartselect.__eq__ = self.DFPartselect__eq__org 317 | DFPointer.__eq__ = self.DFPointer__eq__org 318 | DFConcat.__eq__ = self.DFConcat__eq__org 319 | 320 | def return_false(self, other): 321 | return False 322 | 323 | def DFConstant_eq_org(self, other): 324 | if type(self) != type(other): return False 325 | return self.value == other.value 326 | 327 | def DFEvalValue_eq_org(self, other): 328 | if type(self) != type(other): return False 329 | return (self.value == other.value and self.width == other.width and 330 | self.isfloat == other.isfloat and self.isstring == other.isstring) 331 | 332 | def DFUndefined_eq_org(self, other): 333 | if type(self) != type(other): return False 334 | return self.width == other.width 335 | 336 | def DFHighImpedance_eq_org(self, other): 337 | if type(self) != type(other): return False 338 | return self.width == other.width 339 | 340 | def DFTerminal_eq_org(self, other): 341 | if type(self) != type(other): return False 342 | return self.name == other.name 343 | 344 | def DFBranch_eq_org(self, other): 345 | if type(self) != type(other): return False 346 | return (self.condnode == other.condnode and self.truenode == other.truenode and 347 | self.falsenode == other.falsenode) 348 | 349 | def DFOperator_eq_org(self, other): 350 | if type(self) != type(other): return False 351 | return self.operator == other.operator and self.nextnodes == other.nextnodes 352 | 353 | def DFPartselect_eq_org(self, other): 354 | if type(self) != type(other): return False 355 | return self.var == other.var and self.msb == other.msb and self.lsb == other.lsb 356 | 357 | def DFPointer_eq_org(self, other): 358 | if type(self) != type(other): return False 359 | return self.var == other.var and self.ptr == other.ptr 360 | 361 | def DFConcat_eq_org(self, other): 362 | if type(self) != type(other): return False 363 | return self.nextnodes == other.nextnodes 364 | 365 | def eval_value(tree): 366 | if isinstance(tree, DFOperator): 367 | for nextnode in self.nextnodes: 368 | assert(isinstance(nextnode, DFEvalValue) 369 | or isinstance(nextnode, DFIntConst) 370 | or isinstance(nextnode, DFOperator) 371 | or isinstance(nextnode, DFTerminal)) 372 | if self.operator == 'Plus': 373 | return eval_value(nextnodes[0]) + eval_value(nextnodes[1]) 374 | elif self.operator == 'Minus': 375 | return eval_value(nextnodes[0]) - eval_value(nextnodes[1]) 376 | elif self.operator == 'Times': 377 | return eval_value(nextnodes[0]) * eval_value(nextnodes[1]) 378 | else: 379 | raise Exception('unimplemented for this type tree' + str(type(tree))) 380 | elif isinstance(tree, DFTerminal): 381 | if self.get_scope(scopedict) in binddict.keys(): 382 | return binddict[self.get_scope(scopedict)][0].tree.eval() 383 | else: 384 | raise verror.ImplementationError() 385 | elif isinstance(tree, DFIntConst): 386 | return tree.eval() 387 | elif isinstance(tree, DFEvalValue): 388 | return tree.value 389 | elif tree is None: 390 | return 0 391 | else: 392 | raise Exception('Unexpected error@bindlibrary') 393 | -------------------------------------------------------------------------------- /pyverilog_toolbox/verify_tool/cnt_analyzer.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # cnt_analyzer.py 3 | # 4 | # 5 | # 6 | # 7 | # Copyright (C) 2015, Ryosuke Fukatani 8 | # License: Apache 2.0 9 | #------------------------------------------------------------------------------- 10 | 11 | import sys 12 | import os 13 | 14 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 15 | 16 | from pyverilog.dataflow.dataflow import * 17 | from pyverilog_toolbox.verify_tool.dataflow_facade import dataflow_facade, out_as_html 18 | from pyverilog_toolbox.verify_tool.bindlibrary import eval_value 19 | 20 | import pyverilog.controlflow.splitter as splitter 21 | 22 | 23 | class CntAnalyzer(dataflow_facade): 24 | compare_ope = ('Eq', 'NotEq', 'GreaterEq', 'LessEq', 'GreaterThan', 'LessThan') 25 | plus_ope = ('NotEq', 'LessEq', 'LessThan') 26 | load_ope = ('Eq', 'GreaterEq', 'GreaterThan') 27 | eq_ope = ('Eq', 'GreaterEq', 'LessEq') 28 | 29 | def analyze_cnt(self): 30 | self.make_term_ref_dict() 31 | self.cnt_dict = {} 32 | 33 | for tv, tk, bvi, bit, term_lsb in self.binds.walk_reg_each_bit(): 34 | if not 'cnt' in str(tk) and not 'count' in str(tk): continue 35 | if not eval_value(tv.msb): continue #1bit signal is not counter. 36 | 37 | target_tree = self.makeTree(tk) 38 | funcdict = splitter.split(target_tree) 39 | funcdict = splitter.remove_reset_condition(funcdict) 40 | 41 | up_cond = self.filter(funcdict, self.active_ope, op='Plus') 42 | up_cond = {conds[-1] for conds in up_cond.keys()} 43 | down_cond = self.filter(funcdict, self.active_ope, op='Minus') 44 | down_cond = {conds[-1] for conds in down_cond.keys()} 45 | 46 | new_counter = self.cnt_factory(str(tk), up_cond, down_cond) 47 | new_counter.set_msb(eval_value(tv.msb)) 48 | new_counter.set_reset_value(self.get_reset_value(target_tree, str(bvi.getResetName()))) 49 | 50 | load_const_dict = self.filter(funcdict, self.active_load_const) 51 | load_const_dict = {conds[-1]: eval_value(value) for conds, value in load_const_dict.items()} 52 | new_counter.set_load_const_cond(load_const_dict) 53 | 54 | if load_const_dict: 55 | new_counter.set_max_load_const(max(load_const_dict.values())) 56 | ## else: #free run counter 57 | ## new_counter.set_max_load_const(2 ** new_counter.msb - 1) 58 | new_counter.make_load_dict(self.binds) 59 | new_counter.make_change_cond(self.binds) 60 | self.cnt_dict[new_counter.name] = new_counter 61 | 62 | for cnt_name, counter in self.cnt_dict.items(): 63 | for child_cnt in self.term_ref_dict[cnt_name] & set(self.cnt_dict.keys()) - set([cnt_name]): 64 | self.cnt_dict[child_cnt].mother_cnts.add(cnt_name) 65 | 66 | for counter in self.cnt_dict.values(): 67 | print(counter.tostr() + '\n') 68 | 69 | return self.cnt_dict 70 | 71 | def make_cnt_event_all(self): 72 | """ [FUNCTIONS] make cnt_event_dict[(cnt_val, operator)] 73 | ex. 74 | always @(posedge CLK or negedge RSTN) begin 75 | if(!RSTN) begin 76 | reg1 <= 0; 77 | end else if(cnt == 2'd3) begin 78 | reg1 <= 1'd1; 79 | end else begin 80 | reg1 <= 0; 81 | end 82 | end 83 | cnt_event_dict[3,'Eq)] = (reg1 <= 1'd1,) 84 | """ 85 | def make_cnt_ref_info(cond_dict, cnt_name): 86 | cnt_ref_info = [] 87 | for cond, term_value in cond_dict.items(): 88 | reffered_cnts = [] 89 | opes = self.binds.extract_all_dfxxx(cond, set([]), 0, DFOperator) 90 | opes = set([ope[0] for ope in opes]) 91 | for ope in opes: 92 | if not ope.operator in self.compare_ope: continue 93 | if cnt_name == str(ope.children()[0]): 94 | ope.comp_target = ope.children()[0] 95 | lsb = 0 96 | elif (isinstance(ope.children()[0], DFPartselect) and 97 | cnt_name == str(ope.children()[0].var)): 98 | ope.comp_target = ope.children()[0].var 99 | lsb = eval_value(ope.children()[0].lsb) 100 | else: 101 | continue 102 | ope.comp_value = eval_value(ope.children()[1]) 103 | ope.comp_target.mother_node = ope 104 | reffered_cnts.append(ope.comp_target) 105 | if reffered_cnts: 106 | cnt_ref_info.append((reffered_cnts, term_value, cond, lsb)) 107 | return cnt_ref_info 108 | 109 | for cnt_name, counter in self.cnt_dict.items(): 110 | cnt_ref_dict = {} 111 | for term_name in self.term_ref_dict[cnt_name]: 112 | if term_name == cnt_name: continue #exclude self reference 113 | scope = self.binds.get_scope(term_name) 114 | target_tree = self.makeTree(scope) 115 | funcdict = splitter.split(target_tree) 116 | funcdict = splitter.remove_reset_condition(funcdict) 117 | if not funcdict: continue 118 | cond_dict = {func[-1]: term_value for func, term_value in funcdict.items()}#extract last condition 119 | cnt_ref_info = make_cnt_ref_info(cond_dict, cnt_name) 120 | if cnt_ref_info: 121 | cnt_ref_dict[term_name] = cnt_ref_info 122 | counter.make_cnt_event_dict(cnt_ref_dict) 123 | 124 | def get_reset_value(self, target_tree, reset_name): 125 | if target_tree.condnode.operator == 'Ulnot': 126 | reset_from_tree = str(target_tree.condnode.nextnodes[0]) 127 | else: 128 | reset_from_tree = str(target_tree.condnode) 129 | assert reset_from_tree == reset_name 130 | return eval_value(target_tree.truenode) 131 | 132 | def filter(self, funcdict, condition, **kwargs): 133 | ret_funcdict = {} 134 | for condlist, func in funcdict.items(): 135 | if not condition(func, **kwargs): continue 136 | ret_funcdict[condlist] = func 137 | return ret_funcdict 138 | 139 | def active_ope(self, node, **kwargs): 140 | if 'op' in kwargs.keys(): 141 | return isinstance(node, DFOperator) and node.operator == kwargs['op'] 142 | raise Exception('Need op arg.') 143 | 144 | def active_load_const(self, node): 145 | """ [FUNCTION] 146 | Judge loading const or not. 147 | """ 148 | return isinstance(node, DFIntConst) or isinstance(node, DFEvalValue) 149 | 150 | def cnt_factory(self, name, up_cond, down_cond): 151 | if up_cond and down_cond: 152 | return up_down_cnt_profile(name, up_cond, down_cond) 153 | elif up_cond: 154 | return up_cnt_profile(name, up_cond) 155 | elif down_cond: 156 | return down_cnt_profile(name, down_cond) 157 | else: 158 | raise Exception(name + ' is irregular counter.') 159 | 160 | def decorate_html(html_name): 161 | temp_html = open('temp.html', 'r') 162 | out_html = open(html_name, 'w') 163 | for line in temp_html: 164 | if 'name:' in line: 165 | out_html.write('' + line + '
') 166 | elif 'Exist counters:\n' == line: 167 | out_html.write('' + line + '
') 168 | elif 'Counter events:\n' == line: 169 | out_html.write('
' + line + '
') 170 | elif ': No counter events.' in line: 171 | out_html.write('' + line + '
') 172 | else: 173 | out_html.write(line + '
') 174 | temp_html.close() 175 | out_html.close() 176 | 177 | @out_as_html(decorate_html) 178 | def show(self): 179 | print("Exist counters:") 180 | self.analyze_cnt() 181 | self.make_cnt_event_all() 182 | print("Counter events:") 183 | for counter in self.cnt_dict.values(): 184 | if counter.cnt_event_dict: 185 | print(counter.name + ': ' + str(counter.cnt_event_dict.values())) 186 | else: 187 | print(counter.name + ': No counter events.') 188 | 189 | class cnt_profile(object): 190 | compare_ope = ('Eq', 'NotEq', 'GreaterEq', 'LessEq', 'GreaterThan', 'LessThan') 191 | plus_ope = ('NotEq', 'LessEq', 'LessThan') 192 | load_ope = ('Eq', 'GreaterEq', 'GreaterThan') 193 | eq_ope = ('Eq', 'GreaterEq', 'LessEq') 194 | 195 | def __init__(self, name, change_dict): 196 | self.name = name 197 | self.change_dict = change_dict 198 | self._set_category() 199 | self.mother_cnts = set([]) 200 | def _set_category(self): 201 | self.category = 'abstract counter' 202 | raise Exception('this method must be overridden.') 203 | def set_reset_value(self, value): 204 | self.reset_value = value 205 | def set_load_const_cond(self, load_const_cond): 206 | self.load_const_cond = load_const_cond 207 | def set_max_load_const(self, max_load_const): 208 | """ [FUNCTIONS] 209 | ex. 210 | if(up_cnt >= 3'd7) begin 211 | up_cnt <= 3; 212 | end else if(up_cnt >= 3'd7) begin 213 | up_cnt <= 5; 214 | 215 | self.max_load_const = 5 216 | """ 217 | self.max_load_const = max_load_const 218 | def make_load_dict(self, binds): 219 | """ [FUNCTIONS] 220 | self.load_dict[load_const] = (cnt value when load execute) 221 | if prulal condition for same load_const, only store max value. 222 | ex. 223 | 224 | if(up_cnt >= 3'd7) begin 225 | up_cnt <= 0; 226 | end else if(up_cnt >= 3'd7) begin 227 | up_cnt <= 0; 228 | 229 | self.load_dict[0] = 7 230 | """ 231 | self.load_dict = {} 232 | for cond, value in self.load_const_cond.items(): 233 | if value != 0 and value != self.max_load_const: continue 234 | tree_list = binds.extract_all_dfxxx(cond, set([]), 0, DFOperator) 235 | tree_list = set([tree for tree in tree_list if tree[0].operator in self.load_ope]) 236 | for tree, bit in tree_list: 237 | if str(tree.nextnodes[0]) == self.name: 238 | cnt_cond = eval_value(tree.nextnodes[1]) 239 | elif str(tree.nextnodes[1]) == self.name: 240 | cnt_cond = eval_value(tree.nextnodes[0]) 241 | else: 242 | continue 243 | if value in self.load_dict.keys() and self.load_dict[value][1] > cnt_cond: 244 | continue 245 | self.load_dict[value] = (tree.operator, cnt_cond) 246 | 247 | def make_change_cond(self, binds): 248 | """ [FUNCTIONS] 249 | search condition such as below. 250 | 251 | ex. 252 | if(up_cnt2 != 3'd5 && up_cnt == 3'd5) begin 253 | up_cnt2 <= up_cnt2 + 3'd1; 254 | 255 | This counter's max value = 3'd5 256 | """ 257 | for cond in self.change_dict: 258 | tree_list = binds.extract_all_dfxxx(cond, set([]), 0, DFOperator) 259 | tree_list = set([tree for tree in tree_list if tree[0].operator in self.plus_ope]) 260 | for tree, bit in tree_list: 261 | if str(tree.nextnodes[0]) == self.name: 262 | change_limit_num = eval_value(tree.nextnodes[1]) 263 | elif str(tree.nextnodes[1]) == self.name: 264 | change_limit_num = eval_value(tree.nextnodes[0]) 265 | else: 266 | continue 267 | if not hasattr(self, 'change_cond') or self.change_cond[1] < change_limit_num: 268 | self.change_cond = (tree.operator, change_limit_num) 269 | 270 | def make_cnt_event_dict(self, cnt_ref_dict): 271 | """ [FUNCTIONS] 272 | cnt_ref_branch.append((ref_cnt_set, value)) 273 | cnt_ref_dict[term_name] = cnt_ref_branch 274 | cnt_event_dict[num] = term_name + "=" + value.tocode() 275 | """ 276 | class root_ope_info(object): 277 | def __init__(self, root_ope, cond_lsb, diff_list, branch): 278 | self.root_ope = root_ope 279 | self.cond_lsb = cond_lsb 280 | self.diff_list = diff_list 281 | self.branch = branch 282 | def get_ope(self): 283 | return self.branch.tocode() 284 | 285 | self.cnt_event_dict = {} 286 | for term_name, cnt_ref_info in cnt_ref_dict.items(): 287 | root_ope_info_dict = {} 288 | for ref_cnt, term_value, branch, lsb in cnt_ref_info: 289 | if len(ref_cnt) != 1: 290 | raise Exception('Found redundunt condition description @' + term_name) 291 | ref_cnt = tuple(ref_cnt)[0] 292 | root_ope = ref_cnt.mother_node 293 | root_ope_info_dict[ref_cnt, term_value] = root_ope_info(root_ope, 0, lsb, branch) 294 | 295 | for ref_cnt, term_value in root_ope_info_dict.keys(): 296 | root_info = root_ope_info_dict[ref_cnt, term_value] 297 | root_ope = root_info.root_ope 298 | num = root_ope.comp_value * (2 ** root_info.cond_lsb) 299 | if num not in self.cnt_event_dict.keys(): 300 | self.cnt_event_dict[num] = [] 301 | self.cnt_event_dict[num].append(term_name + '=' + term_value.tocode() + ' @' + root_info.get_ope()) 302 | 303 | def calc_cnt_period(self): 304 | if hasattr(self, 'change_cond'): 305 | if self.change_cond[0] in self.eq_ope: 306 | return self.change_cond[1] 307 | else: 308 | return self.change_cond[1] - 1 309 | elif self.load_dict and self.load_dict[0]: 310 | if self.load_dict[0][0] in self.eq_ope: 311 | return self.load_dict[0][1] 312 | else: 313 | return self.load_dict[0][1] - 1 314 | return 2 ** (self.msb + 1) - 1 #freerun 315 | 316 | def set_msb(self, msb): 317 | self.msb = msb 318 | 319 | def tostr(self): 320 | return ("name: " + self.name + 321 | "\ncategory: " + self.category + 322 | "\nreset val: " + str(self.reset_value) + 323 | "\nmax_val: " + str(self.calc_cnt_period()) + 324 | "\nmother counter:" + str(tuple(self.mother_cnts))) 325 | 326 | class up_down_cnt_profile(cnt_profile): 327 | 328 | def __init__(self, name, up_cond, down_cond): 329 | self.name = name 330 | self.up_cond = up_cond 331 | self.down_cond = down_cond 332 | self._set_category() 333 | def _set_category(self): 334 | self.category = 'up/down counter' 335 | 336 | class up_cnt_profile(cnt_profile): 337 | def _set_category(self): 338 | self.category = 'up counter' 339 | 340 | class down_cnt_profile(cnt_profile): 341 | def _set_category(self): 342 | self.category = 'down counter' 343 | def calc_cnt_period(self): 344 | if self.load_const_cond and self.load_const_cond.values(): 345 | return max(self.load_const_cond.values()) - 1 346 | return 2 ** (self.msb + 1) - 1 347 | 348 | if __name__ == '__main__': 349 | cnt_analyzer = CntAnalyzer("../testcode/norm_cnt3.v") 350 | cnt_analyzer.show() 351 | 352 | -------------------------------------------------------------------------------- /pyverilog_toolbox/verify_tool/codeclone_finder.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # codeclone_finder.py 3 | # 4 | # 5 | # Copyright (C) 2015, Ryosuke Fukatani 6 | # License: Apache 2.0 7 | #------------------------------------------------------------------------------- 8 | 9 | import sys 10 | import os 11 | from collections import OrderedDict 12 | 13 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 14 | 15 | from pyverilog.dataflow.dataflow import * 16 | from pyverilog_toolbox.verify_tool.dataflow_facade import dataflow_facade, out_as_html 17 | from pyverilog_toolbox.verify_tool.bindlibrary import eval_value 18 | 19 | import pyverilog.controlflow.splitter as splitter 20 | 21 | 22 | class CodeCloneFinder(dataflow_facade): 23 | 24 | def search_regclone(self): 25 | """[FUNCTIONS] 26 | search register pairs that always hold same values. 27 | 28 | ex. 29 | always @(posedge CLK or negedge RST) begin 30 | if(RST) begin 31 | reg1 <= 1'b0; 32 | end else begin 33 | reg1 <= IN; 34 | end 35 | end 36 | 37 | assign in1 = IN; 38 | 39 | always @(posedge CLK or negedge RST) begin 40 | if(RST) begin 41 | reg2 <= 1'b0; 42 | end else begin 43 | reg2 <= in1; 44 | end 45 | end 46 | """ 47 | code_dict = {} 48 | for tv, tk, bvi, bit, term_lsb in self.binds.walk_reg_each_bit(): 49 | if not 'Reg' in tv.termtype: continue 50 | target_tree = self.makeTree(tk) 51 | code_dict[tk, bit] = target_tree.tocode() 52 | 53 | #sort for assign code(same assign reg must line up next to) 54 | #and assure output order for test: + str(t[0]) 55 | cd_order = OrderedDict(sorted(code_dict.items(), key=lambda t: t[1] + str(t[0]))) 56 | clone_regs = [] 57 | cd_values = list(cd_order.values()) 58 | cd_keys = list(cd_order.keys()) 59 | for cnt in range(len(cd_order.keys()) - 1): 60 | if cd_values[cnt] == cd_values[cnt + 1]: 61 | clone_regs.append((cd_keys[cnt], cd_keys[cnt + 1])) 62 | 63 | if clone_regs: 64 | print('Clone reg pairs:') 65 | self.deploy_reg_info(clone_regs) 66 | else: 67 | print('There isn\'t clone reg pair.') 68 | return clone_regs 69 | 70 | def search_invert_regs(self): 71 | """[FUNCTIONS] 72 | search register pairs that always hold invert values. 73 | 74 | ex. 75 | always @(posedge CLK or negedge RST) begin 76 | if(RST) begin 77 | reg1 <= 1'b0; 78 | end else begin 79 | reg1 <= IN; 80 | end 81 | end 82 | 83 | always @(posedge CLK or negedge RST) begin 84 | if(RST) begin 85 | reg2 <= 1'b1; 86 | end else begin 87 | reg2 <= !IN; 88 | end 89 | end 90 | """ 91 | def judge_invert_reg(values, target_values): 92 | assert len(values) == len(target_values) 93 | for val, target_val in zip(values, target_values): 94 | if isinstance(val, DFEvalValue) and isinstance(target_val, DFEvalValue): 95 | if eval_value(val) == eval_value(target_val): 96 | return False 97 | elif str(val) == 'Ulnot': 98 | if val.nextnodes[0].tocode() != target_val.tocode(): 99 | return False 100 | elif str(target_val) == 'Ulnot': 101 | if val.tocode() != target_val.nextnodes[0].tocode(): 102 | return False 103 | else: 104 | return False 105 | return True 106 | 107 | functable = {} 108 | for tv, tk, bvi, bit, term_lsb in self.binds.walk_reg_each_bit(): 109 | if not 'Reg' in tv.termtype: continue 110 | target_tree = self.makeTree(tk) 111 | functable[tk, bit] = splitter.split(target_tree) 112 | ft_order = OrderedDict(sorted(functable.items(), key=lambda t: str(t[0]))) 113 | 114 | invert_regs = [] 115 | ft_values = list(ft_order.values()) 116 | ft_keys = list(ft_order.keys()) 117 | for cnt in range(len(ft_order.keys()) - 1): 118 | for target_cnt in range(cnt + 1, len(ft_keys)):#roop for same formal branch 119 | if ft_values[cnt].keys() != ft_values[target_cnt].keys(): break #not same branch 120 | if judge_invert_reg(ft_values[cnt].values(), ft_values[target_cnt].values()): 121 | invert_regs.append((ft_keys[cnt], ft_keys[target_cnt])) 122 | if invert_regs: 123 | print('Invert reg pairs:') 124 | self.deploy_reg_info(invert_regs) 125 | else: 126 | print('There isn\'t invert reg pair.') 127 | return invert_regs 128 | 129 | def deploy_reg_info(self, regs): 130 | for reg in regs: 131 | print(str(reg[0][0]) + '[' + str(reg[0][1]) + '] and ' + 132 | str(reg[1][0]) + '[' + str(reg[1][1]) + ']') 133 | 134 | def decorate_html(html_name): 135 | temp_html = open('temp.html', 'r') 136 | out_html = open(html_name, 'w') 137 | for line in temp_html: 138 | line = line.replace('Clone reg pairs:', '' + 'Clone reg pairs:' + '' + '
') 139 | line = line.replace('Invert reg pairs:', '
' + '' + 'Invert reg pairs:' + '' + '
') 140 | out_html.write(line + '
') 141 | temp_html.close() 142 | out_html.close() 143 | 144 | @out_as_html(decorate_html) 145 | def show(self): 146 | self.search_regclone() 147 | self.search_invert_regs() 148 | 149 | if __name__ == '__main__': 150 | CodeCloneFinder("../testcode/reg_clone.v").show() 151 | 152 | -------------------------------------------------------------------------------- /pyverilog_toolbox/verify_tool/combloop_finder.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # combloop_finder.py 3 | # 4 | # finding combinational loop in RTL design. 5 | # 6 | # 7 | # Copyright (C) 2015, Ryosuke Fukatani 8 | # License: Apache 2.0 9 | #------------------------------------------------------------------------------- 10 | 11 | import sys 12 | import os 13 | 14 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 15 | 16 | from pyverilog.dataflow.dataflow import * 17 | from pyverilog_toolbox.verify_tool.dataflow_facade import dataflow_facade, out_as_html 18 | 19 | 20 | class CombLoopFinder(dataflow_facade): 21 | 22 | def decorate_html(html_name): 23 | temp_html = open('temp.html', 'r') 24 | out_html = open(html_name, 'w') 25 | for line in temp_html: 26 | out_html.write(line + '
') 27 | temp_html.close() 28 | out_html.close() 29 | 30 | @out_as_html(decorate_html) 31 | def search_combloop(self): 32 | binds = self.binds 33 | for tv, tk, bvi, bit, term_lsb in binds.walk_reg_each_bit(): 34 | if 'Reg' in tv.termtype and not bvi.isCombination(): continue 35 | target_tree = self.makeTree(tk) 36 | binds.search_combloop(target_tree, bit - term_lsb, str(tk), bit - term_lsb) 37 | print('There is no combinational loop.') 38 | 39 | if __name__ == '__main__': 40 | c_finder = CombLoopFinder("../testcode/not_combloop.v") 41 | c_finder.search_combloop() 42 | 43 | -------------------------------------------------------------------------------- /pyverilog_toolbox/verify_tool/dataflow_facade.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # get_dataflow_facade.py 3 | # 4 | # interface of register map analyzer 5 | # 6 | # 7 | # Copyright (C) 2015, Ryosuke Fukatani 8 | # License: Apache 2.0 9 | #------------------------------------------------------------------------------- 10 | 11 | import sys 12 | import os 13 | 14 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 15 | 16 | from optparse import OptionParser 17 | import pyverilog.utils.util as util 18 | import pyverilog.dataflow.bindvisitor as BindVisitor 19 | from pyverilog.dataflow.dataflow_analyzer import VerilogDataflowAnalyzer 20 | from pyverilog.dataflow.optimizer import VerilogDataflowOptimizer 21 | from pyverilog.dataflow.dataflow import * 22 | from pyverilog_toolbox.verify_tool.bindlibrary import BindLibrary 23 | from pyverilog.controlflow.controlflow_analyzer import VerilogControlflowAnalyzer 24 | 25 | def out_as_html(html_deco): 26 | def _helper(out_func): 27 | def __helper(self): 28 | if hasattr(self, 'html_name'): 29 | temp_sysout = sys.stdout 30 | sys.stdout = open('temp.html', 'w') 31 | return_val = out_func(self) 32 | sys.stdout.close() 33 | sys.stdout = temp_sysout 34 | html_deco(self.html_name) 35 | else: 36 | return_val = out_func(self) 37 | return return_val 38 | return __helper 39 | return _helper 40 | 41 | 42 | def _createAlwaysinfo(self, node, scope): 43 | """ [FUNCTIONS] 44 | This function is replaced by BindVisitor._createAlwaysinfo (in bindvisitor.py) in pyverilog_toolbox. 45 | But this correspondence is temporary. 46 | """ 47 | 48 | senslist = [] 49 | clock_edge = None 50 | clock_name = None 51 | clock_bit = None 52 | reset_edge = None 53 | reset_name = None 54 | reset_bit = None 55 | 56 | for l in node.sens_list.list: 57 | if l.sig is None: 58 | continue 59 | if isinstance(l.sig, Pointer): 60 | signame = self._get_signal_name(l.sig.var) 61 | bit = int(l.sig.ptr.value) 62 | else: 63 | signame = self._get_signal_name(l.sig) 64 | bit = 0 65 | ## if signaltype.isClock(signame): 66 | ## clock_name = self.searchTerminal(signame, scope) 67 | ## clock_edge = l.type 68 | ## declared_lsb = self.dataflow.terms[clock_name].lsb.eval() 69 | ## clock_bit = bit - declared_lsb 70 | ## elif signaltype.isReset(signame): 71 | ## reset_name = self.searchTerminal(signame, scope) 72 | ## reset_edge = l.type 73 | ## declared_lsb = self.dataflow.terms[reset_name].lsb.eval() 74 | ## reset_bit = bit - declared_lsb 75 | ## else: 76 | ## senslist.append(l) 77 | try: 78 | if self._is_reset(node, l.sig, l.type): 79 | reset_name = self.searchTerminal(signame, scope) 80 | reset_edge = l.type 81 | declared_lsb = self.dataflow.terms[reset_name].lsb.eval() 82 | reset_bit = bit - declared_lsb 83 | else: 84 | clock_name = self.searchTerminal(signame, scope) 85 | clock_edge = l.type 86 | declared_lsb = self.dataflow.terms[clock_name].lsb.eval() 87 | clock_bit = bit - declared_lsb 88 | except KeyError: 89 | sys.exit("Error!: " + signame + " isn't declared @" + str(scope) + ".") 90 | 91 | if clock_edge is not None and len(senslist) > 0: 92 | raise verror.FormatError('Illegal sensitivity list') 93 | if reset_edge is not None and len(senslist) > 0: 94 | raise verror.FormatError('Illegal sensitivity list') 95 | 96 | return (clock_name, clock_edge, clock_bit, reset_name, reset_edge, reset_bit,senslist) 97 | 98 | def _is_reset(self, node, sig, edge): 99 | """ [FUNCTIONS] 100 | This function is assigned as BindVisitor._is_reset (in bindvisitor.py) in pyverilog_toolbox. 101 | But this correspondence is temporary. 102 | """ 103 | if not isinstance(node.statement.statements[0], IfStatement): 104 | return False 105 | if isinstance(node.statement.statements[0].cond, Ulnot) and edge == 'negedge': 106 | target = node.statement.statements[0].cond.children()[0] 107 | elif edge == 'posedge': 108 | target = node.statement.statements[0].cond 109 | else: 110 | return False 111 | 112 | if isinstance(target, pyverilog.vparser.ast.Pointer): 113 | if sig.ptr == target.ptr: 114 | target = target.var 115 | else: 116 | return False 117 | 118 | return target == sig 119 | 120 | class dataflow_facade(VerilogControlflowAnalyzer): 121 | """ [CLASSES] 122 | Facade pattern for getting dataflow. 123 | You can get dataflow by dataflow_facade(Verilog file name). 124 | If commandline option exists, first argument is regard as verilog file name. 125 | """ 126 | def __init__(self, code_file_name, topmodule='', config_file=None): 127 | #TODO this corrspondence is temporal. 128 | BindVisitor._createAlwaysinfo = _createAlwaysinfo.__get__(BindVisitor) 129 | BindVisitor._is_reset = _is_reset.__get__(BindVisitor) 130 | # 131 | (topmodule, terms, binddict, resolved_terms, resolved_binddict, 132 | constlist, fsm_vars) = self.get_dataflow(code_file_name) 133 | 134 | VerilogControlflowAnalyzer.__init__(self, topmodule, terms, binddict, 135 | resolved_terms, resolved_binddict, constlist, fsm_vars) 136 | self.binds = BindLibrary(binddict, terms) 137 | 138 | def get_dataflow(self, code_file_name, topmodule='', config_file=None): 139 | optparser = OptionParser() 140 | optparser.add_option("-t","--top",dest="topmodule", 141 | default="TOP", help="Top module, Default=TOP") 142 | 143 | optparser.add_option("-I","--include", dest="include", action="append", 144 | default=[],help="Include path") 145 | optparser.add_option("-D",dest="define", action="append", 146 | default=[], help="Macro Definition") 147 | optparser.add_option("-S", dest="config_file", default=[], help="config_file") 148 | optparser.add_option("-s", "--search", dest="searchtarget", action="append", 149 | default=[], help="Search Target Signal") 150 | 151 | (options, args) = optparser.parse_args() 152 | 153 | if args: 154 | filelist = args 155 | elif code_file_name: 156 | if hasattr(code_file_name, "__iter__") and not isinstance(code_file_name, str): 157 | filelist = code_file_name 158 | else: 159 | filelist = (code_file_name,) 160 | else: 161 | raise Exception("Verilog file is not assigned.") 162 | 163 | for f in filelist: 164 | if not os.path.exists(f): raise IOError("file not found: " + f) 165 | 166 | if not topmodule: 167 | topmodule = options.topmodule 168 | 169 | analyzer = VerilogDataflowAnalyzer(filelist, topmodule, 170 | preprocess_include=options.include, 171 | preprocess_define=options.define) 172 | analyzer.generate() 173 | 174 | directives = analyzer.get_directives() 175 | terms = analyzer.getTerms() 176 | binddict = analyzer.getBinddict() 177 | 178 | optimizer = VerilogDataflowOptimizer(terms, binddict) 179 | 180 | optimizer.resolveConstant() 181 | resolved_terms = optimizer.getResolvedTerms() 182 | resolved_binddict = optimizer.getResolvedBinddict() 183 | constlist = optimizer.getConstlist() 184 | if config_file: 185 | self.config_file = config_file 186 | elif options.config_file: 187 | self.config_file = options.config_file 188 | 189 | fsm_vars = (['fsm', 'state', 'count', 'cnt', 'step', 'mode'] + options.searchtarget) 190 | return options.topmodule, terms, binddict, resolved_terms, resolved_binddict, constlist, fsm_vars 191 | 192 | def make_term_ref_dict(self): 193 | self.term_ref_dict = {} 194 | for tv,tk,bvi,bit,term_lsb in self.binds.walk_reg_each_bit(): 195 | if 'Rename' in tv.termtype: continue 196 | target_tree = self.makeTree(tk) 197 | tree_list = self.binds.extract_all_dfxxx(target_tree, set([]), bit - term_lsb, DFTerminal) 198 | for tree, bit in tree_list: 199 | if str(tree) not in self.term_ref_dict.keys(): 200 | self.term_ref_dict[str(tree)] = set([]) 201 | self.term_ref_dict[str(tree)].add(str(tk)) 202 | 203 | def make_extract_dfterm_dict(self): 204 | return_dict = {} 205 | binds = BindLibrary(self.resolved_binddict, self.resolved_terms) 206 | for tv, tk, bvi, bit, term_lsb in binds.walk_reg_each_bit(): 207 | tree = self.makeTree(tk) 208 | trees = binds.extract_all_dfxxx(tree, set([]), bit - term_lsb, DFTerminal) 209 | return_dict[(str(tk), bit)] = set([str(tree) for tree in trees]) 210 | return return_dict 211 | 212 | def decorate_html(html_name): 213 | temp_html = open('temp.html', 'r') 214 | out_html = open(html_name, 'w') 215 | for line in temp_html: 216 | if 'Term:\n' in line: 217 | out_html.write('' + line + '' + '
') 218 | elif 'Bind:\n' in line: 219 | out_html.write('
' + '' + line + '' + '
') 220 | else: 221 | out_html.write(line + '
') 222 | temp_html.close() 223 | out_html.close() 224 | 225 | @out_as_html(decorate_html) 226 | def print_dataflow(self): 227 | """[FUNCTIONS] 228 | print dataflow information. 229 | Compatible with Pyverilog. (Mainly used in gui_main.py) 230 | """ 231 | terms = self.binds._terms 232 | print('Term:') 233 | for tk, tv in sorted(terms.items(), key=lambda x: len(x[0])): 234 | print(tv.tostr()) 235 | 236 | binddict = self.binds._binddict 237 | print('Bind:') 238 | for bk, bv in sorted(binddict.items(), key=lambda x: len(x[0])): 239 | for bvi in bv: 240 | print(bvi.tostr()) 241 | 242 | @out_as_html(decorate_html) 243 | def print_controlflow(self): 244 | """[FUNCTIONS] 245 | print controlflow information. 246 | Compatible with Pyverilog. (Mainly used in gui_main.py) 247 | """ 248 | fsms = self.getFiniteStateMachines() 249 | 250 | for signame, fsm in fsms.items(): 251 | print('# SIGNAL NAME: %s' % signame) 252 | print('# DELAY CNT: %d' % fsm.delaycnt) 253 | fsm.view() 254 | if not options.nograph: 255 | fsm.tograph(filename=util.toFlatname(signame)+'.'+options.graphformat, nolabel=options.nolabel) 256 | loops = fsm.get_loop() 257 | print('Loop') 258 | for loop in loops: 259 | print(loop) 260 | 261 | if __name__ == '__main__': 262 | df = dataflow_facade("../testcode/complex_partselect.v") 263 | df.html_name='log.html' 264 | df.print_dataflow() 265 | #df.print_controlflow() 266 | -------------------------------------------------------------------------------- /pyverilog_toolbox/verify_tool/formal_verifier.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # formal_verifier.py 3 | # 4 | # 5 | # Copyright (C) 2015, Ryosuke Fukatani 6 | # License: Apache 2.0 7 | #------------------------------------------------------------------------------- 8 | 9 | import sys 10 | import os 11 | 12 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 13 | 14 | from pyverilog.dataflow.dataflow import * 15 | from pyverilog_toolbox.verify_tool.dataflow_facade import dataflow_facade 16 | from pyverilog_toolbox.verify_tool.bindlibrary import eval_value 17 | from sympy import Symbol, sympify 18 | from types import MethodType 19 | from pyverilog.utils.util import toTermname 20 | 21 | import pyverilog.utils.op2mark as op2mark 22 | 23 | class FormalVerifier(dataflow_facade): 24 | 25 | def __init__(self, code_file_name, topmodule=''): 26 | dataflow_facade.__init__(self, code_file_name, topmodule=topmodule) 27 | t_manager = term_manager() 28 | t_manager.set_scope_dict(self.binds.scope_dict) 29 | t_manager.set_terms(self.binds._terms) 30 | 31 | DFBranch.tocode_org = MethodType(DFBranch.tocode, None, DFBranch) 32 | DFBranch.tocode = MethodType(DFBranch_tocode, None, DFBranch) 33 | DFOperator.tocode_org = MethodType(DFOperator.tocode, None, DFOperator) 34 | DFOperator.tocode = MethodType(DFOperator_tocode, None, DFOperator) 35 | DFOperator.is_reduction = MethodType(DFOperator_is_reduction, None, DFOperator) 36 | DFOperator.is_algebra = MethodType(DFOperator_is_algebra, None, DFOperator) 37 | DFTerminal.tocode_org = MethodType(DFTerminal.tocode, None, DFTerminal) 38 | DFTerminal.tocode = MethodType(DFTerminal_tocode, None, DFTerminal) 39 | 40 | def write_back_DFmethods(self): 41 | DFTerminal.tocode = MethodType(DFTerminal.tocode_org, None, DFTerminal) 42 | DFBranch.tocode = MethodType(DFBranch.tocode_org, None, DFBranch) 43 | DFOperator.tocode = MethodType(DFOperator.tocode_org, None, DFOperator) 44 | del DFOperator.tocode_org 45 | del DFOperator.is_reduction 46 | del DFOperator.is_algebra 47 | 48 | def calc_truth_table(self, var_name): 49 | """[FUNCTIONS] 50 | Wrapper for _calc_truth_table (for assuring execute write_back_DFmethods.) 51 | """ 52 | try: 53 | truth_table = self._calc_truth_table(var_name) 54 | finally: 55 | self.write_back_DFmethods() 56 | return truth_table 57 | 58 | def _declare_symbols(self, tree_names): 59 | symbols = {} 60 | for tree in tree_names: 61 | symbol_name = tree.replace('.', '_') 62 | scope = toTermname(str(tree)) 63 | term = self.terms[scope] 64 | msb = eval_value(term.msb) 65 | lsb = eval_value(term.lsb) 66 | if msb == lsb: 67 | symbols[symbol_name] = Symbol(symbol_name) 68 | else: 69 | for i in range(lsb, msb + 1): 70 | symbol_name_bit = term_manager().publish_new_name(symbol_name, i) 71 | symbols[symbol_name_bit] = Symbol(symbol_name_bit) 72 | 73 | symbols = self._declare_renamed_symbols(symbols) 74 | return symbols 75 | 76 | def _declare_renamed_symbols(self, symbols): 77 | for signal in term_manager().renamed_signals: 78 | signal = signal.replace('.', '_') 79 | symbols[signal] = Symbol(signal) 80 | return symbols 81 | 82 | def _delete_renamed_symbols(self, symbols): 83 | """ [FUNCTIONS] 84 | For not assined constant value to tree under algebra. 85 | """ 86 | for signal in term_manager().renamed_signals: 87 | signal = signal.replace('.', '_') 88 | symbols.pop(signal) 89 | return symbols 90 | 91 | def _make_expr(self, target_tree): 92 | """ [FUNCTIONS] 93 | Make expr and symbols. 94 | expr: sympy expression 95 | symbol: sympy variable 96 | """ 97 | term_manager().flash_renamed_signals() 98 | trees = self.binds.extract_all_dfxxx(target_tree, set([]), 0, DFTerminal) 99 | tree_names = [str(tree[0]) for tree in trees] 100 | expr_str = self.to_sympy_expr(target_tree.tocode()) 101 | symbols = self._declare_symbols(tree_names) 102 | expr = sympify(expr_str, symbols, convert_xor=False) 103 | symbols = self._delete_renamed_symbols(symbols) #to not assign const value to algebra 104 | return expr, symbols 105 | 106 | def _calc_truth_table(self, var_name): 107 | """[FUNCTIONS] 108 | Sweep variable and calculate trutu_table. 109 | """ 110 | for tv, tk, bvi, bit, term_lsb in self.binds.walk_reg_each_bit(): 111 | if str(tk) != var_name: continue 112 | 113 | target_tree = self.makeTree(tk) 114 | msb = eval_value(tv.msb) 115 | lsb = eval_value(tv.lsb) 116 | #TODO case of multi bit (not algebra) 117 | expr, symbols = self._make_expr(target_tree) 118 | 119 | truth_table = {} 120 | for result, var_state in self.walk_truth_table(symbols, expr): 121 | truth_table[str(var_state)] = result 122 | print('result:' + str(result) + '@' + str(var_state)) 123 | return truth_table 124 | 125 | def walk_truth_table(self, symbols, expr): 126 | init_expr = expr 127 | for cnt in range(0, 2 ** len(symbols.keys())): 128 | expr = init_expr 129 | var_state = [] 130 | for i, var in enumerate(sorted(symbols.keys(), key=lambda t: str(t))): 131 | value = (format(cnt, 'b').zfill(len(symbols.keys()))[i]) == '1' 132 | var_state.append(var + ': ' + str(value)) 133 | expr = expr.subs(var, value) 134 | yield expr, var_state 135 | 136 | def to_sympy_expr(self, expr): 137 | """ [FUNCTIONS] 138 | verilog operators 139 | [] : implemented (convert [i] to __i__) 140 | ! ~ : implemented 141 | * / % : unsupported 142 | + - 143 | << >> 144 | == != : implemented 145 | < <= > => 146 | & : implemented 147 | ^ ^~ : implemented 148 | | : implemented 149 | && : implemented 150 | || : implemented 151 | ? : implemented 152 | reduction : implemented 153 | """ 154 | replace_words = (('!', '~'), ('||', '|'), ('&&', '&'), ('==', '^~'), 155 | ('!=', '^'), ) 156 | for comb in replace_words: 157 | expr = expr.replace(comb[0], comb[1]) 158 | return expr 159 | 160 | def DFBranch_tocode(self, dest='dest', always=''): 161 | ret = '(' 162 | if self.condnode is not None: ret += '(' + self.condnode.tocode(dest) + ')' 163 | ret += '& ' 164 | if self.truenode is not None: ret += self.truenode.tocode(dest) 165 | else: ret += dest 166 | ret += " | " 167 | if self.falsenode is not None: 168 | ret += '(~' + self.condnode.tocode(dest) + '&' 169 | ret += self.falsenode.tocode(dest) + ')' 170 | else: ret += dest 171 | ret += ")" 172 | return ret 173 | 174 | def DFOperator_is_reduction(self): 175 | if self.operator in ('Uand', 'Uor', 'Uxor', 'Uxnor'): 176 | return len(self.nextnodes) == 1 177 | else: 178 | return False 179 | 180 | def DFOperator_is_algebra(self): 181 | if self.operator in ('Plus', 'Minus', 'LessThan', 'GreaterThan', 'LessEq', 'GreaterEq'): 182 | return True 183 | else: 184 | return False 185 | 186 | def DFTerminal_tocode(self, dest='dest'): 187 | if term_manager().is_under_algebra: 188 | return term_manager().publish_new_name(str(self)).replace('.', '_') 189 | else: 190 | return self.tocode_org() 191 | 192 | def DFOperator_tocode(self): 193 | if self.is_algebra(): #if operator is algebra, nextnodes aren't sweeped. 194 | term_manager().set_is_under_algebra(True) 195 | code = self.tocode_org() 196 | term_manager().set_is_under_algebra(False) 197 | return code 198 | elif self.is_reduction(): 199 | if isinstance(self.nextnodes[0], DFPartselect): 200 | term = self.nextnodes[0].var 201 | msb = eval_value(self.nextnodes[0].msb) 202 | lsb = eval_value(self.nextnodes[0].lsb) 203 | elif isinstance(self.nextnodes[0], DFTerminal): 204 | term = term_manager().get_term(str(self.nextnodes[0])) 205 | msb = eval_value(term.msb) 206 | lsb = eval_value(term.lsb) 207 | else: 208 | raise Exception('Unexpected exception.') 209 | mark = op2mark.op2mark(self.operator) 210 | return mark.join([term_manager().publish_new_name(str(term), i).replace('.', '_') for i in range(lsb, msb + 1)]) 211 | else: 212 | return self.tocode_org() 213 | 214 | class term_manager(object): 215 | """ [CLASSES] 216 | Singleton class for manage terminals for DFxxx. 217 | """ 218 | _singleton = None 219 | def __new__(cls, *argc, **argv): 220 | if cls._singleton == None: 221 | cls._singleton = object.__new__(cls) 222 | cls.is_under_algebra = False 223 | cls.renamed_signals = [] 224 | return cls._singleton 225 | 226 | def set_scope_dict(self, scope_dict): 227 | self.scope_dict = scope_dict 228 | 229 | def set_terms(self, terms): 230 | self.terms = terms 231 | 232 | def get_term(self, signal): 233 | scope = toTermname(signal) 234 | return self.terms[scope] 235 | 236 | def set_is_under_algebra(self, flag=False): 237 | self.is_under_algebra = flag 238 | 239 | def publish_new_name(self, signal, bit=None): 240 | if bit is None: 241 | new_name = signal + '_' 242 | else: 243 | new_name = signal + '__' + str(bit) + '__' 244 | while new_name in self.scope_dict.keys(): 245 | new_name += '_' 246 | if bit is None: 247 | self.renamed_signals.append(new_name) 248 | return new_name 249 | 250 | def flash_renamed_signals(self): 251 | self.renamed_signals = [] 252 | 253 | if __name__ == '__main__': 254 | fv = FormalVerifier("../testcode/fv_test.v") 255 | fv.calc_truth_table('TOP.I') 256 | 257 | -------------------------------------------------------------------------------- /pyverilog_toolbox/verify_tool/metrics_calculator.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # metrics_calculator.py 3 | # 4 | #Calculate comde metrics for register level, function level and module level. 5 | # 6 | # Copyright (C) 2015, Ryosuke Fukatani 7 | # License: Apache 2.0 8 | #------------------------------------------------------------------------------- 9 | 10 | import sys 11 | import os 12 | from collections import OrderedDict 13 | 14 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 15 | 16 | from pyverilog.utils.util import getScope 17 | from pyverilog.dataflow.dataflow import * 18 | from pyverilog_toolbox.verify_tool.dataflow_facade import dataflow_facade, out_as_html 19 | 20 | import pyverilog.controlflow.splitter as splitter 21 | 22 | 23 | class MetricsCalculator(dataflow_facade): 24 | 25 | def __init__(self, code_file_name, topmodule='TOP', parameter_file='', result_file='metrics.log'): 26 | """[FUNCTIONS] 27 | code_file_name: calculation target(verilog file) 28 | parameter_file: metrics calculation parameter file 29 | result_file: metrics calculation result 30 | """ 31 | dataflow_facade.__init__(self, code_file_name, topmodule=topmodule) 32 | self.result_file_name = result_file 33 | self.function_metrics_elements = {} 34 | 35 | #initialize parameters for module metrics 36 | module_elements.coef_for_input = 3 37 | module_elements.pow_for_input = 1 38 | module_elements.coef_for_output = 3 39 | module_elements.pow_for_output = 1 40 | module_elements.coef_for_reg = 1 41 | module_elements.pow_for_reg = 1 42 | module_elements.coef_for_clk = 2 43 | module_elements.pow_for_clk = 2 44 | module_elements.coef_for_rst = 2 45 | module_elements.pow_for_rst = 1 46 | #initialize parameters for module metrics 47 | reg_elements.coef_for_branch = 1 48 | reg_elements.pow_for_branch = 1 49 | reg_elements.coef_for_nest = 1 50 | reg_elements.pow_for_nest = 2 51 | #initialize parameters for function metrics 52 | func_elements.coef_for_var = 2 53 | func_elements.pow_for_var = 1 54 | #iniatialize parameters for display 55 | self.module_disp_limit = 10 56 | self.func_disp_limit = 20 57 | self.reg_disp_limit = 20 58 | 59 | if hasattr(self, 'config_file'): 60 | self.config_calc_para_by_file() 61 | 62 | def config_calc_para_by_file(self): 63 | """ [FUNCTIONS] 64 | define write_flag, read_flag, write_data, address, read_data for regmap_analyzer. 65 | """ 66 | try: 67 | setup_file = open(self.config_file, "r") 68 | for readline in setup_file: 69 | if readline[0] == '#': continue 70 | words = readline.split(':') 71 | if len(words) == 2: 72 | #config parameters for module metrics 73 | if words[0] == 'COEF_FOR_INPUT': 74 | module_elements.coef_for_input = int(words[1]) 75 | elif words[0] == 'POW_FOR_INPUT': 76 | module_elements.pow_for_input = int(words[1]) 77 | elif words[0] == 'COEF_FOR_OUTPUT': 78 | module_elements.coef_for_output = int(words[1]) 79 | elif words[0] == 'POW_FOR_OUTPUT': 80 | module_elements.pow_for_output = int(words[1]) 81 | elif words[0] == 'COEF_FOR_REG': 82 | module_elements.coef_for_reg = int(words[1]) 83 | elif words[0] == 'POW_FOR_REG': 84 | module_elements.pow_for_reg = int(words[1]) 85 | elif words[0] == 'COEF_FOR_CLK': 86 | module_elements.coef_for_clk = int(words[1]) 87 | elif words[0] == 'POW_FOR_CLK': 88 | module_elements.pow_for_clk = int(words[1]) 89 | elif words[0] == 'COEF_FOR_RST': 90 | module_elements.coef_for_rst = int(words[1]) 91 | elif words[0] == 'POW_FOR_RST': 92 | module_elements.pow_for_rst = int(words[1]) 93 | 94 | #config parameters for module metrics 95 | elif words[0] == 'COEF_FOR_BRANCH': 96 | reg_elements.coef_for_branch = int(words[1]) 97 | elif words[0] == 'POW_FOR_BRANCH': 98 | reg_elements.pow_for_branch = int(words[1]) 99 | elif words[0] == 'COEF_FOR_NEST': 100 | reg_elements.coef_for_nest = int(words[1]) 101 | elif words[0] == 'POW_FOR_NEST': 102 | reg_elements.pow_for_nest = int(words[1]) 103 | 104 | #config parameters for function metrics 105 | elif words[0] == 'COEF_FOR_VAR': 106 | func_elements.coef_for_var = int(words[1]) 107 | elif words[0] == 'NEST_FOR_VAR': 108 | func_elements.pow_for_var = int(words[1]) 109 | 110 | #config parameters for display 111 | elif words[0] == 'MODULE_DISP_LIMIT': 112 | self.module_disp_limit = int(words[1]) 113 | elif words[0] == 'REG_DISP_LIMIT': 114 | self.reg_disp_limit = int(words[1]) 115 | elif words[0] == 'FUNC_DISP_LIMIT': 116 | self.func_disp_limit = int(words[1]) 117 | setup_file.close() 118 | except IOError: 119 | print(self.config_file + " can't open for read.") 120 | 121 | def synth_profile(self): 122 | f_elements_dict = self.calc_function_metrics() 123 | self.f_profile = function_profile(f_elements_dict, self.func_disp_limit) 124 | r_elements_dict = self.calc_reg_metrics() 125 | self.r_profile = reg_profile(r_elements_dict, self.reg_disp_limit) 126 | m_elements_dict = self.calc_module_metrics() 127 | self.m_profile = module_profile(m_elements_dict, self.module_disp_limit) 128 | 129 | return self.m_profile, self.r_profile, self.f_profile 130 | 131 | def calc_function_metrics(self): 132 | func_metrics_elements = {} 133 | for tv,tk in self.binds.walk_signal(): 134 | if not 'Function' in tv.termtype: continue 135 | for i, bind in enumerate(self.resolved_binddict[tk]): 136 | trees = self.binds.extract_all_dfxxx(bind.tree, set([]), 0, DFTerminal) 137 | if len(trees) > 1: # omit 1 variable function 138 | func_metrics_elements[str(getScope(tk)), i] = func_elements() 139 | func_metrics_elements[str(getScope(tk)), i].set_var(len(trees)) 140 | 141 | branch_cnt = self.walk_for_count_branch(bind.tree) 142 | func_metrics_elements[str(getScope(tk)), i].set_branch_cnt(branch_cnt) 143 | 144 | _, nest_cnt = self.walk_for_count_nest(bind.tree, count = 1) 145 | func_metrics_elements[str(getScope(tk)), i].set_nest_cnt(nest_cnt) 146 | return func_metrics_elements 147 | 148 | def calc_reg_metrics(self): 149 | reg_metrics_elements = {} 150 | for tv, tk in self.binds.walk_signal(): 151 | if not 'Reg' in tv.termtype: continue 152 | branch_cnt = 0 153 | if not tk in self.binddict.keys(): continue #no implement reg 154 | for i, bind in enumerate(self.binddict[tk]): 155 | reg_metrics_elements[str(getScope(tk)), i] = reg_elements() 156 | 157 | branch_cnt = self.walk_for_count_branch(bind.tree) 158 | reg_metrics_elements[str(getScope(tk)), i].set_branch_cnt(branch_cnt) 159 | 160 | _, nest_cnt = self.walk_for_count_nest(bind.tree, count = 1) 161 | reg_metrics_elements[str(getScope(tk)), i].set_nest_cnt(nest_cnt) 162 | 163 | return reg_metrics_elements 164 | 165 | def walk_for_count_branch(self, tree, count=0): 166 | """ [FUNCTIONS] 167 | Count up if/else/case branches number for register/function metrics. 168 | """ 169 | if isinstance(tree, DFBranch): 170 | count += 1 171 | count = self.walk_for_count_branch(tree.truenode, count) 172 | count = self.walk_for_count_branch(tree.falsenode, count) 173 | return count 174 | 175 | def walk_for_count_nest(self, tree, count=0, max_count=0): 176 | """ [FUNCTIONS] 177 | Count up depth of if/else/case nest for register/function metrics. 178 | """ 179 | if isinstance(tree, DFBranch): 180 | count += 1 181 | count, max_count = self.walk_for_count_nest(tree.truenode, count, max_count) 182 | count -= 1 183 | count, max_count = self.walk_for_count_nest(tree.falsenode, count, max_count) 184 | max_count = max_count if count < max_count else count 185 | return count, max_count 186 | 187 | def calc_module_metrics(self): 188 | module_metrics_elements = {} 189 | def initialize_elements_dict(): 190 | for tv, tk in self.binds.walk_signal(): 191 | if 'Function' in tv.termtype or 'Rename' in tv.termtype: continue 192 | if not str(getScope(tk)) in module_metrics_elements.keys(): 193 | module_metrics_elements[str(getScope(tk))] = module_elements() 194 | 195 | initialize_elements_dict() 196 | for tv, tk in self.binds.walk_signal(): 197 | if 'Function' in tv.termtype or 'Rename' in tv.termtype: continue 198 | for eachtype in tv.termtype: 199 | module_metrics_elements[str(getScope(tk))].add_element(eachtype) 200 | if 'Reg' in tv.termtype and tk in self.binddict.keys(): 201 | for bvi in self.binddict[tk]: 202 | module_metrics_elements[str(getScope(tk))].add_clk(bvi.getClockName()) 203 | module_metrics_elements[str(getScope(tk))].add_rst(bvi.getResetName()) 204 | return module_metrics_elements 205 | 206 | def decorate_html(html_name): 207 | temp_html = open('temp.html', 'r') 208 | out_html = open(html_name, 'w') 209 | for line in temp_html: 210 | if 'Module metrics\n' == line or 'Register metrics\n' == line or 'Function metrics\n' == line: 211 | out_html.write('
' + '' + line + '' + '
' + '
') 212 | elif '(twice larger than average)' in line: 213 | out_html.write('' + line + '' + '
') 214 | else: 215 | out_html.write(line + '
') 216 | temp_html.close() 217 | out_html.close() 218 | 219 | @out_as_html(decorate_html) 220 | def show(self): 221 | if not hasattr(self, 'm_profile'): 222 | raise Exception('This function must be called after synthesize profile.') 223 | self.m_profile.show() 224 | self.r_profile.show() 225 | self.f_profile.show() 226 | 227 | class metrics_profile(object): 228 | """ [CLASSES] 229 | Abstract metrics profile for one RTL design. 230 | This class will be inherited by module/register/function profile. 231 | """ 232 | def __init__(self, elements_dict, disp_limit=0): 233 | def sort_by_metrics_score(input_dict): 234 | return_dict = OrderedDict() 235 | for key, value in reversed(sorted(input_dict.items(), key=lambda x: x[1])): 236 | return_dict[key] = value 237 | return return_dict 238 | 239 | self.elements_dict = elements_dict 240 | metrics_dict = {key: elements.calc_metrics() for key, elements in self.elements_dict.items()} 241 | self.m_ordered = sort_by_metrics_score(metrics_dict) 242 | self.disp_limit = disp_limit 243 | self.level = 'abstract' 244 | 245 | def get_total_score(self): 246 | return sum(self.m_ordered.values()) 247 | 248 | def get_average_score(self): 249 | if self.m_ordered: 250 | return self.get_total_score() / len(self.m_ordered.values()) 251 | else: 252 | return 0 253 | 254 | def show(self): 255 | """ [FUNCTIONS] 256 | Display metrics score. 257 | If disp_limit = 0,all scores are displayed. 258 | """ 259 | print('\n\n' + self.level + ' metrics\nTotal: ' + str(self.get_total_score())) 260 | print('Average: ' + str(self.get_average_score())) 261 | print('\nEach score:') 262 | 263 | cnt = 0 264 | for key, value in self.m_ordered.items(): 265 | if len(key) > 1: 266 | print(self.level + ':' + str(key[0])) 267 | else: 268 | print(self.level + ':' + str(key)) 269 | if value > 2 * self.get_average_score(): 270 | print('total: ' + str(value) + '(twice larger than average)') 271 | else: 272 | print('total: ' + str(value)) 273 | self.elements_dict[key].show() 274 | cnt += 1 275 | if cnt == self.disp_limit: 276 | break 277 | 278 | class module_profile(metrics_profile): 279 | def __init__(self, elements_dict, disp_limit=0): 280 | metrics_profile.__init__(self, elements_dict, disp_limit) 281 | self.level = 'Module' 282 | 283 | class reg_profile(metrics_profile): 284 | def __init__(self, elements_dict, disp_limit=0): 285 | metrics_profile.__init__(self, elements_dict, disp_limit) 286 | self.level = 'Register' 287 | 288 | class function_profile(metrics_profile): 289 | def __init__(self, elements_dict, disp_limit=0): 290 | metrics_profile.__init__(self, elements_dict, disp_limit) 291 | self.level = 'Function' 292 | 293 | class metrics_elements(object): 294 | def calc_metrics(self): pass 295 | 296 | class reg_elements(metrics_elements): 297 | """ [CLASSES] 298 | Elements for calculating one register metrics. 299 | """ 300 | def __init__(self): 301 | self.if_nest_num = 0 302 | self.if_num = 0 303 | 304 | def set_branch_cnt(self, branch_cnt): 305 | self.branch_cnt = branch_cnt 306 | 307 | def set_nest_cnt(self, nest_cnt): 308 | self.nest_cnt = nest_cnt 309 | 310 | def calc_metrics(self): 311 | return ((self.branch_cnt * self.coef_for_branch) ** self.pow_for_branch + 312 | (self.nest_cnt * self.coef_for_nest) ** self.pow_for_nest) 313 | 314 | def show(self): 315 | print('Number of branch: ' + str(self.branch_cnt)) 316 | print('Max nest: ' + str(self.nest_cnt)) 317 | 318 | class func_elements(reg_elements): 319 | """ [CLASSES] 320 | Elements for calculating one function metrics. 321 | """ 322 | def __init__(self): 323 | self.var_num = 0 324 | 325 | def set_var(self, var_num): 326 | self.var_num = var_num 327 | 328 | def calc_metrics(self): 329 | return (self.var_num * self.coef_for_var) ** self.pow_for_var + reg_elements.calc_metrics(self) 330 | 331 | def show(self): 332 | print('Number of branch: ' + str(self.branch_cnt)) 333 | print('Max nest: ' + str(self.nest_cnt)) 334 | print('Number of variables: ' + str(self.var_num)) 335 | 336 | class module_elements(metrics_elements): 337 | """ [CLASSES] 338 | Elements for calculating one module metrics. 339 | """ 340 | def __init__(self): 341 | self.input_num = 0 342 | self.output_num = 0 343 | self.reg_num = 0 344 | self.clks = set([]) 345 | self.rsts = set([]) 346 | 347 | def add_element(self, element_type): 348 | if element_type == 'Input': 349 | self.input_num += 1 350 | elif element_type == 'Output': 351 | self.output_num += 1 352 | elif element_type == 'Reg': 353 | self.reg_num += 1 354 | 355 | def add_clk(self, clk_name): 356 | self.clks.add(clk_name) 357 | 358 | def add_rst(self, rst_name): 359 | self.rsts.add(rst_name) 360 | 361 | def calc_metrics(self): 362 | return ((self.input_num * self.coef_for_input) ** self.pow_for_input + 363 | (self.output_num * self.coef_for_output) ** self.pow_for_output + 364 | (self.reg_num * self.coef_for_reg) ** self.pow_for_reg + 365 | (len(self.clks) * self.coef_for_clk) ** self.pow_for_clk + 366 | (len(self.rsts) * self.coef_for_rst) ** self.pow_for_rst) 367 | 368 | def show(self): 369 | print('Number of input ports: ' + str(self.input_num)) 370 | print('Number of output ports: ' + str(self.output_num)) 371 | print('Number of registers: ' + str(self.reg_num)) 372 | print('Number of clocks: ' + str(len(self.clks))) 373 | print('Number of resets: ' + str(len(self.rsts))) 374 | 375 | if __name__ == '__main__': 376 | c_m = MetricsCalculator("../testcode/metrics_func.v") 377 | c_m.synth_profile() 378 | #c_m.html_name='log.html' 379 | c_m.show() 380 | 381 | -------------------------------------------------------------------------------- /pyverilog_toolbox/verify_tool/regmap_analyzer.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # regmap_analyzer.py 3 | # 4 | # Register map analyzer 5 | # 6 | # 7 | # Copyright (C) 2015, Ryosuke Fukatani 8 | # License: Apache 2.0 9 | #------------------------------------------------------------------------------- 10 | 11 | import sys 12 | import os 13 | import csv 14 | 15 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 16 | 17 | from pyverilog.dataflow.dataflow import * 18 | from pyverilog_toolbox.verify_tool.dataflow_facade import dataflow_facade, out_as_html 19 | 20 | import pyverilog.controlflow.splitter as splitter 21 | 22 | 23 | class RegMapAnalyzer(dataflow_facade): 24 | 25 | def __init__(self, code_file_name, setup_file, topmodule='', out_file='out.csv'): 26 | dataflow_facade.__init__(self, code_file_name, topmodule=topmodule) 27 | self.out_file_name = out_file 28 | 29 | if hasattr(self, 'config_file'): 30 | setup_file = self.config_file 31 | self.reg_control = MapFactory(setup_file) 32 | 33 | def getRegMaps(self): 34 | write_map = self.reg_control.create_map('write') 35 | read_map = self.reg_control.create_map('read') 36 | 37 | for tv, tk, bvi, bit, term_lsb in self.binds.walk_reg_each_bit(): 38 | target_tree = self.makeTree(tk) 39 | funcdict = splitter.split(target_tree) 40 | funcdict = splitter.remove_reset_condition(funcdict) 41 | trees = self.binds.extract_all_dfxxx(target_tree, set([]), bit - term_lsb, DFTerminal) 42 | write_map.check_new_reg(str(tv), term_lsb, trees, funcdict, bit) 43 | read_map.check_new_reg(str(tv), term_lsb, trees, funcdict, bit) 44 | self.out_file = open(self.out_file_name, "w") 45 | write_map.output_csv(self.out_file) 46 | read_map.output_csv(self.out_file) 47 | self.out_file.close() 48 | 49 | print('Write_map:\n' + str(write_map.map)) 50 | print('Read_map:\n' + str(read_map.map)) 51 | return write_map, read_map 52 | 53 | def csv2html(self, csv_file_name): 54 | """ [FUNCTIONS] 55 | Convert csv file to html. 56 | Cited from 57 | http://www.ctroms.com/blog/code/python/2011/04/20/csv-to-html-table-with-python/ 58 | (by Chris Trombley) 59 | """ 60 | reader = csv.reader(open(csv_file_name)) 61 | htmlfile = open('log.html', 'w') 62 | rownum = 0 63 | 64 | htmlfile.write('') 65 | for row in reader: 66 | if rownum == 0: 67 | htmlfile.write('') 68 | for column in row: 69 | htmlfile.write('') 70 | htmlfile.write('') 71 | else: 72 | htmlfile.write('') 73 | for column in row: 74 | htmlfile.write('') 75 | htmlfile.write('') 76 | rownum += 1 77 | htmlfile.write('
' + column + '
' + column + '
') 78 | 79 | class MapFactory(object): 80 | def __init__(self, file_name): 81 | write_flag, read_flag, address, write_data, read_data = self._read_setup_file(file_name) 82 | self.write_flag = write_flag 83 | self.read_flag = read_flag 84 | self.address = address 85 | self.write_data = write_data 86 | self.read_data = read_data 87 | def create_map(self, element): 88 | if element == 'write': 89 | return WriteMap(self.write_flag, self.address, self.write_data) 90 | elif element == 'read': 91 | return ReadMap(self.read_flag, self.address, self.read_data) 92 | def _read_setup_file(self, file_name): 93 | """ [FUNCTIONS] 94 | define write_flag, read_flag, write_data, address, read_data for regmap_analyzer. 95 | """ 96 | try: 97 | setup_file = open(file_name, "r") 98 | for readline in setup_file: 99 | if readline[0] == '#': continue 100 | word_list = readline.split(':') 101 | if len(word_list) == 2: 102 | if word_list[0] == 'WRITE_FLAG': 103 | write_flag = word_list[1].replace('\n', '') 104 | elif word_list[0] == 'READ_FLAG': 105 | read_flag = word_list[1].replace('\n', '') 106 | elif word_list[0] == 'ADDRESS': 107 | address = word_list[1].replace('\n', '') 108 | elif word_list[0] == 'READ_DATA': 109 | read_data = word_list[1].replace('\n', '') 110 | elif word_list[0] == 'WRITE_DATA': 111 | write_data = word_list[1].replace('\n', '') 112 | setup_file.close() 113 | return write_flag, read_flag, address, write_data, read_data 114 | 115 | except IOError: 116 | print(file_name + " can't open for read.") 117 | return 118 | 119 | class WriteMap(object): 120 | 121 | def __init__(self, flag, address, data): 122 | self.flag = flag 123 | self.address = address 124 | self.data = data 125 | if flag == 'None': 126 | self.finger_print_signals = set([address, data]) 127 | else: 128 | self.finger_print_signals = set([flag, address, data]) 129 | self.map = {} 130 | self.this_map_name = '\nWrite Map\n' 131 | 132 | def output_csv(self, file_handle): 133 | file_handle.write(self.this_map_name) 134 | self.calc_map_spec() 135 | file_handle.write('ADD,') 136 | for i in range(self.max_bit - 1, -1, -1): 137 | file_handle.write(str(i) + ',') 138 | for address, reg in sorted(self.map.items(), key=lambda x: x[0]): 139 | file_handle.write('\n') 140 | file_handle.write(str(address) + ',') 141 | for i in range(self.max_bit - 1, -1, -1): 142 | if i in reg.keys(): 143 | signal = reg[i] 144 | file_handle.write(signal[0] + '[' + str(signal[1]) + ']' + ',') 145 | else: 146 | file_handle.write(',') 147 | 148 | def calc_map_spec(self): 149 | max_bit = 0 150 | for address, reg in self.map.items(): 151 | if max(reg.keys()) > max_bit: 152 | max_bit = max(reg.keys()) 153 | self.max_bit = max_bit + 1 154 | if self.map: 155 | self.max_address = max(self.map.keys()) 156 | 157 | def _add_new_reg(self, sig_name, sig_bit, address, map_bit): 158 | if address not in self.map.keys(): 159 | self.map[address] = {} 160 | if map_bit not in self.map[address]: 161 | self.map[address][map_bit] = (sig_name, sig_bit) 162 | else: 163 | assert self.map[address][map_bit][0] == sig_name, 'duplicated address is exist @ADR:' + str(address) 164 | 165 | def get_map_info(self, trees, funcdict): 166 | for tree in trees: 167 | sig_name = str(tree[0]) 168 | if sig_name == self.data: 169 | bit = tree[1] 170 | address, map_lsb = self.get_address(funcdict) 171 | return address, bit 172 | 173 | def get_address(self, funcdict): 174 | return_val = -1 175 | for key, verb in funcdict.items(): 176 | if isinstance(verb, DFPartselect): 177 | signal = str(verb.var) 178 | map_lsb = verb.lsb.value 179 | else: 180 | signal = str(verb) 181 | map_lsb = 0 182 | if signal == self.data: 183 | if return_val == -1: 184 | return_val = key[-1].nextnodes[1].value 185 | else: 186 | assert return_val == key[-1].nextnodes[1].value 187 | return return_val, map_lsb 188 | 189 | def check_new_reg(self, reg_name, term_lsb, trees, funcdict, map_bit): 190 | sig_names = set([str(tree[0]) for tree in trees]) 191 | if self.finger_print_signals.issubset(sig_names): 192 | address, bit = self.get_map_info(trees, funcdict) 193 | if address != -1: 194 | self._add_new_reg(reg_name, map_bit, address, bit) 195 | 196 | class ReadMap(WriteMap): 197 | def __init__(self, flag, address, data): 198 | WriteMap.__init__(self, flag, address, data) 199 | self.this_map_name = '\nRead Map\n' 200 | 201 | def check_new_reg(self, reg_name, term_lsb, trees, funcdict, map_bit): 202 | sig_names = set([str(tree[0]) for tree in trees]) | set([reg_name]) 203 | if self.finger_print_signals.issubset(sig_names): 204 | self.get_map_info(trees, funcdict, map_bit) 205 | 206 | def get_map_info(self, trees, funcdict, map_bit): 207 | def is_data_sig(sig_name, verb): 208 | if isinstance(verb, DFPartselect): 209 | return sig_name == str(verb.var) 210 | elif hasattr(verb, 'nextnodes'): 211 | return sig_name in str(verb.nextnodes) 212 | else: 213 | return sig_name == str(verb) 214 | 215 | for key, verb in funcdict.items(): 216 | for ope in key: 217 | nextnodes_str = [str(nextnode) for nextnode in ope.nextnodes] 218 | if str(ope) == 'Eq' and self.address in nextnodes_str: 219 | address = ope.nextnodes[1].value if str(ope.nextnodes[0]) == self.address else ope.nextnodes[0].value 220 | for tree in trees: 221 | sig_name = str(tree[0]) 222 | sig_bit = tree[1] 223 | if is_data_sig(sig_name, verb): 224 | self._add_new_reg(sig_name, sig_bit, address, map_bit) 225 | 226 | 227 | if __name__ == '__main__': 228 | ranalyzer = RegMapAnalyzer("../testcode/regmap_split.v", "../testcode/setup.txt") 229 | ranalyzer.getRegMaps() 230 | -------------------------------------------------------------------------------- /pyverilog_toolbox/verify_tool/unreferenced_finder.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # unreferenced_finder.py 3 | # 4 | # 5 | # Copyright (C) 2015, Ryosuke Fukatani 6 | # License: Apache 2.0 7 | #------------------------------------------------------------------------------- 8 | 9 | import sys 10 | import os 11 | 12 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 13 | 14 | from pyverilog.dataflow.dataflow import * 15 | from pyverilog_toolbox.verify_tool.dataflow_facade import dataflow_facade, out_as_html 16 | from pyverilog_toolbox.verify_tool.bindlibrary import eval_value 17 | 18 | class UnreferencedFinder(dataflow_facade): 19 | 20 | def decorate_html(html_name): 21 | temp_html = open('temp.html', 'r') 22 | out_html = open(html_name, 'w') 23 | for line in temp_html: 24 | out_html.write(line + '
') 25 | temp_html.close() 26 | out_html.close() 27 | 28 | @out_as_html(decorate_html) 29 | def search_unreferenced(self): 30 | """[FUNCTIONS] 31 | search input/reg/wire which not referenced any other output/reg/wire. 32 | """ 33 | signals = [] 34 | for tv, tk in self.binds.walk_signal(): 35 | #Exclude parameter and function. 36 | if not set(['Input', 'Reg', 'Wire']) & tv.termtype: continue 37 | if 'Output' in tv.termtype: continue #because referenced as output. 38 | signals.append(str(tk)) 39 | 40 | for tv, tk, bvi, bit, term_lsb in self.binds.walk_reg_each_bit(): 41 | target_tree = self.makeTree(tk) 42 | trees = self.binds.extract_all_dfxxx(target_tree, set([]), bit - tv.lsb.eval(), DFTerminal) 43 | trees.add((bvi.getClockName(), bvi.getClockBit())) 44 | trees.add((bvi.getResetName(), bvi.getResetBit())) 45 | for tree, bit in trees: 46 | if str(tree) in signals: 47 | signals.remove(str(tree)) 48 | if signals: 49 | print("finded unreferenced variables: " + str(signals)) 50 | else: 51 | print("There isn't unreferenced variables.") 52 | return signals 53 | 54 | @out_as_html(decorate_html) 55 | def search_floating(self): 56 | floating_signals = [] 57 | for tv, tk in self.binds.walk_signal(): 58 | if not set(['Reg', 'Wire']) & tv.termtype: continue 59 | if not tk in self.binddict.keys(): 60 | floating_signals.append(str(tk)) 61 | else: 62 | term_bits = set([i for i in range(eval_value(tv.lsb), eval_value(tv.msb) + 1)]) 63 | for bind in self.binddict[tk]: 64 | bind_bits = set([ i for i in range(eval_value(bind.lsb), eval_value(bind.msb) + 1)]) 65 | term_bits = term_bits - (term_bits & bind_bits) #delete if binded 66 | if not term_bits: continue 67 | for float_bit in term_bits: 68 | floating_signals.append(str(tk) + '[' + str(float_bit) + ']') 69 | 70 | if floating_signals: 71 | print("floating nodes: " + str(floating_signals)) 72 | else: 73 | print("There isn't floating nodes.") 74 | return floating_signals 75 | 76 | if __name__ == '__main__': 77 | u_finder = UnreferencedFinder("../testcode/floating.v") 78 | u_finder.search_unreferenced() 79 | u_finder.search_floating() 80 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=1 3 | 4 | [egg_info] 5 | tag_svn_revision=false -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | import io 4 | import os 5 | import re 6 | 7 | version = '0.0.6' 8 | 9 | CURRENT_DIR = os.path.abspath(os.path.dirname(__file__)) 10 | 11 | def read(filename): 12 | return io.open(os.path.join(CURRENT_DIR, filename), encoding='utf-8').read() 13 | 14 | setup(name='pyverilog_toolbox', 15 | version=version, 16 | description='Pyverilog-based verification/design tools', 17 | keywords = 'Verilog, Register Map, code clone, code metrics', 18 | author='Ryosuke Fukatani', 19 | author_email='nannyakannya@gmail.com', 20 | url='https://github.com/fukatani/Pyverilog_toolbox', 21 | license="Apache License 2.0", 22 | packages=find_packages(), 23 | package_data={ 'pyverilog_toolbox' : ['testcode/*'], }, 24 | long_description=read('Readme.rst'), 25 | ) 26 | 27 | --------------------------------------------------------------------------------