├── .gitignore ├── COPYING ├── MANIFEST.in ├── README.rst ├── crcgen ├── crcgen_test.py ├── libcrcgen ├── __init__.py ├── __main__.py ├── generator.py ├── generator_test.py ├── main.py ├── parameters.py ├── reference.py ├── util.py └── version.py ├── maintenance └── makerelease.sh └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /MANIFEST 3 | __pycache__ 4 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include COPYING 2 | include README.html 3 | include README.rst 4 | include crcgen_test.py 5 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | CRC algorithm HDL code generator (VHDL, Verilog, MyHDL) 2 | ======================================================= 3 | 4 | `Homepage `_ 5 | 6 | `Git repository `_ 7 | 8 | `Github repository `_ 9 | 10 | This tool generates VHDL, Verilog or MyHDL code for use in FPGAs to calculate CRC (Cyclic Redundancy Check) checksums. 11 | 12 | The generated HDL code is synthesizable and combinatorial. That means the calculation runs in one clock cycle on an FPGA. 13 | 14 | Any combination of CRC algorithm parameters and polynomial coefficients can be selected. 15 | 16 | 17 | Example usage 18 | ============= 19 | 20 | Display all options: 21 | 22 | .. code:: sh 23 | 24 | crcgen -h 25 | 26 | 27 | Generate Verilog code for CRC-32: 28 | 29 | .. code:: sh 30 | 31 | crcgen -a CRC-32 -v 32 | 33 | 34 | Generate VHDL code for CRC-32: 35 | 36 | .. code:: sh 37 | 38 | crcgen -a CRC-32 -V 39 | 40 | 41 | Generate Verilog code for a custom non-standard CRC or any standard algorithm that's not included in crcgen's -a list: 42 | 43 | .. code:: sh 44 | 45 | crcgen -P "x^8 + x^7 + x^5 + x^4 + x^2 + x + 1" -B16 -R -v 46 | 47 | 48 | Online crcgen 49 | ============= 50 | 51 | An easy to use online version of crcgen that can be used without installing or downloading anything to your machine is available here: 52 | 53 | `Online crcgen `_ 54 | 55 | 56 | License of the generated HDL code 57 | ================================= 58 | 59 | The generated code is Public Domain. 60 | 61 | Permission to use, copy, modify, and/or distribute this software for any 62 | purpose with or without fee is hereby granted. 63 | 64 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 65 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 66 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 67 | SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER 68 | RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 69 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 70 | USE OR PERFORMANCE OF THIS SOFTWARE. 71 | 72 | 73 | License of the generator 74 | ======================== 75 | 76 | Copyright (c) 2019-2023 Michael Büsch 77 | 78 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 79 | 80 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 81 | 82 | You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 83 | -------------------------------------------------------------------------------- /crcgen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # vim: ts=8 sw=8 noexpandtab 3 | # 4 | # CRC code generator 5 | # 6 | # Copyright (c) 2019-2023 Michael Büsch 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License along 19 | # with this program; if not, write to the Free Software Foundation, Inc., 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | # 22 | 23 | if __name__ == "__main__": 24 | from libcrcgen.main import main 25 | import sys 26 | sys.exit(main()) 27 | -------------------------------------------------------------------------------- /crcgen_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Test of CRC generator. 4 | # 5 | # Copyright (C) 2020-2023 Michael Büsch 6 | # 7 | # Some CRC implementations are derived from AVR-libc. 8 | # These copyright notices apply to the AVR-libc parts: 9 | # 10 | # Copyright (c) 2002, 2003, 2004 Marek Michalkiewicz 11 | # Copyright (c) 2005, 2007 Joerg Wunsch 12 | # Copyright (c) 2013 Dave Hylands 13 | # Copyright (c) 2013 Frederic Nadeau 14 | # All rights reserved. 15 | # 16 | # 17 | # Redistribution and use in source and binary forms, with or without 18 | # modification, are permitted provided that the following conditions are met: 19 | # 20 | # * Redistributions of source code must retain the above copyright 21 | # notice, this list of conditions and the following disclaimer. 22 | # 23 | # * Redistributions in binary form must reproduce the above copyright 24 | # notice, this list of conditions and the following disclaimer in 25 | # the documentation and/or other materials provided with the 26 | # distribution. 27 | # 28 | # * Neither the name of the copyright holders nor the names of 29 | # contributors may be used to endorse or promote products derived 30 | # from this software without specific prior written permission. 31 | # 32 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 33 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 36 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 37 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 38 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 39 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 40 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 41 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 42 | # POSSIBILITY OF SUCH DAMAGE. 43 | # 44 | 45 | from libcrcgen import * 46 | from libcrcgen.generator_test import * 47 | from libcrcgen.reference import * 48 | from libcrcgen.util import * 49 | import multiprocessing 50 | import random 51 | 52 | 53 | # Derived from CRC-32 version 2.0.0 by Craig Bruce, 2006-04-29. (Public Domain): 54 | def crc32(crc, data): 55 | crcTable = ( 56 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 57 | 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 58 | 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 59 | 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 60 | 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 61 | 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 62 | 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 63 | 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 64 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 65 | 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 66 | 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 67 | 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 68 | 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 69 | 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 70 | 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 71 | 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 72 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 73 | 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 74 | 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 75 | 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 76 | 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 77 | 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 78 | 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 79 | 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 80 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 81 | 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 82 | 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 83 | 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 84 | 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 85 | 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 86 | 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 87 | 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 88 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 89 | 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 90 | 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 91 | 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 92 | 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, 93 | ) 94 | return (crc >> 8) ^ crcTable[(crc ^ data) & 0xFF] 95 | 96 | # Derived from AVR-libc: 97 | def crc16(crc, data): 98 | crc ^= data 99 | for i in range(8): 100 | if crc & 1: 101 | crc = (crc >> 1) ^ 0xA001 102 | else: 103 | crc = (crc >> 1) 104 | return crc 105 | 106 | # Derived from AVR-libc: 107 | def crc16_ccitt(crc, data): 108 | data ^= crc & 0xFF 109 | data = (data ^ (data << 4)) & 0xFF 110 | return ((((data << 8) & 0xFFFF) | (crc >> 8)) ^ 111 | (data >> 4) ^ 112 | ((data << 3) & 0xFFFF)) 113 | 114 | def crc16_ccitt_reversed(crc, data): 115 | return bitreverse(crc16_ccitt(bitreverse(crc, 16), 116 | bitreverse(data, 8)), 117 | 16) 118 | 119 | # Derived from AVR-libc: 120 | def crc16_xmodem(crc, data): 121 | crc ^= (data << 8) 122 | for i in range(8): 123 | if crc & 0x8000: 124 | crc = ((crc << 1) ^ 0x1021) & 0xFFFF 125 | else: 126 | crc = (crc << 1) & 0xFFFF 127 | return crc 128 | 129 | # Derived from AVR-libc: 130 | def crc8_ibutton(crc, data): 131 | crc ^= data 132 | for i in range(8): 133 | if crc & 1: 134 | crc = (crc >> 1) ^ 0x8C 135 | else: 136 | crc = (crc >> 1) 137 | return crc 138 | 139 | # Derived from AVR-libc: 140 | def crc8_ccitt(crc, data): 141 | crc ^= data 142 | for i in range(8): 143 | if crc & 0x80: 144 | crc = ((crc << 1) ^ 0x07) & 0xFF 145 | else: 146 | crc = (crc << 1) & 0xFF 147 | return crc 148 | 149 | def crc6_itu(crc, data): 150 | for i in range(8): 151 | crc ^= (data & 0x80) >> 2 152 | data <<= 1 153 | if crc & 0x20: 154 | crc = ((crc << 1) ^ 0x03) & 0x3F 155 | else: 156 | crc = (crc << 1) & 0x3F 157 | return crc 158 | 159 | def crcRange(nrBits): 160 | rng = random.Random() 161 | rng.seed(42) 162 | mask = (1 << nrBits) - 1 163 | for i in range(0x300): 164 | if i == 0: 165 | crc = 0 166 | elif i == 1: 167 | crc = mask 168 | else: 169 | crc = rng.randint(1, mask - 1) 170 | yield crc 171 | 172 | def dataRange(): 173 | yield from (0x00, 0xAA, 0x55, 0xFF, 174 | 0x3E, 0x92, 0x0A, 0x7D, 0x4E, 0x07, 0x23, 0xDD, 175 | 0x4C, 0xE4, 0x1E, 0x8B, 0x5C, 0xD8, 0x1F, 0x74) 176 | 177 | def compareReferenceImpl(name, crcFunc): 178 | print("Testing %s..." % name) 179 | crcParameters = CRC_PARAMETERS[name] 180 | for crc in crcRange(crcParameters["nrBits"]): 181 | for data in dataRange(): 182 | for i in range(5): # Run a couple of iterations. 183 | a = crcFunc(crc, data) 184 | b = CrcReference.crc(crc=crc, 185 | data=data, 186 | polynomial=crcParameters["polynomial"], 187 | nrCrcBits=crcParameters["nrBits"], 188 | shiftRight=crcParameters["shiftRight"]) 189 | if a != b: 190 | raise Exception("%s test FAILED!" % name) 191 | crc = a 192 | data = (data + 1) & 0xFF 193 | 194 | def checkReferenceReversed(nrCrcBits, polynomial): 195 | print(f"Testing CrcReference reversed ({nrCrcBits=}, P={polynomial:X})...") 196 | for shiftRight in (True, False): 197 | for crc in crcRange(nrCrcBits): 198 | for data in dataRange(): 199 | a = CrcReference.crc( 200 | crc=crc, 201 | data=data, 202 | polynomial=polynomial, 203 | nrCrcBits=nrCrcBits, 204 | shiftRight=shiftRight) 205 | b = bitreverse(CrcReference.crc( 206 | crc=bitreverse(crc, nrCrcBits), 207 | data=bitreverse(data, 8), 208 | polynomial=bitreverse(polynomial, nrCrcBits), 209 | nrCrcBits=nrCrcBits, 210 | shiftRight=not shiftRight), 211 | nrCrcBits) 212 | if a != b: 213 | raise Exception("CrcReference reversed test " 214 | "FAILED! (nrCrcBits=%d, P=%X)" % ( 215 | nrCrcBits, polynomial)) 216 | 217 | def checkReferenceNrDataBits(nrCrcBits, polynomial): 218 | print(f"Testing CrcReference with different data word length " 219 | f"({nrCrcBits=}, P={polynomial:X})...") 220 | data = bytes(dataRange()) 221 | for littleEndian in (False, True): 222 | refCrc = CrcReference.crcBlock(crc=0, 223 | data=data, 224 | polynomial=polynomial, 225 | nrCrcBits=nrCrcBits, 226 | nrDataBits=8, 227 | shiftRight=littleEndian) 228 | 229 | crc_data16 = 0 230 | for i in range(0, len(data), 2): 231 | if littleEndian: 232 | word = data[i] | (data[i + 1] << 8) 233 | else: 234 | word = data[i + 1] | (data[i] << 8) 235 | crc_data16 = CrcReference.crc( 236 | crc=crc_data16, 237 | data=word, 238 | polynomial=polynomial, 239 | nrCrcBits=nrCrcBits, 240 | nrDataBits=16, 241 | shiftRight=littleEndian) 242 | if refCrc != crc_data16: 243 | raise Exception(f"CrcRefernce 16 bit word test FAILED! " 244 | f"({nrCrcBits=}, P={polynomial:X})") 245 | 246 | crc_data32 = 0 247 | for i in range(0, len(data), 4): 248 | if littleEndian: 249 | word = (data[i] | (data[i + 1] << 8) | 250 | (data[i + 2] << 16) | (data[i + 3] << 24)) 251 | else: 252 | word = (data[i + 3] | (data[i + 2] << 8) | 253 | (data[i + 1] << 16) | (data[i] << 24)) 254 | crc_data32 = CrcReference.crc( 255 | crc=crc_data32, 256 | data=word, 257 | polynomial=polynomial, 258 | nrCrcBits=nrCrcBits, 259 | nrDataBits=32, 260 | shiftRight=littleEndian) 261 | if refCrc != crc_data32: 262 | raise Exception(f"CrcRefernce 32 bit word test FAILED! " 263 | f"({nrCrcBits=}, P={polynomial:X})") 264 | 265 | def compareGeneratedImpl(optimize, alg, crcParameters, quick): 266 | if quick == "quick": 267 | dataBitsRange = (8, 16) 268 | else: 269 | dataBitsRange = (8, 16, 24, 32, 33, 1) 270 | for nrDataBits in dataBitsRange: 271 | gen = CrcGenTest(P=crcParameters["polynomial"], 272 | nrCrcBits=crcParameters["nrBits"], 273 | nrDataBits=nrDataBits, 274 | shiftRight=crcParameters["shiftRight"], 275 | optimize=optimize) 276 | gen.runTests(name=alg, extra=("-O=%d" % optimize)) 277 | 278 | if __name__ == "__main__": 279 | assert bitreverse(0xE0, 8) == 0x07 280 | assert bitreverse(0x8408, 16) == 0x1021 281 | assert bitreverse(0xEDB88320, 32) == 0x04C11DB7 282 | 283 | print("*** Testing polynomial coefficient conversion ***") 284 | for poly, polyString, nrBits, shiftRight in ( 285 | (0xC96C5795D7870F42, 286 | "x^64 + x^62 + x^57 + x^55 + x^54 + x^53 + x^52 + x^47 + " 287 | "x^46 + x^45 + x^40 + x^39 + x^38 + x^37 + x^35 + x^33 + " 288 | "x^32 + x^31 + x^29 + x^27 + x^24 + x^23 + x^22 + x^21 + " 289 | "x^19 + x^17 + x^13 + x^12 + x^10 + x^9 + x^7 + x^4 + x + 1", 290 | 64, True), 291 | (0xEDB88320, 292 | "x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + " 293 | "x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1", 294 | 32, True), 295 | (0xa001, 296 | "x^16 + x^15 + x^2 + 1", 297 | 16, True), 298 | (0x1021, 299 | "x^16 + x^12 + x^5 + 1", 300 | 16, False), 301 | (0x8408, 302 | "x^16 + x^12 + x^5 + 1", 303 | 16, True), 304 | (0x8C, 305 | "x^8 + x^5 + x^4 + 1", 306 | 8, True), 307 | (0xE0, 308 | "x^8 + x^2 + x + 1", 309 | 8, True), 310 | (0x07, 311 | "x^8 + x^2 + x + 1", 312 | 8, False), 313 | (0x03, 314 | "x^3 + x + 1", 315 | 3, False), 316 | (0x01, 317 | "x^3 + 1", 318 | 3, False), 319 | (0x02, 320 | "x^3 + x", 321 | 3, False), 322 | ): 323 | print("Testing %s..." % polyString) 324 | if poly2int(polyString, nrBits, shiftRight) != poly: 325 | raise Exception("Polynomial '%s' != 0x%X" % (polyString, poly)) 326 | if int2poly(poly, nrBits, shiftRight) != polyString: 327 | raise Exception("Polynomial 0x%X != '%s'" % (poly, polyString)) 328 | 329 | print("*** Comparing reference implementation to itself reversed ***") 330 | params = ( 331 | (32, 0xEDB88320), 332 | (16, 0xA001), 333 | (16, 0x1021), 334 | (8, 0x07), 335 | (8, 0x8C), 336 | ) 337 | with multiprocessing.Pool() as p: 338 | p.starmap(checkReferenceReversed, params) 339 | 340 | print("*** Comparing reference implementation to itself with different data word length ***") 341 | params = ( 342 | (32, 0xEDB88320), 343 | (16, 0xA001), 344 | (16, 0x1021), 345 | (8, 0x07), 346 | (8, 0x8C), 347 | ) 348 | with multiprocessing.Pool() as p: 349 | p.starmap(checkReferenceNrDataBits, params) 350 | 351 | print("*** Comparing reference implementation to discrete implementations ***") 352 | params = ( 353 | ("CRC-32", crc32), 354 | ("CRC-16", crc16), 355 | ("CRC-16-CCITT", crc16_ccitt_reversed), 356 | ("CRC-16-CCITT", crc16_xmodem), 357 | ("CRC-8-CCITT", crc8_ccitt), 358 | ("CRC-8-IBUTTON", crc8_ibutton), 359 | ("CRC-6-ITU", crc6_itu), 360 | ) 361 | with multiprocessing.Pool() as p: 362 | p.starmap(compareReferenceImpl, params) 363 | 364 | def makeParams(allOptPermut, quick="not_quick"): 365 | if allOptPermut: 366 | for optimize in reversed(range(1 << CrcGen.OPT_ALL.bit_length())): 367 | yield optimize, "CRC-16", CRC_PARAMETERS["CRC-16"], quick 368 | else: 369 | for alg, crcParameters in CRC_PARAMETERS.items(): 370 | yield CrcGen.OPT_ALL, alg, crcParameters, quick 371 | print("*** Comparing generated CRC functions " 372 | "to reference implementation (with all optimization option permutations)***") 373 | with multiprocessing.Pool() as p: 374 | p.starmap(compareGeneratedImpl, tuple(makeParams(allOptPermut=True, quick="quick"))) 375 | print("*** Comparing all generated CRC functions " 376 | "to reference implementation (with full optimization)***") 377 | with multiprocessing.Pool() as p: 378 | p.starmap(compareGeneratedImpl, tuple(makeParams(allOptPermut=False))) 379 | -------------------------------------------------------------------------------- /libcrcgen/__init__.py: -------------------------------------------------------------------------------- 1 | from .generator import CrcGen, CrcGenError 2 | from .parameters import CRC_PARAMETERS 3 | from .util import int2poly, poly2int 4 | -------------------------------------------------------------------------------- /libcrcgen/__main__.py: -------------------------------------------------------------------------------- 1 | # vim: ts=8 sw=8 noexpandtab 2 | # 3 | # CRC code generator 4 | # 5 | # Copyright (c) 2019-2023 Michael Büsch 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | # 21 | 22 | if __name__ == "__main__": 23 | from libcrcgen.main import main 24 | import sys 25 | sys.exit(main()) 26 | -------------------------------------------------------------------------------- /libcrcgen/generator.py: -------------------------------------------------------------------------------- 1 | # vim: ts=8 sw=8 noexpandtab 2 | # 3 | # CRC code generator 4 | # 5 | # Copyright (c) 2020-2023 Michael Büsch 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | # 21 | 22 | from dataclasses import dataclass 23 | from libcrcgen.util import int2poly 24 | 25 | __all__ = [ 26 | "CrcGen", 27 | "CrcGenError", 28 | ] 29 | 30 | @dataclass(frozen=True) 31 | class AbstractBit: 32 | def flatten(self): 33 | return [self, ] 34 | 35 | def optimize(self, sortLex): 36 | pass 37 | 38 | @dataclass(frozen=True) 39 | class Bit(AbstractBit): 40 | name: str 41 | index: int 42 | 43 | def gen_python(self, level=0): 44 | return f"{self.name}[{self.index}]" 45 | 46 | def gen_c(self, level=0): 47 | return f"b({self.name}, {self.index})" 48 | 49 | def gen_verilog(self, level=0): 50 | return f"{self.name}[{self.index}]" 51 | 52 | def gen_vhdl(self, level=0): 53 | return f"{self.name}({self.index})" 54 | 55 | def gen_myhdl(self, level=0): 56 | return f"{self.name}[{self.index}]" 57 | 58 | def sortKey(self): 59 | return f"{self.name}_{self.index:07}" 60 | 61 | @dataclass(frozen=True) 62 | class ConstBit(AbstractBit): 63 | value: int 64 | 65 | def gen_python(self, level=0): 66 | return "1" if self.value else "0" 67 | 68 | def gen_c(self, level=0): 69 | return "1u" if self.value else "0u" 70 | 71 | def gen_verilog(self, level=0): 72 | return "1'b1" if self.value else "1'b0" 73 | 74 | def gen_vhdl(self, level=0): 75 | return 'b"1"' if self.value else 'b"0"' 76 | 77 | def gen_myhdl(self, level=0): 78 | return "1" if self.value else "0" 79 | 80 | def sortKey(self): 81 | return "1" if self.value else "0" 82 | 83 | class XOR: 84 | __slots__ = ( 85 | "__items", 86 | ) 87 | 88 | def __init__(self, *items): 89 | self.__items = items 90 | 91 | def flatten(self): 92 | newItems = [ item 93 | for subItem in self.__items 94 | for item in subItem.flatten() ] 95 | self.__items = newItems 96 | return newItems 97 | 98 | def optimize(self, sortLex): 99 | newItems = [] 100 | haveBits = {} 101 | constOnes = [] 102 | for item in self.__items: 103 | if isinstance(item, Bit): 104 | # Store bit for even/uneven count analysis. 105 | haveBits[item] = haveBits.get(item, 0) + 1 106 | elif isinstance(item, ConstBit): 107 | # Constant 0 does not change the XOR result. Remove it. 108 | if item.value: 109 | constOnes.append(item) 110 | else: 111 | # This is something else. Keep it. 112 | newItems.append(item) 113 | # An even count of the same bit is equal to zero. Remove them. 114 | # An uneven count of the same bit is equal to one of them. Keep one. 115 | newItems.extend(bit for bit, count in haveBits.items() 116 | if count % 2) 117 | # If there's an uneven amount of constant ones, keep one of them. 118 | if len(constOnes) % 2: 119 | newItems.append(constOnes[0]) 120 | if sortLex: 121 | # XOR can be arranged in any order. 122 | newItems.sort(key=lambda item: item.sortKey()) 123 | if not newItems: 124 | # All items have been optimized out. 125 | # This term shall be zero. 126 | newItems.append(ConstBit(0)) 127 | self.__items = newItems 128 | 129 | def gen_python(self, level=0): 130 | return self.__gen("(", ")", level, " ^ ", lambda item: item.gen_python(level + 1)) 131 | 132 | def gen_c(self, level=0): 133 | return self.__gen("(", ")", level, " ^ ", lambda item: item.gen_c(level + 1)) 134 | 135 | def gen_verilog(self, level=0): 136 | return self.__gen("(", ")", level, " ^ ", lambda item: item.gen_verilog(level + 1)) 137 | 138 | def gen_vhdl(self, level=0): 139 | return self.__gen("(", ")", level, " xor ", lambda item: item.gen_vhdl(level + 1)) 140 | 141 | def gen_myhdl(self, level=0): 142 | return self.__gen("(", ")", level, " ^ ", lambda item: item.gen_myhdl(level + 1)) 143 | 144 | def __gen(self, prefix, suffix, level, oper, itemGen): 145 | assert self.__items, "Empty XOR." 146 | if level == 0: 147 | prefix = suffix = "" 148 | return prefix + (oper.join(itemGen(item) for item in self.__items)) + suffix 149 | 150 | def sortKey(self): 151 | return "__".join(item.sortKey() for item in self.__items) 152 | 153 | class Word: 154 | __slots__ = ( 155 | "__items", 156 | ) 157 | 158 | def __init__(self, *items): 159 | # items must be LSB first. 160 | self.__items = list(items) 161 | 162 | def __getitem__(self, index): 163 | return self.__items[index] 164 | 165 | def flatten(self): 166 | for item in self.__items: 167 | item.flatten() 168 | 169 | def optimize(self, sortLex): 170 | for item in self.__items: 171 | item.optimize(sortLex) 172 | 173 | class CrcGenError(Exception): 174 | pass 175 | 176 | class CrcGen: 177 | """Combinatorial CRC algorithm generator. 178 | """ 179 | 180 | OPT_FLATTEN = 1 << 0 # Flatten the bit operation tree 181 | OPT_ELIMINATE = 1 << 1 # Eliminate redundant operations 182 | OPT_LEX = 1 << 2 # Sort the operands in lexicographical order where possible 183 | 184 | OPT_NONE = 0 185 | OPT_ALL = OPT_FLATTEN | OPT_ELIMINATE | OPT_LEX 186 | 187 | def __init__(self, 188 | P, 189 | nrCrcBits, 190 | nrDataBits=8, 191 | shiftRight=False, 192 | optimize=OPT_ALL): 193 | self._P = P 194 | self._nrCrcBits = nrCrcBits 195 | self._nrDataBits = nrDataBits 196 | self._shiftRight = shiftRight 197 | self._optimize = optimize 198 | 199 | def __gen(self, dataVarName, crcVarName): 200 | nrCrcBits = self._nrCrcBits 201 | nrDataBits = self._nrDataBits 202 | P = self._P 203 | if nrCrcBits < 1 or nrDataBits < 1: 204 | raise CrcGenError("Invalid number of bits.") 205 | 206 | # Construct the function input data word. 207 | inData = Word(*( 208 | Bit(dataVarName, i) 209 | for i in range(nrDataBits) 210 | )) 211 | 212 | # Construct the function input CRC word. 213 | inCrc = Word(*( 214 | Bit(crcVarName, i) 215 | for i in range(nrCrcBits) 216 | )) 217 | 218 | # Helper function to XOR a polynomial bit with the data bit 'dataBit', 219 | # if the decision bit 'queryBit' is set. 220 | # This is done reversed, because the polynomial is constant. 221 | def xor_P(dataBit, queryBit, bitNr): 222 | if (P >> bitNr) & 1: 223 | return XOR(dataBit, queryBit) 224 | return dataBit 225 | 226 | # Helper function to optimize the algorithm. 227 | # This removes unnecessary operations. 228 | def optimize(word, sort=False): 229 | if self._optimize & self.OPT_FLATTEN: 230 | word.flatten() 231 | if self._optimize & self.OPT_ELIMINATE: 232 | word.optimize(sortLex=(sort and (self._optimize & self.OPT_LEX))) 233 | return word 234 | 235 | # Run the shift register for each input data bit. 236 | word = inCrc 237 | if self._shiftRight: 238 | for i in range(nrDataBits): 239 | # Run the shift register once. 240 | bits = [] 241 | for j in range(nrCrcBits): 242 | # Shift to the right: j + 1 243 | stateBit = word[j + 1] if j < nrCrcBits - 1 else ConstBit(0) 244 | # XOR the input bit with LSB. 245 | queryBit = XOR(word[0], inData[i]) 246 | # XOR the polynomial coefficient, if the query bit is set. 247 | stateBit = xor_P(stateBit, queryBit, j) 248 | bits.append(stateBit) 249 | word = optimize(Word(*bits)) 250 | else: 251 | for i in reversed(range(nrDataBits)): 252 | # Run the shift register once. 253 | bits = [] 254 | for j in range(nrCrcBits): 255 | # Shift to the left: j - 1 256 | stateBit = word[j - 1] if j > 0 else ConstBit(0) 257 | # XOR the input bit with MSB. 258 | queryBit = XOR(word[nrCrcBits - 1], inData[i]) 259 | # XOR the polynomial coefficient, if the query bit is set. 260 | stateBit = xor_P(stateBit, queryBit, j) 261 | bits.append(stateBit) 262 | word = optimize(Word(*bits)) 263 | word = optimize(word, sort=True) 264 | 265 | return word 266 | 267 | def __header(self, language): 268 | return f"""\ 269 | THIS IS GENERATED {language.upper()} CODE. 270 | https://bues.ch/h/crcgen 271 | 272 | This code is Public Domain. 273 | Permission to use, copy, modify, and/or distribute this software for any 274 | purpose with or without fee is hereby granted. 275 | 276 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 277 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 278 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 279 | SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER 280 | RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 281 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 282 | USE OR PERFORMANCE OF THIS SOFTWARE.""" 283 | 284 | def __algDescription(self): 285 | pstr = int2poly(self._P, self._nrCrcBits, self._shiftRight) 286 | shift = "right (little endian)" if self._shiftRight else "left (big endian)" 287 | return (f"CRC polynomial coefficients: {pstr}\n" 288 | f" 0x{self._P:X} (hex)\n" 289 | f"CRC width: {self._nrCrcBits} bits\n" 290 | f"CRC shift direction: {shift}\n" 291 | f"Input word width: {self._nrDataBits} bits\n") 292 | 293 | def genPython(self, 294 | funcName="crc", 295 | crcVarName="crc", 296 | dataVarName="data"): 297 | word = self.__gen(dataVarName, crcVarName) 298 | ret = [] 299 | ret.append("# vim: ts=4 sw=4 expandtab") 300 | ret.append("") 301 | ret.extend("# " + l for l in self.__header("Python").splitlines()) 302 | ret.append("") 303 | ret.extend("# " + l for l in self.__algDescription().splitlines()) 304 | ret.append("") 305 | ret.append(f"def {funcName}({crcVarName}, {dataVarName}):") 306 | ret.append(f" class bitwrapper:") 307 | ret.append(f" def __init__(self, x):") 308 | ret.append(f" self.x = x") 309 | ret.append(f" def __getitem__(self, i):") 310 | ret.append(f" return (self.x >> i) & 1") 311 | ret.append(f" def __setitem__(self, i, x):") 312 | ret.append(f" self.x = (self.x | (1 << i)) if x else (self.x & ~(1 << i))") 313 | ret.append(f" {crcVarName} = bitwrapper({crcVarName})") 314 | ret.append(f" {dataVarName} = bitwrapper({dataVarName})") 315 | ret.append(f" ret = bitwrapper(0)") 316 | for i, bit in enumerate(word): 317 | ret.append(f" ret[{i}] = {bit.gen_python()}") 318 | ret.append(" return ret.x") 319 | return "\n".join(ret) 320 | 321 | def genVerilog(self, 322 | genFunction=True, 323 | name="crc", 324 | inDataName="inData", 325 | inCrcName="inCrc", 326 | outCrcName="outCrc"): 327 | word = self.__gen(inDataName, inCrcName) 328 | ret = [] 329 | ret.append("// vim: ts=4 sw=4 expandtab") 330 | ret.append("") 331 | ret.extend("// " + l for l in self.__header("Verilog").splitlines()) 332 | ret.append("") 333 | if not genFunction: 334 | ret.append(f"`ifndef {name.upper()}_V_") 335 | ret.append(f"`define {name.upper()}_V_") 336 | ret.append("") 337 | ret.extend("// " + l for l in self.__algDescription().splitlines()) 338 | ret.append("") 339 | if genFunction: 340 | ret.append(f"function automatic [{self._nrCrcBits - 1}:0] {name};") 341 | else: 342 | ret.append(f"module {name} (") 343 | end = ";" if genFunction else "," 344 | ret.append(f" input [{self._nrCrcBits - 1}:0] {inCrcName}{end}") 345 | ret.append(f" input [{self._nrDataBits - 1}:0] {inDataName}{end}") 346 | if genFunction: 347 | ret.append("begin") 348 | else: 349 | ret.append(f" output [{self._nrCrcBits - 1}:0] {outCrcName}") 350 | ret.append(");") 351 | for i, bit in enumerate(word): 352 | assign = "" if genFunction else "assign " 353 | assignName = name if genFunction else outCrcName 354 | ret.append(f" {assign}{assignName}[{i}] = {bit.gen_verilog()};") 355 | if genFunction: 356 | ret.append("end") 357 | ret.append("endfunction") 358 | else: 359 | ret.append("endmodule") 360 | ret.append("") 361 | ret.append(f"`endif // {name.upper()}_V_") 362 | return "\n".join(ret) 363 | 364 | def genVHDL(self, 365 | name="crc", 366 | inDataName="inData", 367 | inCrcName="inCrc", 368 | outCrcName="outCrc"): 369 | word = self.__gen(inDataName, inCrcName) 370 | ret = [] 371 | ret.append(f"-- vim: ts=4 sw=4 expandtab") 372 | ret.append(f"") 373 | ret.extend(f"-- " + l for l in self.__header("VHDL").splitlines()) 374 | ret.append(f"") 375 | ret.extend(f"-- " + l for l in self.__algDescription().splitlines()) 376 | ret.append(f"") 377 | ret.append(f"library IEEE;") 378 | ret.append(f"use IEEE.std_logic_1164.all;") 379 | ret.append(f"") 380 | ret.append(f"entity {name} is") 381 | ret.append(f" port (") 382 | ret.append(f" {inCrcName}: in std_logic_vector({self._nrCrcBits - 1} downto 0);") 383 | ret.append(f" {inDataName}: in std_logic_vector({self._nrDataBits - 1} downto 0);") 384 | ret.append(f" {outCrcName}: out std_logic_vector({self._nrCrcBits - 1} downto 0)") 385 | ret.append(f" );") 386 | ret.append(f"end entity {name};") 387 | ret.append(f"") 388 | ret.append(f"architecture Behavioral of {name} is") 389 | ret.append(f"begin") 390 | for i, bit in enumerate(word): 391 | ret.append(f" {outCrcName}({i}) <= {bit.gen_vhdl()};") 392 | ret.append(f"end architecture Behavioral;") 393 | return "\n".join(ret) 394 | 395 | def genMyHDL(self, 396 | blockName="crc", 397 | inDataName="inData", 398 | inCrcName="inCrc", 399 | outCrcName="outCrc"): 400 | word = self.__gen(inDataName, inCrcName) 401 | ret = [] 402 | ret.append("# vim: ts=4 sw=4 expandtab") 403 | ret.append("") 404 | ret.extend("# " + l for l in self.__header("MyHDL").splitlines()) 405 | ret.append("") 406 | ret.extend("# " + l for l in self.__algDescription().splitlines()) 407 | ret.append("") 408 | ret.append("from myhdl import *") 409 | ret.append("") 410 | ret.append("@block") 411 | ret.append(f"def {blockName}({inCrcName}, {inDataName}, {outCrcName}):") 412 | ret.append(" @always_comb") 413 | ret.append(" def logic():") 414 | for i, bit in enumerate(word): 415 | ret.append(f" {outCrcName}.next[{i}] = {bit.gen_myhdl()}") 416 | ret.append(" return logic") 417 | ret.append("") 418 | ret.append("if __name__ == '__main__':") 419 | ret.append(f" instance = {blockName}(") 420 | ret.append(f" {inCrcName}=Signal(intbv(0)[{self._nrCrcBits}:]),") 421 | ret.append(f" {inDataName}=Signal(intbv(0)[{self._nrDataBits}:]),") 422 | ret.append(f" {outCrcName}=Signal(intbv(0)[{self._nrCrcBits}:])") 423 | ret.append(f" )") 424 | ret.append(f" instance.convert(hdl='Verilog')") 425 | ret.append(f" instance.convert(hdl='VHDL')") 426 | return "\n".join(ret) 427 | 428 | def genC(self, 429 | funcName="crc", 430 | crcVarName="crc", 431 | dataVarName="data", 432 | static=False, 433 | inline=False, 434 | declOnly=False, 435 | includeGuards=True, 436 | includes=True): 437 | word = self.__gen(dataVarName, crcVarName) 438 | def makeCType(nrBits, name): 439 | if nrBits <= 8: 440 | cBits = 8 441 | elif nrBits <= 16: 442 | cBits = 16 443 | elif nrBits <= 32: 444 | cBits = 32 445 | elif nrBits <= 64: 446 | cBits = 64 447 | else: 448 | raise CrcGenError("C code generator: " + name + " sizes " 449 | "bigger than 64 bit " 450 | "are not supported.") 451 | return f"uint{cBits}_t" 452 | cCrcType = makeCType(self._nrCrcBits, "CRC") 453 | cDataType = makeCType(self._nrDataBits, "Input data") 454 | ret = [] 455 | ret.append("// vim: ts=4 sw=4 expandtab") 456 | ret.append("") 457 | ret.extend("// " + l for l in self.__header("C").splitlines()) 458 | ret.append("") 459 | if includeGuards: 460 | ret.append(f"#ifndef {funcName.upper()}_H_") 461 | ret.append(f"#define {funcName.upper()}_H_") 462 | if includes: 463 | ret.append("") 464 | ret.append("#include ") 465 | ret.append("") 466 | ret.extend("// " + l for l in self.__algDescription().splitlines()) 467 | ret.append("") 468 | if not declOnly: 469 | ret.append("#ifdef b") 470 | ret.append("# undef b") 471 | ret.append("#endif") 472 | ret.append("#define b(x, b) (((x) >> (b)) & 1u)") 473 | ret.append("") 474 | extern = "extern " if declOnly else "" 475 | static = "static " if static and not declOnly else "" 476 | inline = "inline " if inline and not declOnly else "" 477 | end = ";" if declOnly else "" 478 | ret.append(f"{extern}{static}{inline}{cCrcType} " 479 | f"{funcName}({cCrcType} {crcVarName}, {cDataType} {dataVarName}){end}") 480 | if not declOnly: 481 | ret.append("{") 482 | ret.append(f" {cCrcType} ret;") 483 | for i, bit in enumerate(word): 484 | operator = "|=" if i > 0 else " =" 485 | ret.append(f" ret {operator} ({cCrcType})({bit.gen_c()}) << {i};") 486 | ret.append(" return ret;") 487 | ret.append("}") 488 | ret.append("#undef b") 489 | if includeGuards: 490 | ret.append("") 491 | ret.append(f"#endif /* {funcName.upper()}_H_ */") 492 | return "\n".join(ret) 493 | -------------------------------------------------------------------------------- /libcrcgen/generator_test.py: -------------------------------------------------------------------------------- 1 | # vim: ts=8 sw=8 noexpandtab 2 | # 3 | # CRC code generator 4 | # 5 | # Copyright (c) 2020-2023 Michael Büsch 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | # 21 | 22 | from libcrcgen.generator import * 23 | from libcrcgen.reference import * 24 | from libcrcgen.util import * 25 | 26 | __all__ = [ 27 | "CrcGenTest", 28 | ] 29 | 30 | class CrcGenTest(CrcGen): 31 | def runTests(self, name=None, extra=None): 32 | tmpdir = None 33 | try: 34 | import random 35 | rng = random.Random() 36 | rng.seed(424242) 37 | 38 | print(f"Testing{(' ' + name) if name else ''} " 39 | f"P=0x{self._P:X}, " 40 | f"nrCrcBits={self._nrCrcBits}, " 41 | f"shiftRight={int(bool(self._shiftRight))}, " 42 | f"nrDataBits={self._nrDataBits}" 43 | f"{(', ' + extra) if extra else ''} ...") 44 | 45 | # Generate the CRC function as Python code. 46 | pyCode = self.genPython(funcName="crc_pyimpl") 47 | execEnv = {} 48 | exec(pyCode, execEnv) 49 | crc_pyimpl = execEnv["crc_pyimpl"] 50 | 51 | # Generate the CRC function as C code. 52 | import os, time, importlib, shutil 53 | from cffi import FFI 54 | ffibuilder = FFI() 55 | ffibuilder.set_source("testmod_crcgen", self.genC()) 56 | ffibuilder.cdef(self.genC(declOnly=True, 57 | includeGuards=False, 58 | includes=False)) 59 | tmpdir = f"tmp_{os.getpid()}_{int(time.time() * 1e6)}" 60 | ffibuilder.compile(tmpdir=tmpdir, verbose=False) 61 | testmod_crcgen = importlib.import_module(tmpdir + ".testmod_crcgen") 62 | crc_cimpl = testmod_crcgen.lib.crc 63 | 64 | # Compare the reference implementation to the Python and C code. 65 | crcMask = (1 << self._nrCrcBits) - 1 66 | dataMask = (1 << self._nrDataBits) - 1 67 | for i in range(32): 68 | if i == 0: 69 | crc = 0 70 | elif i == 1: 71 | crc = crcMask 72 | else: 73 | crc = rng.randint(1, crcMask - 1) 74 | for j in range(min(64, dataMask + 1)): 75 | if j == 0: 76 | data = 0 77 | elif j == 1: 78 | data = dataMask 79 | else: 80 | data = rng.randint(1, dataMask - 1) 81 | for k in range(3): 82 | ref = CrcReference.crc( 83 | crc=crc, 84 | data=data, 85 | polynomial=self._P, 86 | nrCrcBits=self._nrCrcBits, 87 | nrDataBits=self._nrDataBits, 88 | shiftRight=self._shiftRight) 89 | py = crc_pyimpl(crc, data) 90 | c = crc_cimpl(crc, data) 91 | if ref != py or ref != c: 92 | raise CrcGenError( 93 | f"Test failed: " 94 | f"P=0x{self._P:X}, " 95 | f"nrCrcBits={self._nrCrcBits}, " 96 | f"shiftRight={int(bool(self._shiftRight))}, " 97 | f"nrDataBits={self._nrDataBits}, " 98 | f"data=0x{data:X}, " 99 | f"ref=0x{ref:X}, " 100 | f"py=0x{py:X}, " 101 | f"c=0x{c:X}") 102 | crc = ref 103 | data = (data + 1) & dataMask 104 | finally: 105 | if tmpdir: 106 | shutil.rmtree(tmpdir, ignore_errors=True) 107 | -------------------------------------------------------------------------------- /libcrcgen/main.py: -------------------------------------------------------------------------------- 1 | # vim: ts=8 sw=8 noexpandtab 2 | # 3 | # CRC code generator 4 | # 5 | # Copyright (c) 2019-2023 Michael Büsch 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | # 21 | 22 | from libcrcgen import CrcGen, CrcGenError, CRC_PARAMETERS, poly2int, int2poly 23 | 24 | import sys 25 | import argparse 26 | 27 | __all__ = [ 28 | "main", 29 | ] 30 | 31 | def poly_convert(p, nr_crc_bits, shift_right): 32 | if nr_crc_bits is None: 33 | raise CrcGenError("-B|--nr-crc-bits is required for -T|--polynomial-convert") 34 | p = p.strip() 35 | try: 36 | # Hex format 37 | if not p.startswith("0x"): 38 | raise ValueError 39 | p = int(p[2:], 16) 40 | p = int2poly(p, nr_crc_bits, shift_right) 41 | print(p) 42 | return 0 43 | except ValueError: 44 | pass 45 | try: 46 | # Decimal format 47 | p = int(p, 10) 48 | p = int2poly(p, nr_crc_bits, shift_right) 49 | print(p) 50 | return 0 51 | except ValueError: 52 | pass 53 | try: 54 | # Polynomial coefficient format 55 | p = poly2int(p, nr_crc_bits, shift_right) 56 | print(f"0x{p:X}") 57 | return 0 58 | except ValueError: 59 | pass 60 | raise CrcGenError("-T|--polynomial-convert: Invalid polynomial") 61 | 62 | def main(): 63 | try: 64 | def argInt(string): 65 | if string.startswith("0x"): 66 | return int(string[2:], 16) 67 | return int(string) 68 | p = argparse.ArgumentParser( 69 | description="CRC algorithm HDL code generator (VHDL, Verilog, MyHDL)" 70 | ) 71 | g = p.add_mutually_exclusive_group(required=True) 72 | g.add_argument("-v", "--verilog-function", action="store_true", 73 | help="Generate Verilog function") 74 | g.add_argument("-m", "--verilog-module", action="store_true", 75 | help="Generate Verilog module") 76 | g.add_argument("-V", "--vhdl", action="store_true", 77 | help="Generate VHDL module") 78 | g.add_argument("-M", "--myhdl", action="store_true", 79 | help="Generate MyHDL block") 80 | g.add_argument("-p", "--python", action="store_true", 81 | help="Generate Python code (mainly useful for testing purposes)") 82 | g.add_argument("-c", "--c", action="store_true", 83 | help="Generate C code (mainly useful for testing purposes)") 84 | g.add_argument("-T", "--polynomial-convert", metavar="POLYNOMIAL", type=str, 85 | help="Convert a polynomial from string to int or vice versa and then exit.") 86 | g.add_argument("--test", action="store_true", 87 | help=argparse.SUPPRESS) 88 | p.add_argument("-a", "--algorithm", type=str, 89 | choices=CRC_PARAMETERS.keys(), default="CRC-32", 90 | help="Select the CRC algorithm. " 91 | "Individual algorithm parameters (e.g. polynomial) can be overridden with the options below.") 92 | p.add_argument("-P", "--polynomial", type=str, 93 | help="Use this CRC polynomial for code generation") 94 | p.add_argument("-B", "--nr-crc-bits", type=argInt, 95 | help="Number of CRC bits.") 96 | p.add_argument("-b", "--nr-data-bits", type=argInt, default="8", 97 | help="Number of input data word bits.") 98 | g = p.add_mutually_exclusive_group() 99 | g.add_argument("-R", "--shift-right", action="store_true", 100 | help="CRC algorithm shift direction: right shift") 101 | g.add_argument("-L", "--shift-left", action="store_true", 102 | help="CRC algorithm shift direction: left shift") 103 | p.add_argument("-n", "--name", type=str, default="crc", 104 | help="Generated function/module name") 105 | p.add_argument("-D", "--data-param", type=str, default="data", 106 | help="Generated function/module data parameter name") 107 | p.add_argument("-C", "--crc-in-param", type=str, default="crcIn", 108 | help="Generated function/module crc input parameter name") 109 | p.add_argument("-o", "--crc-out-param", type=str, default="crcOut", 110 | help="Generated module crc output parameter name") 111 | p.add_argument("-S", "--static", action="store_true", 112 | help="Generate static C function. (only if -c)") 113 | p.add_argument("-I", "--inline", action="store_true", 114 | help="Generate inline C function. (only if -c)") 115 | p.add_argument("-O", "--optimize", type=argInt, default=CrcGen.OPT_ALL, 116 | help=f"Select individual algorithm optimizer steps. " 117 | f"The argument to the -O option can be any sum of the following integers: " 118 | f"-O{CrcGen.OPT_FLATTEN} (Flatten the bit operation tree), " 119 | f"-O{CrcGen.OPT_ELIMINATE} (Eliminate redundant operations), " 120 | f"-O{CrcGen.OPT_LEX} (Sort the operands in lexicographical order where possible). " 121 | f"-O{CrcGen.OPT_NONE} disables all optimizer steps. " 122 | f"If this option is not given, then by default all optimizer steps are enabled (-O{CrcGen.OPT_ALL}).") 123 | args = p.parse_args() 124 | 125 | if (args.nr_crc_bits is not None and 126 | args.nr_crc_bits < 1): 127 | raise CrcGenError("Invalid -B|--nr-crc-bits argument.") 128 | if args.nr_data_bits < 1: 129 | raise CrcGenError("Invalid -b|--nr-data-bits argument.") 130 | 131 | if args.polynomial_convert is not None: 132 | return poly_convert(args.polynomial_convert, 133 | args.nr_crc_bits, 134 | args.shift_right) 135 | 136 | crcParameters = CRC_PARAMETERS[args.algorithm].copy() 137 | if args.nr_crc_bits is not None: 138 | crcParameters["nrBits"] = args.nr_crc_bits 139 | if args.shift_right: 140 | crcParameters["shiftRight"] = True 141 | if args.shift_left: 142 | crcParameters["shiftRight"] = False 143 | if args.polynomial is not None: 144 | crcParameters["polynomial"] = poly2int( 145 | args.polynomial, 146 | crcParameters["nrBits"], 147 | crcParameters["shiftRight"]) 148 | 149 | polynomial = crcParameters["polynomial"] 150 | nrCrcBits = crcParameters["nrBits"] 151 | shiftRight = crcParameters["shiftRight"] 152 | 153 | if polynomial > ((1 << nrCrcBits) - 1): 154 | raise CrcGenError(f"Invalid polynomial. " 155 | f"It is bigger than the CRC width " 156 | f"of (2**{nrCrcBits})-1.") 157 | 158 | if args.test: 159 | from libcrcgen.generator_test import CrcGenTest 160 | CrcGenClass = CrcGenTest 161 | else: 162 | CrcGenClass = CrcGen 163 | 164 | gen = CrcGenClass(P=polynomial, 165 | nrCrcBits=nrCrcBits, 166 | nrDataBits=args.nr_data_bits, 167 | shiftRight=shiftRight, 168 | optimize=args.optimize) 169 | if args.test: 170 | gen.runTests() 171 | else: 172 | if args.python: 173 | print(gen.genPython(funcName=args.name, 174 | crcVarName=args.crc_in_param, 175 | dataVarName=args.data_param)) 176 | elif args.verilog_function: 177 | print(gen.genVerilog(genFunction=True, 178 | name=args.name, 179 | inDataName=args.data_param, 180 | inCrcName=args.crc_in_param, 181 | outCrcName=args.crc_out_param)) 182 | elif args.verilog_module: 183 | print(gen.genVerilog(genFunction=False, 184 | name=args.name, 185 | inDataName=args.data_param, 186 | inCrcName=args.crc_in_param, 187 | outCrcName=args.crc_out_param)) 188 | elif args.vhdl: 189 | print(gen.genVHDL(name=args.name, 190 | inDataName=args.data_param, 191 | inCrcName=args.crc_in_param, 192 | outCrcName=args.crc_out_param)) 193 | elif args.myhdl: 194 | print(gen.genMyHDL(blockName=args.name, 195 | inDataName=args.data_param, 196 | inCrcName=args.crc_in_param, 197 | outCrcName=args.crc_out_param)) 198 | elif args.c: 199 | print(gen.genC(funcName=args.name, 200 | crcVarName=args.crc_in_param, 201 | dataVarName=args.data_param, 202 | static=args.static, 203 | inline=args.inline)) 204 | else: 205 | assert False 206 | return 0 207 | except CrcGenError as e: 208 | print("ERROR: " + str(e), file=sys.stderr) 209 | return 1 210 | -------------------------------------------------------------------------------- /libcrcgen/parameters.py: -------------------------------------------------------------------------------- 1 | # vim: ts=8 sw=8 noexpandtab 2 | # 3 | # CRC code generator 4 | # 5 | # Copyright (c) 2019-2023 Michael Büsch 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | # 21 | 22 | __all__ = [ 23 | "CRC_PARAMETERS", 24 | ] 25 | 26 | CRC_PARAMETERS = { 27 | "CRC-64-ECMA" : { 28 | "polynomial" : 0xC96C5795D7870F42, 29 | "nrBits" : 64, 30 | "shiftRight" : True, 31 | }, 32 | "CRC-64-ISO" : { 33 | "polynomial" : 0xD800000000000000, 34 | "nrBits" : 64, 35 | "shiftRight" : True, 36 | }, 37 | "CRC-32" : { 38 | "polynomial" : 0xEDB88320, 39 | "nrBits" : 32, 40 | "shiftRight" : True, 41 | }, 42 | "CRC-16" : { 43 | "polynomial" : 0xA001, 44 | "nrBits" : 16, 45 | "shiftRight" : True, 46 | }, 47 | "CRC-16-CCITT" : { 48 | "polynomial" : 0x1021, 49 | "nrBits" : 16, 50 | "shiftRight" : False, 51 | }, 52 | "CRC-8-CCITT" : { 53 | "polynomial" : 0x07, 54 | "nrBits" : 8, 55 | "shiftRight" : False, 56 | }, 57 | "CRC-8-IBUTTON" : { 58 | "polynomial" : 0x8C, 59 | "nrBits" : 8, 60 | "shiftRight" : True, 61 | }, 62 | "CRC-6-ITU" : { 63 | "polynomial" : 0x03, 64 | "nrBits" : 6, 65 | "shiftRight" : False, 66 | }, 67 | } 68 | -------------------------------------------------------------------------------- /libcrcgen/reference.py: -------------------------------------------------------------------------------- 1 | # vim: ts=8 sw=8 noexpandtab 2 | # 3 | # CRC code generator 4 | # 5 | # Copyright (c) 2019-2023 Michael Büsch 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | # 21 | 22 | __all__ = [ 23 | "CrcReference", 24 | ] 25 | 26 | from typing import Iterable 27 | 28 | class CrcReference: 29 | """Generic CRC reference implementation. 30 | """ 31 | 32 | @classmethod 33 | def crc(cls, 34 | crc: int, 35 | data: int, 36 | polynomial: int, 37 | nrCrcBits: int, 38 | nrDataBits: int = 8, 39 | shiftRight: bool = False): 40 | 41 | crcMask = (1 << nrCrcBits) - 1 42 | msb = 1 << (nrCrcBits - 1) 43 | lsb = 1 44 | if shiftRight: 45 | for i in range(nrDataBits): 46 | crc ^= data & 1 47 | data >>= 1 48 | if crc & lsb: 49 | crc = ((crc >> 1) ^ polynomial) & crcMask 50 | else: 51 | crc = (crc >> 1) & crcMask 52 | else: 53 | for i in range(nrDataBits): 54 | crc ^= ((data >> (nrDataBits - 1)) & 1) << (nrCrcBits - 1) 55 | data <<= 1 56 | if crc & msb: 57 | crc = ((crc << 1) ^ polynomial) & crcMask 58 | else: 59 | crc = (crc << 1) & crcMask 60 | return crc 61 | 62 | @classmethod 63 | def crcBlock(cls, 64 | crc: int, 65 | data: Iterable, 66 | polynomial: int, 67 | nrCrcBits: int, 68 | nrDataBits: int = 8, 69 | shiftRight: bool = False, 70 | preFlip: bool = False, 71 | postFlip: bool = False): 72 | 73 | crcMask = (1 << nrCrcBits) - 1 74 | if preFlip: 75 | crc ^= crcMask 76 | for b in data: 77 | crc = cls.crc(crc=crc, 78 | data=b, 79 | polynomial=polynomial, 80 | nrCrcBits=nrCrcBits, 81 | nrDataBits=nrDataBits, 82 | shiftRight=shiftRight) 83 | if postFlip: 84 | crc ^= crcMask 85 | return crc 86 | -------------------------------------------------------------------------------- /libcrcgen/util.py: -------------------------------------------------------------------------------- 1 | # vim: ts=8 sw=8 noexpandtab 2 | # 3 | # CRC code generator 4 | # 5 | # Copyright (c) 2019-2023 Michael Büsch 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | # 21 | 22 | import re 23 | 24 | __all__ = [ 25 | "bitreverse", 26 | "poly2int", 27 | "int2poly", 28 | ] 29 | 30 | def bitreverse(value, nrBits): 31 | """Reverse the bits in an integer. 32 | """ 33 | ret = 0 34 | for _ in range(nrBits): 35 | ret = (ret << 1) | (value & 1) 36 | value >>= 1 37 | return ret 38 | 39 | def poly2int(polyString, nrBits, shiftRight=False): 40 | """Convert polynomial coefficient string to binary integer. 41 | """ 42 | polyString = polyString.lower().strip(); 43 | if polyString.startswith("0x"): 44 | # Hex format 45 | try: 46 | poly = int(polyString[2:], 16) 47 | except ValueError: 48 | raise ValueError("Invalid polynomial coefficient format.") 49 | else: 50 | try: 51 | # Decimal format 52 | poly = int(polyString, 10) 53 | except ValueError: 54 | # Polynomial coefficient format 55 | polyString, _ = re.subn(r"\s+", "", polyString) 56 | poly = 0 57 | try: 58 | for bit in polyString.split("+"): 59 | if bit.startswith("x^"): 60 | poly |= 1 << int(bit[2:], 10) 61 | elif bit == "x": 62 | poly |= 1 << 1 63 | elif bit == "1": 64 | poly |= 1 << 0 65 | else: 66 | raise ValueError 67 | except ValueError: 68 | raise ValueError("Invalid polynomial coefficient format.") 69 | poly &= (1 << nrBits) - 1 70 | if shiftRight: 71 | poly = bitreverse(poly, nrBits) 72 | return poly 73 | 74 | def int2poly(poly, nrBits, shiftRight=False): 75 | """Convert binary integer polynomial coefficient to string. 76 | """ 77 | poly &= (1 << nrBits) - 1 78 | if shiftRight: 79 | poly = bitreverse(poly, nrBits) 80 | p = [] 81 | shift = 0 82 | while poly: 83 | if poly & 1: 84 | if shift == 0: 85 | p.append("1") 86 | elif shift == 1: 87 | p.append("x") 88 | else: 89 | p.append(f"x^{shift}") 90 | shift += 1 91 | poly >>= 1 92 | p.append(f"x^{nrBits}") 93 | return " + ".join(reversed(p)) 94 | -------------------------------------------------------------------------------- /libcrcgen/version.py: -------------------------------------------------------------------------------- 1 | # vim: ts=8 sw=8 noexpandtab 2 | # 3 | # CRC code generator 4 | # 5 | # Copyright (c) 2019-2023 Michael Büsch 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | # 21 | 22 | __all__ = [ 23 | "VERSION_MAJOR", 24 | "VERSION_MINOR", 25 | "VERSION_EXTRA", 26 | "VERSION_STRING", 27 | ] 28 | 29 | VERSION_MAJOR = 2 30 | VERSION_MINOR = 6 31 | VERSION_EXTRA = "" 32 | VERSION_STRING = "%d.%d%s" % (VERSION_MAJOR, VERSION_MINOR, VERSION_EXTRA) 33 | -------------------------------------------------------------------------------- /maintenance/makerelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | srcdir="$(realpath "$0" | xargs dirname)" 4 | srcdir="$srcdir/.." 5 | 6 | die() { echo "$*"; exit 1; } 7 | 8 | # Import the makerelease.lib 9 | # https://bues.ch/cgit/misc.git/plain/makerelease.lib 10 | for path in $(echo "$PATH" | tr ':' ' '); do 11 | [ -f "$MAKERELEASE_LIB" ] && break 12 | MAKERELEASE_LIB="$path/makerelease.lib" 13 | done 14 | [ -f "$MAKERELEASE_LIB" ] && . "$MAKERELEASE_LIB" || die "makerelease.lib not found." 15 | 16 | hook_get_version() 17 | { 18 | local file="$1/libcrcgen/version.py" 19 | local maj="$(cat "$file" | grep -Ee '^VERSION_MAJOR\s+=\s+' | head -n1 | awk '{print $3;}')" 20 | local min="$(cat "$file" | grep -Ee '^VERSION_MINOR\s+=\s+' | head -n1 | awk '{print $3;}')" 21 | local ext="$(cat "$file" | grep -Ee '^VERSION_EXTRA\s+=\s+' | head -n1 | awk '{print $3;}' | cut -d'"' -f2)" 22 | version="${maj}.${min}${ext}" 23 | } 24 | 25 | hook_post_checkout() 26 | { 27 | default_hook_post_checkout "$@" 28 | 29 | rm -r "$1"/maintenance 30 | } 31 | 32 | hook_regression_tests() 33 | { 34 | default_hook_regression_tests "$@" 35 | 36 | # Run selftests 37 | python3 "$1/crcgen_test.py" 38 | } 39 | 40 | project=crcgen 41 | default_archives=py-sdist-xz 42 | makerelease "$@" 43 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | basedir = os.path.abspath(os.path.dirname(__file__)) 5 | 6 | from libcrcgen.version import VERSION_STRING 7 | from setuptools import setup 8 | 9 | with open(os.path.join(basedir, "README.rst"), "rb") as fd: 10 | readmeText = fd.read().decode("UTF-8") 11 | 12 | setup( 13 | name = "crcgen", 14 | version = VERSION_STRING, 15 | description = "CRC algorithm HDL code generator (VHDL, Verilog, MyHDL)", 16 | license = "GNU General Public License v2 or later", 17 | author = "Michael Büsch", 18 | author_email = "m@bues.ch", 19 | url = "https://bues.ch/h/crcgen", 20 | python_requires = ">=3.7", 21 | scripts = [ 22 | "crcgen", 23 | ], 24 | packages = [ 25 | "libcrcgen", 26 | ], 27 | keywords = "CRC Verilog VHDL MyHDL FPGA codegenerator", 28 | classifiers = [ 29 | "Development Status :: 5 - Production/Stable", 30 | "Environment :: Console", 31 | "Intended Audience :: Developers", 32 | "Intended Audience :: Education", 33 | "Intended Audience :: Information Technology", 34 | "Intended Audience :: Science/Research", 35 | "Intended Audience :: Telecommunications Industry", 36 | "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", 37 | "License :: Public Domain", 38 | "Operating System :: OS Independent", 39 | "Programming Language :: Python", 40 | "Programming Language :: Python :: 3", 41 | "Topic :: Education", 42 | "Topic :: Scientific/Engineering", 43 | "Topic :: Software Development", 44 | "Topic :: Software Development :: Code Generators", 45 | "Topic :: Utilities", 46 | ], 47 | long_description=readmeText, 48 | long_description_content_type="text/x-rst", 49 | ) 50 | 51 | # vim: ts=8 sw=8 noexpandtab 52 | --------------------------------------------------------------------------------