diff --git a/.gitignore b/.gitignore index 913fd12..9b5db0a 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,8 @@ fpga/**/simulation/ # Python virtualenv created by devbox .venv/ + +# Generated FPGA image and capture data +arduino/strix_capture/bitstream.h +arduino/strix_capture/signature.h +captures/ diff --git a/arduino/libraries/VidorBoot/COPYING.LESSER b/arduino/libraries/VidorBoot/COPYING.LESSER new file mode 100644 index 0000000..f6683e7 --- /dev/null +++ b/arduino/libraries/VidorBoot/COPYING.LESSER @@ -0,0 +1,501 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Moe Ghoul, President of Vice + +That's all there is to it! diff --git a/arduino/libraries/VidorBoot/README.md b/arduino/libraries/VidorBoot/README.md new file mode 100644 index 0000000..e2ad0e7 --- /dev/null +++ b/arduino/libraries/VidorBoot/README.md @@ -0,0 +1,6 @@ +# VidorBoot + +Unmodified subset of Arduino's [VidorPeripherals](https://github.com/vidor-libraries/VidorPeripherals) 1.1.0, vendored so STRIX can start its own FPGA image without linking VidorPeripherals' bundled bitstream. + +- `VidorUtils`, `VidorMailbox`, `VidorJTAG`, `utility/jtag_host.*`: Copyright (c) 2018 Arduino SA, GNU Lesser General Public License 2.1 or later (see `COPYING.LESSER`). +- `utility/libxsvf.h`, `utility/tap.c`: Lib(X)SVF, Copyright (C) 2009 RIEGL Research ForschungsGmbH and Clifford Wolf, permissive license stated in the file headers. diff --git a/arduino/libraries/VidorBoot/library.properties b/arduino/libraries/VidorBoot/library.properties new file mode 100644 index 0000000..0ad7c35 --- /dev/null +++ b/arduino/libraries/VidorBoot/library.properties @@ -0,0 +1,9 @@ +name=VidorBoot +version=1.1.0 +author=Arduino SA +maintainer=STRIX +sentence=Minimal MKR Vidor 4000 FPGA boot: clock enable and jump to the user image over JTAG. +paragraph=Subset of Arduino's VidorPeripherals 1.1.0 (VidorUtils, VidorMailbox, VidorJTAG and the JTAG host), without its bundled bitstream. +category=Other +url=https://github.com/vidor-libraries/VidorPeripherals +architectures=samd diff --git a/arduino/libraries/VidorBoot/src/VidorBoot.h b/arduino/libraries/VidorBoot/src/VidorBoot.h new file mode 100644 index 0000000..ca728df --- /dev/null +++ b/arduino/libraries/VidorBoot/src/VidorBoot.h @@ -0,0 +1,6 @@ +// Entry header for this library. Including it (rather than VidorUtils.h directly) makes the +// Arduino build pick this library instead of an installed VidorPeripherals, whose own +// bitstream would otherwise be linked in. +#pragma once + +#include "VidorUtils.h" diff --git a/arduino/libraries/VidorBoot/src/VidorJTAG.cpp b/arduino/libraries/VidorBoot/src/VidorJTAG.cpp new file mode 100644 index 0000000..83a28f7 --- /dev/null +++ b/arduino/libraries/VidorBoot/src/VidorJTAG.cpp @@ -0,0 +1,242 @@ +/* + This file is part of the VidorBoot/VidorPeripherals/VidorGraphics library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +extern "C" { + #include "utility/jtag_host.h" +} + +#include "VidorJTAG.h" + +#define JI_PULSE_NCONFIG 0x001 +#define JI_CHECK_STATUS 0x004 +#define JI_USER0 0x00c +#define JI_USER1 0x00e + +#define JBC_WRITE 0 +#define JBC_READ 1 + +#define JTAG_VENDOR_ID 0x06E +#define VJTAG_ID 0x84 + +VidorJTAGClass::VidorJTAGClass() : + _id(-1) +{ +} + +VidorJTAGClass::~VidorJTAGClass() +{ +} + +int VidorJTAGClass::begin(bool reset) +{ + LIBXSVF_HOST_SETUP(); + + _libxsvfHost.tap_state = LIBXSVF_TAP_INIT; + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_RESET); + + if (reset) { + VidorJTAGClass::reset(); + delay(100); + } + + if (!checkStatus()) { + return 0; + } + + _id = idScan(); + if (_id == -1) { + return 0; + } + + _lastVIR = -1; + + return 1; +} + +void VidorJTAGClass::end() +{ + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_RESET); + + LIBXSVF_HOST_SHUTDOWN(); +} + +int VidorJTAGClass::reset() +{ + if (!loadInstruction(JI_PULSE_NCONFIG)) { + return 0; + } + + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRSHIFT); + + return 1; +} + +int VidorJTAGClass::writeBuffer(uint32_t address, const uint32_t* data, size_t len) +{ + if (!loadVirtualInstruction(JBC_WRITE)) { + return -1; + } + + if (!loadInstruction(JI_USER0)) { + return -1; + } + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRSHIFT); + + address = (address << 2) | 0x00000003; + jtag_host_pulse_tdi((uint8_t*)&address, sizeof(address)); + jtag_host_pulse_tdi((uint8_t*)data, len * sizeof(data[0])); + jtag_host_pulse_tdi_0(2); + + return len; +} + +int VidorJTAGClass::readBuffer(uint32_t address, uint32_t* data, size_t len) +{ + if (!loadVirtualInstruction(JBC_WRITE)) { + return -1; + } + + if (!loadInstruction(JI_USER0)) { + return -1; + } + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRSHIFT); + + address = (address << 2) | 0x00000003; + jtag_host_pulse_tdi((uint8_t*)&address, sizeof(address)); + + if (!loadVirtualInstruction(JBC_READ)) { + return -1; + } + + if (!loadInstruction(JI_USER0)) { + return -1; + } + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRSHIFT); + + jtag_host_pulse_tdo((uint8_t*)data, sizeof(data[0])); + jtag_host_pulse_tdo((uint8_t*)data, len * sizeof(data[0])); + return len; +} + +int VidorJTAGClass::checkStatus() +{ + if (!loadInstruction(JI_CHECK_STATUS)) { + return 0; + } + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRSHIFT); + + int tdo; + + for (int i = 0; i < 409; i++) { + tdo = jtag_host_pulse_tdo_bit(); + } + + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRUPDATE); + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_IDLE); + + return (tdo != 0); +} + +int VidorJTAGClass::idScan() +{ + loadInstruction(JI_USER1); + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRSHIFT); + jtag_host_pulse_tdi_0(64); + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRUPDATE); + loadInstruction(JI_USER0); + + uint32_t record = 0; + + for (int i = 0; i < 8; i++) { + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRSHIFT); + + record = (record >> 4) | (jtag_host_pulse_tdio(4, 0x00) << 28); + + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRUPDATE); + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_IDLE); + } + + if (((record >> 8) & 0x7ff) != JTAG_VENDOR_ID) { + return -1; + } + + _numSlaves = (record >> 19) & 0xff; + for (_numSlaveBits = 0; (1 << _numSlaveBits) < (_numSlaves + 1); _numSlaveBits++); + _virSize = record & 0xff; + + for (int j = 0; j < _numSlaves; j++) { + record = 0; + + for (int i = 0; i < 8; i++) { + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRSHIFT); + + record = (record >> 4) | (jtag_host_pulse_tdio(4, 0x00) << 28); + + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRUPDATE); + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_IDLE); + } + + if (((record >> 19) & 0xff) == VJTAG_ID && ((record >> 8) & 0x7ff) == JTAG_VENDOR_ID) { + return j; + } + } + + return -1; +} + +int VidorJTAGClass::loadVirtualInstruction(uint32_t instruction) +{ + if (_lastVIR == instruction) { + return 1; + } + + if (!loadInstruction(JI_USER1)) { + return 0; + } + + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRSHIFT); + + uint32_t code = ((_id + 1) << _virSize) | instruction; + + jtag_host_pulse_tdi_instruction(_virSize + _numSlaveBits, code); + + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_DRUPDATE); + + _lastVIR = instruction; + + return 1; +} + +int VidorJTAGClass::loadInstruction(uint32_t instruction) +{ + if (_libxsvfHost.tap_state != LIBXSVF_TAP_IDLE || _libxsvfHost.tap_state != LIBXSVF_TAP_RESET) { + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_IDLE); + } + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_IRSHIFT); + + if (jtag_host_pulse_tdio_instruction(10, instruction) != 0x155) { + return 0; + } + + libxsvf_tap_walk(&_libxsvfHost, LIBXSVF_TAP_IRUPDATE); + + return 1; +} + +VidorJTAGClass VidorJTAG; diff --git a/arduino/libraries/VidorBoot/src/VidorJTAG.h b/arduino/libraries/VidorBoot/src/VidorJTAG.h new file mode 100644 index 0000000..2fb5dae --- /dev/null +++ b/arduino/libraries/VidorBoot/src/VidorJTAG.h @@ -0,0 +1,59 @@ +/* + This file is part of the VidorBoot/VidorPeripherals/VidorGraphics library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __VIDOR_JTAG_H__ +#define __VIDOR_JTAG_H__ + +extern "C" { + #include +} + +#include + +class VidorJTAGClass { +public: + VidorJTAGClass(); + virtual ~VidorJTAGClass(); + + int begin(bool reset = false); + void end(); + + int reset(); + + int writeBuffer(uint32_t address, const uint32_t* data, size_t len); + int readBuffer(uint32_t address, uint32_t* data, size_t len); + +private: + int checkStatus(); + int idScan(); + int loadVirtualInstruction(uint32_t instruction); + int loadInstruction(uint32_t instruction); + +private: + struct libxsvf_host _libxsvfHost; + int _numSlaves; + int _numSlaveBits; + int _virSize; + int _id; + uint32_t _lastVIR; +}; + +extern VidorJTAGClass VidorJTAG; + +#endif diff --git a/arduino/libraries/VidorBoot/src/VidorMailbox.cpp b/arduino/libraries/VidorBoot/src/VidorMailbox.cpp new file mode 100644 index 0000000..d718f7a --- /dev/null +++ b/arduino/libraries/VidorBoot/src/VidorMailbox.cpp @@ -0,0 +1,110 @@ +/* + This file is part of the VidorBoot/VidorPeripherals/VidorGraphics library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "VidorJTAG.h" + +#include "VidorMailbox.h" + +#define MB_BASE 0x00000000 +#define MB_INT_PIN 31 +#define MB_TIMEOUT 5000 + +VidorMailboxClass::VidorMailboxClass() {} + +int VidorMailboxClass::begin() +{ + if (!VidorJTAG.begin()) { + return 0; + } + + uint32_t rpc[1]; + rpc[0] = 0; + + VidorJTAG.writeBuffer(MB_BASE, rpc, 1); + + pinMode(MB_INT_PIN, OUTPUT); + digitalWrite(MB_INT_PIN, LOW); + + return 1; +} + +void VidorMailboxClass::end() +{ + VidorJTAG.end(); + + digitalWrite(MB_INT_PIN, LOW); + pinMode(MB_INT_PIN, INPUT); +} + +void VidorMailboxClass::reload() +{ + VidorJTAG.reset(); +} + +int VidorMailboxClass::sendCommand(const uint32_t data[], size_t len) +{ + if (data[0] == 0) { + return -1; + } + if (write(0x00, data, len) != (int)len) { + return -1; + } + + digitalWrite(MB_INT_PIN, HIGH); + digitalWrite(MB_INT_PIN, LOW); + + for (unsigned long start = millis(); (millis() - start) < MB_TIMEOUT;) { + uint32_t result; + read(0x00, &result, 1); + + if (result == 0) { + read(0x01, &result, 1); + + return result; + } + } + + return -1; +} + +int VidorMailboxClass::sendEvent(const uint32_t data[], size_t len) +{ + if (write(0x00, data, len) != (int)len) { + return -1; + } + + digitalWrite(MB_INT_PIN, HIGH); + digitalWrite(MB_INT_PIN, LOW); + + return len; +} + +int VidorMailboxClass::read(uint32_t address, uint32_t data[], size_t len) +{ + VidorJTAG.readBuffer(MB_BASE + address, data, len); + + return len; +} + +int VidorMailboxClass::write(uint32_t address, const uint32_t data[], size_t len) +{ + return VidorJTAG.writeBuffer(MB_BASE + address, data, len); +} + +VidorMailboxClass VidorMailbox; diff --git a/arduino/libraries/VidorBoot/src/VidorMailbox.h b/arduino/libraries/VidorBoot/src/VidorMailbox.h new file mode 100644 index 0000000..54ceab3 --- /dev/null +++ b/arduino/libraries/VidorBoot/src/VidorMailbox.h @@ -0,0 +1,41 @@ +/* + This file is part of the VidorBoot/VidorPeripherals/VidorGraphics library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __VIDOR_MAILBOX_H__ +#define __VIDOR_MAILBOX_H__ + +#include + +class VidorMailboxClass { +public: + VidorMailboxClass(); + + int begin(); + void end(); + void reload(); + + int sendCommand(const uint32_t data[], size_t len); + int sendEvent(const uint32_t data[], size_t len); + int read(uint32_t address, uint32_t data[], size_t len); + int write(uint32_t address, const uint32_t data[], size_t len); +}; + +extern VidorMailboxClass VidorMailbox; + +#endif diff --git a/arduino/libraries/VidorBoot/src/VidorUtils.cpp b/arduino/libraries/VidorBoot/src/VidorUtils.cpp new file mode 100644 index 0000000..7bbf286 --- /dev/null +++ b/arduino/libraries/VidorBoot/src/VidorUtils.cpp @@ -0,0 +1,52 @@ +/* + This file is part of the VidorBoot/VidorPeripherals/VidorGraphics library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "VidorMailbox.h" + +#include "VidorUtils.h" + +VidorUtils::VidorUtils() {} + +int VidorUtils::begin(bool jumpToApp) +{ + // Start clocking the FPGA; this function is declared weak and can be overridden + // with a custom implementation (or can be left untouched if FPGA is clocked internally) + enableFpgaClock(); + + int ret = VidorMailbox.begin(); + + if (ret == 1 && jumpToApp) { + uint32_t evt[1]; + evt[0] = 0 | 3; + VidorMailbox.sendEvent(evt, 1); + } + + return ret; +} + +void VidorUtils::end() +{ + VidorMailbox.end(); + disableFpgaClock(); +} + +void VidorUtils::reload() +{ + VidorMailbox.reload(); +} diff --git a/arduino/libraries/VidorBoot/src/VidorUtils.h b/arduino/libraries/VidorBoot/src/VidorUtils.h new file mode 100644 index 0000000..98edd46 --- /dev/null +++ b/arduino/libraries/VidorBoot/src/VidorUtils.h @@ -0,0 +1,46 @@ +/* + This file is part of the VidorBoot/VidorPeripherals/VidorGraphics library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __VIDOR_UTILS_H__ +#define __VIDOR_UTILS_H__ + +// Defines for fpga_bitstream_signature section +#define no_data 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xFF, 0xFF, 0xFF, 0xFF, \ + 0x00, 0x00, 0x00, 0x00 \ + +#define NO_BOOTLOADER no_data +#define NO_APP no_data +#define NO_USER_DATA no_data + +__attribute__((weak)) void enableFpgaClock() {} +__attribute__((weak)) void disableFpgaClock() {} + +class VidorUtils { +public: + VidorUtils(); + + int begin(bool jumpToApp = true); + void end(); + void reload(); +}; + +#endif diff --git a/arduino/libraries/VidorBoot/src/utility/jtag_host.cpp b/arduino/libraries/VidorBoot/src/utility/jtag_host.cpp new file mode 100644 index 0000000..92b4eb5 --- /dev/null +++ b/arduino/libraries/VidorBoot/src/utility/jtag_host.cpp @@ -0,0 +1,200 @@ +/* + This file is part of the VidorBoot/VidorPeripherals/VidorGraphics library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +extern "C" { + #include "jtag_host.h" +} + +#include +#include + +#define TMS 28 // PA14 | SERCOM2/ PAD[2] +#define TCK 27 // PA13 -> SPI CLK | SERCOM2/ PAD[1] +#define TDO 29 // PA15 -> MISO | SERCOM2/ PAD[3] +#define TDI 26 // PA12 -> MOSI | SERCOM2/ PAD[0] + +#define TMS_HIGH() (digitalPinToPort(TMS)->OUTSET.reg = digitalPinToBitMask(TMS)) +#define TMS_LOW() (digitalPinToPort(TMS)->OUTCLR.reg = digitalPinToBitMask(TMS)) + +#define TCK_HIGH() (digitalPinToPort(TCK)->OUTSET.reg = digitalPinToBitMask(TCK)) +#define TCK_LOW() (digitalPinToPort(TCK)->OUTCLR.reg = digitalPinToBitMask(TCK)) +#define TCK_PMUX() (digitalPinToPort(TCK)->PINCFG[g_APinDescription[TCK].ulPin].bit.PMUXEN = 1) +#define TCK_UNPMUX() (digitalPinToPort(TCK)->PINCFG[g_APinDescription[TCK].ulPin].bit.PMUXEN = 0) + +#define TDI_HIGH() (digitalPinToPort(TDI)->OUTSET.reg = digitalPinToBitMask(TDI)) +#define TDI_LOW() (digitalPinToPort(TDI)->OUTCLR.reg = digitalPinToBitMask(TDI)) +#define TDI_PMUX() (digitalPinToPort(TDI)->PINCFG[g_APinDescription[TDI].ulPin].bit.PMUXEN = 1) +#define TDI_UNPMUX() (digitalPinToPort(TDI)->PINCFG[g_APinDescription[TDI].ulPin].bit.PMUXEN = 0) + +#define TDO_PMUX() (digitalPinToPort(TDO)->PINCFG[g_APinDescription[TDO].ulPin].bit.PMUXEN = 1) +#define TDO_UNPMUX() (digitalPinToPort(TDO)->PINCFG[g_APinDescription[TDO].ulPin].bit.PMUXEN = 0) + +#define TMS_WRITE(val) {if (val) { TMS_HIGH(); } else { TMS_LOW(); }} +#define TDI_WRITE(val) {if (val) { TDI_HIGH(); } else { TDI_LOW(); }} +#define TDO_READ() ((digitalPinToPort(TDO)->IN.reg & digitalPinToBitMask(TDO)) != 0) + +#define SPI_JTAG SPI1 +SPISettings JTAG_SPISettings(12000000, LSBFIRST, SPI_MODE0); + +extern "C" { + int jtag_host_setup() + { + pinMode(TMS, OUTPUT); + pinMode(TCK, OUTPUT); + pinMode(TDO, INPUT); + pinMode(TDI, OUTPUT); + + TMS_LOW(); + TCK_LOW(); + TDI_LOW(); + + SPI_JTAG.begin(); + SPI_JTAG.beginTransaction(JTAG_SPISettings); + + TCK_UNPMUX(); + TDI_UNPMUX(); + + return 0; + } + + int jtag_host_shutdown() + { + TMS_LOW(); + TCK_LOW(); + TDI_LOW(); + + pinMode(TMS, INPUT); + pinMode(TCK, INPUT); + pinMode(TDO, INPUT); + pinMode(TDI, INPUT); + + return 0; + } + + void jtag_host_pulse_tck(int tms) + { + TMS_WRITE(tms); + + TCK_LOW(); + TCK_HIGH(); + } + + int jtag_host_pulse_tdo_bit() + { + TDI_LOW(); + + TCK_LOW(); + TCK_HIGH(); + + return TDO_READ(); + } + + unsigned int jtag_host_pulse_tdio_instruction(int bits, unsigned int out) + { + unsigned int in = 0; + + for (int i = 0; i < bits; i++) { + TDI_WRITE(out & 1); + + if (i != (bits - 1)) { + TCK_LOW(); + TCK_HIGH(); + + in |= (TDO_READ() << i); + + out >>= 1; + } + } + + return in; + } + + void jtag_host_pulse_tdi_instruction(int bits, unsigned int out) + { + for (int i = 0; i < bits; i++) { + TDI_WRITE(out & 1); + + if (i != (bits - 1)) { + TCK_LOW(); + TCK_HIGH(); + + out >>= 1; + } + } + } + + unsigned int jtag_host_pulse_tdio(int bits, unsigned int out) + { + unsigned int in = 0; + + for (int i = 0; i < bits; i++) { + TDI_WRITE(out & 1); + + TCK_LOW(); + TCK_HIGH(); + + in |= (TDO_READ() << i); + + out >>= 1; + } + + return in; + } + + void jtag_host_pulse_tdi_0(int bits) + { + digitalWrite(TDI, LOW); + + for (int i = 0; i < bits; i++) { + TCK_LOW(); + TCK_HIGH(); + } + } + + void jtag_host_pulse_tdi(const uint8_t* data, size_t size) + { + TCK_LOW(); + + TCK_PMUX(); + TDI_PMUX(); + + for (size_t i = 0; i < size; i++) { + SPI_JTAG.transfer(*data++); + } + + TCK_UNPMUX(); + TDI_UNPMUX(); + } + + void jtag_host_pulse_tdo(uint8_t* data, size_t size) + { + TCK_LOW(); + + TCK_PMUX(); + TDO_PMUX(); + + for (size_t i = 0; i < size; i++) { + *data = SPI_JTAG.transfer(0x00); + data++; + } + + TCK_UNPMUX(); + TDO_UNPMUX(); + } +} diff --git a/arduino/libraries/VidorBoot/src/utility/jtag_host.h b/arduino/libraries/VidorBoot/src/utility/jtag_host.h new file mode 100644 index 0000000..28822b4 --- /dev/null +++ b/arduino/libraries/VidorBoot/src/utility/jtag_host.h @@ -0,0 +1,56 @@ +/* + This file is part of the VidorBoot/VidorPeripherals/VidorGraphics library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __JTAG_HOST_H__ +#define __JTAG_HOST_H__ + +#include +#include + +// setups the JTAG host +extern int jtag_host_setup(); + +// shutdowns the JTAG host +extern int jtag_host_shutdown(); + +// set TMS to the value and pulses TCK +extern void jtag_host_pulse_tck(int tms); + +// pulses TCK and returns value of TDO pin +extern int jtag_host_pulse_tdo_bit(); + +// pulses out an instrution of bits length, returns data pulsed in from TDO +extern unsigned int jtag_host_pulse_tdio_instruction(int bits, unsigned int out); + +// pulses out an instrution of bits length +extern void jtag_host_pulse_tdi_instruction(int bits, unsigned int out); + +// pulses out data to TDI of bits length, returns data pulsed in from TDO +extern unsigned int jtag_host_pulse_tdio(int bits, unsigned int out); + +// pulses out bits 0's to TDI +extern void jtag_host_pulse_tdi_0(int bits); + +// pulses out bytes to TDI +extern void jtag_host_pulse_tdi(const uint8_t* data, size_t size); + +// pulses in bytes from TDO +extern void jtag_host_pulse_tdo(uint8_t* data, size_t size); + +#endif diff --git a/arduino/libraries/VidorBoot/src/utility/libxsvf.h b/arduino/libraries/VidorBoot/src/utility/libxsvf.h new file mode 100644 index 0000000..13cab9a --- /dev/null +++ b/arduino/libraries/VidorBoot/src/utility/libxsvf.h @@ -0,0 +1,168 @@ +/* + * Lib(X)SVF - A library for implementing SVF and XSVF JTAG players + * + * Copyright (C) 2009 RIEGL Research ForschungsGmbH + * Copyright (C) 2009 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef LIBXSVF_H +#define LIBXSVF_H + +#ifndef ARDUINO +enum libxsvf_mode { + LIBXSVF_MODE_SVF = 1, + LIBXSVF_MODE_XSVF = 2, + LIBXSVF_MODE_SCAN = 3 +}; +#endif + +enum libxsvf_tap_state { + /* Special States */ + LIBXSVF_TAP_INIT = 0, + LIBXSVF_TAP_RESET = 1, + LIBXSVF_TAP_IDLE = 2, + /* DR States */ + LIBXSVF_TAP_DRSELECT = 3, + LIBXSVF_TAP_DRCAPTURE = 4, + LIBXSVF_TAP_DRSHIFT = 5, + LIBXSVF_TAP_DREXIT1 = 6, + LIBXSVF_TAP_DRPAUSE = 7, + LIBXSVF_TAP_DREXIT2 = 8, + LIBXSVF_TAP_DRUPDATE = 9, + /* IR States */ + LIBXSVF_TAP_IRSELECT = 10, + LIBXSVF_TAP_IRCAPTURE = 11, + LIBXSVF_TAP_IRSHIFT = 12, + LIBXSVF_TAP_IREXIT1 = 13, + LIBXSVF_TAP_IRPAUSE = 14, + LIBXSVF_TAP_IREXIT2 = 15, + LIBXSVF_TAP_IRUPDATE = 16 +}; + +#ifndef ARDUINO +enum libxsvf_mem { + LIBXSVF_MEM_XSVF_TDI_DATA = 0, + LIBXSVF_MEM_XSVF_TDO_DATA = 1, + LIBXSVF_MEM_XSVF_TDO_MASK = 2, + LIBXSVF_MEM_XSVF_ADDR_MASK = 3, + LIBXSVF_MEM_XSVF_DATA_MASK = 4, + LIBXSVF_MEM_SVF_COMMANDBUF = 5, + LIBXSVF_MEM_SVF_SDR_TDI_DATA = 6, + LIBXSVF_MEM_SVF_SDR_TDI_MASK = 7, + LIBXSVF_MEM_SVF_SDR_TDO_DATA = 8, + LIBXSVF_MEM_SVF_SDR_TDO_MASK = 9, + LIBXSVF_MEM_SVF_SDR_RET_MASK = 10, + LIBXSVF_MEM_SVF_SIR_TDI_DATA = 11, + LIBXSVF_MEM_SVF_SIR_TDI_MASK = 12, + LIBXSVF_MEM_SVF_SIR_TDO_DATA = 13, + LIBXSVF_MEM_SVF_SIR_TDO_MASK = 14, + LIBXSVF_MEM_SVF_SIR_RET_MASK = 15, + LIBXSVF_MEM_SVF_HDR_TDI_DATA = 16, + LIBXSVF_MEM_SVF_HDR_TDI_MASK = 17, + LIBXSVF_MEM_SVF_HDR_TDO_DATA = 18, + LIBXSVF_MEM_SVF_HDR_TDO_MASK = 19, + LIBXSVF_MEM_SVF_HDR_RET_MASK = 20, + LIBXSVF_MEM_SVF_HIR_TDI_DATA = 21, + LIBXSVF_MEM_SVF_HIR_TDI_MASK = 22, + LIBXSVF_MEM_SVF_HIR_TDO_DATA = 23, + LIBXSVF_MEM_SVF_HIR_TDO_MASK = 24, + LIBXSVF_MEM_SVF_HIR_RET_MASK = 25, + LIBXSVF_MEM_SVF_TDR_TDI_DATA = 26, + LIBXSVF_MEM_SVF_TDR_TDI_MASK = 27, + LIBXSVF_MEM_SVF_TDR_TDO_DATA = 28, + LIBXSVF_MEM_SVF_TDR_TDO_MASK = 29, + LIBXSVF_MEM_SVF_TDR_RET_MASK = 30, + LIBXSVF_MEM_SVF_TIR_TDI_DATA = 31, + LIBXSVF_MEM_SVF_TIR_TDI_MASK = 32, + LIBXSVF_MEM_SVF_TIR_TDO_DATA = 33, + LIBXSVF_MEM_SVF_TIR_TDO_MASK = 34, + LIBXSVF_MEM_SVF_TIR_RET_MASK = 35, + LIBXSVF_MEM_NUM = 36 +}; +#endif + +struct libxsvf_host { +#ifndef ARDUINO + int (*setup)(struct libxsvf_host *h); + int (*shutdown)(struct libxsvf_host *h); + void (*udelay)(struct libxsvf_host *h, long usecs, int tms, long num_tck); + int (*getbyte)(struct libxsvf_host *h); + int (*sync)(struct libxsvf_host *h); + int (*pulse_tck)(struct libxsvf_host *h, int tms, int tdi, int tdo, int rmask, int sync); + void (*pulse_sck)(struct libxsvf_host *h); + void (*set_trst)(struct libxsvf_host *h, int v); + int (*set_frequency)(struct libxsvf_host *h, int v); + void (*report_tapstate)(struct libxsvf_host *h); + void (*report_device)(struct libxsvf_host *h, unsigned long idcode); + void (*report_status)(struct libxsvf_host *h, const char *message); + void (*report_error)(struct libxsvf_host *h, const char *file, int line, const char *message); + void *(*realloc)(struct libxsvf_host *h, void *ptr, int size, enum libxsvf_mem which); +#endif + enum libxsvf_tap_state tap_state; +#ifndef ARDUINO + void *user_data; +#endif +}; + +#ifndef ARDUINO +int libxsvf_play(struct libxsvf_host *, enum libxsvf_mode mode); +const char *libxsvf_state2str(enum libxsvf_tap_state tap_state); +const char *libxsvf_mem2str(enum libxsvf_mem which); + +/* Internal API */ +int libxsvf_svf(struct libxsvf_host *h); +int libxsvf_xsvf(struct libxsvf_host *h); +int libxsvf_scan(struct libxsvf_host *h); +#endif +int libxsvf_tap_walk(struct libxsvf_host *, enum libxsvf_tap_state); + +/* Host accessor macros (see README) */ +#ifdef ARDUINO + +#ifdef __cplusplus +extern "C"{ +#endif // __cplusplus + extern int jtag_host_setup(); + extern int jtag_host_shutdown(); + extern void jtag_host_pulse_tck(int tms); +#ifdef __cplusplus +}; +#endif // __cplusplus + +#define LIBXSVF_HOST_SETUP() jtag_host_setup() +#define LIBXSVF_HOST_SHUTDOWN() jtag_host_shutdown() +#define LIBXSVF_HOST_PULSE_TCK(_tms, _tdi, _tdo, _rmask, _sync) { (void)h; jtag_host_pulse_tck(_tms); } +#define LIBXSVF_HOST_REPORT_TAPSTATE() +#define LIBXSVF_HOST_REPORT_ERROR(_msg) +#else +#define LIBXSVF_HOST_SETUP() h->setup(h) +#define LIBXSVF_HOST_SHUTDOWN() h->shutdown(h) +#define LIBXSVF_HOST_UDELAY(_usecs, _tms, _num_tck) h->udelay(h, _usecs, _tms, _num_tck) +#define LIBXSVF_HOST_GETBYTE() h->getbyte(h) +#define LIBXSVF_HOST_SYNC() (h->sync ? h->sync(h) : 0) +#define LIBXSVF_HOST_PULSE_TCK(_tms, _tdi, _tdo, _rmask, _sync) h->pulse_tck(h, _tms, _tdi, _tdo, _rmask, _sync) +#define LIBXSVF_HOST_PULSE_SCK() do { if (h->pulse_sck) h->pulse_sck(h); } while (0) +#define LIBXSVF_HOST_SET_TRST(_v) do { if (h->set_trst) h->set_trst(h, _v); } while (0) +#define LIBXSVF_HOST_SET_FREQUENCY(_v) (h->set_frequency ? h->set_frequency(h, _v) : -1) +#define LIBXSVF_HOST_REPORT_TAPSTATE() do { if (h->report_tapstate) h->report_tapstate(h); } while (0) +#define LIBXSVF_HOST_REPORT_DEVICE(_v) do { if (h->report_device) h->report_device(h, _v); } while (0) +#define LIBXSVF_HOST_REPORT_STATUS(_msg) do { if (h->report_status) h->report_status(h, _msg); } while (0) +#define LIBXSVF_HOST_REPORT_ERROR(_msg) h->report_error(h, __FILE__, __LINE__, _msg) +#define LIBXSVF_HOST_REALLOC(_ptr, _size, _which) h->realloc(h, _ptr, _size, _which) +#endif + +#endif + diff --git a/arduino/libraries/VidorBoot/src/utility/tap.c b/arduino/libraries/VidorBoot/src/utility/tap.c new file mode 100644 index 0000000..8ff575b --- /dev/null +++ b/arduino/libraries/VidorBoot/src/utility/tap.c @@ -0,0 +1,175 @@ +/* + * Lib(X)SVF - A library for implementing SVF and XSVF JTAG players + * + * Copyright (C) 2009 RIEGL Research ForschungsGmbH + * Copyright (C) 2009 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "libxsvf.h" + +static void tap_transition(struct libxsvf_host *h, int v) +{ + LIBXSVF_HOST_PULSE_TCK(v, -1, -1, 0, 0); +} + +int libxsvf_tap_walk(struct libxsvf_host *h, enum libxsvf_tap_state s) +{ + int i, j; + for (i=0; s != h->tap_state; i++) + { + switch (h->tap_state) + { + /* Special States */ + case LIBXSVF_TAP_INIT: + for (j = 0; j < 6; j++) + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_RESET; + break; + case LIBXSVF_TAP_RESET: + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_IDLE; + break; + case LIBXSVF_TAP_IDLE: + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_DRSELECT; + break; + + /* DR States */ + case LIBXSVF_TAP_DRSELECT: + if (s >= LIBXSVF_TAP_IRSELECT || s == LIBXSVF_TAP_RESET) { + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_IRSELECT; + } else { + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_DRCAPTURE; + } + break; + case LIBXSVF_TAP_DRCAPTURE: + if (s == LIBXSVF_TAP_DRSHIFT) { + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_DRSHIFT; + } else { + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_DREXIT1; + } + break; + case LIBXSVF_TAP_DRSHIFT: + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_DREXIT1; + break; + case LIBXSVF_TAP_DREXIT1: + if (s == LIBXSVF_TAP_DRPAUSE) { + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_DRPAUSE; + } else { + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_DRUPDATE; + } + break; + case LIBXSVF_TAP_DRPAUSE: + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_DREXIT2; + break; + case LIBXSVF_TAP_DREXIT2: + if (s == LIBXSVF_TAP_DRSHIFT) { + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_DRSHIFT; + } else { + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_DRUPDATE; + } + break; + case LIBXSVF_TAP_DRUPDATE: + if (s == LIBXSVF_TAP_IDLE) { + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_IDLE; + } else { + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_DRSELECT; + } + break; + + /* IR States */ + case LIBXSVF_TAP_IRSELECT: + if (s == LIBXSVF_TAP_RESET) { + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_RESET; + } else { + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_IRCAPTURE; + } + break; + case LIBXSVF_TAP_IRCAPTURE: + if (s == LIBXSVF_TAP_IRSHIFT) { + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_IRSHIFT; + } else { + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_IREXIT1; + } + break; + case LIBXSVF_TAP_IRSHIFT: + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_IREXIT1; + break; + case LIBXSVF_TAP_IREXIT1: + if (s == LIBXSVF_TAP_IRPAUSE) { + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_IRPAUSE; + } else { + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_IRUPDATE; + } + break; + case LIBXSVF_TAP_IRPAUSE: + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_IREXIT2; + break; + case LIBXSVF_TAP_IREXIT2: + if (s == LIBXSVF_TAP_IRSHIFT) { + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_IRSHIFT; + } else { + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_IRUPDATE; + } + break; + case LIBXSVF_TAP_IRUPDATE: + if (s == LIBXSVF_TAP_IDLE) { + tap_transition(h, 0); + h->tap_state = LIBXSVF_TAP_IDLE; + } else { + tap_transition(h, 1); + h->tap_state = LIBXSVF_TAP_DRSELECT; + } + break; + + default: + LIBXSVF_HOST_REPORT_ERROR("Illegal tap state."); + return -1; + } +#ifndef ARDUINO + if (h->report_tapstate) + LIBXSVF_HOST_REPORT_TAPSTATE(); +#endif + if (i>10) { + LIBXSVF_HOST_REPORT_ERROR("Loop in tap walker."); + return -1; + } + } + + return 0; +} diff --git a/arduino/strix_capture/fpga_image.cpp b/arduino/strix_capture/fpga_image.cpp new file mode 100644 index 0000000..5409672 --- /dev/null +++ b/arduino/strix_capture/fpga_image.cpp @@ -0,0 +1,13 @@ +// STRIX FPGA image, written to the FPGA's flash by the Vidor bootloader after upload. +// bitstream.h and signature.h are generated by `devbox run image`. +#include + +__attribute__((used, section(".fpga_bitstream_signature"))) +const unsigned char fpga_signature[4096] = { +#include "signature.h" +}; + +__attribute__((used, section(".fpga_bitstream"))) +const unsigned char fpga_bitstream[] = { +#include "bitstream.h" +}; diff --git a/arduino/strix_capture/strix_capture.ino b/arduino/strix_capture/strix_capture.ino new file mode 100644 index 0000000..1aad97f --- /dev/null +++ b/arduino/strix_capture/strix_capture.ino @@ -0,0 +1,141 @@ +// STRIX raw capture tool: boots the STRIX FPGA image and serves raw ring-oscillator +// captures over USB serial for jitter characterization (see tools/capture.py). +// +// Commands (one per line): +// id print the FPGA identification +// cap capture a burst and send it as "DATA \n" + n raw bytes +// source: 0 tap A, 1 enhanced A, 2 enhanced B, 3 pair XOR + +#include +#include +#include "wiring_private.h" + +// STRIX SPI link: SERCOM0 on its alternate pins A3 MOSI (PAD0), A4 SCK (PAD1), A6 MISO (PAD3). +SPIClassSAMD strixSpi(&sercom0, A6, A4, A3, SPI_PAD_0_SCK_1, SERCOM_RX_PAD_3); +const int STRIX_SS = A5; +const SPISettings STRIX_SPI_SETTINGS(8000000, MSBFIRST, SPI_MODE0); + +enum : uint8_t { + REG_ID0 = 0x00, + REG_VERSION = 0x04, + REG_SOURCE = 0x08, + REG_DECIM_HIGH = 0x09, + REG_DECIM_LOW = 0x0A, + REG_CONTROL = 0x0B, + REG_STATUS = 0x0C, + REG_DATA = 0x10, +}; + +const uint8_t READ = 0x80; +const uint8_t CONTROL_START = 0x01; +const uint8_t STATUS_DONE = 0x02; +const size_t CAPTURE_BYTES = 32768; +const size_t CHUNK_BYTES = 512; + +VidorUtils fpga; + +void beginTransaction(uint8_t command) { + strixSpi.beginTransaction(STRIX_SPI_SETTINGS); + digitalWrite(STRIX_SS, LOW); + strixSpi.transfer(command); +} + +void endTransaction() { + digitalWrite(STRIX_SS, HIGH); + strixSpi.endTransaction(); +} + +void writeRegister(uint8_t reg, uint8_t value) { + beginTransaction(reg); + strixSpi.transfer(value); + endTransaction(); +} + +uint8_t readRegister(uint8_t reg) { + beginTransaction(READ | reg); + uint8_t value = strixSpi.transfer(0); + endTransaction(); + return value; +} + +void printId() { + char id[5] = {0}; + for (uint8_t i = 0; i < 4; i++) id[i] = readRegister(REG_ID0 + i); + Serial.print(id); + Serial.print(" v"); + Serial.println(readRegister(REG_VERSION)); +} + +void capture(uint8_t source, uint16_t decimation) { + writeRegister(REG_SOURCE, source); + writeRegister(REG_DECIM_HIGH, decimation >> 8); + writeRegister(REG_DECIM_LOW, decimation & 0xFF); + writeRegister(REG_CONTROL, CONTROL_START); + + unsigned long start = millis(); + while (!(readRegister(REG_STATUS) & STATUS_DONE)) { + // 262,144 samples at 120 MHz take up to 143 s at the maximum decimation of 65535. + if (millis() - start > 180000) { + Serial.println("ERR timeout"); + return; + } + } + + Serial.print("DATA "); + Serial.println(CAPTURE_BYTES); + + static uint8_t chunk[CHUNK_BYTES]; + for (size_t sent = 0; sent < CAPTURE_BYTES; sent += CHUNK_BYTES) { + beginTransaction(READ | REG_DATA); + for (size_t i = 0; i < CHUNK_BYTES; i++) chunk[i] = strixSpi.transfer(0); + endTransaction(); + Serial.write(chunk, CHUNK_BYTES); + } +} + +void handleCommand(String line) { + line.trim(); + if (line == "id") { + printId(); + } else if (line.startsWith("cap ")) { + int space = line.indexOf(' ', 4); + if (space < 0) { + Serial.println("ERR usage: cap "); + return; + } + long source = line.substring(4, space).toInt(); + long decimation = line.substring(space + 1).toInt(); + if (source < 0 || source > 3 || decimation < 1 || decimation > 65535) { + Serial.println("ERR source 0-3, decimation 1-65535"); + return; + } + capture(source, decimation); + } else if (line.length() > 0) { + Serial.println("ERR unknown command"); + } +} + +void setup() { + Serial.begin(115200); + while (!Serial); + + // Starts the 48 MHz FPGA clock and switches the FPGA from Arduino's loader to our image. + if (!fpga.begin(true)) { + Serial.println("ERR FPGA boot failed"); + } + delay(100); // configuration and PLL lock + + pinMode(STRIX_SS, OUTPUT); + digitalWrite(STRIX_SS, HIGH); + strixSpi.begin(); + // SPIClass::begin() applies the pins' default mux; A3-A6 need SERCOM0's alternate mux. + pinPeripheral(A3, PIO_SERCOM_ALT); + pinPeripheral(A4, PIO_SERCOM_ALT); + pinPeripheral(A6, PIO_SERCOM_ALT); + + printId(); +} + +void loop() { + if (Serial.available()) handleCommand(Serial.readStringUntil('\n')); +} diff --git a/devbox.json b/devbox.json index 15a918f..7e23f2a 100644 --- a/devbox.json +++ b/devbox.json @@ -28,6 +28,18 @@ ], "fpga": [ "cd fpga/src && quartus_sh --flow compile strix" + ], + "image": [ + "python3 tools/vidor_image.py fpga/src/output_files/strix.ttf arduino/strix_capture" + ], + "build-capture": [ + "arduino-cli compile --fqbn arduino:samd:mkrvidor4000 --libraries arduino/libraries arduino/strix_capture" + ], + "upload-capture": [ + "arduino-cli upload --fqbn arduino:samd:mkrvidor4000 -p ${PORT:-/dev/ttyACM0} arduino/strix_capture" + ], + "capture": [ + "python3 tools/capture.py --port ${PORT:-/dev/ttyACM0} --out captures" ] } } diff --git a/tools/capture.py b/tools/capture.py new file mode 100644 index 0000000..0e2db04 --- /dev/null +++ b/tools/capture.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +"""Collect raw ring-oscillator captures from the STRIX capture sketch and estimate jitter. + +For each source and decimation k (keep every k-th sample at 120 MHz) the script stores the +capture, estimates the per-bit entropy from conditional block entropies, and converts it to a +predictability P and relative predictability 2P-1. Accumulated phase jitter grows with sqrt(k), +so fitting a jittered square-wave sampling model to P(k) gives the effective jitter per sample +interval, in ring cycles. The estimate is approximate: the entropy is measured from past +samples, while the model assumes the ideal phase is known. On synthetic captures with known +random-walk jitter it overestimates by a factor of about 1.2-1.3, so divide by 1.3 when sizing +the XOR combining conservatively. +""" + +import argparse +import math +import sys +from pathlib import Path + +import numpy as np +import serial + +SOURCES = {0: "tap A", 1: "enhanced A", 2: "enhanced B", 3: "pair XOR"} +DEFAULT_DECIMATIONS = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096] + + +# Board communication + +def open_board(port: str) -> serial.Serial: + board = serial.Serial(port, 115200, timeout=200) + board.reset_input_buffer() + board.write(b"id\n") + while True: + line = board.readline().decode(errors="replace").strip() + if not line: + sys.exit("no answer from the capture sketch") + if line.startswith("STRX"): + print(f"board: {line}") + return board + if line.startswith("ERR"): + sys.exit(f"board: {line}") + + +def capture(board: serial.Serial, source: int, decimation: int) -> bytes: + board.write(f"cap {source} {decimation}\n".encode()) + header = board.readline().decode(errors="replace").strip() + if not header.startswith("DATA "): + sys.exit(f"board: {header or 'timeout'}") + length = int(header.split()[1]) + data = board.read(length) + if len(data) != length: + sys.exit(f"short capture: {len(data)} of {length} bytes") + return data + + +# Statistics + +def binary_entropy(p: float) -> float: + if p <= 0.0 or p >= 1.0: + return 0.0 + return -(p * math.log2(p) + (1 - p) * math.log2(1 - p)) + + +def predictability_from_entropy(h: float) -> float: + """Inverse of the binary entropy on P >= 0.5, by bisection.""" + low, high = 0.5, 1.0 + for _ in range(60): + mid = (low + high) / 2 + if binary_entropy(mid) > h: + low = mid + else: + high = mid + return (low + high) / 2 + + +def block_entropy(bits: np.ndarray, length: int) -> float: + weights = 1 << np.arange(length) + windows = np.lib.stride_tricks.sliding_window_view(bits, length) + counts = np.bincount(windows @ weights, minlength=1 << length) + p = counts[counts > 0] / counts.sum() + return float(-(p * np.log2(p)).sum()) + + +def analyze(data: bytes, order: int) -> dict: + bits = np.unpackbits(np.frombuffer(data, dtype=np.uint8)).astype(np.int64) + signed = 2.0 * bits - 1.0 + entropy = block_entropy(bits, order + 1) - block_entropy(bits, order) + predictability = predictability_from_entropy(entropy) + return { + "ones": float(bits.mean()), + "autocorr": float(np.mean(signed[:-1] * signed[1:]) - np.mean(signed) ** 2), + "entropy": entropy, + "relative_predictability": 2 * predictability - 1, + } + + +# Jitter model: a square wave sampled at a nominal phase mu in the high half-cycle, with +# Gaussian phase noise of standard deviation sigma (in cycles). Averaging the probability of +# reading the expected level over mu gives the predictability of a sample. + +_erf = np.vectorize(math.erf) + + +def model_predictability(sigma: float) -> float: + mu = np.linspace(0.0, 0.5, 201) + x = np.arange(-math.ceil(6 * sigma) - 1, math.ceil(6 * sigma) + 2) + cdf = lambda edge: 0.5 * (1 + _erf((edge[None, :] - mu[:, None]) / (sigma * math.sqrt(2)))) + p_high = (cdf(x + 0.5) - cdf(x)).sum(axis=1) + return float(p_high.mean()) + + +def fit_jitter(decimations: list[int], relative: list[float]) -> float: + """Least-squares fit of sigma_1 in sigma_k = sigma_1 * sqrt(k), via a lookup table.""" + table_sigma = np.logspace(-5, 1, 600) + table_relative = np.array([2 * model_predictability(s) - 1 for s in table_sigma]) + ks = np.array(decimations, dtype=float) + measured = np.array(relative) + + def model(sigma_1: float) -> np.ndarray: + return np.interp(np.log(sigma_1 * np.sqrt(ks)), np.log(table_sigma), table_relative) + + candidates = np.logspace(-5, 0, 1000) + errors = [np.sum((model(s) - measured) ** 2) for s in candidates] + return float(candidates[int(np.argmin(errors))]) + + +# Main + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("--port", default="/dev/ttyACM0") + parser.add_argument("--out", type=Path, default=Path("captures")) + parser.add_argument("--sources", type=int, nargs="+", default=list(SOURCES)) + parser.add_argument("--decimations", type=int, nargs="+", default=DEFAULT_DECIMATIONS) + parser.add_argument("--order", type=int, default=8, help="context bits for the entropy estimate") + parser.add_argument("--analyze-only", action="store_true", help="reuse captures in --out") + args = parser.parse_args() + + args.out.mkdir(parents=True, exist_ok=True) + board = None if args.analyze_only else open_board(args.port) + + results = {} + print(f"{'source':<11} {'k':>5} {'ones':>7} {'autocorr':>9} {'H/bit':>7} {'2P-1':>8}") + for source in args.sources: + for k in args.decimations: + path = args.out / f"src{source}_dec{k}.bin" + if board: + path.write_bytes(capture(board, source, k)) + if not path.exists(): + continue + r = analyze(path.read_bytes(), args.order) + results[(source, k)] = r + print(f"{SOURCES[source]:<11} {k:>5} {r['ones']:7.4f} {r['autocorr']:9.4f} " + f"{r['entropy']:7.4f} {r['relative_predictability']:8.5f}") + + print("\nfitted jitter per 120 MHz sample interval (ring cycles, rms):") + for source in args.sources: + # Near-random points sit on the estimator's noise floor and would bias the fit. + points = [(k, results[(source, k)]["relative_predictability"]) + for k in args.decimations + if (source, k) in results and results[(source, k)]["relative_predictability"] > 0.1] + if len(points) >= 3: + ks, rel = zip(*points) + print(f" {SOURCES[source]:<11} {fit_jitter(list(ks), list(rel)):.5f}") + + print("\ncheck: enhanced A should match (tap A)^3 in relative predictability") + for k in args.decimations: + if (0, k) in results and (1, k) in results: + tap = results[(0, k)]["relative_predictability"] + enhanced = results[(1, k)]["relative_predictability"] + print(f" k={k:<5} tap^3={tap ** 3:8.5f} enhanced={enhanced:8.5f}") + + +if __name__ == "__main__": + main() diff --git a/tools/vidor_image.py b/tools/vidor_image.py new file mode 100644 index 0000000..eaec459 --- /dev/null +++ b/tools/vidor_image.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Turn a Quartus .ttf bitstream into the app image embedded in a MKR Vidor 4000 sketch. + +The Vidor bootloader expects the bitstream bytes bit-reversed relative to Quartus' .ttf +output, written as a C initializer list (bitstream.h), plus a signature block (signature.h) +describing where the image goes in the FPGA's flash. The app image slot starts at 512 KB. +""" + +import argparse +import hashlib +from pathlib import Path + +APP_OFFSET_KB = 512 +IMAGE_TYPE_APP = 1 + + +def reverse_bits(byte: int) -> int: + return int(f"{byte:08b}"[::-1], 2) + + +def read_ttf(path: Path) -> bytes: + values = (field.strip() for field in path.read_text().split(",")) + return bytes(reverse_bits(int(v)) for v in values if v) + + +def format_initializer(data: bytes) -> str: + lines = [] + for start in range(0, len(data), 16): + chunk = data[start:start + 16] + if len(chunk) == 16: + lines.append(", ".join(f"{b:3d}" for b in chunk) + ",") + else: + lines.append("".join(f"{b:3d}, " for b in chunk)) + return "\n".join(lines) + "\n" + + +def hex_bytes(data: bytes) -> str: + return ", ".join(f"0x{b:02x}" for b in data) + "," + + +def format_signature(image_text: str, length: int) -> str: + digest = hashlib.sha256(image_text.encode()).digest() + return "\n".join([ + "NO_BOOTLOADER,", + hex_bytes((APP_OFFSET_KB * 1024).to_bytes(4, "little")), + hex_bytes(length.to_bytes(4, "little")), + hex_bytes(digest), + hex_bytes(IMAGE_TYPE_APP.to_bytes(4, "little")), + "0x00, 0x00, 0x00, 0x00, // force flag", + "NO_USER_DATA,", + ]) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("ttf", type=Path, help="Quartus .ttf bitstream") + parser.add_argument("out_dir", type=Path, help="directory for bitstream.h and signature.h") + args = parser.parse_args() + + data = read_ttf(args.ttf) + image_text = format_initializer(data) + args.out_dir.mkdir(parents=True, exist_ok=True) + (args.out_dir / "bitstream.h").write_text(image_text) + (args.out_dir / "signature.h").write_text(format_signature(image_text, len(data))) + print(f"{len(data)} bytes -> {args.out_dir}/bitstream.h, signature.h") + + +if __name__ == "__main__": + main()