Repository: soundpaint/rp2040pio Branch: main Commit: d1598cf27692 Files: 210 Total size: 1.2 MB Directory structure: gitextract_vcqxfqub/ ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── defs.mak └── java/ ├── META-INF/ │ ├── MANIFEST.MF.CodeObserver │ ├── MANIFEST.MF.Diagram │ ├── MANIFEST.MF.DocTool │ ├── MANIFEST.MF.FifoObserver │ ├── MANIFEST.MF.GPIOObserver │ ├── MANIFEST.MF.Monitor │ ├── MANIFEST.MF.Observer │ └── MANIFEST.MF.Server ├── Makefile.CodeObserver ├── Makefile.Diagram ├── Makefile.DocTool ├── Makefile.FifoObserver ├── Makefile.GPIOObserver ├── Makefile.Monitor ├── Makefile.Observer ├── Makefile.Server ├── examples/ │ ├── addition.mon │ ├── apa102-mini.mon │ ├── apa102-rgb555.mon │ ├── auto-push-pull.mon │ ├── autopull.mon │ ├── blink.mon │ ├── bmc-rx.mon │ ├── bmc-tx.mon │ ├── clocked-input.mon │ ├── exec-example.mon │ ├── ext-wave.mon │ ├── hello.mon │ ├── hub75.mon │ ├── i2c.mon │ ├── logic-analyser.mon │ ├── manchester-rx.mon │ ├── manchester-tx.mon │ ├── manual-pull.mon │ ├── pull-example1.mon │ ├── pull-example2.mon │ ├── pull-example3.mon │ ├── pull-example4.mon │ ├── pwm.mon │ ├── somewhat-manual-pull.mon │ ├── spi-cpha0-cs.mon │ ├── spi-cpha0.mon │ ├── spi-cpha1-cs.mon │ ├── spi-cpha1.mon │ ├── spi-tx-fast.mon │ ├── squarewave-fast.mon │ ├── squarewave-wrap.mon │ ├── squarewave.hex │ ├── squarewave.mon │ ├── st7789-lcd.mon │ ├── uart-rx-mini.mon │ ├── uart-rx.mon │ ├── uart-tx.mon │ ├── ws2812-led.mon │ ├── ws2812-parallel.mon │ ├── ws2812.hex │ └── ws2812.mon ├── media/ │ ├── add12x12.xcf │ ├── cycle16x16.xcf │ ├── del12x12.xcf │ ├── floppy-blue16x16.xcf │ ├── gpl-2.0-standalone.html │ ├── led-gray16x16.xcf │ ├── led-green-off16x16.xcf │ ├── led-green-on16x16.xcf │ ├── led-red-off16x16.xcf │ ├── led-red-on16x16.xcf │ ├── quit16x16_antialiased.xcf │ ├── quit16x16_pixels.xcf │ ├── swapv12x12.xcf │ └── trash16x16.xcf └── org/ └── soundpaint/ └── rp2040pio/ ├── AddressSpace.java ├── Bit.java ├── Clock.java ├── CmdOptions.java ├── CollapsiblePanel.java ├── Constants.java ├── Decoder.java ├── Direction.java ├── EmulationServer.java ├── Emulator.java ├── FIFO.java ├── GPIO.java ├── GPIOIOBank0Registers.java ├── GPIOIOBank0RegistersImpl.java ├── GPIOPadsBank0Registers.java ├── GPIOPadsBank0RegistersImpl.java ├── IOUtils.java ├── IRQ.java ├── Instruction.java ├── LocalAddressSpace.java ├── MasterClock.java ├── Memory.java ├── PIO.java ├── PIOEmuRegisters.java ├── PIOEmuRegistersImpl.java ├── PIOGPIO.java ├── PIORegisters.java ├── PIORegistersImpl.java ├── PLL.java ├── ParseException.java ├── PicoEmuRegisters.java ├── PicoEmuRegistersImpl.java ├── PinState.java ├── RegisterSet.java ├── RemoteAddressSpaceClient.java ├── RemoteAddressSpaceServer.java ├── SM.java ├── SwingUtils.java ├── doctool/ │ ├── DocsBuilder.java │ ├── ExampleScriptsDocsBuilder.java │ ├── MonitorCommandsDocsBuilder.java │ ├── RegistersDocs.java │ └── RegistersDocsBuilder.java ├── monitor/ │ ├── Command.java │ ├── CommandRegistry.java │ ├── Monitor.java │ ├── MonitorUtils.java │ ├── ScriptInfo.java │ └── commands/ │ ├── BreakPoints.java │ ├── Clear.java │ ├── Clock.java │ ├── Enter.java │ ├── Execute.java │ ├── Fifo.java │ ├── Gpio.java │ ├── Help.java │ ├── Interrupt.java │ ├── Label.java │ ├── Load.java │ ├── PinCtrl.java │ ├── Quit.java │ ├── Read.java │ ├── Registers.java │ ├── Reset.java │ ├── Save.java │ ├── Script.java │ ├── SideSet.java │ ├── Sm.java │ ├── Trace.java │ ├── Unassemble.java │ ├── Unload.java │ ├── Version.java │ ├── Wait.java │ ├── Wrap.java │ └── Write.java ├── observer/ │ ├── ActionPanel.java │ ├── ConnectDialog.java │ ├── GUIObserver.java │ ├── LicenseView.java │ ├── MenuBar.java │ ├── Observer.java │ ├── code/ │ │ ├── ActionPanel.java │ │ ├── CodeObserver.java │ │ ├── CodeSmViewPanel.java │ │ └── CodeViewPanel.java │ ├── diagram/ │ │ ├── AbstractSignal.java │ │ ├── ActionPanel.java │ │ ├── ClockSignal.java │ │ ├── Constants.java │ │ ├── CycleRuler.java │ │ ├── Diagram.java │ │ ├── DiagramModel.java │ │ ├── DiagramViewPanel.java │ │ ├── LegendPanel.java │ │ ├── MenuBar.java │ │ ├── RegisterBitSignal.java │ │ ├── RegisterIntSignal.java │ │ ├── ScriptDialog.java │ │ ├── ScriptSelectionPanel.java │ │ ├── Signal.java │ │ ├── SignalDialog.java │ │ ├── SignalFactory.java │ │ ├── SignalFactoryPanel.java │ │ ├── SignalFilter.java │ │ ├── SignalLabelPanel.java │ │ ├── SignalPanel.java │ │ ├── SignalRendering.java │ │ ├── SignalTypePanel.java │ │ ├── SignalsDialog.java │ │ ├── SignalsPropertiesPanel.java │ │ ├── SmSelectionPanel.java │ │ ├── TelemetryPanel.java │ │ ├── ToolTip.java │ │ ├── ValueFilterPanel.java │ │ ├── ValueRenderingPanel.java │ │ ├── ValueSourcePanel.java │ │ └── ValuedSignal.java │ ├── fifo/ │ │ ├── ActionPanel.java │ │ ├── FifoEntriesViewPanel.java │ │ ├── FifoObserver.java │ │ └── FifoViewPanel.java │ └── gpio/ │ ├── GPIOArrayPanel.java │ ├── GPIOObserver.java │ ├── GPIOPanel.java │ ├── GPIOViewPanel.java │ ├── PIOGPIOArrayPanel.java │ └── PIOGPIOPanel.java └── sdk/ ├── GPIOSDK.java ├── PIOSDK.java ├── Panic.java ├── Program.java ├── ProgramParser.java ├── SDK.java └── SMConfig.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ build jar rst-doc *~ ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's 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 give any other recipients of the Program a copy of this License along with the Program. 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 Program or any portion of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, 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 Program, 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 Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) 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; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, 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 executable. However, as a special exception, the source code 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. If distribution of executable or 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 counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program 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. 5. 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 Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program 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 to this License. 7. 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 Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program 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 Program. 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. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program 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. 9. The Free Software Foundation may publish revised and/or new versions of the 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 Program 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 Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, 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 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 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 Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. 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 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. 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. 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. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: Makefile ================================================ # Global Makefile for RP2040 PIO emulator # # Copyright (C) 2021 Jürgen Reuter # # 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. # # 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. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # For updates and more info or contacting the author, visit: # # # Author's web site: www.juergen-reuter.de ROOT_DIR=. include defs.mak all: cd $(JAVA_DIR) ; make -f Makefile.Server all cd $(JAVA_DIR) ; make -f Makefile.Monitor all cd $(JAVA_DIR) ; make -f Makefile.Observer all cd $(JAVA_DIR) ; make -f Makefile.Diagram all cd $(JAVA_DIR) ; make -f Makefile.GPIOObserver all cd $(JAVA_DIR) ; make -f Makefile.CodeObserver all cd $(JAVA_DIR) ; make -f Makefile.FifoObserver all cd $(JAVA_DIR) ; make -f Makefile.DocTool all run: all cd $(JAVA_DIR) ; make -f Makefile.Server run tags: - find $(JAVA_DIR) -name \*.java -exec etags {} \; -print objclean: rm -rf $(ROOT_BUILD_DIR) rm -rf $(JAR_DIR) rm -rf $(RST_DOC_DIR) bkpclean: - find $(JAVA_DIR) -name \*~ -exec /bin/rm -f {} \; -print rm -f *~ coreclean: rm -f core core.* vgcore.* rm -f $(JAVA_DIR)/core $(JAVA_DIR)/core.* $(JAVA_DIR)/vgcore.* clean: objclean distclean: objclean bkpclean coreclean tarball: distclean @TGZ_DATE=`date +%Y-%m-%d_%H-%M-%S` ; \ PROJECT_NAME=rpi2040pio ; \ PROJECT_PATH=`basename \`pwd\`` ; \ TGZ_PREFIX=$$PROJECT_NAME\_$$TGZ_DATE ; cd .. ; \ tar cvf ./$$TGZ_PREFIX.tar.bz2 \ --exclude=untracked \ --exclude=.git \ --transform=s/$$PROJECT_PATH/$$TGZ_PREFIX/ \ --bzip2 $$PROJECT_PATH # Local Variables: # coding:utf-8 # mode:Makefile # End: ================================================ FILE: README.md ================================================ # RP2040 PIO Emulator An emulator of the RP2040 state machines. This emulator is _not_ intended as a real-time emulation of the actual hardware, but as a handy tool for understanding how the PIO works and for testing and debugging when developing code for the PIO. For detailed information, see [the full docs on readthedocs.io][1] ## Motivation What is the purpose of an RP2040 emulator if you can easily use the original? There are a number of good reasons for doing so: * To be able to trace / single step a PIO program as an invaluable aid for developing and debugging, which is not easily possible directly on the RP2040 hardware. * To inspect all of the PIO's internal state while developing and debugging a PIO program. This feature applies even to those parts of the PIO that are not accessible when running on real RP2040 hardware, such as: * the contents of PIO registers X, Y, ISR and OSR, * the current value of the ISR / OSR shift count or * the number of an instruction's pending delays. In contrast, the emulator has access to the PIO's complete internal logical state (otherwise, the emulation could not correctly work). * To debug your PIO program in the context of your IDE: The emulator will typically run on your development host machine. That is, there is no need to upload the PIO program to a real RPI2040 for each and every tiny change, thus saving time and stress in the course of small and frequent development cycles. * To be able to automatically generate detailed timing diagrams straight from an emulated run of a PIO program. Timing diagrams are highly useful for debugging as well as for documentation, as proof of concept or fact sheet. The selection of signals shown on the diagram is freely configurable. * To be able to debug a PIO program even if there is no RP2040 hardware at hand. (Fun fact: The RP2040 is still sold out at all of the distributors easily accessible to me. That is, I still do not own an RP2040 of my own, but solely have to rely on the specs and other sources of documentation.) * A motivation that applies to me personally: If I succeed in implementing a (more or less) correctly working emulation of the PIO, than I am somewhat confident that I have basically understood how the PIO works and what capabilities it has when it comes to writing PIO programs for it. ## Current Features * The core PIO emulator is already running, though it has not yet been extensively tested. That is, there are surely still many bugs in the emulation, such that emulation may behave wrong, especially for programs that use more exotic and therefore not well tested features of the PIO. * The emulator can be used to automatically generate timing diagrams. However, configuration is still hard-wired in the source code (in the ```run()``` method of class ```org.soundpaint.rp2040pio.Main```). With one of the next commits, flexible configuration and program loading will be made possible through a configuration file and/or via command line arguments, rather than having to recompile the source code of the emulator. Since there are surely still lots of bugs in the emulation, note that, as of now, the generated timing diagrams may be faulty as well.


Fig. 1: Timing Diagram Generated from Emulation of squarewave Program

## Future Plans A command line based monitor program will be provided to interactively: * load programs, * list (unassemble) program code, * modify programs, * start / stop / synchronize state machines, * single step / trace into programs, * define break points, * inspect current contents of state machine registers, GPIO pins, FIFO registers, shift registers, etc. * manipulate the current contents of registers and pins, * generate timing diagrams, and more. Maybe, I will also provide a graphical user interface that depicts the internal state of the PIO in a graphical and more vivid manner. * Support for interaction with the PIO's outer world. In particular: * Support specifying when and which logical values will be put onto the GPIO pins from an external source. * Support specifying when and which values will be shifted into the TX FIFO or shifted out from the RX FIFO via DMA access onto the PIO. * Support specifying when and which operations are triggered by DMA access onto the PIO memory-mapped registers. * Support specifying when and which instructions are inserted by DMA access onto the PIO memory-mapped SMx_INSTR registers. * Support specifying when an interrupt is requested by DMA access onto the PIO memory-mapped interrupt registers. * Generate a warning whenever a race condition is detected. In particular, generate a warning when multiple state machines concurrently access the same resource, like writing simultaneously to the same GPIO pin. Warnings can be reported in the monitor program while debugging a program, as well as marked on the timing diagram. * Generate a warning upon FIFO overflow / underflow. * Generate a warning when reading from or writing to a GPIO pin that has pin direction that conflicts with the type of access. * When generating a timing diagram, support alternative backends such as PDF or SVG. * If an alternative backend for timing diagrams provides tooltips, add tooltips with additional info such as showing the complete instruction (with all of its parameters) when hovering over the instruction mnemonic in the timing diagram. * Also add tooltips with descriptive / explanatory text for all warnings, where appropriate. [1]: https://rp2040pio-docs.readthedocs.io/ ================================================ FILE: defs.mak ================================================ # Global Makefile definitions for RP2040 PIO emulator # # Copyright (C) 2021 Jürgen Reuter # # 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. # # 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. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # For updates and more info or contacting the author, visit: # # # Author's web site: www.juergen-reuter.de JAVA_DIR=$(ROOT_DIR)/java ROOT_BUILD_DIR=$(ROOT_DIR)/build JAR_DIR=$(ROOT_DIR)/jar RST_DOC_DIR=$(ROOT_DIR)/rst-doc # Local Variables: # coding:utf-8 # mode:Makefile # End: ================================================ FILE: java/META-INF/MANIFEST.MF.CodeObserver ================================================ Manifest-Version: 1.0 Specification-Title: N.A. Created-By: Jürgen Reuter Implementation-Title: RP2040 PIO Emulator Code Observer Specification-Vendor: Juergen Reuter Implementation-Vendor: Juergen Reuter Main-Class: org.soundpaint.rp2040pio.observer.code.CodeObserver ================================================ FILE: java/META-INF/MANIFEST.MF.Diagram ================================================ Manifest-Version: 1.0 Specification-Title: N.A. Created-By: Jürgen Reuter Implementation-Title: RP2040 PIO Emulator Diagram Specification-Vendor: Juergen Reuter Implementation-Vendor: Juergen Reuter Main-Class: org.soundpaint.rp2040pio.observer.diagram.Diagram ================================================ FILE: java/META-INF/MANIFEST.MF.DocTool ================================================ Manifest-Version: 1.0 Specification-Title: N.A. Created-By: Jürgen Reuter Implementation-Title: RP2040 PIO Emulator Documentation Tool Specification-Vendor: Juergen Reuter Implementation-Vendor: Juergen Reuter Main-Class: org.soundpaint.rp2040pio.doctool.DocsBuilder ================================================ FILE: java/META-INF/MANIFEST.MF.FifoObserver ================================================ Manifest-Version: 1.0 Specification-Title: N.A. Created-By: Jürgen Reuter Implementation-Title: RP2040 PIO Emulator FIFO Observer Specification-Vendor: Juergen Reuter Implementation-Vendor: Juergen Reuter Main-Class: org.soundpaint.rp2040pio.observer.fifo.FifoObserver ================================================ FILE: java/META-INF/MANIFEST.MF.GPIOObserver ================================================ Manifest-Version: 1.0 Specification-Title: N.A. Created-By: Jürgen Reuter Implementation-Title: RP2040 PIO Emulator GPIO Observer Specification-Vendor: Juergen Reuter Implementation-Vendor: Juergen Reuter Main-Class: org.soundpaint.rp2040pio.observer.gpio.GPIOObserver ================================================ FILE: java/META-INF/MANIFEST.MF.Monitor ================================================ Manifest-Version: 1.0 Specification-Title: N.A. Created-By: Jürgen Reuter Implementation-Title: RP2040 PIO Emulator Monitor Specification-Vendor: Juergen Reuter Implementation-Vendor: Juergen Reuter Main-Class: org.soundpaint.rp2040pio.monitor.Monitor ================================================ FILE: java/META-INF/MANIFEST.MF.Observer ================================================ Manifest-Version: 1.0 Specification-Title: N.A. Created-By: Jürgen Reuter Implementation-Title: RP2040 PIO Emulator Command-Line Observer Specification-Vendor: Juergen Reuter Implementation-Vendor: Juergen Reuter Main-Class: org.soundpaint.rp2040pio.observer.Observer ================================================ FILE: java/META-INF/MANIFEST.MF.Server ================================================ Manifest-Version: 1.0 Specification-Title: N.A. Created-By: Jürgen Reuter Implementation-Title: RP2040 PIO Emulation Server Specification-Vendor: Juergen Reuter Implementation-Vendor: Juergen Reuter Main-Class: org.soundpaint.rp2040pio.EmulationServer ================================================ FILE: java/Makefile.CodeObserver ================================================ # Makefile for Java tree of RP2040 PIO emulator # # Copyright (C) 2021 Jürgen Reuter # # 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. # # 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. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # For updates and more info or contacting the author, visit: # # # Author's web site: www.juergen-reuter.de ROOT_DIR=.. include ../defs.mak BUILD_DIR=$(ROOT_BUILD_DIR)/CodeObserver COMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR) RUN_CLASSPATH=. PIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio JAVA_SRC=$(wildcard $(PIO_DIR)/observer/code/CodeObserver.java) JAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC)) MEDIA_SRC_DIR=$(JAVA_DIR)/media MEDIA_OBJ_DIR=$(BUILD_DIR)/media MEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html) MEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC)) EXAMPLES_SRC_DIR=$(JAVA_DIR)/examples EXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples EXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon) EXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC)) JAR_OBJ=$(JAR_DIR)/rp2040pio_codeobserver.jar all: obj jar obj: $(BUILD_DIR) $(JAVA_OBJ) \ $(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \ $(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ) $(BUILD_DIR): mkdir -p $@ $(MEDIA_OBJ_DIR): echo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR) mkdir -p $@ $(EXAMPLES_OBJ_DIR): echo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR) mkdir -p $@ $(JAR_DIR): mkdir -p $@ jar: $(JAR_DIR) $(JAR_OBJ) $(JAR_OBJ): $(JAVA_OBJ) cd $(BUILD_DIR) ; \ jar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.CodeObserver . $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/% cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon cp -pf $< $@ depend: run: all cd $(JAR_DIR) ; java -jar $(JAR_OBJ) #cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main objclean: - rm -rf $(BUILD_DIR) jarclean: - rm -rf $(JAR_DIR) clean: objclean jarclean .SUFFIXES: $(SUFFIXES) .java .class $(BUILD_DIR)%class: $(JAVA_DIR)%java javac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $< # Local Variables: # coding:utf-8 # mode:Makefile # End: ================================================ FILE: java/Makefile.Diagram ================================================ # Makefile for Java tree of RP2040 PIO emulator # # Copyright (C) 2021 Jürgen Reuter # # 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. # # 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. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # For updates and more info or contacting the author, visit: # # # Author's web site: www.juergen-reuter.de ROOT_DIR=.. include ../defs.mak BUILD_DIR=$(ROOT_BUILD_DIR)/Diagram COMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR) RUN_CLASSPATH=. PIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio JAVA_SRC=$(wildcard $(PIO_DIR)/observer/diagram/Diagram.java) JAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC)) MEDIA_SRC_DIR=$(JAVA_DIR)/media MEDIA_OBJ_DIR=$(BUILD_DIR)/media MEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html) MEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC)) EXAMPLES_SRC_DIR=$(JAVA_DIR)/examples EXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples EXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon) EXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC)) JAR_OBJ=$(JAR_DIR)/rp2040pio_diagram.jar all: obj jar obj: $(BUILD_DIR) $(JAVA_OBJ) \ $(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \ $(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ) $(BUILD_DIR): mkdir -p $@ $(MEDIA_OBJ_DIR): echo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR) mkdir -p $@ $(EXAMPLES_OBJ_DIR): echo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR) mkdir -p $@ $(JAR_DIR): mkdir -p $@ jar: $(JAR_DIR) $(JAR_OBJ) $(JAR_OBJ): $(JAVA_OBJ) cd $(BUILD_DIR) ; \ jar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.Diagram . $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/% cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon cp -pf $< $@ depend: run: all cd $(JAR_DIR) ; java -jar $(JAR_OBJ) #cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.observer.diagram.Diagram objclean: - rm -rf $(BUILD_DIR) jarclean: - rm -rf $(JAR_DIR) clean: objclean jarclean .SUFFIXES: $(SUFFIXES) .java .class $(BUILD_DIR)%class: $(JAVA_DIR)%java javac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $< # Local Variables: # coding:utf-8 # mode:Makefile # End: ================================================ FILE: java/Makefile.DocTool ================================================ # Makefile for Java tree of RP2040 PIO emulator # # Copyright (C) 2021 Jürgen Reuter # # 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. # # 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. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # For updates and more info or contacting the author, visit: # # # Author's web site: www.juergen-reuter.de ROOT_DIR=.. include ../defs.mak BUILD_DIR=$(ROOT_BUILD_DIR)/DocTool COMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR) RUN_CLASSPATH=. PIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio JAVA_SRC=$(wildcard $(PIO_DIR)/doctool/RegistersDocsBuilder.java) JAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC)) MEDIA_SRC_DIR=$(JAVA_DIR)/media MEDIA_OBJ_DIR=$(BUILD_DIR)/media MEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html) MEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC)) EXAMPLES_SRC_DIR=$(JAVA_DIR)/examples EXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples EXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon) EXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC)) JAR_OBJ=$(JAR_DIR)/rp2040pio_doctool.jar RST_DOC_OBJ = \ $(RST_DOC_DIR)/example-scripts.rst \ $(RST_DOC_DIR)/monitor-commands.rst \ $(RST_DOC_DIR)/pico-emu-registers.rst \ $(RST_DOC_DIR)/pio-emu-registers.rst all: obj jar doc obj: $(BUILD_DIR) $(JAVA_OBJ) \ $(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \ $(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ) $(BUILD_DIR): mkdir -p $@ $(MEDIA_OBJ_DIR): echo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR) mkdir -p $@ $(EXAMPLES_OBJ_DIR): echo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR) mkdir -p $@ $(JAR_DIR): mkdir -p $@ $(RST_DOC_DIR): mkdir -p $@ jar: $(JAR_DIR) $(JAR_OBJ) $(JAR_OBJ): $(JAVA_OBJ) cd $(BUILD_DIR) ; \ jar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.DocTool . $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/% cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon cp -pf $< $@ depend: doc: $(RST_DOC_DIR) $(JAR_OBJ) $(RST_DOC_OBJ) $(RST_DOC_OBJ): $(JAR_OBJ) cd $(RST_DOC_DIR) ; java -jar $(JAR_OBJ) run: all cd $(JAR_DIR) ; java -jar $(JAR_OBJ) #cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main objclean: - rm -rf $(BUILD_DIR) jarclean: - rm -rf $(JAR_DIR) clean: objclean jarclean .SUFFIXES: $(SUFFIXES) .java .class $(BUILD_DIR)%class: $(JAVA_DIR)%java javac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $< # Local Variables: # coding:utf-8 # mode:Makefile # End: ================================================ FILE: java/Makefile.FifoObserver ================================================ # Makefile for Java tree of RP2040 PIO emulator # # Copyright (C) 2021 Jürgen Reuter # # 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. # # 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. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # For updates and more info or contacting the author, visit: # # # Author's web site: www.juergen-reuter.de ROOT_DIR=.. include ../defs.mak BUILD_DIR=$(ROOT_BUILD_DIR)/FifoObserver COMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR) RUN_CLASSPATH=. PIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio JAVA_SRC=$(wildcard $(PIO_DIR)/observer/fifo/FifoObserver.java) JAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC)) MEDIA_SRC_DIR=$(JAVA_DIR)/media MEDIA_OBJ_DIR=$(BUILD_DIR)/media MEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html) MEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC)) EXAMPLES_SRC_DIR=$(JAVA_DIR)/examples EXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples EXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon) EXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC)) JAR_OBJ=$(JAR_DIR)/rp2040pio_fifoobserver.jar all: obj jar obj: $(BUILD_DIR) $(JAVA_OBJ) \ $(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \ $(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ) $(BUILD_DIR): mkdir -p $@ $(MEDIA_OBJ_DIR): echo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR) mkdir -p $@ $(EXAMPLES_OBJ_DIR): echo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR) mkdir -p $@ $(JAR_DIR): mkdir -p $@ jar: $(JAR_DIR) $(JAR_OBJ) $(JAR_OBJ): $(JAVA_OBJ) cd $(BUILD_DIR) ; \ jar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.FifoObserver . $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/% cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon cp -pf $< $@ depend: run: all cd $(JAR_DIR) ; java -jar $(JAR_OBJ) #cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main objclean: - rm -rf $(BUILD_DIR) jarclean: - rm -rf $(JAR_DIR) clean: objclean jarclean .SUFFIXES: $(SUFFIXES) .java .class $(BUILD_DIR)%class: $(JAVA_DIR)%java javac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $< # Local Variables: # coding:utf-8 # mode:Makefile # End: ================================================ FILE: java/Makefile.GPIOObserver ================================================ # Makefile for Java tree of RP2040 PIO emulator # # Copyright (C) 2021 Jürgen Reuter # # 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. # # 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. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # For updates and more info or contacting the author, visit: # # # Author's web site: www.juergen-reuter.de ROOT_DIR=.. include ../defs.mak BUILD_DIR=$(ROOT_BUILD_DIR)/GPIOObserver COMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR) RUN_CLASSPATH=. PIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio JAVA_SRC=$(wildcard $(PIO_DIR)/observer/gpio/GPIOObserver.java) JAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC)) MEDIA_SRC_DIR=$(JAVA_DIR)/media MEDIA_OBJ_DIR=$(BUILD_DIR)/media MEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html) MEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC)) EXAMPLES_SRC_DIR=$(JAVA_DIR)/examples EXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples EXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon) EXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC)) JAR_OBJ=$(JAR_DIR)/rp2040pio_gpioobserver.jar all: obj jar obj: $(BUILD_DIR) $(JAVA_OBJ) \ $(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \ $(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ) $(BUILD_DIR): mkdir -p $@ $(MEDIA_OBJ_DIR): echo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR) mkdir -p $@ $(EXAMPLES_OBJ_DIR): echo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR) mkdir -p $@ $(JAR_DIR): mkdir -p $@ jar: $(JAR_DIR) $(JAR_OBJ) $(JAR_OBJ): $(JAVA_OBJ) cd $(BUILD_DIR) ; \ jar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.GPIOObserver . $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/% cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon cp -pf $< $@ depend: run: all cd $(JAR_DIR) ; java -jar $(JAR_OBJ) #cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main objclean: - rm -rf $(BUILD_DIR) jarclean: - rm -rf $(JAR_DIR) clean: objclean jarclean .SUFFIXES: $(SUFFIXES) .java .class $(BUILD_DIR)%class: $(JAVA_DIR)%java javac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $< # Local Variables: # coding:utf-8 # mode:Makefile # End: ================================================ FILE: java/Makefile.Monitor ================================================ # Makefile for Java tree of RP2040 PIO emulator # # Copyright (C) 2021 Jürgen Reuter # # 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. # # 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. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # For updates and more info or contacting the author, visit: # # # Author's web site: www.juergen-reuter.de ROOT_DIR=.. include ../defs.mak BUILD_DIR=$(ROOT_BUILD_DIR)/Monitor COMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR) RUN_CLASSPATH=. PIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio JAVA_SRC=$(wildcard $(PIO_DIR)/monitor/Monitor.java) JAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC)) MEDIA_SRC_DIR=$(JAVA_DIR)/media MEDIA_OBJ_DIR=$(BUILD_DIR)/media MEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html) MEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC)) EXAMPLES_SRC_DIR=$(JAVA_DIR)/examples EXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples EXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon) EXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC)) JAR_OBJ=$(JAR_DIR)/rp2040pio_monitor.jar all: obj jar obj: $(BUILD_DIR) $(JAVA_OBJ) \ $(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \ $(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ) $(BUILD_DIR): mkdir -p $@ $(MEDIA_OBJ_DIR): echo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR) mkdir -p $@ $(EXAMPLES_OBJ_DIR): echo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR) mkdir -p $@ $(JAR_DIR): mkdir -p $@ jar: $(JAR_DIR) $(JAR_OBJ) $(JAR_OBJ): $(JAVA_OBJ) cd $(BUILD_DIR) ; \ jar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.Monitor . $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/% cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon cp -pf $< $@ depend: run: all cd $(JAR_DIR) ; java -jar $(JAR_OBJ) #cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main objclean: - rm -rf $(BUILD_DIR) jarclean: - rm -rf $(JAR_DIR) clean: objclean jarclean .SUFFIXES: $(SUFFIXES) .java .class $(BUILD_DIR)%class: $(JAVA_DIR)%java javac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $< # Local Variables: # coding:utf-8 # mode:Makefile # End: ================================================ FILE: java/Makefile.Observer ================================================ # Makefile for Java tree of RP2040 PIO emulator # # Copyright (C) 2021 Jürgen Reuter # # 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. # # 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. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # For updates and more info or contacting the author, visit: # # # Author's web site: www.juergen-reuter.de ROOT_DIR=.. include ../defs.mak BUILD_DIR=$(ROOT_BUILD_DIR)/Observer COMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR) RUN_CLASSPATH=. PIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio JAVA_SRC=$(wildcard $(PIO_DIR)/observer/Observer.java) JAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC)) MEDIA_SRC_DIR=$(JAVA_DIR)/media MEDIA_OBJ_DIR=$(BUILD_DIR)/media MEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html) MEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC)) EXAMPLES_SRC_DIR=$(JAVA_DIR)/examples EXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples EXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon) EXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC)) JAR_OBJ=$(JAR_DIR)/rp2040pio_observer.jar all: obj jar obj: $(BUILD_DIR) $(JAVA_OBJ) \ $(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \ $(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ) $(BUILD_DIR): mkdir -p $@ $(MEDIA_OBJ_DIR): echo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR) mkdir -p $@ $(EXAMPLES_OBJ_DIR): echo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR) mkdir -p $@ $(JAR_DIR): mkdir -p $@ jar: $(JAR_DIR) $(JAR_OBJ) $(JAR_OBJ): $(JAVA_OBJ) cd $(BUILD_DIR) ; \ jar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.Observer . $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/% cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon cp -pf $< $@ depend: run: all cd $(JAR_DIR) ; java -jar $(JAR_OBJ) #cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main objclean: - rm -rf $(BUILD_DIR) jarclean: - rm -rf $(JAR_DIR) clean: objclean jarclean .SUFFIXES: $(SUFFIXES) .java .class $(BUILD_DIR)%class: $(JAVA_DIR)%java javac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $< # Local Variables: # coding:utf-8 # mode:Makefile # End: ================================================ FILE: java/Makefile.Server ================================================ # Makefile for Java tree of RP2040 PIO emulator # # Copyright (C) 2021 Jürgen Reuter # # 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. # # 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. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # For updates and more info or contacting the author, visit: # # # Author's web site: www.juergen-reuter.de ROOT_DIR=.. include ../defs.mak BUILD_DIR=$(ROOT_BUILD_DIR)/EmulationServer COMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR) RUN_CLASSPATH=. PIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio JAVA_SRC=$(wildcard $(PIO_DIR)/EmulationServer.java) JAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC)) MEDIA_SRC_DIR=$(JAVA_DIR)/media MEDIA_OBJ_DIR=$(BUILD_DIR)/media MEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html) MEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC)) EXAMPLES_SRC_DIR=$(JAVA_DIR)/examples EXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples EXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon) EXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC)) JAR_OBJ=$(JAR_DIR)/rp2040pio_server.jar all: obj jar obj: $(BUILD_DIR) $(JAVA_OBJ) \ $(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \ $(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ) $(BUILD_DIR): mkdir -p $@ $(MEDIA_OBJ_DIR): echo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR) mkdir -p $@ $(EXAMPLES_OBJ_DIR): echo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR) mkdir -p $@ $(JAR_DIR): mkdir -p $@ jar: $(JAR_DIR) $(JAR_OBJ) $(JAR_OBJ): $(JAVA_OBJ) cd $(BUILD_DIR) ; \ jar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.Server . $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/% cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex cp -pf $< $@ $(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon cp -pf $< $@ depend: run: all cd $(JAR_DIR) ; java -jar $(JAR_OBJ) #cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main objclean: - rm -rf $(BUILD_DIR) jarclean: - rm -rf $(JAR_DIR) clean: objclean jarclean .SUFFIXES: $(SUFFIXES) .java .class $(BUILD_DIR)%class: $(JAVA_DIR)%java javac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $< # Local Variables: # coding:utf-8 # mode:Makefile # End: ================================================ FILE: java/examples/addition.mon ================================================ # Script: Addition # # Monitor script for addition example (see RP2040 # datasheet, Sect. 3.6.9. "Addition"). # To be executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=8 --target=0 # The code. # ; This program uses the two's complement identity x + y == ~(~x - y) enter -a 0 -v 0x80a0 # pull enter -a 1 -v 0xa02f # mov x, ~osr enter -a 2 -v 0x80a0 # pull enter -a 3 -v 0xa047 # mov y, osr enter -a 4 -v 0x0006 # jmp test # ; this loop is equivalent to the following C code: # incr: ; while (y--) enter -a 5 -v 0x0046 # jmp x-- test ; x--; # test: ; This has the effect of subtracting y from x, eventually. enter -a 6 -v 0x0085 # jmp y-- incr enter -a 7 -v 0xa0c9 # mov isr, ~x enter -a 8 -v 0x8020 # push # Set up the program for execution on PIO 0, SM 0 with side-set 0. side-set --pio=0 --sm=0 --count=0 sm --pio=0 --sm=0 --enable=true # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=9 # Set up FIFO with 2 example operands fifo --tx --enqueue --value 0x0003 fifo --tx --enqueue --value 0x0005 # Result 0x3 + 0x5 = 0x8 should eventually appear in RX FIFO. # Done. quit ================================================ FILE: java/examples/apa102-mini.mon ================================================ # Script: APA102 Mini # Group: APA102 # # Monitor script for APA102 Mini example. # To be executed on PIO 0, SM 0. # pin_din is mapped to GPIO0 in this example. # pin_clk is mapped to GPIO1 in this example. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/apa102/apa102.pio # Configure Wrap. wrap --pio=0 --sm=0 --wrap=1 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=1 --opt=false # The code. # loop: # .wrap_target enter -a 00 -v 0x6001 # out pins, 1 side 0 enter -a 01 -v 0xb042 # nop side 1 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=2 ######## # Init program (analoguous to apa102_mini_program_init() in apa102.pio). ######## # Set pin_din and pin_clk to 0, direction out. gpio --pio=0 --gpio=0 --clear gpio --pio=0 --gpio=0 --enable gpio --pio=0 --gpio=1 --clear gpio --pio=0 --gpio=1 --enable # GPIO init pin_din and pin_clk. gpio --pio=0 --gpio=0 --init gpio --pio=0 --gpio=1 --init # SM Config Set Out Pins(base=pin_din, count=1). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=1 # SM Config Set Side-Set Pins(pin_clk). side-set --pio=0 --sm=0 --base=1 # SM Config Set Out Shift(left, autopull, threshold 32) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=32 # We only need TX, so get an 8-deep FIFO! fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0x00 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. ######## # Put example values into FIFO. fifo --enqueue --tx --value 0xa55a0ff0 fifo --enqueue --tx --value 0x1e3c78f0 # Done. quit ================================================ FILE: java/examples/apa102-rgb555.mon ================================================ # Script: APA102 RGB555 # Group: APA102 # # Monitor script for AP102 RGB555 example. # To be executed on PIO 0, SM 0. # output pin is mapped to GPIO0. # Make a full reset of the emulator. reset # Since there is no initialization in # https://github.com/raspberrypi/pico-examples # /blob/master/pio/apa102/apa102.pio, we just do it # on our own, extracting all relevant info from # the PIO program itself. # Configure Wrap. wrap --pio=0 --sm=0 --wrap=14 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=0 --opt=false # The code. # .wrap_target # public pixel_out: enter -a 00 -v 0x80e0 # pull ifempty enter -a 01 -v 0xe022 # set x, 2 # colour_loop: enter -a 02 -v 0x40e5 # in osr, 5 enter -a 03 -v 0x6065 # out null, 5 enter -a 04 -v 0x4063 # in null, 3 enter -a 05 -v 0x0042 # jmp x-- colour_loop enter -a 06 -v 0x4048 # in y, 8 enter -a 07 -v 0xa0d6 # mov isr, ::isr enter -a 08 -v 0x6061 # out null, 1 # public bit_run: enter -a 09 -v 0xe03f # set x, 31 # bit_out: enter -a 10 -v 0xe000 # set pins, 0 enter -a 11 -v 0xa606 # mov pins, isr [6] enter -a 12 -v 0xe001 # set pins, 1 enter -a 13 -v 0x46c1 # in isr, 1 [6] enter -a 14 -v 0x004a # jmp x-- bit_out # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=15 ######## # Init program (based on comments and PIO code). ######## # SM Config Set Out Pins(base=pin, count=1). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=1 # SM Config Set Out Shift(right, no autopull) fifo --pio=0 --sm=0 --tx --shift-right fifo --pio=0 --sm=0 --tx --auto=false # SM Config Set In Shift(right, no autopush) fifo --pio=0 --sm=0 --rx --shift-right fifo --pio=0 --sm=0 --rx --auto=false # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # Set consecutive pins, here just a single one. gpio --pio=0 --gpio=0 --clear # initialize out with 0 # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --enable # set direction out # GPIO Init gpio --pio=0 --gpio=0 --init # We only need TX, so get an 8-deep FIFO! fifo --pio=0 --sm=0 --join --tx ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to program entry point. registers --address=0x00 ######## # End of SM initialization. ######## # Set brightness value. Only lowest most 5 bits can be freely chosen, # i.e. valid brightness values are in the range 0…31. The upper 3 bits must be # 1. That is, valid values for setting Y are values in the range 224…255. # In this example program, we choose 245 (=0xf5). registers --y=0xf5 # brightness bit pattern 10101, with three leading "1"s # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # See https://github.com/raspberrypi/pico-examples # /blob/master/pio/apa102/apa102.pio ("APA102 command structure") for # details on the encoding of the resulting 32 bit frames that are # output to the GPIO pin. # FIFO entries: Each entry contains 2 pixels, each consisting of 5+5+5 # bits for colors red, green, and blue, respectively. The first pixel # is coded in the lower 16 bits, the next one in the upper 16 bits. # Hence, the overall binary bit representation of a FIFO entry is: # 0b0bbbbbgggggrrrrr0bbbbbgggggrrrrr. fifo --enqueue --tx --value 0x03e0001f # red, then green fifo --enqueue --tx --value 0x7fff7c00 # blue, then white # Done. quit ================================================ FILE: java/examples/auto-push-pull.mon ================================================ # Script: Auto-Push-Pull # Group: Autopush and Autopull # # Monitor script for auto-push-pull example (see RP2040 # datasheet, Sect. 3.5.4. "Autopush and Autopull"). # To be executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=1 --target=0 # Configure Side Set side-set --pio=0 --sm=0 --count=0 --opt=false # no side-set # The code. # .wrap_target enter -a 0 -v 0x6020 # out x, 32 enter -a 1 -v 0x4020 # in x,32 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=2 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Configure IN / OUT shift. fifo --pio=0 --sm=0 --rx --shift-left # ISR shift left fifo --pio=0 --sm=0 --rx --auto=true # auto push on fifo --pio=0 --sm=0 --rx --threshold=32 # auto push threshold fifo --pio=0 --sm=0 --tx --shift-left # OSR shift left fifo --pio=0 --sm=0 --tx --auto=true # auto pull on fifo --pio=0 --sm=0 --tx --threshold=32 # auto pull threshold # Configure FIFO join: No join. fifo --pio=0 --sm=0 --unjoin --tx fifo --pio=0 --sm=0 --unjoin --rx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. fifo --enqueue --tx --value 0x0 fifo --enqueue --tx --value 0x1 fifo --enqueue --tx --value 0x2 fifo --enqueue --tx --value 0x3 # Done. quit ================================================ FILE: java/examples/autopull.mon ================================================ # Script: Autopull # Group: Autopush and Autopull # # Monitor script for auto pull example (see RP2040 # datasheet, Sect. 3.5.4. "Autopush and Autopull"). # To be executed on PIO 0, SM 0. # Clock (side-set) output is mapped to GPIO 0. # Data (OUT) ouput is mapped to GPIO 1. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=1 --target=0 # Configure Side Set side-set --pio=0 --sm=0 --count=1 --opt=false # .side_set 1 # The code. # .wrap_target enter -a 0 -v 0x6101 # out pins, 1 side 0 [1] ; Shift out data bit and toggle clock low enter -a 1 -v 0xb142 # nop side 1 [1] ; toggle clock high # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=2 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Connect GPIO pins 0 and 1 with PIO 0. gpio --pio=0 --gpio=0 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=1 --init # tell GPIO to connect to PIO0 # Set direction out for these two pins. gpio --pio=0 --gpio=0 --enable # set direction out gpio --pio=0 --gpio=1 --enable # set direction out # Configure out shift. fifo --pio=0 --sm=0 --tx --shift-left # OSR shift left fifo --pio=0 --sm=0 --tx --auto=true # auto pull on fifo --pio=0 --sm=0 --tx --threshold=4 # auto pull threshold # Configure FIFO join: Join TX. fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # Configure clock (side-set) for GPIO 0. side-set --pio=0 --sm=0 --base=0 # Configure data (OUT) to output a single bit to GPIO 1. pinctrl --pio=0 --sm=0 --out-count=1 --out-base=1 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. fifo --enqueue --tx --value 0xffff0000 # bit pattern 11111111...00000000 fifo --enqueue --tx --value 0x0000ffff # bit pattern 00000000...11111111 fifo --enqueue --tx --value 0xaaaaaaaa # bit pattern 10101010...10101010 # Done. quit ================================================ FILE: java/examples/blink.mon ================================================ # Script: PIO Blink # # Monitor script for PIO Blink example. # To be executed on PIO 0, SM 0. # For LED on OUT pin, mapped to GPIO0. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/pio_blink/blink.pio # Configure Wrap. wrap --pio=0 --sm=0 --wrap=7 --target=2 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=0 --opt=false # The code. enter -a 00 -v 0x80a0 # pull block enter -a 01 -v 0x6040 # out y, 32 # .wrap_target enter -a 02 -v 0xa022 # mov x, y enter -a 03 -v 0xe001 # set pins, 1 ; turn LED on # lp1: enter -a 04 -v 0x0044 # jmp x-- lp1 ; (x + 1) cycles delay, x is 32 bit number enter -a 05 -v 0xa022 # mov x, y enter -a 06 -v 0xe000 # set pins, 0 ; turn LED off # lp2: enter -a 07 -v 0x0047 # jmp x-- lp2 ; delay for same number of cycles again # .wrap ; blink forever # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=8 ######## # Init program (analoguous to st7789_lcd_program_init() in st7789_lcd.pio). ######## # GPIO init OUT pin gpio --pio=0 --gpio=0 --init # OUT pin on GPIO0 # Set consecutive pindirs, here just a single one for OUT pin. gpio --pio=0 --gpio=0 --enable # set direction out # SM Config Set Set Pins(base=0, count=1). pinctrl --pio=0 --sm=0 --set-base=0 --set-count=1 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0x00 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. ######## # Put LED blink period length (number of clock cycles per phase minus # 3) into FIFO. For example, a value of 4 will result in overall # blink cycle length of 2 * (4 + 3) = 14 clock cycles. fifo --enqueue --tx --value 0x00000004 # Done. quit ================================================ FILE: java/examples/bmc-rx.mon ================================================ # Script: Differential Manchester RX # Group: Differential Manchester (BMC) TX and RX # # Monitor script for BMC RX example (see RP2040 datasheet, # Sect. 3.6.6. "Differential Manchester (BMC) TX and RX"). To be # executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=9 --target=5 # Configure Side Set side-set --pio=0 --sm=0 --count=0 --opt=false # The code. # public start: # initial_high: enter -a 0 -v 0x2ba0 # wait 1 pin, 0 [11] enter -a 1 -v 0x00c4 # jmp pin high_0 # high_1: enter -a 2 -v 0x4021 # in x, 1 enter -a 3 -v 0x0000 # jmp initial_high # high_0: enter -a 4 -v 0x4141 # in y, 1 [1] # .wrap_target # initial_low: enter -a 5 -v 0x2b20 # wait 0 pin, 0 [11] enter -a 6 -v 0x00c9 # jmp pin low_1 # low_0: enter -a 7 -v 0x4041 # in y, 1 enter -a 8 -v 0x0000 # jmp initial_high # low_1: enter -a 9 -v 0x4121 # in x, 1 [1] # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=10 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --disable # set direction in # Connect GPIO 0 with PIO 0 gpio --pio=0 --gpio=0 --init # Configure side set base (for WAIT). side-set --pio=0 --sm=0 --base=0 # SM Config Set JMP PIN (for JMP). pinctrl --pio=0 --sm=0 --jmp-pin=0 # Configure in shift. fifo --pio=0 --sm=0 --rx --shift-right fifo --pio=0 --sm=0 --rx --auto=true # auto push fifo --pio=0 --sm=0 --rx --threshold=32 # Join FIFO RX fifo --join --rx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Set X and Y to 1 and 0, to conveniently emit these to ISR/FIFO registers --x=1 registers --y=0 # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # While running the PIO program by triggering cycles, insert now and # then either: gpio --gpio=0 --set # or: gpio --gpio=0 --clear # after an appropriate amount of cycles to change GPIO 0 input, as # appropriate for testing / emulating external data input to the BMC # RX PIO program. # Done. quit ================================================ FILE: java/examples/bmc-tx.mon ================================================ # Script: Differential Manchester TX # Group: Differential Manchester (BMC) TX and RX # # Monitor script for BMC TX example (see RP2040 datasheet, # Sect. 3.6.6. "Differential Manchester (BMC) TX and RX"). To be # executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=7 --target=0 # Configure Side Set side-set --pio=0 --sm=0 --count=1 --opt=true # The code. # public start: # initial_high: enter -a 0 -v 0x7821 # out x, 1 side 1 enter -a 1 -v 0x0623 # jmp !x high_0 [6] # high_1: enter -a 2 -v 0x1700 # jmp initial_high side 0 [7] # high_0: enter -a 3 -v 0x0704 # jmp initial_low [7] # initial_low: enter -a 4 -v 0x7021 # out x, 1 side 0 enter -a 5 -v 0x0627 # jmp !x low_0 [6] # low_1: enter -a 6 -v 0x1f04 # jmp initial_low side 1 [7] # low_0: enter -a 7 -v 0x0700 # jmp initial_high [7] # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=8 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Set GPIO output value to 0. gpio --pio=0 --gpio=0 --clear # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --enable # set direction out # Connect GPIO 0 with PIO 0 gpio --pio=0 --gpio=0 --init # Configure side set base. side-set --pio=0 --sm=0 --base=0 # Configure out shift. fifo --pio=0 --sm=0 --tx --shift-right fifo --pio=0 --sm=0 --tx --auto=true # auto pull fifo --pio=0 --sm=0 --tx --threshold=32 # Join FIFO TX fifo --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Unlike the initialization in differential_manchester.pio, we do # *not* execute a blocking pull here since we feed in data for # immediate consumption a few lines down from here. # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. fifo --enqueue --tx --value 0x115a88a5 fifo --enqueue --tx --value 0xf0f0f0f0 # Done. quit # Hint: In the Diagram Creator, you can select 16 as "Cycles" value, # and start the "emulate" button with one of the two "OUT x, 01" # instructions being the next instruction to be executed. Then, you # can watch bit by bit producing the corresponding output level at # GPIO0, with the bits shifted one by one out of OSR. ================================================ FILE: java/examples/clocked-input.mon ================================================ # Script: Clocked Input # # Monitor script for Clocked Input example. # To be executed on PIO 0, SM 0. # Data input pin is mapped to GPIO0. # Clock input pin is mapped to GPIO1. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/clocked_input/clocked_input.pio # Configure Wrap. wrap --pio=0 --sm=0 --wrap=2 --target=0 # The code. # .wrap_target enter -a 00 -v 0x2021 # wait 0 pin 1 enter -a 01 -v 0x20a1 # wait 1 pin 1 enter -a 02 -v 0x4001 # in pins, 1 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=3 ######## # Init program (analoguous to clocked_input_program_init() in # clocked_input.pio). ######## # SM Config Set In Pins(base=pin). pinctrl --pio=0 --sm=0 --in-base=0 # Set consecutive pindirs, here two pins 0 and 1 for data and clock. gpio --pio=0 --gpio=0 --disable # set direction in gpio --pio=0 --gpio=1 --disable # set direction in # GPIO init pins. gpio --pio=0 --gpio=0 --init gpio --pio=0 --gpio=1 --init # SM Config Set In Shift(left, autopush, threshold 8) fifo --pio=0 --sm=0 --rx --shift-left fifo --pio=0 --sm=0 --rx --auto=true fifo --pio=0 --sm=0 --rx --threshold=8 # Join FIFO RX fifo --join --rx ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0x00 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. ######## # To see the clocked input in action, you currently have to manually # provide some input at GPIO0 (data input) and GPIO1 (clock input). # For that purpose, in the Monitor client application, use commands # like "gpio --gpio=0 --clear" or "gpio --gpio=0 --set" to select # value 0 or 1 as data value for GPIO0, and "gpio --gpio=1 --clear" # or "gpio --gpio=1 --set" to control the clock signal on GPIO1. # # Whenever on GPIO1 (the clock input) a change from input value 0 to # input value 1 is detected, on the next PIO clock cycle, the value # that is then found as input on GPIO0 will be recorded as a bit. Bits # are collected and packaged as bytes (=8 bits), and each byte, as soon # as complete, will be pushed onto the RX FIFO. # Done. quit ================================================ FILE: java/examples/exec-example.mon ================================================ # Script: Exec Example # # Monitor script for EXEC'd instructions # example. # # To be executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # The code. # hang: enter -a 0 -v 0x0000 # jmp hang # execute: enter -a 1 -v 0x60e0 # out exec, 32 enter -a 2 -v 0x0001 # jmp execute # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=3 # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # SM Config Set Out Shift(left, autopull, threshold 32) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=32 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0x00 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Force jump to program location 1. execute --pio=0 --sm=0 --force=0x0001 # jmp 01 # Put PIO instruction opcodes into FIFO. fifo --enqueue --tx --value 0x00006020 # out x, 32 fifo --enqueue --tx --value 0x12345678 # data to be OUTed fifo --enqueue --tx --value 0x00004020 # in x, 32 fifo --enqueue --tx --value 0x00008020 # push # Done. quit ================================================ FILE: java/examples/ext-wave.mon ================================================ # Script: External Wave # Group: Squarewave # # Monitor script for providing an external square wave signal put # onto GPIO pad 23, that changes the pin's status each 3rd clock # cycle. # This script serves as an example how to automatically set GPIO # pads in sync with another script that is executed in parallel. # See chapter "Interfacing With External Data" in the RP2040 PIO # Emulator documentation for details. # We do *not* reset the emulator, but assume that this script is run # with the emulator already properly set up. Furthermore, we assume # that the emulator starts with clock cycle 0. Monitor scripts do not # (yet) provide loops; hence we have to unroll any periodic activity # for now. # Set GPIO pin 23 to 1 gpio --gpio=23 --set # Show effect gpio # Await *absolute* clock cycle 0x00000002. # # But (just for case that we are already beyond that cycle), as # fallback, finally give up after 5 cycles, or, alternatively, after # 300 seconds. wait --address=0x58000014 --value=0x2 --cycles=5 --time=300000 # The PIO emulator performs instruction fetch & decode during clock # cycle phase 0, and instruction execution during cycle phase 1. # Therefore, the trace command of the monitor application displays the # GPIO state immediately after clock phase 1 has settled, # i.e. immediately after instruction execution has been completed. To # avoid a race between the monitor showing the GPIO state after # completion of cycle phase 1 and this external signal supplying # script updating the GPIO state also immediately after completion of # cycle phase 1, scripts like this one are advised to update signal # state instead immediately after clock phase 0 has settled # (i.e. after PIO instruction fetch & decode, but before instruction # execution). For that purpose, we do another wait for the next cycle # phase 0 to become settled. wait --address=0x5800000c --value=1 --cycles=0 --time=0 # Now, clear GPIO pin 23 to 1 gpio --gpio=23 --clear # Show effect gpio # Now wait again, but this time nor for an absolute amount of clock # cycles, but for 3 clock cycles to pass. This can be done by # specifying a value match condition that surely will never be occur, # and additionally specify a timeout of 3 cycles, such that the wait # command will be for sure terminated after the timeout (rather than # by the value to be matched). Specifically, option "--mask=0" will # mask out all bits, such that always a value of 0 will be received, # such that waiting for "--value=1" will never succeed, such that # finally the timeout will take effect after 3 cycles. Also, turn off # the default milliseconds timeout of 100 seconds by setting it to 0. wait --address=0x58000014 --mask=0 --value=1 --cycles=3 --time=0 wait --address=0x5800000c --value=1 --cycles=0 --time=0 # GPIO pin 23 := 1, and show effect. gpio --gpio=23 --set gpio # Wait again 3 cycles wait --address=0x58000014 --mask=0 --value=1 --cycles=3 --time=0 wait --address=0x5800000c --value=1 --cycles=0 --time=0 # GPIO pin 23 := 0, and show effect. gpio --gpio=23 --clear gpio # Wait. wait --address=0x58000014 --mask=0 --value=1 --cycles=3 --time=0 wait --address=0x5800000c --value=1 --cycles=0 --time=0 # GPIO pin 23 := 1, and show effect. gpio --gpio=23 --set gpio # And so on, for some more cycles... wait --address=0x58000014 --mask=0 --value=1 --cycles=3 --time=0 wait --address=0x5800000c --value=1 --cycles=0 --time=0 gpio --gpio=23 --clear gpio wait --address=0x58000014 --mask=0 --value=1 --cycles=3 --time=0 wait --address=0x5800000c --value=1 --cycles=0 --time=0 gpio --gpio=23 --set gpio # And the same with abbreviated syntax. Note that options "--cycles" # and "--time" can be completely dropped if we choose to use the # default values, which is ok, since we expect the wait to finish # within a very short period of time. wa -a 0x58000014 -m 0 -v 1 -c 3 wa -a 0x5800000c -v 1 g -g 23 -c g wa -a 0x58000014 -m 0 -v 1 -c 3 wa -a 0x5800000c -v 1 g -g 23 -s g # And some more cycles, this time without showing each change. wa -a 0x58000014 -m 0 -v 1 -c 3 wa -a 0x5800000c -v 1 g -g 23 -c wa -a 0x58000014 -m 0 -v 1 -c 3 wa -a 0x5800000c -v 1 g -g 23 -s # And finally quit, when we are done. q # Done. ================================================ FILE: java/examples/hello.mon ================================================ # Script: Hello PIO # # Monitor script for Hello PIO example. # To be executed on PIO 0, SM 0. # pin is mapped to GPIO0 in this example. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/hello_pio/hello.pio # The code. # loop: enter -a 00 -v 0x80a0 # pull enter -a 01 -v 0x6001 # out pins, 1 enter -a 02 -v 0x0000 # jmp loop # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=3 ######## # Init program (analoguous to hello_program_init() in hello.pio). ######## # SM Config Set Out Pins(base=pin, count=1). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=1 # GPIO init pin. gpio --pio=0 --gpio=0 --init # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --enable # set direction out ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0x00 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. ######## # Put example values into FIFO. Only the most significant bit of # each FIFO word is output to the GPIO output pin. The value of # all other bits is irrelevant. fifo --enqueue --tx --value 0x80000000 fifo --enqueue --tx --value 0x00000000 fifo --enqueue --tx --value 0x80000000 fifo --enqueue --tx --value 0x80000000 # Done. quit ================================================ FILE: java/examples/hub75.mon ================================================ # Script: HUB75 # # Monitor script for HUB75 example. # To be executed on PIO0, SM0 and SM1. # # This example consists of *two* PIO programs # that are executed in parallel on two # different state machines. The first PIO program # is to be executed on SM0 and cares for output pins # GPIO6…GPIO10, GPIO12 and GPIO13. The second PIO # program is to be executed on SM1 and cares for # GPIO0…GPIO5 and GPIO11. In particular: # # * SM1 RGB (rrggbb) out data pins, mapped to GPIO0…GPIO5. # * SM0 Row Select out pins are A-E, mapped to GPIO6…GPIO10. # * SM1 side-set pin 0 is clock_pin, mapped to GPIO11. # * SM0 side-set pin 0 is Latch (aka Strobe), mapped to GPIO12. # * SM0 side-set pin 1 is OEn, mapped to GPIO13. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/hub75/hub75.pio ################################################# #### #### First of the two PIO programs ("hub75_row"): #### ################################################# # Configure Wrap. wrap --pio=0 --sm=0 --wrap=2 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=2 --opt=false # The code. # .wrap_target enter -a 00 -v 0x7705 # out pins, 5 [7] side 0x2 enter -a 01 -v 0x7f3b # out x, 27 [7] side 0x3 # pulse_loop: enter -a 02 -v 0x0042 # jmp x-- pulse_loop side 0x0 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=2 ######## # Init program (analoguous to hub75_row_program_init() in hub75.pio). ######## # Set consecutive pindirs (row_base_pin=6, count=5). gpio --pio=0 --gpio=6 --enable # row "A": set direction out gpio --pio=0 --gpio=7 --enable # row "B": set direction out gpio --pio=0 --gpio=8 --enable # row "C": set direction out gpio --pio=0 --gpio=9 --enable # row "D": set direction out gpio --pio=0 --gpio=10 --enable # row "E": set direction out # Set consecutive pindirs (base=latch_base_pin=12, count=2). gpio --pio=0 --gpio=12 --enable # "STB": set direction out gpio --pio=0 --gpio=13 --enable # "OEn": set direction out # GPIO init row "A" pin. gpio --pio=0 --gpio=6 --init # row "A" pin on GPIO6 # GPIO init row "B" pin. gpio --pio=0 --gpio=7 --init # row "B" pin on GPIO7 # GPIO init row "C" pin. gpio --pio=0 --gpio=8 --init # row "C" pin on GPIO8 # GPIO init row "D" pin. gpio --pio=0 --gpio=9 --init # row "D" pin on GPIO9 # GPIO init row "E" pin. gpio --pio=0 --gpio=10 --init # row "E" pin on GPIO10 # GPIO init Latch pin. gpio --pio=0 --gpio=12 --init # Latch pin on GPIO12 # GPIO init Oen pin. gpio --pio=0 --gpio=13 --init # Oen pin on GPIO13 # SM Config Set Out Pins(base=row_base_pin=6, count=5). pinctrl --pio=0 --sm=0 --out-base=6 --out-count=5 # SM Config Set Side-Set Pins(base=latch_base_pin=12). side-set --pio=0 --sm=0 --base=12 # SM Config Set Out Shift(right, autopull, threshold 32) fifo --pio=0 --sm=0 --tx --shift-right fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=32 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --pio=0 --sm=0 --address=0x00 ######## # End of SM initialization. ######## # DO NOT YET ENABLE STATE MACHINE 0 OF PIO 0. Because, while # initializing state machine 1, we execute a forced instruction, # resulting in also executing the first cycle on SM0, if it were # enabled already now. Instead, we enable SM0 together with SM1 after # initialization of SM1. ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Lowest 5 bits define ouputs A, B, C, # D, E. Upper 27 bits define number of cycles to set Latch and Oen # both to 0, before continuing with next row. In this example, we # choose 7 cycles to keep synchronized with the other PIO program that # is executed on SM1. fifo --pio=0 --sm=0 --enqueue --tx --value 0x000000f0 # row 16, wait 7 cycles fifo --pio=0 --sm=0 --enqueue --tx --value 0x000000f1 # row 17, wait 7 cycles fifo --pio=0 --sm=0 --enqueue --tx --value 0x000000f2 # row 18, wait 7 cycles ########################################################## #### #### Second of the two PIO programs ("hub75_data_rgb888"): #### ########################################################## # Configure Wrap. wrap --pio=0 --sm=1 --wrap=18 --target=3 # Configure Side Set Count. side-set --pio=0 --sm=1 --count=1 --opt=false # The code. # public entry_point: # .wrap_target # public shift0: enter -a 03 -v 0x80a0 # pull side 0 enter -a 04 -v 0x40e1 # in osr, 1 side 0 enter -a 05 -v 0x6068 # out null, 8 side 0 enter -a 06 -v 0x40e1 # in osr, 1 side 0 enter -a 07 -v 0x6068 # out null, 8 side 0 enter -a 08 -v 0x40e1 # in osr, 1 side 0 enter -a 09 -v 0x6060 # out null, 32 side 0 # public shift1: enter -a 10 -v 0x80a0 # pull side 0 enter -a 11 -v 0x50e1 # in osr, 1 side 1 enter -a 12 -v 0x7068 # out null, 8 side 1 enter -a 13 -v 0x50e1 # in osr, 1 side 1 enter -a 14 -v 0x7068 # out null, 8 side 1 enter -a 15 -v 0x50e1 # in osr, 1 side 1 enter -a 16 -v 0x7060 # out null, 32 side 1 enter -a 17 -v 0x507a # in null, 26 side 1 enter -a 18 -v 0xb016 # mov pins, ::isr side 1 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=1 --address=3 --count=16 ######## # Init program (analoguous to hub75_data_rgb888_program_init() # in hub75.pio). ######## # Set consecutive pindirs (rgb_base_pin=7, count=6). gpio --pio=0 --gpio=0 --enable # "R0": set direction out gpio --pio=0 --gpio=1 --enable # "G0": set direction out gpio --pio=0 --gpio=2 --enable # "B0": set direction out gpio --pio=0 --gpio=3 --enable # "R1": set direction out gpio --pio=0 --gpio=4 --enable # "G1": set direction out gpio --pio=0 --gpio=5 --enable # "B1": set direction out # Set consecutive pindirs (clock_pin=11, count=1). gpio --pio=0 --gpio=11 --enable # set direction out # GPIO init RGB pins. gpio --pio=0 --gpio=0 --init # R0 gpio --pio=0 --gpio=1 --init # G0 gpio --pio=0 --gpio=2 --init # B0 gpio --pio=0 --gpio=3 --init # R1 gpio --pio=0 --gpio=4 --init # G1 gpio --pio=0 --gpio=5 --init # B1 # GPIO init clock pins. gpio --pio=0 --gpio=11 --init # SM Config Set Out Pins(base=rgb_base_pin=0, count=6). pinctrl --pio=0 --sm=1 --out-base=0 --out-count=6 # SM Config Set Side-Set Pins(base=clock_pin). side-set --pio=0 --sm=1 --base=11 # SM Config Set Out Shift(right, autopull, threshold 24) fifo --pio=0 --sm=1 --tx --shift-right fifo --pio=0 --sm=1 --tx --auto=true fifo --pio=0 --sm=1 --tx --threshold=24 # SM Config Set In Shift(left, no autopush, threshold 32) fifo --pio=0 --sm=1 --rx --shift-left fifo --pio=0 --sm=1 --rx --auto=false fifo --pio=0 --sm=1 --rx --threshold=32 # We only need TX, so get an 8-deep FIFO! fifo --pio=0 --sm=1 --join --tx ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=1 --enable=false # Clear FIFOs. fifo --pio=0 --sm=1 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=1 --clear-tx-stall fifo --pio=0 --sm=1 --clear-tx-over fifo --pio=0 --sm=1 --clear-rx-under fifo --pio=0 --sm=1 --clear-rx-stall # Restart SM. sm --pio=0 --sm=1 --restart # Restart clock. clock --pio=0 --sm=1 --restart # Set instruction pointer (PC) to address 3. registers --pio=0 --sm=1 --address=0x03 ######## # End of SM initialization. ######## execute --pio=0 --sm=1 --force=0x80a0 trace --cycles=1 # In file https://github.com/raspberrypi/pico-examples # /blob/master/pio/hub75/hub75.c, the PULL instructions # of the second PIO programs are periodically overwritten # by different op-codes, while the PIO program is running. # For demonstration purposes, do this here only once and # *before* enabling the state machines, such that they do # not start running before the program is patched, since # we currently do all control from this script. We use n=3 # in this example. enter -a 03 -v 0x6063 # out null, 3 side 0 enter -a 10 -v 0x6063 # out null, 3 side 0 # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=1 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Upper byte is unused. Lower 3 bytes # contain RGB pixels. fifo --pio=0 --sm=1 --enqueue --tx --value 0x005a0ff0 fifo --pio=0 --sm=1 --enqueue --tx --value 0x00f05aa5 fifo --pio=0 --sm=1 --enqueue --tx --value 0x00a0ff05 fifo --pio=0 --sm=1 --enqueue --tx --value 0x0005aa5f # Done. quit ================================================ FILE: java/examples/i2c.mon ================================================ # Script: I²C # # Monitor script for I²C example. # To be executed on PIO 0, SM 0. # pin_sda is fed to GPIO 0. # pin_scl is fed to GPIO 1. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/i2c/i2c.pio # Configure Wrap. wrap --pio=0 --sm=0 --wrap=17 --target=12 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=1 --opt=true # The code. # do_nack: enter -a 00 -v 0x008c #jmp y-- entry_point # Continue if NAK was expected enter -a 01 -v 0xc030 #irq wait 0 rel # Otherwise stop, ask for help # do_byte: enter -a 02 -v 0xe027 #set x, 7 # Loop 8 times # bitloop: enter -a 03 -v 0x6781 #out pindirs, 1 [7] # Serialise write data enter -a 04 -v 0xba42 #nop side 1 [2] # SCL rising edge enter -a 05 -v 0x24a1 #wait 1 pin, 1 [4] # Allow clock to be stretched enter -a 06 -v 0x4701 #in pins, 1 [7] # Sample read data in middle enter -a 07 -v 0x1743 #jmp x-- bitloop side 0 [7] ; SCL falling edge # ; Handle ACK pulse enter -a 08 -v 0x6781 #out pindirs, 1 [7] # On reads, we provide the ACK enter -a 09 -v 0xbf42 #nop side 1 [7] # SCL rising edge enter -a 10 -v 0x27a1 #wait 1 pin, 1 [7] # Allow clock to be stretched enter -a 11 -v 0x12c0 #jmp pin do_nack side 0 [2] # Test SDA for ACK/NAK # public entry_point: # .wrap_target enter -a 12 -v 0x6026 #out x, 6 # Unpack Instr count enter -a 13 -v 0x6041 #out y, 1 # Unpack the NAK ignore bit enter -a 14 -v 0x0022 #jmp !x do_byte # Instr == 0, data record. enter -a 15 -v 0x6060 #out null, 32 # Instr > 0, OSR rem. invalid # do_exec: enter -a 16 -v 0x60f0 #out exec, 16 # Exec one instr / FIFO word enter -a 17 -v 0x0050 #jmp x-- do_exec # Repeat n + 1 times # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=18 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # I/O Mapping. Assume pin_sda=0 and pin_scl=1. # SM Config Set Out Pins(base=pin_sda, count=1). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=1 # SM Config Set Set Pins(base=pin_sda, count=1). pinctrl --pio=0 --sm=0 --set-base=0 --set-count=1 # SM Config Set In Pins(base=pin_sda). pinctrl --pio=0 --sm=0 --in-base=0 # SM Config Set Side-Set Pins(pin_scl). side-set --pio=0 --sm=0 --base=1 # SM Config Set JMP PIN (jmp_pin=pin_sda). pinctrl --pio=0 --sm=0 --jmp-pin=0 # SM Config Set Out Shift(left, autopull, threshold 16) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=16 # SM Config Set In Shift(left, autopush, threshold 8) fifo --pio=0 --sm=0 --rx --shift-left fifo --pio=0 --sm=0 --rx --auto=true fifo --pio=0 --sm=0 --rx --threshold=8 # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # TODO: Try to avoid glitch: # gpio_pull_up(pin_scl) # gpio_pull_up(pin_sda) # Set pins for pin_sda, pin_scl gpio --pio=0 --gpio=0 --set # pin_sda: high gpio --pio=0 --gpio=1 --set # pin_scl: high # Set pindirs for pin_sda, pin_scl gpio --pio=0 --gpio=0 --enable # pin_sda: output gpio --pio=0 --gpio=1 --enable # pin_scl: output # GPIO init pin_sda. gpio --pio=0 --gpio=0 --init # Set GPIO Override Invert for pin_sda. gpio --gpio=0 --override-oe --invert # GPIO init pin_scl. gpio --pio=0 --gpio=1 --init # Set GPIO Override Invert for pin_scl. gpio --gpio=1 --override-oe --invert # Set pins for pin_sda, pin_scl gpio --pio=0 --gpio=0 --clear # pin_sda: low gpio --pio=0 --gpio=1 --clear # pin_scl: low interrupt --pio=0 --sm=0 --disable --irq0 interrupt --pio=0 --sm=0 --disable --irq1 interrupt --pio=0 --sm=0 --value=false ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0c. registers --address=0x0c ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Only the lowest 8 bits are used (8 # data bits, 1 stop bit). # Instructions to put into FIFO for executing START/STOP/REPSTART: # # f780 set pindirs, 0 side 0 [7] ; SCL = 0, SDA = 0 # f781 set pindirs, 1 side 0 [7] ; SCL = 0, SDA = 1 # ff80 set pindirs, 0 side 1 [7] ; SCL = 1, SDA = 0 # ff81 set pindirs, 1 side 1 [7] ; SCL = 1, SDA = 1 # Instruction format to put into FIFO: # # | 15:10 | 9 | 8:1 | 0 | # | Instr | Final | Data | NAK | # # Instr = 0 for data payload. For more details on this format, see: # https://github.com/raspberrypi/pico-examples/tree/master/pio/i2c/i2c.pio. # 0x0400: Instr = 1 => announce n + 1 = 2 instructions # 0x0000: Instructions always align with FIFO word => add padding fifo --enqueue --tx --value 0x04000000 # Now, the n + 1 = 2 instructions, one instruction per FIFO word # 0xf780: Instruction "SCL = 0, SDA = 0" # 0xa042: Instruction "nop" fifo --enqueue --tx --value 0xf7800000 fifo --enqueue --tx --value 0xa0420000 # 0x014a: data byte 0xa5 # 0x0201: data byte 0x00, marked as final byte, but NAK => stop fifo --enqueue --tx --value 0x014a0201 # Do *not* pull SCL input to 0 (no clock stretching). # Otherwise, the program would get stuck at the # "wait 1 pin, 1" instruction. gpio --gpio=1 --set # Done. quit ================================================ FILE: java/examples/logic-analyser.mon ================================================ # Script: Logic Analyser # # Monitor script for Logic Analyser example. # To be executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/logic_analyser/logic_analyser.c # Configure Side Set Count. side-set --pio=0 --sm=0 --count=0 --opt=false # The code. # .wrap_target enter -a 00 -v 0x4007 # in pins, pin_count ; in this example, pin_count=7 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=1 ######## # Init program (analoguous to logic_analyser_init() in logic_analyser.c). ######## # SM Config Set In Pins(base=pin_base). pinctrl --pio=0 --sm=0 --in-base=0 # Configure Wrap. wrap --pio=0 --sm=0 --wrap=0 --target=0 # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # SM Config Set In Shift(right, autopush, threshold) fifo --pio=0 --sm=0 --rx --shift-right fifo --pio=0 --sm=0 --rx --auto=true fifo --pio=0 --sm=0 --rx --threshold=28 # 4 samples á 7 bits # We only need RX, so get an 8-deep FIFO! fifo --pio=0 --sm=0 --join --rx ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0x00 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. ######## # Put some values onto GPIO pins to be sampled. # Bit pattern: 1010101 for GPIOs 0…6. gpio --gpio=0 --set gpio --gpio=1 --clear gpio --gpio=2 --set gpio --gpio=3 --clear gpio --gpio=4 --set gpio --gpio=5 --clear gpio --gpio=6 --set # Done. quit ================================================ FILE: java/examples/manchester-rx.mon ================================================ # Script: Manchester Serial RX # Group: Manchester Serial TX and RX # # Monitor script for Manchester Serial RX example (see RP2040 # datasheet, Sect. 3.6.5. "Manchester Serial TX and RX"). To be # executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=5 --target=3 # Configure Side Set side-set --pio=0 --sm=0 --count=0 --opt=false # The code. # start_of_0: enter -a 0 -v 0x2020 # wait 0 pin 0 enter -a 1 -v 0x4841 # in y, 1 [8] enter -a 2 -v 0x00c0 # jmp pin start_of_0 # .wrap_target # start_of_1: enter -a 3 -v 0x20a0 # wait 1 pin 0 enter -a 4 -v 0x4821 # in x, 1 [8] enter -a 5 -v 0x00c0 # jmp pin start_of_0 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=6 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --disable # set direction in # Connect GPIO 0 with PIO 0 gpio --pio=0 --gpio=0 --init # Configure side set base (for WAIT). side-set --pio=0 --sm=0 --base=0 # SM Config Set JMP PIN (for JMP). pinctrl --pio=0 --sm=0 --jmp-pin=0 # Configure in shift. fifo --pio=0 --sm=0 --rx --shift-right fifo --pio=0 --sm=0 --rx --auto=true # auto push fifo --pio=0 --sm=0 --rx --threshold=32 # Join FIFO RX fifo --join --rx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Set X and Y to 1 and 0, to conveniently emit these to ISR/FIFO registers --x=1 registers --y=0 # Assume idle low, first bit is 0. Wait until 0 detected. execute --pio=0 --sm=0 --force=0x22a0 # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # While running the PIO program by triggering cycles, insert now and # then either: gpio --gpio=0 --set # or: gpio --gpio=0 --clear # after an appropriate amount of cycles to change GPIO 0 input, as # appropriate for testing / emulating external data input to the # Manchester Serial RX PIO program. # Done. quit ================================================ FILE: java/examples/manchester-tx.mon ================================================ # Script: Manchester Serial TX # Group: Manchester Serial TX and RX # # Monitor script for Manchester Serial TX example (see RP2040 # datasheet, Sect. 3.6.5. "Manchester Serial TX and RX"). To be # executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=5 --target=0 # Configure Side Set side-set --pio=0 --sm=0 --count=1 --opt=true # The code. # .wrap_target # do_1: enter -a 0 -v 0xb542 # nop side 0 [5] enter -a 1 -v 0x1b04 # jmp get_bit side 1 [3] # do_0: enter -a 2 -v 0xbd42 # nop side 1 [5] enter -a 3 -v 0xb342 # nop side 0 [3] # public start: # get_bit: enter -a 4 -v 0x6021 # out x, 1 enter -a 5 -v 0x0022 # jmp !x do_0 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=6 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Set GPIO output value to 0. gpio --pio=0 --gpio=0 --clear # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --enable # set direction out # Connect GPIO 0 with PIO 0 gpio --pio=0 --gpio=0 --init # Configure side set base. side-set --pio=0 --sm=0 --base=0 # Configure out shift. fifo --pio=0 --sm=0 --tx --shift-right fifo --pio=0 --sm=0 --tx --auto=true # auto pull fifo --pio=0 --sm=0 --tx --threshold=32 # Join FIFO TX fifo --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. fifo --enqueue --tx --value 0x00000000 fifo --enqueue --tx --value 0x0ff0a55a fifo --enqueue --tx --value 0x12345678 # Done. quit # Hint: In the Diagram Creator, you can select 12 as "Cycles" value, # and start the "emulate" button with the "OUT x, 1" instruction # being the next instruction to be executed. Then, you can watch bit # by bit producing the corresponding output level at GPIO0, with the # bits shifted one by one out of OSR. ================================================ FILE: java/examples/manual-pull.mon ================================================ # Script: Manual Pull # Group: Autopush and Autopull # # Monitor script for manual pull example (see RP2040 # datasheet, Sect. 3.5.4. "Autopush and Autopull"). # To be executed on PIO 0, SM 0. # Clock (side-set) output is mapped to GPIO 0. # Data (OUT) ouput is mapped to GPIO 1. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=4 --target=0 # Configure Side Set side-set --pio=0 --sm=0 --count=1 --opt=true # .side_set 1 opt # The code. # .wrap_target enter -a 0 -v 0xe022 # set x, 2 ; X = bit count - 2 enter -a 1 -v 0x99a0 # pull side 1 [1] ; Stall here if no TX data # bitloop: enter -a 2 -v 0x7101 # out pins, 1 side 0 [1] ; Shift out data bit and toggle clock low enter -a 3 -v 0x1942 # jmp x-- bitloop side 1 [1] ; Loop runs 3 times enter -a 4 -v 0x7001 # out pins, 1 side 0 ; Shift out last bit before reloading X # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=5 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Connect GPIO pins 0 and 1 with PIO 0. gpio --pio=0 --gpio=0 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=1 --init # tell GPIO to connect to PIO0 # Set direction out for these two pins. gpio --pio=0 --gpio=0 --enable # set direction out gpio --pio=0 --gpio=1 --enable # set direction out # Configure out shift. fifo --pio=0 --sm=0 --tx --shift-left # OSR shift left fifo --pio=0 --sm=0 --tx --auto=false # no auto pull # Configure FIFO join: Join TX. fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # Configure clock (side-set) for GPIO 0. side-set --pio=0 --sm=0 --base=0 # Configure data (OUT) to output a single bit to GPIO 1. pinctrl --pio=0 --sm=0 --out-count=1 --out-base=1 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. fifo --enqueue --tx --value 0xaaaaaaaa # bit pattern 10101010... fifo --enqueue --tx --value 0xffffffff # bit pattern 11111111... # Done. quit ================================================ FILE: java/examples/pull-example1.mon ================================================ # Script: Pull Example 1 # Group: Autopush and Autopull # # Monitor script for pull_example1 (see RP2040 # datasheet, Sect. 3.2.3.1. "Output Shift # Register (OSR)"). # To be executed on PIO 0, SM 0. # # Each FIFO entries holds 4 bytes of output data. # Each 2nd cycle, the next byte is output on pins # GPIO0…GPIO7. # Make a full reset of the emulator. reset # The code. # loop: enter -a 0 -v 0x6008 # out pins, 8 # public entry_point: enter -a 1 -v 0x80a0 # pull enter -a 2 -v 0x6108 # out pins, 8 [1] enter -a 3 -v 0x6108 # out pins, 8 [1] enter -a 4 -v 0x6008 # out pins, 8 enter -a 5 -v 0x0000 # jmp loop # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=6 ################ # Init program. ################ # Connect GPIO pins 0…7 with PIO 0. gpio --pio=0 --gpio=0 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=1 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=2 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=3 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=4 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=5 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=6 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=7 --init # tell GPIO to connect to PIO0 # Set direction out for these two pins. gpio --pio=0 --gpio=0 --enable # set direction out gpio --pio=0 --gpio=1 --enable # set direction out gpio --pio=0 --gpio=2 --enable # set direction out gpio --pio=0 --gpio=3 --enable # set direction out gpio --pio=0 --gpio=4 --enable # set direction out gpio --pio=0 --gpio=5 --enable # set direction out gpio --pio=0 --gpio=6 --enable # set direction out gpio --pio=0 --gpio=7 --enable # set direction out # SM Config Set Out Pins(base=0, count=8). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=8 # SM Config Set Out Shift(left, no autopull) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=false # Configure FIFO join: Join TX. fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 1. registers --address=1 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. # bit patterns 10101010, 11111111, 01010101, 00000000 fifo --enqueue --tx --value 0xaaff5500 # bit patterns 00010001, 11101110, 00110011, 11001100 fifo --enqueue --tx --value 0x11ee33cc # bit patterns 01110111, 10001000, 11111111, 00000000 fifo --enqueue --tx --value 0x7788ff00 # Done. quit ================================================ FILE: java/examples/pull-example2.mon ================================================ # Script: Pull Example 2 # Group: Autopush and Autopull # # Monitor script for pull_example2 (see RP2040 # datasheet, Sect. 3.2.3.1. "Output Shift # Register (OSR)"). # To be executed on PIO 0, SM 0. # # Each FIFO entries holds 4 bytes of output data. # Each 2nd cycle, the next byte is output on pins # GPIO0…GPIO7. # Make a full reset of the emulator. reset # The code. # loop: enter -a 0 -v 0x6008 # out pins, 8 # public entry_point: enter -a 1 -v 0x0000 # jmp loop # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=2 ################ # Init program. ################ # Connect GPIO pins 0…7 with PIO 0. gpio --pio=0 --gpio=0 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=1 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=2 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=3 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=4 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=5 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=6 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=7 --init # tell GPIO to connect to PIO0 # Set direction out for these two pins. gpio --pio=0 --gpio=0 --enable # set direction out gpio --pio=0 --gpio=1 --enable # set direction out gpio --pio=0 --gpio=2 --enable # set direction out gpio --pio=0 --gpio=3 --enable # set direction out gpio --pio=0 --gpio=4 --enable # set direction out gpio --pio=0 --gpio=5 --enable # set direction out gpio --pio=0 --gpio=6 --enable # set direction out gpio --pio=0 --gpio=7 --enable # set direction out # SM Config Set Out Pins(base=0, count=8). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=8 # SM Config Set Out Shift(left, autopull, threshold 32) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=32 # Configure FIFO join: Join TX. fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 1. registers --address=1 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. # bit patterns 10101010, 11111111, 01010101, 00000000 fifo --enqueue --tx --value 0xaaff5500 # bit patterns 00010001, 11101110, 00110011, 11001100 fifo --enqueue --tx --value 0x11ee33cc # bit patterns 01110111, 10001000, 11111111, 00000000 fifo --enqueue --tx --value 0x7788ff00 # Done. quit ================================================ FILE: java/examples/pull-example3.mon ================================================ # Script: Pull Example 3 # Group: Autopush and Autopull # # Monitor script for pull_example3 (see RP2040 # datasheet, Sect. 3.2.3.1. "Output Shift # Register (OSR)"). # To be executed on PIO 0, SM 0. # # Each FIFO entries holds 4 bytes of output data. # Each 2nd cycle, the next byte is output on pins # GPIO0…GPIO7. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=0 --target=0 # The code. # public entry_point: # .wrap_target # loop: enter -a 0 -v 0x6108 # out pins, 8 [1] # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=1 ################ # Init program. ################ # Connect GPIO pins 0…7 with PIO 0. gpio --pio=0 --gpio=0 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=1 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=2 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=3 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=4 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=5 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=6 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=7 --init # tell GPIO to connect to PIO0 # Set direction out for these two pins. gpio --pio=0 --gpio=0 --enable # set direction out gpio --pio=0 --gpio=1 --enable # set direction out gpio --pio=0 --gpio=2 --enable # set direction out gpio --pio=0 --gpio=3 --enable # set direction out gpio --pio=0 --gpio=4 --enable # set direction out gpio --pio=0 --gpio=5 --enable # set direction out gpio --pio=0 --gpio=6 --enable # set direction out gpio --pio=0 --gpio=7 --enable # set direction out # SM Config Set Out Pins(base=0, count=8). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=8 # SM Config Set Out Shift(left, autopull, threshold 32) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=32 # Configure FIFO join: Join TX. fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. # bit patterns 10101010, 11111111, 01010101, 00000000 fifo --enqueue --tx --value 0xaaff5500 # bit patterns 00010001, 11101110, 00110011, 11001100 fifo --enqueue --tx --value 0x11ee33cc # bit patterns 01110111, 10001000, 11111111, 00000000 fifo --enqueue --tx --value 0x7788ff00 # Done. quit ================================================ FILE: java/examples/pull-example4.mon ================================================ # Script: Pull Example 4 # Group: Autopush and Autopull # # Monitor script like pull_example3 (see RP2040 # datasheet, Sect. 3.2.3.1. "Output Shift # Register (OSR)"), but with an output of 1 byte # every system cycle. # To be executed on PIO 0, SM 0. # # Each FIFO entries holds 4 bytes of output data. # Each 2nd cycle, the next byte is output on pins # GPIO0…GPIO7. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=0 --target=0 # The code. # public entry_point: # .wrap_target # loop: enter -a 0 -v 0x6008 # out pins, 8 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=1 ################ # Init program. ################ # Connect GPIO pins 0…7 with PIO 0. gpio --pio=0 --gpio=0 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=1 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=2 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=3 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=4 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=5 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=6 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=7 --init # tell GPIO to connect to PIO0 # Set direction out for these two pins. gpio --pio=0 --gpio=0 --enable # set direction out gpio --pio=0 --gpio=1 --enable # set direction out gpio --pio=0 --gpio=2 --enable # set direction out gpio --pio=0 --gpio=3 --enable # set direction out gpio --pio=0 --gpio=4 --enable # set direction out gpio --pio=0 --gpio=5 --enable # set direction out gpio --pio=0 --gpio=6 --enable # set direction out gpio --pio=0 --gpio=7 --enable # set direction out # SM Config Set Out Pins(base=0, count=8). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=8 # SM Config Set Out Shift(left, autopull, threshold 32) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=32 # Configure FIFO join: Join TX. fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. # bit patterns 10101010, 11111111, 01010101, 00000000 fifo --enqueue --tx --value 0xaaff5500 # bit patterns 00010001, 11101110, 00110011, 11001100 fifo --enqueue --tx --value 0x11ee33cc # bit patterns 01110111, 10001000, 11111111, 00000000 fifo --enqueue --tx --value 0x7788ff00 # Done. quit ================================================ FILE: java/examples/pwm.mon ================================================ # Script: PWM # # Monitor script for PWM example (see RP2040 datasheet, # Sect. 3.6.8. "PWM"). To be executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=6 --target=0 # Configure Side Set side-set --pio=0 --sm=0 --count=1 --opt=true # The code. enter -a 0 -v 0x9080 # pull noblock side 0 enter -a 1 -v 0xa027 # mov x, osr enter -a 2 -v 0xa046 # mov y, isr # countloop: enter -a 3 -v 0x00a5 # jmp x!=y noset enter -a 4 -v 0x1806 # jmp skip side 1 # noset: enter -a 5 -v 0xa042 # nop # skip: enter -a 6 -v 0x0083 # jmp y-- countloop # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=7 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Connect GPIO 0 with PIO 0 gpio --pio=0 --gpio=0 --init # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --enable # set direction out # Configure side set base. side-set --pio=0 --sm=0 --base=0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Set PWM period. The RP foundation's pwm.c sample program puts the # value into TX FIFO ("pio_sm_put_blocking"), then moves it to OSR by # executing a "PULL" command, and finally moves it from OSR to ISR by # executing an "OUT" command with target ISR. Thanks to our monitor # application, we can achieve the same effect with just a single # command, by directly assigning the value to the ISR register. registers --isr=5 # Put example values into FIFO. Maximum valid value is the PWM period. fifo --enqueue --tx --value 0x3 fifo --enqueue --tx --value 0x5 fifo --enqueue --tx --value 0x2 # Done. quit ================================================ FILE: java/examples/somewhat-manual-pull.mon ================================================ # Script: Somewhat Manual Pull # Group: Autopush and Autopull # # Monitor script for partial auto pull example (see RP2040 # datasheet, Sect. 3.5.4 "Autopush and Autopull"). # To be executed on PIO 0, SM 0. # Clock (side-set) output is mapped to GPIO 0. # Data (OUT) ouput is mapped to GPIO 1. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=1 --target=0 # Configure Side Set side-set --pio=0 --sm=0 --count=1 --opt=false # .side_set 1 # The code. # .wrap_target enter -a 0 -v 0x6101 # out pins, 1 side 0 [1] ; Shift out data bit and toggle clock low enter -a 1 -v 0x91e0 # pull ifempty side 1 [1] ; Stall here if no TX data # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=2 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Connect GPIO pins 0 and 1 with PIO 0. gpio --pio=0 --gpio=0 --init # tell GPIO to connect to PIO0 gpio --pio=0 --gpio=1 --init # tell GPIO to connect to PIO0 # Set direction out for these two pins. gpio --pio=0 --gpio=0 --enable # set direction out gpio --pio=0 --gpio=1 --enable # set direction out # Configure out shift. fifo --pio=0 --sm=0 --tx --shift-left # OSR shift left fifo --pio=0 --sm=0 --tx --auto=false # auto pull off fifo --pio=0 --sm=0 --tx --threshold=4 # auto pull threshold # Configure FIFO join: Join TX. fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # Configure clock (side-set) for GPIO 0. side-set --pio=0 --sm=0 --base=0 # Configure data (OUT) to output a single bit to GPIO 1. pinctrl --pio=0 --sm=0 --out-count=1 --out-base=1 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. fifo --enqueue --tx --value 0xffff0000 # bit pattern 11111111...00000000 fifo --enqueue --tx --value 0x0000ffff # bit pattern 00000000...11111111 fifo --enqueue --tx --value 0xaaaaaaaa # bit pattern 10101010...10101010 # Done. quit ================================================ FILE: java/examples/spi-cpha0-cs.mon ================================================ # Script: SPI CPHA0 with Chip Select # Group: SPI # # Monitor script for SPI CPHA0 with Chip Select example. # To be executed on PIO 0, SM 0. # SCK is side-set pin 0. # CSn is side-set pin 1 (n=1). # MOSI is OUT pin (host-to-device). # MISO is IN pin (device-to-host). # MOSI and MISO mapped to same pin, so we get loopback. # n_bits = 8 in this example. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/spi/spi.pio # Configure Wrap. wrap --pio=0 --sm=0 --wrap=8 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=2 --opt=false # The code. # .wrap_target # bitloop: enter -a 00 -v 0x6101 # out pins, 1 side 0x0 [1] enter -a 01 -v 0x4801 # in pins, 1 side 0x1 enter -a 02 -v 0x0840 # jmp x-- bitloop side 0x1 enter -a 03 -v 0x6001 # out pins, 1 side 0x0 enter -a 04 -v 0xa022 # mov x, y side 0x0 enter -a 05 -v 0x4801 # in pins, 1 side 0x1 enter -a 06 -v 0x08e0 # jmp !osre bitloop side 0x1 enter -a 07 -v 0xa142 # nop side 0x0 [1] # public entry_point: enter -a 08 -v 0x91e0 # pull ifempty side 0x2 [1] # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=9 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # SM Config Set Out Pins(base=pin_mosi, count=1). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=1 # SM Config Set In Pins(base=pin_miso). pinctrl --pio=0 --sm=0 --in-base=0 # SM Config Set Side-Set Pins(pin_sck): pin_sck=GPIO1, pin_csn=GPIO2. side-set --pio=0 --sm=0 --base=1 # SM Config Set Out Shift(left, autopull, threshold n_bits) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=8 # SM Config Set In Shift(left, autopush, threshold n_bits) fifo --pio=0 --sm=0 --rx --shift-left fifo --pio=0 --sm=0 --rx --auto=true fifo --pio=0 --sm=0 --rx --threshold=8 # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # Set Pins: pin_csn=1, pin_sck=0, pin_mosi=0. gpio --pio=0 --gpio=0 --clear # pin_mosi=0 gpio --pio=0 --gpio=1 --clear # pin_sck=0 gpio --pio=0 --gpio=2 --set # pin_csn=1 # Set PinDirs: pin_csn=out, pin_sck=out, pin_mosi=out, pin_miso=in gpio --pio=0 --gpio=0 --disable # pin_miso=in gpio --pio=0 --gpio=0 --enable # pin_mosi=out gpio --pio=0 --gpio=1 --enable # pin_sck=out gpio --pio=0 --gpio=2 --enable # pin_csn=out # GPIO Init gpio --pio=0 --gpio=0 --init # pin_miso gpio --pio=0 --gpio=0 --init # pin_mosi gpio --pio=0 --gpio=1 --init # pin_sck gpio --pio=0 --gpio=2 --init # pin_csn # Optionally (for CPOL=1), set pin_sck Override Invert. gpio --gpio=1 --override-out --invert # TODO / NOT IMPLEMENTED: # SPI is synchronous, so bypass input synchroniser to reduce input delay. # hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to program entry point. registers --address=0x08 ######## # End of SM initialization. ######## registers --x=6 registers --y=6 # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Since we chose n_bits = 8 in this # example, only the most upper bits per FIFO word are significant. fifo --enqueue --tx --value 0xff000000 fifo --enqueue --tx --value 0x55000000 # Done. quit ================================================ FILE: java/examples/spi-cpha0.mon ================================================ # Script: SPI CPHA0 # Group: SPI # # Monitor script for SPI CPHA0 example. # To be executed on PIO 0, SM 0. # SCK is side-set pin. # MOSI is OUT pin. # MISO is IN pin. # MOSI and MISO mapped to same pin, so we get loopback. # n_bits = 8 in this example. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/spi/spi.pio # Configure Wrap. wrap --pio=0 --sm=0 --wrap=1 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=1 --opt=false # The code. # .wrap_target enter -a 00 -v 0x6101 # out pins, 1 side 0 [1] enter -a 01 -v 0x5101 # in pins, 1 side 1 [1] # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=2 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # SM Config Set Out Pins(base=pin_mosi, count=1). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=1 # SM Config Set In Pins(base=pin_miso). pinctrl --pio=0 --sm=0 --in-base=0 # SM Config Set Side-Set Pins(pin_sck). side-set --pio=0 --sm=0 --base=1 # SM Config Set Out Shift(left, autopull, threshold n_bits) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=8 # SM Config Set In Shift(left, autopush, threshold n_bits) fifo --pio=0 --sm=0 --rx --shift-left fifo --pio=0 --sm=0 --rx --auto=true fifo --pio=0 --sm=0 --rx --threshold=8 # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # MOSI, SCK output are low, MISO is input gpio --pio=0 --gpio=0 --disable # MISO input gpio --pio=0 --gpio=0 --init # MISO init gpio --pio=0 --gpio=0 --enable # MOSI output gpio --pio=0 --gpio=0 --clear # MOSI low gpio --pio=0 --gpio=0 --init # MISO / MOSI init gpio --pio=0 --gpio=1 --enable # SCK output gpio --pio=0 --gpio=1 --clear # SCK low gpio --pio=0 --gpio=1 --init # SCK init # Optionally (for CPOL=1), set pin_sck Override Invert. gpio --gpio=1 --override-out --invert # TODO / NOT IMPLEMENTED: # SPI is synchronous, so bypass input synchroniser to reduce input delay. # hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0x00 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Since we chose n_bits = 8 in this # example, only the most upper bits per FIFO word are significant. fifo --enqueue --tx --value 0xff000000 fifo --enqueue --tx --value 0x55000000 # Done. quit ================================================ FILE: java/examples/spi-cpha1-cs.mon ================================================ # Script: SPI CPHA1 with Chip Select # Group: SPI # # Monitor script for SPI CPHA1 with Chip Select example. # To be executed on PIO 0, SM 0. # SCK is side-set pin 0. # CSn is side-set pin 1 (n=1). # MOSI is OUT pin (host-to-device). # MISO is IN pin (device-to-host). # MOSI and MISO mapped to same pin, so we get loopback. # n_bits = 8 in this example. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/spi/spi.pio # Configure Wrap. wrap --pio=0 --sm=0 --wrap=8 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=2 --opt=false # The code. # .wrap_target # bitloop: enter -a 00 -v 0x6901 # out pins, 1 side 0x1 [1] enter -a 01 -v 0x4001 # in pins, 1 side 0x0 enter -a 02 -v 0x0040 # jmp x-- bitloop side 0x0 enter -a 03 -v 0x6801 # out pins, 1 side 0x1 enter -a 04 -v 0xa822 # mov x, y side 0x1 enter -a 05 -v 0x4001 # in pins, 1 side 0x0 enter -a 06 -v 0x00e0 # jmp !osre bitloop side 0x0 # public entry_point: enter -a 07 -v 0x91e0 # pull ifempty side 0x2 [1] enter -a 08 -v 0xa142 # nop side 0x0 [1] # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=9 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # SM Config Set Out Pins(base=pin_mosi, count=1). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=1 # SM Config Set In Pins(base=pin_miso). pinctrl --pio=0 --sm=0 --in-base=0 # SM Config Set Side-Set Pins(pin_sck): pin_sck=GPIO1, pin_csn=GPIO2. side-set --pio=0 --sm=0 --base=1 # SM Config Set Out Shift(left, autopull, threshold n_bits) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=8 # SM Config Set In Shift(left, autopush, threshold n_bits) fifo --pio=0 --sm=0 --rx --shift-left fifo --pio=0 --sm=0 --rx --auto=true fifo --pio=0 --sm=0 --rx --threshold=8 # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # Set Pins: pin_csn=1, pin_sck=0, pin_mosi=0. gpio --pio=0 --gpio=0 --clear # pin_mosi=0 gpio --pio=0 --gpio=1 --clear # pin_sck=0 gpio --pio=0 --gpio=2 --set # pin_csn=1 # Set PinDirs: pin_csn=out, pin_sck=out, pin_mosi=out, pin_miso=in gpio --pio=0 --gpio=0 --disable # pin_miso=in gpio --pio=0 --gpio=0 --enable # pin_mosi=out gpio --pio=0 --gpio=1 --enable # pin_sck=out gpio --pio=0 --gpio=2 --enable # pin_csn=out # GPIO Init gpio --pio=0 --gpio=0 --init # pin_miso gpio --pio=0 --gpio=0 --init # pin_mosi gpio --pio=0 --gpio=1 --init # pin_sck gpio --pio=0 --gpio=2 --init # pin_csn # Optionally (for CPOL=1), set pin_sck Override Invert. gpio --gpio=1 --override-out --invert # TODO / NOT IMPLEMENTED: # SPI is synchronous, so bypass input synchroniser to reduce input delay. # hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to program entry point. registers --address=0x07 ######## # End of SM initialization. ######## registers --x=6 registers --y=6 # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Since we chose n_bits = 8 in this # example, only the most upper bits per FIFO word are significant. fifo --enqueue --tx --value 0xff000000 fifo --enqueue --tx --value 0x55000000 # Done. quit ================================================ FILE: java/examples/spi-cpha1.mon ================================================ # Script: SPI CPHA1 # Group: SPI # # Monitor script for SPI CPHA1 example. # To be executed on PIO 0, SM 0. # SCK is side-set pin. # MOSI is OUT pin. # MISO is IN pin. # MOSI and MISO mapped to same pin, so we get loopback. # n_bits = 8 in this example. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/spi/spi.pio # Configure Wrap. wrap --pio=0 --sm=0 --wrap=2 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=1 --opt=false # The code. # .wrap_target enter -a 00 -v 0x6021 # out x, 1 side 0 enter -a 01 -v 0xb101 # mov pins, x side 1 [1] enter -a 02 -v 0x4001 # in pins, 1 side 0 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=3 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # SM Config Set Out Pins(base=pin_mosi, count=1). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=1 # SM Config Set In Pins(base=pin_miso). pinctrl --pio=0 --sm=0 --in-base=0 # SM Config Set Side-Set Pins(pin_sck). side-set --pio=0 --sm=0 --base=1 # SM Config Set Out Shift(left, autopull, threshold n_bits) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=8 # SM Config Set In Shift(left, autopush, threshold n_bits) fifo --pio=0 --sm=0 --rx --shift-left fifo --pio=0 --sm=0 --rx --auto=true fifo --pio=0 --sm=0 --rx --threshold=8 # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # MOSI, SCK output are low, MISO is input gpio --pio=0 --gpio=0 --disable # MISO input gpio --pio=0 --gpio=0 --init # MISO init gpio --pio=0 --gpio=0 --enable # MOSI output gpio --pio=0 --gpio=0 --clear # MOSI low gpio --pio=0 --gpio=0 --init # MISO / MOSI init gpio --pio=0 --gpio=1 --enable # SCK output gpio --pio=0 --gpio=1 --clear # SCK low gpio --pio=0 --gpio=1 --init # SCK init # Optionally (for CPOL=1), set pin_sck Override Invert. gpio --gpio=1 --override-out --invert # TODO / NOT IMPLEMENTED: # SPI is synchronous, so bypass input synchroniser to reduce input delay. # hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0x00 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Since we chose n_bits = 8 in this # example, only the most upper bits per FIFO word are significant. fifo --enqueue --tx --value 0xff000000 fifo --enqueue --tx --value 0x55000000 # Done. quit ================================================ FILE: java/examples/spi-tx-fast.mon ================================================ # Script: SPI TX Fast # Group: SPI # # Monitor script for SPI TX Fast example, # as described in RP2040 datasheet, # Sect. 3.5.1. "Side-set". # # To be executed on PIO 0, SM 0. # Data output is fed to GPIO 0. # Clock output is fed to GPIO 1. # Make a full reset of the emulator. reset # Configure Side Set Count. side-set --pio=0 --sm=0 --count=1 --opt=false # The code. # loop: enter -a 0 -v 0x6001 # out pins, 1 side 0 enter -a 1 -v 0x1000 # jmp loop side 1 # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=2 ################ # Init program. ################ # Outputs GPIO0=data, GPIO1=clock. # Initially drive output-high on clock output. gpio --pio=0 --gpio=0 --enable # output gpio --pio=0 --gpio=0 --init gpio --pio=0 --gpio=1 --enable # output gpio --pio=0 --gpio=1 --set # high gpio --pio=0 --gpio=1 --init # OUT shifts to right, autopull, threshold 32 (use all bits). fifo --pio=0 --sm=0 --tx --shift-right fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=32 # data on GPIO0 pinctrl --pio=0 --sm=0 --out-count=1 --out-base=0 # clock on GPIO1 side-set --pio=0 --sm=0 --base=1 # We only need TX, so get an 8-deep FIFO! fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Bits are serialized in LSB to MSB order. fifo --enqueue --tx --value 0x00ff55aa fifo --enqueue --tx --value 0xcc33ee11 # Done. quit ================================================ FILE: java/examples/squarewave-fast.mon ================================================ # Script: Squarewave Fast # Group: Squarewave # # Monitor script for Squarewave example ("fast" version). # To be executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/squarewave/squarewave.c # Configure Wrap. wrap --pio=0 --sm=0 --wrap=2 --target=1 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=0 --opt=false # The code. enter -a 0 -v 0xe081 # set pindirs, 1 ; Set pin to output # .wrap_target enter -a 1 -v 0xe001 # set pins, 1 ; Drive pin high enter -a 2 -v 0xe000 # set pins, 0 ; Drive pin low # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=3 # Configure Clock Divider, here, for demonstration purposes, # with average divisor 2.5. See RP2040 datasheet, Sect. 3.5.5., # Fig. 47, for the effect of a clock divider of value 2.5 onto # the CLK_ENABLE signal. # # Note that the fast version in combination with divisor 2.5 # will result in an assymmetrical output signal (2+3 cycles), since # there are no fractional clock cycles, but the divisor specifies # the _average_ time division. To get a symmetrical signal, use # instead a non-fractional divisor such as 1, 2 or 3. clock --pio=0 --sm=0 --divider=2.5 # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --enable # set direction out # Connect GPIO 0 with PIO 0 gpio --pio=0 --gpio=0 --init # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true # Done. quit ================================================ FILE: java/examples/squarewave-wrap.mon ================================================ # Script: Squarewave Wrap # Group: Squarewave # # Monitor script for Squarewave example ("wrap" version). # To be executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/squarewave/squarewave.c # Configure Wrap. wrap --pio=0 --sm=0 --wrap=2 --target=1 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=0 --opt=false # The code. enter -a 0 -v 0xe081 # set pindirs, 1 ; Set pin to output # .wrap_target enter -a 1 -v 0xe101 # set pins, 1 [1] ; Drive pin high & delay for one cycle enter -a 2 -v 0xe100 # set pins, 0 [1] ; Drive pin low & delay for one cycle # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=3 # Configure Clock Divider, here, for demonstration purposes, # with average divisor 2.5. See RP2040 datasheet, Sect. 3.5.5., # Fig. 47, for the effect of a clock divider of value 2.5 onto # the CLK_ENABLE signal. clock --pio=0 --sm=0 --divider=2.5 # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --enable # set direction out # Connect GPIO 0 with PIO 0 gpio --pio=0 --gpio=0 --init # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true # Done. quit ================================================ FILE: java/examples/squarewave.hex ================================================ # .program squarewave # ; periodically turns off / on GPIO0 e081 e101 e000 0001 ================================================ FILE: java/examples/squarewave.mon ================================================ # Script: Squarewave # Group: Squarewave # # Monitor script for Squarewave example (base version). # To be executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/squarewave/squarewave.c # Configure Side Set Count. side-set --pio=0 --sm=0 --count=0 --opt=false # The code. enter -a 0 -v 0xe081 # set pindirs, 1 ; Set pin to output # again: enter -a 1 -v 0xe101 # set pins, 1 [1] ; Drive pin high & delay for one cycle enter -a 2 -v 0xe000 # set pins, 0 ; Drive pin low enter -a 3 -v 0x0001 # jmp again ; Set PC to label `again` # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=4 # Configure Clock Divider, here, for demonstration purposes, # with average divisor 2.5. See RP2040 datasheet, Sect. 3.5.5., # Fig. 47, for the effect of a clock divider of value 2.5 onto # the CLK_ENABLE signal. clock --pio=0 --sm=0 --divider=2.5 # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --enable # set direction out # Connect GPIO 0 with PIO 0 gpio --pio=0 --gpio=0 --init # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true # Done. quit ================================================ FILE: java/examples/st7789-lcd.mon ================================================ # Script: ST7789 LCD # # Monitor script for ST7789 LCD example. # To be executed on PIO 0, SM 0. # Data on OUT pin, mapped to GPIO0. # Clock on side-set pin, mapped to GPIO1. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/st7789_lcd/st7789_lcd.pio # Configure Wrap. wrap --pio=0 --sm=0 --wrap=1 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=1 --opt=false # The code. # .wrap_target enter -a 00 -v 0x6001 # out pins, 1 side 0 ; stall here if no data (clock low) enter -a 01 -v 0xb042 # nop side 1 # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=2 ######## # Init program (analoguous to st7789_lcd_program_init() in st7789_lcd.pio). ######## # GPIO init data_pin gpio --pio=0 --gpio=0 --init # data_pin on GPIO0 # GPIO init clk_pin gpio --pio=0 --gpio=1 --init # clk_pin on GPIO1 # Set consecutive pindirs, here just a single one for data_pin. gpio --pio=0 --gpio=0 --enable # set direction out # Set consecutive pindirs, here just a single one for clk_pin. gpio --pio=0 --gpio=1 --enable # set direction out # SM Config Set Side-Set Pins(base=clk_pin). side-set --pio=0 --sm=0 --base=1 # SM Config Set Out Pins(base=data_pin, count=1). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=1 # Set FIFO Join TX fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 # SM Config Set Out Shift(left, autopull, threshold 8) fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=8 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0x00 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Since we chose n_bits = 8 in this # example, only the most upper bits per FIFO word are significant. fifo --enqueue --tx --value 0xa5000000 fifo --enqueue --tx --value 0x0f000000 fifo --enqueue --tx --value 0x01000000 fifo --enqueue --tx --value 0x0e000000 # Done. quit ================================================ FILE: java/examples/uart-rx-mini.mon ================================================ # Script: UART RX Mini # Group: UART # # Monitor script for UART RX Mini example (see RP2040 datasheet, # Sect. 3.6.4. "UART RX"). To be executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=3 --target=0 # Configure Side Set side-set --pio=0 --sm=0 --count=0 --opt=false # The code. # .wrap_target enter -a 0 -v 0x2020 # wait 0 pin 0 enter -a 1 -v 0xea27 # set x, 7 [10] # bitloop: enter -a 2 -v 0x4001 # in pins, 1 enter -a 3 -v 0x0642 # jmp x-- bitloop [6] # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=4 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --disable # set direction in # Connect GPIO 0 with PIO 0 gpio --pio=0 --gpio=0 --init # Configure side set base (for WAIT and IN). side-set --pio=0 --sm=0 --base=0 # Configure in shift. fifo --pio=0 --sm=0 --rx --shift-right fifo --pio=0 --sm=0 --rx --auto=true # auto push fifo --pio=0 --sm=0 --rx --threshold=8 # Join FIFO RX fifo --join --rx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # While running the PIO program by triggering cycles, insert now and # then either: gpio --gpio=0 --set # or: gpio --gpio=0 --clear # after an appropriate amount of cycles to change GPIO 0 input, as # appropriate for testing / emulating external data input to the # UART RX Mini PIO program. # Done. quit ================================================ FILE: java/examples/uart-rx.mon ================================================ # Script: UART RX # Group: UART # # Monitor script for UART RX example (see RP2040 datasheet, # Sect. 3.6.4. "UART RX"). To be executed on PIO 0, SM 0. # Make a full reset of the emulator. reset # Configure Wrap. wrap --pio=0 --sm=0 --wrap=8 --target=0 # Configure Side Set side-set --pio=0 --sm=0 --count=0 --opt=false # The code. # .wrap_target # start: enter -a 0 -v 0x2020 # wait 0 pin 0 enter -a 1 -v 0xea27 # set x, 7 [10] # bitloop: enter -a 2 -v 0x4001 # in pins, 1 enter -a 3 -v 0x0642 # jmp x-- bitloop [6] enter -a 4 -v 0x00c8 # jmp pin good_stop enter -a 5 -v 0xc014 # irq 4 rel enter -a 6 -v 0x20a0 # wait 1 pin 0 enter -a 7 -v 0x0000 # jmp start # good_stop: enter -a 8 -v 0x8020 # push # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=8 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --disable # set direction in # Connect GPIO 0 with PIO 0 gpio --pio=0 --gpio=0 --init # TODO: Try to avoid glitch: # gpio_pull_up(0) # Configure side set base (for WAIT and IN). side-set --pio=0 --sm=0 --base=0 # SM Config Set JMP PIN. pinctrl --pio=0 --sm=0 --jmp-pin=0 # Configure in shift. fifo --pio=0 --sm=0 --rx --shift-right fifo --pio=0 --sm=0 --rx --auto=false # no auto push fifo --pio=0 --sm=0 --rx --threshold=32 # Join FIFO RX fifo --join --rx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # While running the PIO program by triggering cycles, insert now and # then either: gpio --gpio=0 --set # or: gpio --gpio=0 --clear # after an appropriate amount of cycles to change GPIO 0 input, as # appropriate for testing / emulating external data input to the # UART RX Mini PIO program. # Done. quit ================================================ FILE: java/examples/uart-tx.mon ================================================ # Script: UART TX # Group: UART # # Monitor script for UART TX example. # To be executed on PIO 0, SM 0. # Output is fed to GPIO 0. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/uart_tx/uart_tx.pio # Configure Wrap. wrap --pio=0 --sm=0 --wrap=3 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=1 --opt=true # The code. enter -a 0 -v 0x9fa0 # pull side 1 [7] enter -a 1 -v 0xf727 # set x, 7 side 0 [7] enter -a 2 -v 0x6001 # out pins, 1 # bitloop: enter -a 3 -v 0x0642 # jmp x-- bitloop [6] # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=4 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Tell PIO to initially drive output-high on GPIO 0. gpio --pio=0 --gpio=0 --enable # output gpio --pio=0 --gpio=0 --set # high # Then map PIO onto that pin with the IO muxes. gpio --pio=0 --gpio=0 --init # OUT shifts to right, no autopull. fifo --pio=0 --sm=0 --tx --shift-right fifo --pio=0 --sm=0 --tx --auto=false # We are mapping both OUT and side-set to the same pin, because # sometimes we need to assert user data onto the pin (with OUT) and # sometimes assert constant values (start/stop bit). pinctrl --pio=0 --sm=0 --out-count=1 --out-base=0 side-set --pio=0 --sm=0 --base=0 # We only need TX, so get an 8-deep FIFO! fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Only the lowest 8 bits are used (8 # data bits, 1 stop bit). fifo --enqueue --tx --value 0x000000a5 fifo --enqueue --tx --value 0x000000f0 # Done. quit ================================================ FILE: java/examples/ws2812-led.mon ================================================ # Script: WS2812 LED # Group: WS2812 # # Monitor script for ws2812_led example, # as described in RP2040 datasheet, # Sect. 3.2.3.4. "Scratch Registers". # # To be executed on PIO 0, SM 0. # Output is fed to GPIO 0. # Make a full reset of the emulator. reset # This example uses timing T1=7, T2=8, T3=6. # The code. # public entry_point: enter -a 0 -v 0x80a0 # pull enter -a 1 -v 0xe037 # set x, 23 ; Loop over 24 bits # bitloop: enter -a 2 -v 0xe001 # set pins, 1 ; Drive pin high enter -a 3 -v 0x6541 # out y, 1 [5] ; Shift 1 bit out, and write it to y enter -a 4 -v 0x0066 # jmp !y skip ; Skip the extra delay if the bit was 0 enter -a 5 -v 0xa542 # nop [5] # skip: enter -a 6 -v 0xe500 # set pins, 0 [5] enter -a 7 -v 0x0042 # jmp x-- bitloop ; Jump if x nonzero, and decrement x enter -a 8 -v 0x0000 # jmp entry_point # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=9 ################ # Init program. ################ # Connect GPIO 0 with PIO 0. gpio --pio=0 --gpio=0 --init # tell GPIO to connect to PIO0 # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --enable # set direction out # SM Config Set Set Pins(base=GPIO0, count=1). pinctrl --pio=0 --sm=0 --set-base=0 --set-count=1 # Configure out shift. fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=false # no auto pull # Configure FIFO join: Join TX. fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. # Format of FIFO entry is: RRRRRRRRGGGGGGGGBBBBBBBB00000000. fifo --enqueue --tx --value 0xaa118800 # RGB mode: end with byte 0x00 fifo --enqueue --tx --value 0x55881100 # RGB mode: end with byte 0x00 # Done. quit ================================================ FILE: java/examples/ws2812-parallel.mon ================================================ # Script: WS2812 Parallel # Group: WS2812 # # Monitor script for ws2812_parallel example. # To be executed on PIO 0, SM 0. # Output is fed to GPIO 0. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/ws2812/generated/ws2812.pio.h # # This example uses timing T1=2, T2=5, T3=3. # Note that, according to the discussion in # https://github.com/raspberrypi/pico-feedback/issues/121, # T1=T2=T3=2 may be a better choice. # Configure Wrap. wrap --pio=0 --sm=0 --wrap=3 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=0 --opt=false # The code. # .wrap_target enter -a 0 -v 0x6020 # out x, 32 enter -a 1 -v 0xa10b # mov pins, !null [1] enter -a 2 -v 0xa401 # mov pins, x [4] enter -a 3 -v 0xa103 # mov pins, null [1] # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=4 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Connect any of the GPIOs with PIO 0. In this example, # we choose GPIO 0-3. gpio --pio=0 --gpio=0 --init # tell GPIO0 to connect to PIO0 gpio --pio=0 --gpio=1 --init # tell GPIO1 to connect to PIO0 gpio --pio=0 --gpio=2 --init # tell GPIO2 to connect to PIO0 gpio --pio=0 --gpio=3 --init # tell GPIO3 to connect to PIO0 # Set consecutive pindirs, again 4 GPIOs, base=0. gpio --pio=0 --gpio=0 --enable # set direction out gpio --pio=0 --gpio=1 --enable # set direction out gpio --pio=0 --gpio=2 --enable # set direction out gpio --pio=0 --gpio=3 --enable # set direction out # SM Config Set Out Shift(right, autopull, threshold=32) fifo --pio=0 --sm=0 --tx --shift-right fifo --pio=0 --sm=0 --tx --auto=true fifo --pio=0 --sm=0 --tx --threshold=32 # SM Config Set Out Pins(base=0, count=4). pinctrl --pio=0 --sm=0 --out-base=0 --out-count=4 # SM Config Set Set Pins(base=0, count=4). pinctrl --pio=0 --sm=0 --set-base=0 --set-count=4 # Configure FIFO join: Join TX. fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. Since we have configured only # GPIOs 0…3, only the lowest 4 bits per FIFO word (=last hex digit) # are relevant. fifo --enqueue --tx --value 0x0000000a fifo --enqueue --tx --value 0x00000005 fifo --enqueue --tx --value 0x00000001 fifo --enqueue --tx --value 0x0000000f fifo --enqueue --tx --value 0x00000002 fifo --enqueue --tx --value 0x0000000e # Done. quit ================================================ FILE: java/examples/ws2812.hex ================================================ # .program ws2812 # ; .side_set 1 # ; .wrap_target 6221 1123 1400 a442 # ; .wrap ================================================ FILE: java/examples/ws2812.mon ================================================ # Script: WS2812 # Group: WS2812 # # Monitor script for ws2812 example. # To be executed on PIO 0, SM 0. # Output is fed to GPIO 0. # Make a full reset of the emulator. reset # We loosely follow the initialization sequence as shown in: # https://github.com/raspberrypi/pico-examples # /blob/master/pio/ws2812/generated/ws2812.pio.h # # This example uses timing T1=2, T2=5, T3=3. # Note that, according to the discussion in # https://github.com/raspberrypi/pico-feedback/issues/121, # T1=T2=T3=2 may be a better choice. # Configure Wrap. wrap --pio=0 --sm=0 --wrap=3 --target=0 # Configure Side Set Count. side-set --pio=0 --sm=0 --count=1 --opt=false # The code. # .wrap_target # bitloop: enter -a 0 -v 0x6221 # out x, 01 side 0 [2] enter -a 1 -v 0x1123 # jmp !x, 03 side 1 [1] # do_one: enter -a 2 -v 0x1400 # jmp 00 side 1 [4] # do_zero: enter -a 3 -v 0xa442 # nop side 0 [4] # .wrap # Just for convience and verification, list the program that # we just entered, as viewed by PIO 0, SM 0. unassemble --pio=0 --sm=0 --address=0 --count=4 ######## # Init program (analoguous to ws2812_program_init() in ws2812.pio.h as # created by pioasm). ######## # Connect GPIO 0 with PIO 0. gpio --pio=0 --gpio=0 --init # tell GPIO to connect to PIO0 # Set consecutive pindirs, here just a single one. gpio --pio=0 --gpio=0 --enable # set direction out # Configure side set base. side-set --pio=0 --sm=0 --base=0 # Configure out shift. fifo --pio=0 --sm=0 --tx --shift-left fifo --pio=0 --sm=0 --tx --auto=true # auto pull fifo --pio=0 --sm=0 --tx --threshold=24 # or 32 for rgbw # Configure FIFO join: Join TX. fifo --pio=0 --sm=0 --join --tx # Configure Clock Divider, here as 1.0 (maximum speed). # We choose maximum speed since we do not want to see gaps when # tracing the code. clock --pio=0 --sm=0 --divider=1.0 ######## # Initialize SM (analoguous to function pio_sm_init() in Pico C SDK). ######## # Disable state machine 0 of PIO 0 while executing the following # commands. sm --pio=0 --sm=0 --enable=false # Clear FIFOs. fifo --pio=0 --sm=0 --clear # Clear FIFO debug flags. fifo --pio=0 --sm=0 --clear-tx-stall fifo --pio=0 --sm=0 --clear-tx-over fifo --pio=0 --sm=0 --clear-rx-under fifo --pio=0 --sm=0 --clear-rx-stall # Restart SM. sm --pio=0 --sm=0 --restart # Restart clock. clock --pio=0 --sm=0 --restart # Set instruction pointer (PC) to address 0. registers --address=0 ######## # End of SM initialization. ######## # Enable state machine 0 of PIO 0 to execute the program. sm --pio=0 --sm=0 --enable=true ######## # End of program initialization. # Next, we feed in example data. ######## # Put example values into FIFO. fifo --enqueue --tx --value 0xaa118800 # RGB mode: end with byte 0x00 fifo --enqueue --tx --value 0x55881100 # RGB mode: end with byte 0x00 # Done. quit ================================================ FILE: java/media/gpl-2.0-standalone.html ================================================ GNU General Public License v2.0 - GNU Project - Free Software Foundation (FSF)

GNU GENERAL PUBLIC LICENSE

Version 2, June 1991

Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

Preamble

The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that you know you can do these things.

To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.

Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.

Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.

The precise terms and conditions for copying, distribution and modification follow.

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.

1. You may copy and distribute verbatim copies of the Program's 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 give any other recipients of the Program a copy of this License along with the Program.

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 Program or any portion of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, 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 Program, 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 Program.

In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:

a) 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; or,
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for making modifications to it. For an executable work, 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 executable. However, as a special exception, the source code 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.

If distribution of executable or 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 counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.

4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program 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.

5. 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 Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.

6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program 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 to this License.

7. 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 Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program 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 Program.

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.

8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program 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.

9. The Free Software Foundation may publish revised and/or new versions of the 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 Program 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 Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.

10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, 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

11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 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 Programs

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. 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.

one line to give the program's name and an idea of what it does.
Copyright (C) yyyy  name of author

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.

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.

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.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this when it starts in an interactive mode:

Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
type `show w'.  This is free software, and you are welcome
to redistribute it under certain conditions; type `show c' 
for details.

The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:

Yoyodyne, Inc., hereby disclaims all copyright
interest in the program `Gnomovision'
(which makes passes at compilers) written 
by James Hacker.

signature of Ty Coon, 1 April 1989
Ty Coon, President of Vice

This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.

================================================ FILE: java/org/soundpaint/rp2040pio/AddressSpace.java ================================================ /* * @(#)AddressSpace.java 1.00 21/03/03 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.IOException; public abstract class AddressSpace { static final int REG_ALIAS_RW_BITS = 0x0000; static final int REG_ALIAS_XOR_BITS = 0x1000; static final int REG_ALIAS_SET_BITS = 0x2000; static final int REG_ALIAS_CLR_BITS = 0x3000; private enum AccessMethod { NORMAL_RW, ATOMIC_XOR, ATOMIC_SET, ATOMIC_CLEAR; }; private static AccessMethod[] ACCESS_METHODS = AccessMethod.values(); protected static void checkAddressAligned(final int address) { if ((address & 0x3) != 0x0) { throw new IllegalArgumentException("address not word-aligned: " + String.format("0x%08x", address)); } } private static void checkAddressNormalRWSpace(final int address) { if ((address & 0x3000) != 0x0) { throw new IllegalArgumentException("address is not in the space of " + "normal read / write access: " + String.format("0x%08x", address)); } } public abstract String getEmulatorInfo() throws IOException; public abstract boolean providesAddress(final int address) throws IOException; public abstract String getRegisterSetId(final int address) throws IOException; public abstract String getAddressLabel(final int address) throws IOException; public abstract int readAddress(final int address) throws IOException; public abstract void writeAddressMasked(final int address, final int bits, final int mask, final boolean xor) throws IOException; public abstract int waitAddress(final int address, final int expectedValue, final int mask, final long cyclesTimeout, final long millisTimeout) throws IOException; public void writeAddress(final int address, final int value) throws IOException { checkAddressAligned(address); final AccessMethod accessMethod = ACCESS_METHODS[((address >> 12) & 0x3)]; final int mask; final int bits; switch (accessMethod) { case NORMAL_RW: mask = ~0x0; bits = value; break; case ATOMIC_XOR: mask = value; bits = value; break; case ATOMIC_SET: mask = value; bits = value; break; case ATOMIC_CLEAR: mask = value; bits = 0x0; break; default: throw new InternalError("unexpected case fall-through"); } writeAddressMasked(address & ~0x3000, bits, mask, accessMethod == AccessMethod.ATOMIC_XOR); } public void hwSetBits(final int address, final int mask) throws IOException { checkAddressNormalRWSpace(address); writeAddress(address | REG_ALIAS_SET_BITS, mask); } public void hwClearBits(final int address, final int mask) throws IOException { checkAddressNormalRWSpace(address); writeAddress(address | REG_ALIAS_CLR_BITS, mask); } public void hwXorBits(final int address, final int mask) throws IOException { checkAddressNormalRWSpace(address); writeAddress(address | REG_ALIAS_XOR_BITS, mask); } public void hwWriteMasked(final int address, final int values, final int writeMask) throws IOException { checkAddressNormalRWSpace(address); writeAddressMasked(address, values, writeMask, false); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/Bit.java ================================================ /* * @(#)Bit.java 1.00 21/02/06 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * Representation of a single bit value. */ public enum Bit { LOW(0, '0', "low", "\u001b[30;42m0\u001b[0m", "\u001b[37;41m0\u001b[0m"), HIGH(1, '1', "high", "\u001b[30;42m1\u001b[0m", "\u001b[37;41m1\u001b[0m"); private final int value; private final char charLabel; private final String level; private final String superScriptLabel; private final String subScriptLabel; private final String stringLabel; private Bit(final int value, final char charLabel, final String level, final String superScriptLabel, final String subScriptLabel) { this.value = value; this.charLabel = charLabel; this.level = level; this.superScriptLabel = superScriptLabel; this.subScriptLabel = subScriptLabel; this.stringLabel = String.valueOf(charLabel); } public int getValue() { return value; } public String getLevel() { return level; } public static Bit fromValue(final boolean value) { return value ? HIGH : LOW; } public static Bit fromValue(final int value) { if (value == LOW.value) return LOW; if (value == HIGH.value) return HIGH; throw new IllegalArgumentException("value not a bit: " + value); } public static Bit fromValue(final int value, final Bit defaultValue) { if (value == LOW.value) return LOW; if (value == HIGH.value) return HIGH; return defaultValue; } public String toChar(final Direction direction) { if (direction == null) return String.valueOf(charLabel); return direction == Direction.IN ? superScriptLabel : subScriptLabel; } public Bit inverse() { return this == LOW ? HIGH : LOW; } @Override public String toString() { return stringLabel; } }; /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/Clock.java ================================================ /* * @(#)Clock.java 1.00 21/02/05 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.util.ArrayList; import java.util.List; /** * Clock Signal Provider */ public interface Clock { public enum Phase { PHASE_0_IN_PROGRESS, PHASE_0_STABLE, PHASE_1_IN_PROGRESS, PHASE_1_STABLE }; public static interface TransitionListener { void risingEdge(final long wallClock); void fallingEdge(final long wallClock); } void addTransitionListener(final TransitionListener listener); boolean removeTransitionListener(final TransitionListener listener); long getWallClock(); } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/CmdOptions.java ================================================ /* * @(#)CommadLineOptions.java 1.00 17/01/21 * * Copyright (C) 2017, 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Parsing and managing command line options. */ public class CmdOptions { public abstract static class OptionDeclaration { private final String typeName; private final boolean mandatory; private final Character shortName; private final String longName; private final String defaultValueAsString; private final String description; private OptionDeclaration() { throw new UnsupportedOperationException("unsupported empty constructor"); } private OptionDeclaration(final String typeName, final boolean mandatory, final Character shortName, final String longName, final String defaultValueAsString, final String description) { if ((shortName == null) && (longName == null)) { throw new NullPointerException("either shortName or longName " + "must be non-null"); } this.typeName = typeName; this.mandatory = mandatory; this.shortName = shortName; this.longName = longName; this.description = description; this.defaultValueAsString = defaultValueAsString; } private String getTypeName() { return typeName; } private boolean isMandatory() { return mandatory; } private Character getShortName() { return shortName; } private String getShortNameAsString() { return shortName != null ? String.valueOf(shortName) : null; } private String getLongName() { return longName; } private String getDefaultValueAsString() { return defaultValueAsString; } private String getDescription() { return description; } abstract OptionDefinition define() throws ParseException; private String getOptionHelp() { final StringBuffer sb = new StringBuffer(); sb.append(String.format(" %s", this)); if (description != null) { sb.append(String.format("%n %s", description)); } return sb.toString(); } abstract String getDefaultTypeName(); @Override public String toString() { final StringBuffer sb = new StringBuffer(); if (shortName != null) { if (this instanceof BooleanOptionDeclaration) { sb.append("+"); sb.append(shortName); sb.append(" / -"); sb.append(shortName); } else { sb.append("-"); sb.append(shortName); } if (longName != null) { sb.append(", "); } } if (longName != null) { sb.append("--"); sb.append(longName); } if (!(this instanceof FlagOptionDeclaration) && !(this instanceof BooleanOptionDeclaration)) { sb.append("="); if (typeName != null) { sb.append(typeName); } else { sb.append(getDefaultTypeName()); } } if (defaultValueAsString != null) { sb.append(" (default: "); sb.append(defaultValueAsString != null ? defaultValueAsString : ""); sb.append(")"); } else { sb.append(" (mandatory: "); sb.append(mandatory ? "yes" : "no"); sb.append(")"); } return sb.toString(); } } public static class FlagOptionDeclaration extends OptionDeclaration { public FlagOptionDeclaration(final boolean mandatory, final Character shortName, final String longName, final Flag defaultValue, final String description) { super(null, mandatory, shortName, longName, String.valueOf(defaultValue), description); } String getDefaultTypeName() { return "FLAG"; } OptionDefinition define() throws ParseException { return new FlagOptionDefinition(this); } } public static FlagOptionDeclaration createFlagOption(final boolean mandatory, final Character shortName, final String longName, final Flag defaultValue, final String description) { return new FlagOptionDeclaration(mandatory, shortName, longName, defaultValue, description); } public static class BooleanOptionDeclaration extends OptionDeclaration { public BooleanOptionDeclaration(final boolean mandatory, final Character shortName, final String longName, final Boolean defaultValue, final String description) { super(null, mandatory, shortName, longName, defaultValue != null ? String.valueOf(defaultValue) : null, description); } String getDefaultTypeName() { return "BOOLEAN"; } OptionDefinition define() throws ParseException { return new BooleanOptionDefinition(this); } } public static BooleanOptionDeclaration createBooleanOption(final boolean mandatory, final Character shortName, final String longName, final Boolean defaultValue, final String description) { return new BooleanOptionDeclaration(mandatory, shortName, longName, defaultValue, description); } public static class IntegerOptionDeclaration extends OptionDeclaration { public IntegerOptionDeclaration(final String typeName, final boolean mandatory, final Character shortName, final String longName, final Integer defaultValue, final String description) { super(typeName, mandatory, shortName, longName, defaultValue != null ? String.valueOf(defaultValue) : null, description); } String getDefaultTypeName() { return "INTEGER"; } OptionDefinition define() throws ParseException { return new IntegerOptionDefinition(this); } } public static IntegerOptionDeclaration createIntegerOption(final String typeName, final boolean mandatory, final Character shortName, final String longName, final Integer defaultValue, final String description) { return new IntegerOptionDeclaration(typeName, mandatory, shortName, longName, defaultValue, description); } public static class FloatOptionDeclaration extends OptionDeclaration { public FloatOptionDeclaration(final String typeName, final boolean mandatory, final Character shortName, final String longName, final Float defaultValue, final String description) { super(typeName, mandatory, shortName, longName, defaultValue != null ? String.valueOf(defaultValue) : null, description); } String getDefaultTypeName() { return "FLOAT"; } OptionDefinition define() throws ParseException { return new FloatOptionDefinition(this); } } public static FloatOptionDeclaration createFloatOption(final String typeName, final boolean mandatory, final Character shortName, final String longName, final Float defaultValue, final String description) { return new FloatOptionDeclaration(typeName, mandatory, shortName, longName, defaultValue, description); } public static class StringOptionDeclaration extends OptionDeclaration { public StringOptionDeclaration(final String typeName, final boolean mandatory, final Character shortName, final String longName, final String defaultValue, final String description) { super(typeName, mandatory, shortName, longName, defaultValue, description); } String getDefaultTypeName() { return "STRING"; } OptionDefinition define() throws ParseException { return new StringOptionDefinition(this); } } public static StringOptionDeclaration createStringOption(final String typeName, final boolean mandatory, final Character shortName, final String longName, final String defaultValue, final String description) { return new StringOptionDeclaration(typeName, mandatory, shortName, longName, defaultValue, description); } public static class ParseException extends Exception { private static final long serialVersionUID = 7201021940370903355L; private final OptionDeclaration optionDeclaration; public ParseException(final String message) { this(message, (OptionDeclaration)null); } public ParseException(final String message, final OptionDeclaration optionDeclaration) { super(message); this.optionDeclaration = optionDeclaration; } public ParseException(final String message, final Throwable cause) { this(message, cause, null); } public ParseException(final String message, final Throwable cause, final OptionDeclaration optionDeclaration) { super(message, cause); this.optionDeclaration = optionDeclaration; } public ParseException(final Throwable cause) { this(cause, null); } public ParseException(final Throwable cause, final OptionDeclaration optionDeclaration) { super(cause); this.optionDeclaration = optionDeclaration; } @Override public String getMessage() { return (optionDeclaration != null ? "option " + optionDeclaration + ": " : "") + super.getMessage(); } } private abstract static class OptionDefinition { private final OptionDeclaration declaration; private T defaultValue; private T parsedValue; private OptionDefinition(final OptionDeclaration declaration) throws ParseException { this.declaration = declaration; final String defaultValueAsString = declaration.getDefaultValueAsString(); defaultValue = defaultValueAsString != null ? parse(defaultValueAsString) : null; parsedValue = null; } abstract T parse(final String strValue) throws ParseException; private T parseAndSet(final String strValue) throws ParseException { parsedValue = parse(strValue); return parsedValue; } protected OptionDeclaration getDeclaration() { return declaration; } protected void setParsedValue(final T value) { if (parsedValue == null) { throw new NullPointerException("parsedValue"); } this.parsedValue = parsedValue; } protected void clear() { parsedValue = null; } protected boolean isParsed() { return parsedValue != null; } protected boolean hasDefaultValue() { return defaultValue != null; } private boolean isDefined() { return isParsed() || hasDefaultValue(); } private boolean isDefinedIfMandatory() { return isDefined() || !declaration.isMandatory(); } protected T getValue() { return isParsed() ? parsedValue : defaultValue; } @Override public String toString() { return declaration.toString(); } } public static enum Flag { OFF("off"), ON("on"); private final String displayValue; private Flag(final String displayValue) { this.displayValue = displayValue; } public boolean isOff() { return this == OFF; } public boolean isOn() { return this == ON; } @Override public String toString() { return displayValue; } } public static class FlagOptionDefinition extends OptionDefinition { public FlagOptionDefinition(final FlagOptionDeclaration declaration) throws ParseException { super(declaration); } @Override Flag parse(final String strValue) throws ParseException { final String normalizedStrValue = strValue.trim(); if (Flag.OFF.toString().equals(normalizedStrValue)) { return Flag.OFF; } else if (Flag.ON.toString().equals(normalizedStrValue)) { return Flag.ON; } else { throw new ParseException("'" + Flag.OFF + "' or " + "'" + Flag.ON + "' expected, " + "but found: " + strValue, getDeclaration()); } } } public static class BooleanOptionDefinition extends OptionDefinition { public BooleanOptionDefinition(final BooleanOptionDeclaration declaration) throws ParseException { super(declaration); } @Override Boolean parse(final String strValue) throws ParseException { final String normalizedStrValue = strValue.trim(); if ("false".equals(normalizedStrValue)) { return false; } else if ("true".equals(normalizedStrValue)) { return true; } else { throw new ParseException("'false' or 'true' expected, " + "but found: " + strValue, getDeclaration()); } } } public static class IntegerOptionDefinition extends OptionDefinition { public IntegerOptionDefinition(final IntegerOptionDeclaration declaration) throws ParseException { super(declaration); } @Override Integer parse(final String strValue) throws ParseException { final String normalizedStrValue = strValue.toLowerCase().trim(); try { if (normalizedStrValue.startsWith("0x")) { return Integer.parseUnsignedInt(normalizedStrValue.substring(2), 16); } else { return Integer.parseInt(normalizedStrValue); } } catch (final NumberFormatException e) { throw new ParseException("integer value expected: " + e.getMessage(), getDeclaration()); } } } public static class FloatOptionDefinition extends OptionDefinition { public FloatOptionDefinition(final FloatOptionDeclaration declaration) throws ParseException { super(declaration); } @Override Float parse(final String strValue) throws ParseException { final String normalizedStrValue = strValue.toLowerCase().trim(); try { return Float.parseFloat(normalizedStrValue); } catch (final NumberFormatException e) { throw new ParseException("float value expected: " + e.getMessage(), getDeclaration()); } } } public static class StringOptionDefinition extends OptionDefinition { public StringOptionDefinition(final StringOptionDeclaration declaration) throws ParseException { super(declaration); } @Override String parse(final String strValue) throws ParseException { return strValue; } } private final String prgName; private final String prgSingleLineDescription; private final String prgNotes; private final List> declarations; private final OptionDefinition[] definitions; private String parsedCommand; public CmdOptions(final String prgName, final String prgSingleLineDescription, final String prgNotes, final OptionDeclaration ... declarations) throws ParseException { this(prgName, prgSingleLineDescription, prgNotes, Arrays.asList(declarations)); } public CmdOptions(final String prgName, final String prgSingleLineDescription, final String prgNotes, final List> declarations) throws ParseException { if (prgName == null) { throw new NullPointerException("prgName"); } if (declarations == null) { throw new NullPointerException("declarations"); } this.prgName = prgName; this.prgSingleLineDescription = prgSingleLineDescription; this.prgNotes = prgNotes; this.declarations = declarations; checkShortNamesAreUnique(); checkLongNamesAreUnique(); definitions = createDefinitions(); } private String getPrgName() { return prgName; } private String getPrgSingleLineDescription() { return prgSingleLineDescription; } private String getPrgNotes() { return prgNotes; } public String getUsage() { return prgName + " [OPTION]…"; } public String getOptionsHelp() { final StringBuffer sb = new StringBuffer(); for (final OptionDeclaration declaration : declarations) { sb.append(String.format("%s%n", declaration.getOptionHelp())); } return sb.toString(); } public String getFullInfo() { final StringBuffer sb = new StringBuffer(); sb.append(String.format("Usage: %s%n%n", getUsage())); if (prgSingleLineDescription != null) { sb.append(String.format("%s%n%n", prgSingleLineDescription)); } final String optionsHelp = getOptionsHelp(); if (!optionsHelp.isEmpty()) { sb.append(String.format("Options:%n%n")); sb.append(optionsHelp); } if (prgNotes != null) { sb.append(String.format("%nNotes:%n" + prgNotes + "%n")); } return sb.toString(); } private void checkShortNamesAreUnique() { final Map> shortName2Declaration = new HashMap>(); for (final OptionDeclaration declaration : declarations) { final Character shortName = declaration.getShortName(); if (shortName != null) { if (shortName2Declaration.containsKey(shortName)) { final OptionDeclaration otherDeclaration = shortName2Declaration.get(shortName); final String message = String.format("duplicate short name '%s' for options:%n%s%n%s", shortName, declaration, otherDeclaration); throw new IllegalArgumentException(message); } shortName2Declaration.put(shortName, declaration); } } } private void checkLongNamesAreUnique() { final Map> longName2Declaration = new HashMap>(); for (final OptionDeclaration declaration : declarations) { final String longName = declaration.getLongName(); if (longName2Declaration.containsKey(longName)) { final OptionDeclaration otherDeclaration = longName2Declaration.get(longName); final String message = String.format("duplicate long name \"%s\" for options:%n%s%n%s", longName, declaration, otherDeclaration); throw new IllegalArgumentException(message); } longName2Declaration.put(longName, declaration); } } private static final OptionDefinition[] EMPTY_DEFINITIONS = new OptionDefinition[0]; private OptionDefinition[] createDefinitions() throws ParseException { final ArrayList> definitionList = new ArrayList>(); for (final OptionDeclaration declaration : declarations) { final OptionDefinition definition = declaration.define(); definitionList.add(definition); } return definitionList.toArray(EMPTY_DEFINITIONS); } public void clear() { parsedCommand = null; for (final OptionDefinition definition : definitions) { definition.clear(); } } private boolean updateDefinition(final OptionDefinition definition, String strValue) throws ParseException { if (definition.isParsed()) { throw new ParseException("option redefined", definition.getDeclaration()); } if (definition instanceof FlagOptionDefinition) { if (strValue != null) { throw new ParseException("unexpected surplus argument: " + strValue, definition.getDeclaration()); } definition.parseAndSet(Flag.ON.toString()); return false; } else if (strValue != null) { definition.parseAndSet(strValue); return false; } else { return true; } } private OptionDefinition parseLongOptionIdentifier(final String option) throws ParseException { if (option.isEmpty()) { throw new ParseException("long option must not be empty: --"); } final String name; final String value; final int pos = option.indexOf('='); if (pos >= 0) { name = option.substring(0, pos); value = option.substring(pos + 1); } else { name = option; value = null; } if (name.isEmpty()) { throw new ParseException("long option name must not be empty: --" + option); } OptionDefinition definition = null; for (final OptionDeclaration declaration : declarations) { final String longName = declaration.getLongName(); if (name.equals(longName)) { definition = findDefinitionForDeclaration(declaration); break; } } if (definition != null) { return updateDefinition(definition, value) ? definition : null; } else { throw new ParseException("unknown long option name: " + name); } } private OptionDefinition parseShortOptionIdentifier(final boolean plus, final String name) throws ParseException { if (name.length() != 1) { throw new ParseException("short option name: " + "expected single character, but found: " + name); } OptionDefinition definition = null; for (final OptionDeclaration declaration : declarations) { final String shortName = declaration.getShortNameAsString(); if (name.equals(shortName)) { definition = findDefinitionForDeclaration(declaration); break; } } if (definition != null) { if (definition.getDeclaration() instanceof BooleanOptionDeclaration) { final String value = plus ? "true" : "false"; return updateDefinition(definition, value) ? definition : null; } if (plus) { throw new ParseException("'+' valid only on Boolean options: " + name); } return updateDefinition(definition, null) ? definition : null; } else { throw new ParseException("unknown short option name: " + name); } } private OptionDefinition parseOptionIdentifier(final String arg) throws ParseException { if (arg.startsWith("--")) { return parseLongOptionIdentifier(arg.substring(2)); } else if (arg.startsWith("-")) { return parseShortOptionIdentifier(false, arg.substring(1)); } else if (arg.startsWith("+")) { return parseShortOptionIdentifier(true, arg.substring(1)); } else { throw new ParseException("option identifier expected, but found: " + arg); } } private static boolean isWhiteSpace(final char ch) { return (ch <= ' ') || (ch == '\t') || (ch == '\n') || (ch == '\r'); } private static final String[] EMPTY_STRING_ARRAY = new String[0]; public static String[] splitArgs(final String args) throws ParseException { if ((args == null) || args.isEmpty()) { return EMPTY_STRING_ARRAY; } final List argv = new ArrayList(); final StringBuffer token = new StringBuffer(); boolean inToken = false; boolean quoted = false; boolean escaped = false; for (int pos = 0; pos < args.length(); pos++) { final char ch = args.charAt(pos); if (!inToken) { if (isWhiteSpace(ch)) continue; if (ch == '#') break; token.setLength(0); inToken = true; } if (escaped) { if ((ch != ' ') && (ch != '"') && (ch != '#') && (ch != '\\')) { throw new ParseException("unsupported escaped character: " + ch); } token.append(ch); escaped = false; continue; } if (ch == '\\') { escaped = true; continue; } if (ch == '"') { quoted = !quoted; continue; } if ((ch == ' ') && !quoted) { inToken = false; argv.add(token.toString()); continue; } if ((ch == '#') && !quoted) { break; } token.append(ch); } if (escaped) { throw new ParseException("premature end of line after escape symbol"); } if (quoted) { throw new ParseException("missing closing quotation marks"); } if (inToken) { argv.add(token.toString()); } return argv.toArray(EMPTY_STRING_ARRAY); } public void parse(final String argv[]) throws ParseException { parse(argv, false); } public void parse(final String argv[], final boolean includesCommand) throws ParseException { clear(); OptionDefinition currentOption = null; for (final String arg : argv) { if (arg == null) { throw new NullPointerException("arg"); } if (includesCommand && (parsedCommand == null)) { parsedCommand = arg; continue; } if (currentOption != null) { if (currentOption.isParsed()) { throw new ParseException("option redefined", currentOption.getDeclaration()); } currentOption.parseAndSet(arg); currentOption = null; } else { currentOption = parseOptionIdentifier(arg); } } if (currentOption != null) { throw new ParseException("missing argument", currentOption.getDeclaration()); } for (final OptionDefinition definition : definitions) { if (!definition.isDefinedIfMandatory()) { throw new ParseException("mandatory option not specified", definition.getDeclaration()); } } } public String getCommand() { return parsedCommand; } private OptionDefinition findDefinitionForDeclaration(final OptionDeclaration declaration) { // TODO: Performance: Pre-build a hash map, rather than // iterating each time thorugh all definitions. for (final OptionDefinition definition : definitions) { if (definition.getDeclaration() == declaration) { return definition; } } return null; } private void checkForDeclaration(final OptionDeclaration declaration) { if (!declarations.contains(declaration)) { final String message = String.format("unregistered declaration: %s", declaration); throw new IllegalArgumentException(message); } } public Flag getValue(final FlagOptionDeclaration declaration) { checkForDeclaration(declaration); final FlagOptionDefinition definition = (FlagOptionDefinition)findDefinitionForDeclaration(declaration); return definition != null ? definition.getValue() : null; } public Boolean getValue(final BooleanOptionDeclaration declaration) { checkForDeclaration(declaration); final BooleanOptionDefinition definition = (BooleanOptionDefinition)findDefinitionForDeclaration(declaration); return definition != null ? definition.getValue() : null; } public Integer getValue(final IntegerOptionDeclaration declaration) { checkForDeclaration(declaration); final IntegerOptionDefinition definition = (IntegerOptionDefinition)findDefinitionForDeclaration(declaration); return definition != null ? definition.getValue() : null; } public Float getValue(final FloatOptionDeclaration declaration) { checkForDeclaration(declaration); final FloatOptionDefinition definition = (FloatOptionDefinition)findDefinitionForDeclaration(declaration); return definition != null ? definition.getValue() : null; } public String getValue(final StringOptionDeclaration declaration) { checkForDeclaration(declaration); final StringOptionDefinition definition = (StringOptionDefinition)findDefinitionForDeclaration(declaration); return definition != null ? definition.getValue() : null; } public boolean isDefined(final OptionDeclaration declaration) { final OptionDefinition definition = findDefinitionForDeclaration(declaration); if (definition == null) { throw new InternalError("no such declaration in this set of options: " + declaration); } return definition.isDefined(); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/CollapsiblePanel.java ================================================ /* * @(#)CollapsiblePanel.java 1.00 21/06/05 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.awt.Component; import java.awt.event.ActionListener; import java.util.Objects; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JToggleButton; import javax.swing.JLabel; import javax.swing.JPanel; public class CollapsiblePanel extends JPanel { private static final long serialVersionUID = -1313958807949980972L; private final Component component; private final JToggleButton btToggle; public CollapsiblePanel(final Component component, final String label, final boolean initiallyOpen) { Objects.requireNonNull(component); Objects.requireNonNull(label); this.component = component; setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); btToggle = new JToggleButton(); btToggle.setBorderPainted(false); btToggle.setContentAreaFilled(false); btToggle.setFocusPainted(false); btToggle.setOpaque(false); btToggle.setBorder(BorderFactory.createEmptyBorder()); final ActionListener toggleAction = (action) -> { final boolean isSelected = btToggle.isSelected(); component.setVisible(isSelected); btToggle.setText(isSelected ? "⊟" : "⊞"); }; btToggle.addActionListener(toggleAction); add(createToggleButtonLine(label)); add(component); btToggle.setSelected(initiallyOpen); toggleAction.actionPerformed(null); } private Box createToggleButtonLine(final String label) { final Box hBox = new Box(BoxLayout.LINE_AXIS); hBox.add(btToggle); // Workaround: hBox.add(Box.createHorizontalStrut(5)) modifies Y // layout behavior. => Add JLabel instead. hBox.add(new JLabel(" ")); hBox.add(new JLabel(label)); hBox.add(Box.createHorizontalGlue()); return hBox; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/Constants.java ================================================ /* * @(#)Constants.java 1.00 21/02/27 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; public interface Constants { public static final String PROGRAM_ID = "RP2040 PIO Emulator"; public static final String VERSION_ID = "0.1"; public static final String COPYRIGHT_TAG_LINE = String.format("Copyright © 2021 by Jürgen Reuter, Karlsruhe, Germany.%n"); public static final String CMD_LINE_COPYRIGHT_NOTICE = String.format(COPYRIGHT_TAG_LINE + "%n" + "This software comes with ABSOLUTELY NO WARRANTY;%n" + "for details please look at the license file that you%n" + "should have received together with this software.%n" + "This is free software, and you are welcome to%n" + "redistribute it under certain conditions;%n" + "for details please look at the license file that you%n" + "should have received together with this software.%n"); public static final String MONITOR_COPYRIGHT_NOTICE = String.format(COPYRIGHT_TAG_LINE + "%n" + "This software comes with ABSOLUTELY NO WARRANTY;%n" + "for details please look at the license file that you%n" + "should have received together with this software.%n" + "This is free software, and you are welcome to%n" + "redistribute it under certain conditions;%n" + "for details please look at the license file that you%n" + "should have received together with this software.%n"); public static final String GUI_COPYRIGHT_NOTICE = String.format(COPYRIGHT_TAG_LINE + "%n" + "This software comes with ABSOLUTELY NO WARRANTY;%n" + "for details type `Alt-L' in the main window.%n" + "This is free software, and you are welcome to%n" + "redistribute it under certain conditions;%n" + "type `Alt-L' in the main window for details.%n"); public static String getEmulatorId() { return PROGRAM_ID; } public static String getEmulatorVersion() { return VERSION_ID; } public static String getCmdLineCopyrightNotice() { return CMD_LINE_COPYRIGHT_NOTICE; } public static String getMonitorCopyrightNotice() { return MONITOR_COPYRIGHT_NOTICE; } public static String getGuiCopyrightNotice() { return GUI_COPYRIGHT_NOTICE; } public static String getEmulatorIdAndVersionWithOs() { return getEmulatorId() + " Version " + getEmulatorVersion() + " / jvm " + System.getProperty("java.version") + " / " + System.getProperty("java.class.version"); } public static final int GPIO_NUM = 32; public static final int MEMORY_SIZE = 32; public static final int FIFO_DEPTH = 4; public static final int PIO_NUM = 2; public static final int SM_COUNT = 4; public static final int INTR_NUM = 4; public static final int DEFAULT_FREQUENCY = 1000000000; // address map public static final int IO_BANK0_BASE = 0x40014000; public static final int PADS_BANK0_BASE = 0x4001c000; public static final int PIO0_BASE = 0x50200000; public static final int PIO0_EMU_BASE = PIO0_BASE + 0x08000000; public static final int PIO1_BASE = 0x50300000; public static final int PIO1_EMU_BASE = PIO1_BASE + 0x08000000; public static final int EMULATOR_BASE = 0x58000000; public static int getPIOBaseAddress(final int pioNum) { checkPioNum(pioNum, "PIO index number"); return pioNum == 0 ? PIO0_BASE : PIO1_BASE; } public static int getPIOEmuBaseAddress(final int pioNum) { checkPioNum(pioNum, "PIO index number"); return pioNum == 0 ? PIO0_EMU_BASE : PIO1_EMU_BASE; } // Emulator registers addressing public static final int PICO_PWR_UP_VALUE = 0xa55a5aa5; // GPIO registers addressing public static final int IO_BANK0_GPIO0_CTRL_IRQOVER_LSB = 28; public static final int IO_BANK0_GPIO0_CTRL_IRQOVER_BITS = 0x30000000; public static final int IO_BANK0_GPIO0_CTRL_INOVER_LSB = 16; public static final int IO_BANK0_GPIO0_CTRL_INOVER_BITS = 0x00030000; public static final int IO_BANK0_GPIO0_CTRL_OEOVER_LSB = 12; public static final int IO_BANK0_GPIO0_CTRL_OEOVER_BITS = 0x00003000; public static final int IO_BANK0_GPIO0_CTRL_OUTOVER_LSB = 8; public static final int IO_BANK0_GPIO0_CTRL_OUTOVER_BITS = 0x00000300; public static final int IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB = 0; public static final int IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS = 0x0000001f; public static final int IO_BANK0_GPIO0_STATUS_IRQTOPROC_LSB = 26; public static final int IO_BANK0_GPIO0_STATUS_IRQTOPROC_BITS = 0x04000000; public static final int IO_BANK0_GPIO0_STATUS_IRQFROMPAD_LSB = 24; public static final int IO_BANK0_GPIO0_STATUS_IRQFROMPAD_BITS = 0x01000000; public static final int IO_BANK0_GPIO0_STATUS_INTOPERI_LSB = 19; public static final int IO_BANK0_GPIO0_STATUS_INTOPERI_BITS = 0x00080000; public static final int IO_BANK0_GPIO0_STATUS_INFROMPAD_LSB = 17; public static final int IO_BANK0_GPIO0_STATUS_INFROMPAD_BITS = 0x00020000; public static final int IO_BANK0_GPIO0_STATUS_OETOPAD_LSB = 13; public static final int IO_BANK0_GPIO0_STATUS_OETOPAD_BITS = 0x00002000; public static final int IO_BANK0_GPIO0_STATUS_OEFROMPERI_LSB = 12; public static final int IO_BANK0_GPIO0_STATUS_OEFROMPERI_BITS = 0x00001000; public static final int IO_BANK0_GPIO0_STATUS_OUTTOPAD_LSB = 9; public static final int IO_BANK0_GPIO0_STATUS_OUTTOPAD_BITS = 0x00000200; public static final int IO_BANK0_GPIO0_STATUS_OUTFROMPERI_LSB = 8; public static final int IO_BANK0_GPIO0_STATUS_OUTFROMPERI_BITS = 0x00000100; public static final int PADS_BANK0_GPIO0_IE_LSB = 6; public static final int PADS_BANK0_GPIO0_IE_BITS = 0x00000040; // PIO registers addressing public static final int CTRL_CLKDIV_RESTART_LSB = 8; public static final int CTRL_CLKDIV_RESTART_BITS = 0x00000f00; public static final int CTRL_SM_RESTART_LSB = 4; public static final int CTRL_SM_RESTART_BITS = 0x000000f0; public static final int CTRL_SM_ENABLE_LSB = 0; public static final int CTRL_SM_ENABLE_BITS = 0x0000000f; public static final int FSTAT_TXEMPTY_LSB = 24; public static final int FSTAT_TXEMPTY_BITS = 0x0f000000; public static final int FSTAT_TXFULL_LSB = 16; public static final int FSTAT_TXFULL_BITS = 0x000f0000; public static final int FSTAT_RXEMPTY_LSB = 8; public static final int FSTAT_RXEMPTY_BITS = 0x00000f00; public static final int FSTAT_RXFULL_LSB = 0; public static final int FSTAT_RXFULL_BITS = 0x0000000f; public static final int FDEBUG_TXSTALL_LSB = 24; public static final int FDEBUG_TXSTALL_BITS = 0x0f000000; public static final int FDEBUG_TXOVER_LSB = 16; public static final int FDEBUG_TXOVER_BITS = 0x000f0000; public static final int FDEBUG_RXUNDER_LSB = 8; public static final int FDEBUG_RXUNDER_BITS = 0x00000f00; public static final int FDEBUG_RXSTALL_LSB = 0; public static final int FDEBUG_RXSTALL_BITS = 0x0000000f; public static final int FLEVEL_RX1_LSB = 12; public static final int FLEVEL_RX1_BITS = 0x0000f000; public static final int FLEVEL_TX1_LSB = 8; public static final int FLEVEL_TX1_BITS = 0x00000f00; public static final int FLEVEL_RX0_LSB = 4; public static final int FLEVEL_RX0_BITS = 0x000000f0; public static final int FLEVEL_TX0_LSB = 0; public static final int FLEVEL_TX0_BITS = 0x0000000f; public static final int SM0_CLKDIV_INT_LSB = 16; public static final int SM0_CLKDIV_INT_BITS = 0xffff0000; public static final int SM0_CLKDIV_FRAC_LSB = 8; public static final int SM0_CLKDIV_FRAC_BITS = 0x0000ff00; public static final int SM0_EXECCTRL_EXEC_STALLED_LSB = 31; public static final int SM0_EXECCTRL_EXEC_STALLED_BITS = 0x80000000; public static final int SM0_EXECCTRL_SIDE_EN_LSB = 30; public static final int SM0_EXECCTRL_SIDE_EN_BITS = 0x40000000; public static final int SM0_EXECCTRL_SIDE_PINDIR_LSB = 29; public static final int SM0_EXECCTRL_SIDE_PINDIR_BITS = 0x20000000; public static final int SM0_EXECCTRL_JMP_PIN_LSB = 24; public static final int SM0_EXECCTRL_JMP_PIN_BITS = 0x1f000000; public static final int SM0_EXECCTRL_OUT_EN_SEL_LSB = 19; public static final int SM0_EXECCTRL_OUT_EN_SEL_BITS = 0x00f80000; public static final int SM0_EXECCTRL_INLINE_OUT_EN_LSB = 18; public static final int SM0_EXECCTRL_INLINE_OUT_EN_BITS = 0x00040000; public static final int SM0_EXECCTRL_OUT_STICKY_LSB = 17; public static final int SM0_EXECCTRL_OUT_STICKY_BITS = 0x00020000; public static final int SM0_EXECCTRL_WRAP_TOP_LSB = 12; public static final int SM0_EXECCTRL_WRAP_TOP_BITS = 0x0001f000; public static final int SM0_EXECCTRL_WRAP_BOTTOM_LSB = 7; public static final int SM0_EXECCTRL_WRAP_BOTTOM_BITS = 0x00000f80; public static final int SM0_EXECCTRL_STATUS_SEL_LSB = 4; public static final int SM0_EXECCTRL_STATUS_SEL_BITS = 0x00000010; public static final int SM0_EXECCTRL_STATUS_N_LSB = 0; public static final int SM0_EXECCTRL_STATUS_N_BITS = 0x0000000f; public static final int SM0_SHIFTCTRL_FJOIN_RX_LSB = 31; public static final int SM0_SHIFTCTRL_FJOIN_RX_BITS = 0x80000000; public static final int SM0_SHIFTCTRL_FJOIN_TX_LSB = 30; public static final int SM0_SHIFTCTRL_FJOIN_TX_BITS = 0x40000000; public static final int SM0_SHIFTCTRL_PULL_THRESH_LSB = 25; public static final int SM0_SHIFTCTRL_PULL_THRESH_BITS = 0x3e000000; public static final int SM0_SHIFTCTRL_PUSH_THRESH_LSB = 20; public static final int SM0_SHIFTCTRL_PUSH_THRESH_BITS = 0x01f00000; public static final int SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB = 19; public static final int SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS = 0x00080000; public static final int SM0_SHIFTCTRL_IN_SHIFTDIR_LSB = 18; public static final int SM0_SHIFTCTRL_IN_SHIFTDIR_BITS = 0x00040000; public static final int SM0_SHIFTCTRL_AUTOPULL_LSB = 17; public static final int SM0_SHIFTCTRL_AUTOPULL_BITS = 0x00020000; public static final int SM0_SHIFTCTRL_AUTOPUSH_LSB = 16; public static final int SM0_SHIFTCTRL_AUTOPUSH_BITS = 0x00010000; public static final int SM0_PINCTRL_SIDESET_COUNT_LSB = 29; public static final int SM0_PINCTRL_SIDESET_COUNT_BITS = 0xe0000000; public static final int SM0_PINCTRL_SET_COUNT_LSB = 26; public static final int SM0_PINCTRL_SET_COUNT_BITS = 0x1c000000; public static final int SM0_PINCTRL_OUT_COUNT_LSB = 20; public static final int SM0_PINCTRL_OUT_COUNT_BITS = 0x03f00000; public static final int SM0_PINCTRL_IN_BASE_LSB = 15; public static final int SM0_PINCTRL_IN_BASE_BITS = 0x000f8000; public static final int SM0_PINCTRL_SIDESET_BASE_LSB = 10; public static final int SM0_PINCTRL_SIDESET_BASE_BITS = 0x00007c00; public static final int SM0_PINCTRL_SET_BASE_LSB = 5; public static final int SM0_PINCTRL_SET_BASE_BITS = 0x000003e0; public static final int SM0_PINCTRL_OUT_BASE_LSB = 0; public static final int SM0_PINCTRL_OUT_BASE_BITS = 0x0000001f; public static final int REGISTER_SERVER_DEFAULT_PORT_NUMBER = 2040; // Instruction Origin public static final int INSTR_ORIGIN_UNKNOWN = -3; public static final int INSTR_ORIGIN_EXECD = -2; public static final int INSTR_ORIGIN_FORCED = -1; public static final int INSTR_ORIGIN_MEMORY = 0; public enum GPIO_Function { XIP(0, "xip"), SPI(1, "spi"), UART(2, "uart"), I2C(3, "i2c"), PWM(4, "pwm"), SIO(5, "sio"), PIO0(6, "pio0"), PIO1(7, "pio1"), GPCK(8, "gpck"), USB(9, "usb"), NULL(15, "null"); private static final GPIO_Function[] values = GPIO_Function.values(); private final int value; private final String label; private GPIO_Function(final int value, final String label) { this.value = value; this.label = label; } public int getValue() { return value; } public static GPIO_Function fromValue(final int value) { if ((value >= 0) && (value <= 9)) return values[value]; if (value == 15) return NULL; throw new IllegalArgumentException("value: " + value); } public static GPIO_Function fromValue(final int value, final GPIO_Function defaultValue) { try { return fromValue(value); } catch (final IllegalArgumentException e) { return defaultValue; } } @Override public String toString() { return label; } }; public static void checkBit(final int bit) { if (bit < 0) { throw new IllegalArgumentException("bit < 0: " + bit); } if (bit > 31) { throw new IllegalArgumentException("bit > 31: " + bit); } } public static void checkMSBLSB(final int msb, final int lsb) { if (lsb < 0) { throw new IllegalArgumentException("lsb < 0: " + lsb); } if (msb > 31) { throw new IllegalArgumentException("msb > 31: " + msb); } if (lsb > msb) { throw new IllegalArgumentException("lsb > msb: " + lsb + " > " + msb); } } public static void checkFIFOAddr(final int address, final String label) { if (address < 0) { throw new IllegalArgumentException(label + " < 0" + address); } if (address > (2 * FIFO_DEPTH) - 1) { throw new IllegalArgumentException(label + " > " + ((2 * FIFO_DEPTH) - 1) + ":" + address); } } public static void checkGpioPin(final int pin, final String label) { if (pin < 0) { throw new IllegalArgumentException(label + " < 0: " + pin); } if (pin > GPIO_NUM - 1) { throw new IllegalArgumentException(label + " > " + (GPIO_NUM - 1) + ": " + pin); } } public static void checkGpioPinsCount(final int count, final String label) { if (count < 0) { throw new IllegalArgumentException(label + " < 0: " + count); } if (count > GPIO_NUM) { throw new IllegalArgumentException(label + " > " + GPIO_NUM + ": " + count); } } public static void checkPioNum(final int pioNum, final String label) { if (pioNum < 0) { throw new IllegalArgumentException(label + " < 0: " + pioNum); } if (pioNum > PIO_NUM - 1) { throw new IllegalArgumentException(label + " > " + (PIO_NUM - 1) + ": " + pioNum); } } public static void checkSmMemAddr(final int address, final String label) { if (address < 0) { throw new IllegalArgumentException(label + " < 0: " + address); } if (address > MEMORY_SIZE - 1) { throw new IllegalArgumentException(label + " > " + (MEMORY_SIZE - 1) + ": " + address); } } public static void checkSmNum(final int smNum) { if (smNum < 0) { throw new IllegalArgumentException("smNum < 0: " + smNum); } if (smNum > SM_COUNT - 1) { throw new IllegalArgumentException("smNum > " + (SM_COUNT - 1) + ": " + smNum); } } public static void checkIntrNum(final int intrNum, final String label) { if (intrNum < 0) { throw new IllegalArgumentException(label + " < 0: " + intrNum); } if (intrNum > INTR_NUM - 1) { throw new IllegalArgumentException(label + " > " + (INTR_NUM - 1) + ": " + intrNum); } } public static int checkBitCount(final int bitCount, final String label) { if (bitCount < 0) { throw new IllegalArgumentException(label + " < 0: " + bitCount); } if (bitCount > 31) { throw new IllegalArgumentException(label + " > 31: " + bitCount); } return bitCount > 0 ? bitCount : 32; } /** * Functionally equivalent replacement for GNU GCC's built-in * function __builtin_ctz(). * * @return The number of trailing 0-bits in x, starting at * the least significant bit position. If x is 0, the result is * undefined. */ public static int ctz(final int x) { int count = 0; int shifted = x; while (((shifted & 0x1) == 0x0) && (count < 32)) { shifted >>>= 1; count++; } return count; } public static int hwSetBits(final int oldBits, final int newBits, final int mask, final boolean xor) { return (mask & (xor ? oldBits ^ newBits : newBits)) | (~mask & oldBits); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/Decoder.java ================================================ /* * @(#)Decoder.java 1.00 21/01/31 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.util.List; /** * Instruction Decoder */ public class Decoder { public static class DecodeException extends Exception { private static final long serialVersionUID = -3754988538292081517L; private final Instruction instruction; private final int opCode; private DecodeException() { throw new UnsupportedOperationException("unsupported empty constructor"); } public DecodeException(final Instruction instruction, final int opCode) { super("decode failed: unsupported op-code: " + String.format("%04x", opCode)); this.instruction = instruction; this.opCode = opCode; } public Instruction getInstruction() { return instruction; } public int getOpCode() { return opCode; } } private final Instructions instructions; private class Instructions { private final Instruction.Jmp jmp; private final Instruction.Wait wait; private final Instruction.In in; private final Instruction.Out out; private final Instruction.Push push; private final Instruction.Pull pull; private final Instruction.Mov mov; private final Instruction.Irq irq; private final Instruction.Set set; private final Instruction[] instructionSet; public Instructions() { jmp = new Instruction.Jmp(); wait = new Instruction.Wait(); in = new Instruction.In(); out = new Instruction.Out(); push = new Instruction.Push(); pull = new Instruction.Pull(); mov = new Instruction.Mov(); irq = new Instruction.Irq(); set = new Instruction.Set(); instructionSet = new Instruction[] { jmp, wait, in, out, push, pull, mov, irq, set }; } } public Decoder() { instructions = new Instructions(); } public void reset() { for (final Instruction instruction : instructions.instructionSet) { instruction.reset(); } } public Instruction decode(final short word, final int pinCtrlSidesetCount, final boolean execCtrlSideEn) throws DecodeException { final Instruction instruction; switch ((word >>> 13) & 0x7) { case 0b000: instruction = instructions.jmp; break; case 0b001: instruction = instructions.wait; break; case 0b010: instruction = instructions.in; break; case 0b011: instruction = instructions.out; break; case 0b100: if ((word & 0x80) == 0) instruction = instructions.push; else instruction = instructions.pull; break; case 0b101: instruction = instructions.mov; break; case 0b110: instruction = instructions.irq; break; case 0b111: instruction = instructions.set; break; default: throw new InternalError("unexpected case fall-through"); } return instruction.decode(word, pinCtrlSidesetCount, execCtrlSideEn); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/Direction.java ================================================ /* * @(#)Direction.java 1.00 21/03/19 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * Representation of a single GPIO pin's direction. */ public enum Direction { IN(0, "in"), OUT(1, "out"); private final int value; private final String label; private Direction(final int value, final String label) { this.value = value; this.label = label; } public int getValue() { return value; } public static Direction fromValue(final int value) { if (value == IN.value) return IN; if (value == OUT.value) return OUT; throw new IllegalArgumentException("value not a direction: " + value); } public Direction inverse() { return this == IN ? OUT : IN; } @Override public String toString() { return label; } }; /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/EmulationServer.java ================================================ /* * @(#)EmulationServer.java 1.00 21/03/27 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.IOException; import java.io.PrintStream; import java.util.Arrays; import java.util.List; public class EmulationServer { private static final String PRG_TITLE = "EmulationServer"; private static final String PRG_FULL_NAME = "Emulation Server Version 0.1"; private static final CmdOptions.FlagOptionDeclaration optVersion = CmdOptions.createFlagOption(false, 'V', "version", CmdOptions.Flag.OFF, "display version information and exit"); private static final CmdOptions.FlagOptionDeclaration optHelp = CmdOptions.createFlagOption(false, 'h', "help", CmdOptions.Flag.OFF, "display this help text and exit"); private static final CmdOptions.FlagOptionDeclaration optSilent = CmdOptions.createFlagOption(false, 's', "silent", CmdOptions.Flag.OFF, "print no info at all except fatal errors"); private static final CmdOptions.FlagOptionDeclaration optVerbose = CmdOptions.createFlagOption(false, 'v', "verbose", CmdOptions.Flag.OFF, "print verbose information"); private static final CmdOptions.IntegerOptionDeclaration optPort = CmdOptions.createIntegerOption("PORT", false, 'p', "port", Constants. REGISTER_SERVER_DEFAULT_PORT_NUMBER, "use PORT as server port number"); private static final List> optionDeclarations = Arrays.asList(new CmdOptions.OptionDeclaration[] { optVersion, optHelp, optSilent, optVerbose, optPort }); private final PrintStream console; private final CmdOptions options; private EmulationServer(final PrintStream console, final String[] argv) { if (console == null) { throw new NullPointerException("console"); } this.console = console; options = parseArgs(argv); if (options.getValue(optSilent) != CmdOptions.Flag.ON) { printAbout(); } } private CmdOptions parseArgs(final String argv[]) { final CmdOptions options; try { options = new CmdOptions(PRG_TITLE, PRG_FULL_NAME, null, optionDeclarations); options.parse(argv); checkValidity(options); } catch (final CmdOptions.ParseException e) { console.println(e.getMessage()); System.exit(-1); throw new InternalError(); } if (options.getValue(optVersion) == CmdOptions.Flag.ON) { console.println(PRG_FULL_NAME); console.println(Constants.getEmulatorIdAndVersionWithOs()); System.exit(0); throw new InternalError(); } if (options.getValue(optHelp) == CmdOptions.Flag.ON) { console.println(options.getFullInfo()); System.exit(0); throw new InternalError(); } return options; } private void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { final int port = options.getValue(optPort); if ((port < 0) || (port > 65535)) { throw new CmdOptions. ParseException("PORT must be in the range 0…65535"); } if ((options.getValue(optSilent) == CmdOptions.Flag.ON) && (options.getValue(optVerbose) == CmdOptions.Flag.ON)) { throw new CmdOptions. ParseException("either 'silent' or 'verbose' can be activated"); } } private void printAbout() { console.printf("%s%n%s%n%s%n", "Emulation Server Daemon", Constants.getEmulatorIdAndVersionWithOs(), Constants.getCmdLineCopyrightNotice()); } private void run() { try { final Emulator emulator = new Emulator(console); final LocalAddressSpace memory = new LocalAddressSpace(emulator); final int port = options.getValue(optPort); final RemoteAddressSpaceServer server = new RemoteAddressSpaceServer(console, memory, port); if (options.getValue(optSilent) != CmdOptions.Flag.ON) { console.println("started emulation server at port " + port); } } catch (final IOException e) { console.println("failed starting emulation server: " + e.getMessage()); System.exit(-1); } } public static void main(final String argv[]) { new EmulationServer(System.out, argv).run(); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/Emulator.java ================================================ /* * @(#)Emulator.java 1.00 21/03/19 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.PrintStream; /** * Holds internal subsystems of core emulator. */ public class Emulator { private final PrintStream console; private final MasterClock masterClock; private final GPIO gpio; private final PIO pio0; private final PIO pio1; private Emulator() { throw new UnsupportedOperationException("unsupported empty constructor"); } public Emulator(final PrintStream console) { if (console == null) { throw new NullPointerException("console"); } this.console = console; masterClock = new MasterClock(console); gpio = new GPIO(console, masterClock); pio0 = gpio.getPIO0(); pio1 = gpio.getPIO1(); } public PrintStream getConsole() { return console; } public MasterClock getMasterClock() { return masterClock; } public GPIO getGPIO() { return gpio; } public PIO getPIO0() { return pio0; } public PIO getPIO1() { return pio1; } public void reset() { masterClock.reset(); gpio.reset(); pio0.reset(); pio1.reset(); } public void terminate() { masterClock.terminate(); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/FIFO.java ================================================ /* * @(#)FIFO.java 1.00 21/02/03 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * A pair of an RX FIFO and a TX FIFO, each having a capacity of DEPTH * words of 32 bits. One of the FIFOs' capacity can be reconfigured * to be joined with the capacity of the other FIFO, thus resulting in * 8 words of capacity for that FIFO and leaving no capacity left for * the other FIFO. */ public class FIFO implements Constants { private static int JOINED_FIFO_DEPTH = FIFO_DEPTH + FIFO_DEPTH; private static enum Mode { JoinNone(false, false, FIFO_DEPTH, FIFO_DEPTH), JoinTX(true, false, JOINED_FIFO_DEPTH, 0), JoinRX(false, true, 0, JOINED_FIFO_DEPTH), JoinBoth(true, true, 0, 0); private final boolean joinTX; private final boolean joinRX; private final int txSize; private final int rxSize; private Mode(final boolean joinTX, final boolean joinRX, final int txSize, final int rxSize) { this.joinTX = joinTX; this.joinRX = joinRX; this.txSize = txSize; this.rxSize = rxSize; } private boolean isJoinTX() { return joinTX; } private boolean isJoinRX() { return joinRX; } private int getTXSize() { return txSize; } private int getRXSize() { return rxSize; } private int incPtrTX(final int ptr) { return txSize == 0 ? 0 : (ptr + 1) & (txSize - 1); } private int incPtrRX(final int ptr) { final int rxOffset = JOINED_FIFO_DEPTH - rxSize; return rxSize == 0 ? 0 : ((ptr + 1) & (rxSize - 1)) + rxOffset; } private static Mode fromJoins(final boolean joinTX, final boolean joinRX) { return joinTX ? (joinRX ? JoinBoth : JoinTX) : (joinRX ? JoinRX : JoinNone); } } private final int smNum; private final IRQ irq; private int[] memory; private Mode mode; private int txReadPtr; private int txWritePtr; private boolean txFull; private int rxReadPtr; private int rxWritePtr; private boolean rxFull; private boolean regFDEBUG_TXSTALL; // one of bits 27:24 of FDEBUG private boolean regFDEBUG_TXOVER; // one of bits 19:16 of FDEBUG private boolean regFDEBUG_RXUNDER; // one of bits 11:8 of FDEBUG private boolean regFDEBUG_RXSTALL; // one of bits 3:0 of FDEBUG public FIFO(final int smNum, final IRQ irq) { Constants.checkSmNum(smNum); if (irq == null) { throw new NullPointerException("irq"); } this.smNum = smNum; this.irq = irq; memory = new int[JOINED_FIFO_DEPTH]; reset(); } public synchronized void reset() { reset(false, false); } private void reset(final boolean joinTX, final boolean joinRX) { for (int index = 0; index < memory.length; index++) { memory[index] = 0; } mode = Mode.fromJoins(joinTX, joinRX); regFDEBUG_TXSTALL = false; regFDEBUG_TXOVER = false; regFDEBUG_RXUNDER = false; regFDEBUG_RXSTALL = false; txReadPtr = joinTX && joinRX ? -1 : 0; txWritePtr = txReadPtr; txFull = joinRX; rxReadPtr = joinTX && joinRX ? -1 : (joinRX ? 0 : FIFO_DEPTH); rxWritePtr = rxReadPtr; rxFull = joinTX; irq.setRxNEmpty(smNum, !fstatRxEmpty()); irq.setTxNFull(smNum, !fstatTxFull()); notifyAll(); } public synchronized void setJoinRX(final boolean join) { if (mode.isJoinRX() == join) return; reset(mode.isJoinTX(), join); } public boolean getJoinRX() { return mode.isJoinRX(); } private int getRXSize() { return mode.getRXSize(); } public synchronized int getRXReadPointer() { return rxReadPtr; } public synchronized boolean fstatRxFull() { // bit 0, 1, 2 or 3 (for SM_0…SM_3) of FSTAT return rxFull; } public synchronized boolean fstatRxEmpty() { // bit 8, 9, 10 or 11 (for SM_0…SM_3) of FSTAT return (rxReadPtr == rxWritePtr) && !rxFull; } public synchronized int getRXLevel() { final int rxSize = mode.getRXSize(); return rxFull ? rxSize : (rxSize + rxWritePtr - rxReadPtr) & (rxSize - 1); } /** * @return <code>true</code> if the operation succeeded. */ public synchronized boolean rxPush(final int value, final boolean stallIfFull) { final boolean modified; if (!fstatRxFull()) { memory[rxWritePtr] = value; rxWritePtr = mode.incPtrRX(rxWritePtr); rxFull = rxWritePtr == rxReadPtr; modified = true; } else { if (stallIfFull) { regFDEBUG_RXSTALL = true; } modified = false; } irq.setRxNEmpty(smNum, !fstatRxEmpty()); notifyAll(); return modified; } public synchronized int rxDMARead() { final int value; if (!fstatRxEmpty()) { value = memory[rxReadPtr]; rxReadPtr = mode.incPtrRX(rxReadPtr); rxFull = false; } else { regFDEBUG_RXUNDER = true; value = 0; } irq.setRxNEmpty(smNum, !fstatRxEmpty()); notifyAll(); return value; } public boolean isRXUnder() { return regFDEBUG_RXUNDER; } public void clearRXUnder() { regFDEBUG_RXUNDER = false; } public boolean isRXStall() { return regFDEBUG_RXSTALL; } public void clearRXStall() { regFDEBUG_RXSTALL = false; } public synchronized void setJoinTX(final boolean join) { if (mode.isJoinTX() == join) return; reset(join, mode.isJoinRX()); } public boolean getJoinTX() { return mode.isJoinTX(); } public synchronized int getTXReadPointer() { return txReadPtr; } public synchronized boolean fstatTxFull() { // bit 16, 17, 18 or 19 (for SM_0…SM_3) of FSTAT return txFull; } public synchronized boolean fstatTxEmpty() { // bit 24, 25, 26 or 27 (for SM_0…SM_3) of FSTAT return (txReadPtr == txWritePtr) && !txFull; } public synchronized int getTXLevel() { final int txSize = mode.getTXSize(); return txFull ? txSize : (txSize + txWritePtr - txReadPtr) & (txSize - 1); } public synchronized int txPull(final boolean stallIfEmpty) { final int value; if (!fstatTxEmpty()) { value = memory[txReadPtr]; txReadPtr = mode.incPtrTX(txReadPtr); txFull = false; } else { value = 0; if (stallIfEmpty) { regFDEBUG_TXSTALL = true; } } irq.setTxNFull(smNum, !fstatTxFull()); notifyAll(); return value; } public synchronized void txDMAWrite(final int value) { if (!fstatTxFull()) { memory[txWritePtr] = value; txWritePtr = mode.incPtrTX(txWritePtr); txFull = txWritePtr == txReadPtr; } else { // overwrite most recent value if (txWritePtr >= 0) { memory[txWritePtr] = value; } regFDEBUG_TXOVER = true; } irq.setTxNFull(smNum, !fstatTxFull()); notifyAll(); } public boolean isTXOver() { return regFDEBUG_TXOVER; } public void clearTXOver() { regFDEBUG_TXOVER = false; } public boolean isTXStall() { return regFDEBUG_TXSTALL; } public void clearTXStall() { regFDEBUG_TXSTALL = false; } public int getMemValue(final int address) { Constants.checkFIFOAddr(address, "address"); return memory[address]; } public void setMemValue(final int address, final int value) { Constants.checkFIFOAddr(address, "address"); memory[address] = value; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/GPIO.java ================================================ /* * @(#)GPIO.java 1.00 21/01/31 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.PrintStream; import java.util.function.Function; /** * General-Purpose Set of 32 Peripheral I/O Terminals */ public class GPIO implements Constants { private enum Override { BYPASS((value) -> value, (value) -> value), INVERT((value) -> value.inverse(), (value) -> value.inverse()), ALWAYS_LOW((value) -> Bit.LOW, (value) -> Direction.IN), ALWAYS_HIGH((value) -> Bit.HIGH, (value) -> Direction.OUT); private static final Override[] values = Override.values(); private final Function fnBit; private final Function fnDirection; private Override(final Function fnBit, final Function fnDirection) { if (fnBit == null) throw new NullPointerException("fnBit"); this.fnBit = fnBit; if (fnDirection == null) throw new NullPointerException("fnDirection"); this.fnDirection = fnDirection; } public Bit apply(final Bit value) { return fnBit.apply(value); } public Direction apply(final Direction value) { return fnDirection.apply(value); } public static Override fromValue(final int value) { if (value < 0) { final String message = String.format("value < 0: %d", value); throw new IllegalArgumentException(message); } if (value > values.length) { final String message = String.format("value >= %d: %d", values.length, value); throw new IllegalArgumentException(message); } return values[value]; } public int getValue() { return ordinal(); } } private static class Terminal { private int num; private GPIO_Function function; private Override irqOverride; private Override inputOverride; private Override oeOverride; private Override outputOverride; private Bit externalInput; private Terminal() { throw new UnsupportedOperationException("unsupported empty constructor"); } private Terminal(final int num) { Constants.checkGpioPin(num, "GPIO port"); this.num = num; reset(); } public void reset() { function = GPIO_Function.NULL; irqOverride = Override.BYPASS; inputOverride = Override.BYPASS; oeOverride = Override.BYPASS; outputOverride = Override.BYPASS; externalInput = Bit.LOW; } private Bit getPadIn(final Bit outBeforeOverride, final Direction oeBeforeOverride) { /* * Loopback GPIO output as pad input, if a PIO drives this GPIO * pin as output, while listening to this GPIO pin as input pin. * * See comment in file pico-examples/pio/spi/spi_loopback.c: * * #define PIN_MISO 16 // same as MOSI, so we get loopback * * Note that, as a result from loopback, a PIO may even observe * the other PIO's GPIO pad output. */ final Direction oeAfterOverride = getOeAfterOverride(oeBeforeOverride); return oeAfterOverride == Direction.OUT ? getOutAfterOverride(outBeforeOverride) : externalInput; } private Bit getInputAfterOverride(final Bit outBeforeOverride, final Direction oeBeforeOverride) { final Bit padIn = getPadIn(outBeforeOverride, oeBeforeOverride); return inputOverride.apply(padIn); } private Bit getIrqAfterOverride(final Bit outBeforeOverride, final Direction oeBeforeOverride) { final Bit padIn = getPadIn(outBeforeOverride, oeBeforeOverride); return irqOverride.apply(padIn); } private Direction getOeAfterOverride(final Direction oeBeforeOverride) { return oeOverride.apply(oeBeforeOverride); } private Bit getOutAfterOverride(final Bit outBeforeOverride) { return outputOverride.apply(outBeforeOverride); } } private final PrintStream console; private final PIO pio0; private final PIO pio1; private final Terminal[] terminals; private int regINPUT_SYNC_BYPASS; // bits 0…31 of INPUT_SYNC_BYPASS // (contents currently ignored) private GPIO() { throw new UnsupportedOperationException("unsupported empty constructor"); } public GPIO(final PrintStream console, final MasterClock masterClock) { if (console == null) { throw new NullPointerException("console"); } if (masterClock == null) { throw new NullPointerException("masterClock"); } this.console = console; pio0 = new PIO(0, console, masterClock, this); pio1 = new PIO(1, console, masterClock, this); terminals = new Terminal[GPIO_NUM]; for (int port = 0; port < terminals.length; port++) { terminals[port] = new Terminal(port); } reset(); } public void reset() { for (int port = 0; port < terminals.length; port++) { terminals[port].reset(); } } public PIO getPIO0() { return pio0; } public PIO getPIO1() { return pio1; } public synchronized int getGPIO_PADIN() { int status = 0x0; for (int port = 0; port < terminals.length; port++) { status <<= 0x1; status |= terminals[terminals.length - 1 - port].externalInput.getValue(); } return status; } public synchronized void setGPIO_PADIN(final int bits, final int mask, final boolean xor) { final int status = Constants.hwSetBits(getGPIO_PADIN(), bits, mask, xor); for (int port = 0; port < terminals.length; port++) { terminals[port].externalInput = Bit.fromValue((status >>> port) & 0x1); } } /** * Set GPIOx_CTRL_FUNCSEL to 6 (for PIO0) or 7 (for PIO1), see * Sect. 2.19.2. "Function Select" of RP2040 datasheet for details. */ public void setFunction(final int gpio, final GPIO_Function fn) { Constants.checkGpioPin(gpio, "GPIO port"); if (fn == null) { throw new NullPointerException("fn"); } terminals[gpio].function = fn; } private GPIO_Function getFunction(final int gpio) { Constants.checkGpioPin(gpio, "GPIO port"); return terminals[gpio].function; } public void setCTRL(final int gpio, final int value, final int mask, final boolean xor) { final int ctrl = Constants.hwSetBits(getCTRL(gpio), value, mask, xor); final Terminal terminal = terminals[gpio]; final Override irqOverride = Override.fromValue((ctrl & IO_BANK0_GPIO0_CTRL_IRQOVER_BITS) >> IO_BANK0_GPIO0_CTRL_IRQOVER_LSB); terminal.irqOverride = irqOverride; final Override inputOverride = Override.fromValue((ctrl & IO_BANK0_GPIO0_CTRL_INOVER_BITS) >> IO_BANK0_GPIO0_CTRL_INOVER_LSB); terminal.inputOverride = inputOverride; final Override oeOverride = Override.fromValue((ctrl & IO_BANK0_GPIO0_CTRL_OEOVER_BITS) >> IO_BANK0_GPIO0_CTRL_OEOVER_LSB); terminal.oeOverride = oeOverride; final Override outputOverride = Override.fromValue((ctrl & IO_BANK0_GPIO0_CTRL_OUTOVER_BITS) >> IO_BANK0_GPIO0_CTRL_OUTOVER_LSB); terminal.outputOverride = outputOverride; final GPIO_Function fn = GPIO_Function.fromValue((ctrl & IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS) >> IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB, GPIO_Function.NULL); terminal.function = fn; } public int getCTRL(final int gpio) { Constants.checkGpioPin(gpio, "GPIO port"); final Terminal terminal = terminals[gpio]; return (terminal.irqOverride.getValue() << IO_BANK0_GPIO0_CTRL_IRQOVER_LSB) | (terminal.inputOverride.getValue() << IO_BANK0_GPIO0_CTRL_INOVER_LSB) | (terminal.oeOverride.getValue() << IO_BANK0_GPIO0_CTRL_OEOVER_LSB) | (terminal.outputOverride.getValue() << IO_BANK0_GPIO0_CTRL_OUTOVER_LSB) | (terminal.function.getValue() << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB); } public int getSTATUS(final int gpio) { Constants.checkGpioPin(gpio, "GPIO port"); return (getIrqToProc(gpio).getValue() << IO_BANK0_GPIO0_STATUS_IRQTOPROC_LSB) | (getIrqFromPad(gpio).getValue() << IO_BANK0_GPIO0_STATUS_IRQFROMPAD_LSB) | (getInToPeri(gpio).getValue() << IO_BANK0_GPIO0_STATUS_INTOPERI_LSB) | (getInFromPad(gpio).getValue() << IO_BANK0_GPIO0_STATUS_INFROMPAD_LSB) | (getOeToPad(gpio).getValue() << IO_BANK0_GPIO0_STATUS_OETOPAD_LSB) | (getOeFromPeripheral(gpio).getValue() << IO_BANK0_GPIO0_STATUS_OEFROMPERI_LSB) | (getOutToPad(gpio).getValue() << IO_BANK0_GPIO0_STATUS_OUTTOPAD_LSB) | (getOutFromPeripheral(gpio).getValue() << IO_BANK0_GPIO0_STATUS_OUTFROMPERI_LSB); } private Bit getIrqToProc(final int gpio) { Constants.checkGpioPin(gpio, "GPIO port"); final Bit outBeforeOverride = getOutFromPeripheral(gpio); final Direction oeBeforeOverride = getOeFromPeripheral(gpio); return terminals[gpio].getIrqAfterOverride(outBeforeOverride, oeBeforeOverride); } private Bit getIrqFromPad(final int gpio) { /* * TODO: Clarify: How does / should this method differ from method * getInFromPad()? It seems the RP2040 datasheet does not explain * the difference between interrupt from pad * (IO_BANK0_GPIOx_STATUS_IRQFROMPAD) and input signal from pad * (IO_BANK0_GPIOx_STATUS_INFROMPAD). Maybe, interrupt from pad * is the value of an edge-triggered flip-flop (but how is the * flip-flop reset again?), while input signal from pad is the * pad's current logical value in terms of voltage level? */ Constants.checkGpioPin(gpio, "GPIO port"); final Bit outBeforeOverride = getOutFromPeripheral(gpio); final Direction oeBeforeOverride = getOeFromPeripheral(gpio); return terminals[gpio].getPadIn(outBeforeOverride, oeBeforeOverride); } public int getPinsToPeri(final int base, final int count) { Constants.checkGpioPin(base, "GPIO pin base"); Constants.checkGpioPinsCount(count, "GPIO pin count"); int pins = 0; for (int pin = 0; pin < count; pin++) { pins = (pins << 0x1) | getInToPeri((base - pin - 1) & 0x1f).getValue(); } return pins; } public Bit getInToPeri(final int gpio) { Constants.checkGpioPin(gpio, "GPIO port"); final Bit outBeforeOverride = getOutFromPeripheral(gpio); final Direction oeBeforeOverride = getOeFromPeripheral(gpio); return terminals[gpio].getInputAfterOverride(outBeforeOverride, oeBeforeOverride); } private Bit getInFromPad(final int gpio) { Constants.checkGpioPin(gpio, "GPIO port"); final Bit outBeforeOverride = getOutFromPeripheral(gpio); final Direction oeBeforeOverride = getOeFromPeripheral(gpio); return terminals[gpio].getPadIn(outBeforeOverride, oeBeforeOverride); } private Direction getOeToPad(final int gpio) { Constants.checkGpioPin(gpio, "GPIO port"); final Direction oeBeforeOverride = getOeFromPeripheral(gpio); return terminals[gpio].getOeAfterOverride(oeBeforeOverride); } private Direction getOeFromPeripheral(final int gpio) { switch (getFunction(gpio)) { case XIP: case SPI: case UART: case I2C: case PWM: case SIO: // not implemented by this emulator return Direction.IN; case PIO0: return pio0.getDirection(gpio); case PIO1: return pio1.getDirection(gpio); case GPCK: case USB: // not implemented by this emulator return Direction.IN; case NULL: return Direction.IN; default: throw new InternalError("unexpected case fall-through"); } } private Bit getOutToPad(final int gpio) { Constants.checkGpioPin(gpio, "GPIO port"); final Bit outBeforeOverride = getOutFromPeripheral(gpio); return terminals[gpio].getOutAfterOverride(outBeforeOverride); } private Bit getOutFromPeripheral(final int gpio) { switch (getFunction(gpio)) { case XIP: case SPI: case UART: case I2C: case PWM: case SIO: // not implemented by this emulator return Bit.LOW; case PIO0: return pio0.getLevel(gpio); case PIO1: return pio1.getLevel(gpio); case GPCK: case USB: // not implemented by this emulator return Bit.LOW; case NULL: return Bit.LOW; default: throw new InternalError("unexpected case fall-through"); } } public void setInputSyncByPass(final int bits, final int mask, final boolean xor) { regINPUT_SYNC_BYPASS = Constants.hwSetBits(regINPUT_SYNC_BYPASS, bits, mask, xor); } public int getInputSyncByPass() { return regINPUT_SYNC_BYPASS; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/GPIOIOBank0Registers.java ================================================ /* * @(#)GPIOIOBank0Registers.java 1.00 21/03/20 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.soundpaint.rp2040pio.doctool.RegistersDocs; /** * Facade to the internal GPIO IO Bank 0 subsystem. The layout of * registers follows the list of registers in Sect. 2.19.6 of the * RP2040 datasheet. The facade is in particular intended for use by * the SDK. * * Note: This class implements only a subset of the RP2040 GPIO set of * registers, focussing on those registers that are relevant for PIO * simulation. All GPIO registers specified by the RP2040 datasheet * are addressable, but writing to non-relevant registers or register * bits will have no effect, and reading from non-relevant registers * or register bits will return a constant value of 0. */ public abstract class GPIOIOBank0Registers extends RegisterSet { public enum Regs implements RegistersDocs { GPIO0_STATUS("GPIO status.", new BitsInfo[] { new BitsInfo(null, 31, 27, null, BitsType.RESERVED, null), new BitsInfo("IRQTOPROC", 26, 26, null, BitsType.RO, 0), new BitsInfo(null, 25, 25, null, BitsType.RESERVED, null), new BitsInfo("IRQFROMPAD", 24, 24, null, BitsType.RO, 0), new BitsInfo(null, 23, 20, null, BitsType.RESERVED, null), new BitsInfo("INTOPERI", 19, 19, null, BitsType.RO, 0), new BitsInfo(null, 18, 18, null, BitsType.RESERVED, null), new BitsInfo("INFROMPAD", 17, 17, null, BitsType.RO, 0), new BitsInfo(null, 16, 14, null, BitsType.RESERVED, null), new BitsInfo("OETOPAD", 13, 13, null, BitsType.RO, 0), new BitsInfo("OEFROMPERI", 12, 12, null, BitsType.RO, 0), new BitsInfo(null, 11, 10, null, BitsType.RESERVED, null), new BitsInfo("OUTTOPAD", 9, 9, null, BitsType.RO, 0), new BitsInfo("OUTFROMPERI", 8, 8, null, BitsType.RO, 0), new BitsInfo(null, 7, 0, null, BitsType.RESERVED, null) }), GPIO0_CTRL("GPIO control.", new BitsInfo[] { new BitsInfo(null, 31, 30, null, BitsType.RESERVED, null), new BitsInfo("IRQOVER", 29, 28, null, BitsType.RW, 0), new BitsInfo(null, 27, 18, null, BitsType.RESERVED, null), new BitsInfo("INOVER", 17, 16, null, BitsType.RW, 0), new BitsInfo(null, 15, 14, null, BitsType.RESERVED, null), new BitsInfo("OEOVER", 13, 12, null, BitsType.RW, 0), new BitsInfo(null, 11, 10, null, BitsType.RESERVED, null), new BitsInfo("OUTOVER", 9, 8, null, BitsType.RW, 0), new BitsInfo(null, 7, 5, null, BitsType.RESERVED, null), new BitsInfo("FUNCSEL", 4, 0, null, BitsType.RW, 0x1f) }), GPIO1_STATUS(Regs.GPIO0_STATUS), GPIO1_CTRL(Regs.GPIO0_CTRL), GPIO2_STATUS(Regs.GPIO0_STATUS), GPIO2_CTRL(Regs.GPIO0_CTRL), GPIO3_STATUS(Regs.GPIO0_STATUS), GPIO3_CTRL(Regs.GPIO0_CTRL), GPIO4_STATUS(Regs.GPIO0_STATUS), GPIO4_CTRL(Regs.GPIO0_CTRL), GPIO5_STATUS(Regs.GPIO0_STATUS), GPIO5_CTRL(Regs.GPIO0_CTRL), GPIO6_STATUS(Regs.GPIO0_STATUS), GPIO6_CTRL(Regs.GPIO0_CTRL), GPIO7_STATUS(Regs.GPIO0_STATUS), GPIO7_CTRL(Regs.GPIO0_CTRL), GPIO8_STATUS(Regs.GPIO0_STATUS), GPIO8_CTRL(Regs.GPIO0_CTRL), GPIO9_STATUS(Regs.GPIO0_STATUS), GPIO9_CTRL(Regs.GPIO0_CTRL), GPIO10_STATUS(Regs.GPIO0_STATUS), GPIO10_CTRL(Regs.GPIO0_CTRL), GPIO11_STATUS(Regs.GPIO0_STATUS), GPIO11_CTRL(Regs.GPIO0_CTRL), GPIO12_STATUS(Regs.GPIO0_STATUS), GPIO12_CTRL(Regs.GPIO0_CTRL), GPIO13_STATUS(Regs.GPIO0_STATUS), GPIO13_CTRL(Regs.GPIO0_CTRL), GPIO14_STATUS(Regs.GPIO0_STATUS), GPIO14_CTRL(Regs.GPIO0_CTRL), GPIO15_STATUS(Regs.GPIO0_STATUS), GPIO15_CTRL(Regs.GPIO0_CTRL), GPIO16_STATUS(Regs.GPIO0_STATUS), GPIO16_CTRL(Regs.GPIO0_CTRL), GPIO17_STATUS(Regs.GPIO0_STATUS), GPIO17_CTRL(Regs.GPIO0_CTRL), GPIO18_STATUS(Regs.GPIO0_STATUS), GPIO18_CTRL(Regs.GPIO0_CTRL), GPIO19_STATUS(Regs.GPIO0_STATUS), GPIO19_CTRL(Regs.GPIO0_CTRL), GPIO20_STATUS(Regs.GPIO0_STATUS), GPIO20_CTRL(Regs.GPIO0_CTRL), GPIO21_STATUS(Regs.GPIO0_STATUS), GPIO21_CTRL(Regs.GPIO0_CTRL), GPIO22_STATUS(Regs.GPIO0_STATUS), GPIO22_CTRL(Regs.GPIO0_CTRL), GPIO23_STATUS(Regs.GPIO0_STATUS), GPIO23_CTRL(Regs.GPIO0_CTRL), GPIO24_STATUS(Regs.GPIO0_STATUS), GPIO24_CTRL(Regs.GPIO0_CTRL), GPIO25_STATUS(Regs.GPIO0_STATUS), GPIO25_CTRL(Regs.GPIO0_CTRL), GPIO26_STATUS(Regs.GPIO0_STATUS), GPIO26_CTRL(Regs.GPIO0_CTRL), GPIO27_STATUS(Regs.GPIO0_STATUS), GPIO27_CTRL(Regs.GPIO0_CTRL), GPIO28_STATUS(Regs.GPIO0_STATUS), GPIO28_CTRL(Regs.GPIO0_CTRL), GPIO29_STATUS(Regs.GPIO0_STATUS), GPIO29_CTRL(Regs.GPIO0_CTRL), INTR0("Raw interrupts.", createBitsInfo(0, null)), INTR1("Raw interrupts.", createBitsInfo(1, null)), INTR2("Raw interrupts.", createBitsInfo(2, null)), INTR3("Raw interrupts.", createBitsInfo(3, null)), PROC0_INTE0("Interrupt enable for proc0.", createBitsInfo(0, BitsType.RW)), PROC0_INTE1("Interrupt enable for proc0.", createBitsInfo(1, BitsType.RW)), PROC0_INTE2("Interrupt enable for proc0.", createBitsInfo(2, BitsType.RW)), PROC0_INTE3("Interrupt enable for proc0.", createBitsInfo(3, BitsType.RW)), PROC0_INTF0("Interrupt force for proc0.", createBitsInfo(0, BitsType.RW)), PROC0_INTF1("Interrupt force for proc0.", createBitsInfo(1, BitsType.RW)), PROC0_INTF2("Interrupt force for proc0.", createBitsInfo(2, BitsType.RW)), PROC0_INTF3("Interrupt force for proc0.", createBitsInfo(3, BitsType.RW)), PROC0_INTS0("Interrupt status for proc0.", createBitsInfo(0, BitsType.RO)), PROC0_INTS1("Interrupt status for proc0.", createBitsInfo(1, BitsType.RO)), PROC0_INTS2("Interrupt status for proc0.", createBitsInfo(2, BitsType.RO)), PROC0_INTS3("Interrupt status for proc0.", createBitsInfo(3, BitsType.RO)), PROC1_INTE0("Interrupt enable for proc1.", createBitsInfo(0, BitsType.RW)), PROC1_INTE1("Interrupt enable for proc1.", createBitsInfo(1, BitsType.RW)), PROC1_INTE2("Interrupt enable for proc1.", createBitsInfo(2, BitsType.RW)), PROC1_INTE3("Interrupt enable for proc1.", createBitsInfo(3, BitsType.RW)), PROC1_INTF0("Interrupt force for proc1.", createBitsInfo(0, BitsType.RW)), PROC1_INTF1("Interrupt force for proc1.", createBitsInfo(1, BitsType.RW)), PROC1_INTF2("Interrupt force for proc1.", createBitsInfo(2, BitsType.RW)), PROC1_INTF3("Interrupt force for proc1.", createBitsInfo(3, BitsType.RW)), PROC1_INTS0("Interrupt status for proc1.", createBitsInfo(0, BitsType.RO)), PROC1_INTS1("Interrupt status for proc1.", createBitsInfo(1, BitsType.RO)), PROC1_INTS2("Interrupt status for proc1.", createBitsInfo(2, BitsType.RO)), PROC1_INTS3("Interrupt status for proc1.", createBitsInfo(3, BitsType.RO)), DORMANT_WAKE_INTE0("Interrupt enable for dormant wake.", createBitsInfo(0, BitsType.RW)), DORMANT_WAKE_INTE1("Interrupt enable for dormant wake.", createBitsInfo(1, BitsType.RW)), DORMANT_WAKE_INTE2("Interrupt enable for dormant wake.", createBitsInfo(2, BitsType.RW)), DORMANT_WAKE_INTE3("Interrupt enable for dormant wake.", createBitsInfo(3, BitsType.RW)), DORMANT_WAKE_INTF0("Interrupt force for dormant wake.", createBitsInfo(0, BitsType.RW)), DORMANT_WAKE_INTF1("Interrupt force for dormant wake.", createBitsInfo(1, BitsType.RW)), DORMANT_WAKE_INTF2("Interrupt force for dormant wake.", createBitsInfo(2, BitsType.RW)), DORMANT_WAKE_INTF3("Interrupt force for dormant wake.", createBitsInfo(3, BitsType.RW)), DORMANT_WAKE_INTS0("Interrupt status for dormant wake.", createBitsInfo(0, BitsType.RO)), DORMANT_WAKE_INTS1("Interrupt status for dormant wake.", createBitsInfo(1, BitsType.RO)), DORMANT_WAKE_INTS2("Interrupt status for dormant wake.", createBitsInfo(2, BitsType.RO)), DORMANT_WAKE_INTS3("Interrupt status for dormant wake.", createBitsInfo(3, BitsType.RO)); private static List createBitsInfo(final int octett, final BitsType forcedBitsType) { if (octett < 0) { throw new IllegalArgumentException("octett < 0: " + octett); } if (octett > 3) { throw new IllegalArgumentException("octett > 3: " + octett); } final int gpioMin = octett << 3; final int gpioCount = octett < 3 ? 8 : 6; final int gpioMax = gpioCount << 2; final List bitsInfo = IntStream.rangeClosed(0, gpioMax - 1).boxed() .map(n -> new BitsInfo("GPIO" + (((gpioMax - 1 - n) >> 2) + gpioMin) + "_" + ((n & 2) == 0 ? "EDGE" : "LEVEL") + "_" + ((n & 1) == 0 ? "HIGH" : "LOW"), gpioMax - 1 - n, gpioMax - 1 - n, null, forcedBitsType != null ? forcedBitsType : ((n & 2) == 0 ? BitsType.WC : BitsType.RO), 0)) .collect(Collectors.toList()); if (octett < 3) { return bitsInfo; } else { final List paddedBitsInfo = new ArrayList(); paddedBitsInfo.add(new BitsInfo(null, 31, 24, null, BitsType.RESERVED, null)); paddedBitsInfo.addAll(bitsInfo); return paddedBitsInfo; } } public static String getRegisterSetLabel() { return "GPIO User Bank Pad Control Registers"; } public static String getRegisterSetDescription() { return "The GPIO user bank pad control registers as described in%n" + "Sect. 2.19.6.3 of the RP2040 datasheet.%n" + "Base address for this register set is%n" + String.format("0x%08x.%n", PADS_BANK0_BASE); } private final RegisterDetails registerDetails; private Regs() { throw new UnsupportedOperationException("unsupported empty constructor"); } private Regs(final Regs ref) { this(ref.registerDetails); } private Regs(final Regs ref, final int smNum) { this(ref.registerDetails.createCopyForDifferentSm(smNum)); } private Regs(final String info, final BitsInfo[] bitsInfos) { this(new RegisterDetails(info, bitsInfos)); } private Regs(final String info, final List bitsInfos) { this(new RegisterDetails(info, bitsInfos)); } private Regs(final String info, final int smNum, final BitsInfo[] bitsInfos) { this(new RegisterDetails(info, smNum, bitsInfos)); } private Regs(final String info, final int smNum, final List bitsInfos) { this(new RegisterDetails(info, smNum, bitsInfos)); } private Regs(final RegisterDetails registerDetails) { this.registerDetails = registerDetails; } @Override public String getInfo() { return registerDetails.getInfo(); } @Override public RegisterDetails getRegisterDetails() { return registerDetails; } } protected static final Regs[] REGS = Regs.values(); @Override @SuppressWarnings("unchecked") protected > T[] getRegs() { return (T[])REGS; } protected static final int GPIO_DATA_SIZE = Regs.GPIO1_STATUS.ordinal() - Regs.GPIO0_STATUS.ordinal(); protected static final int PROC_INT_DATA_SIZE = Regs.PROC1_INTE0.ordinal() - Regs.PROC0_INTE0.ordinal(); public static int getAddress(final GPIOIOBank0Registers.Regs register) { if (register == null) { throw new NullPointerException("register"); } return IO_BANK0_BASE + 0x4 * register.ordinal(); } public static int getGPIOAddress(final int gpioNum, final GPIOIOBank0Registers.Regs register) { Constants.checkGpioPin(gpioNum, "GPIO pin number"); if (register == null) { throw new NullPointerException("register"); } switch (register) { case GPIO0_STATUS: case GPIO0_CTRL: break; // ok default: throw new IllegalArgumentException("register not one of GPIO0_*: " + register); } return IO_BANK0_BASE + 0x4 * (register.ordinal() + gpioNum * GPIO_DATA_SIZE); } public static int getIntr(final int intrNum) { Constants.checkIntrNum(intrNum, "INTR number"); return IO_BANK0_BASE + 0x4 * (Regs.INTR0.ordinal() + intrNum); } public static int getProcIntE(final int pioNum, final int inteNum) { Constants.checkPioNum(pioNum, "PIO number"); Constants.checkIntrNum(inteNum, "INTE number"); final int regsOffs = pioNum * PROC_INT_DATA_SIZE + inteNum; return IO_BANK0_BASE + 0x4 * (Regs.PROC0_INTE0.ordinal() + regsOffs); } public static int getProcIntF(final int pioNum, final int intfNum) { Constants.checkPioNum(pioNum, "PIO number"); Constants.checkIntrNum(intfNum, "INTF number"); final int regsOffs = pioNum * PROC_INT_DATA_SIZE + intfNum; return IO_BANK0_BASE + 0x4 * (Regs.PROC0_INTF0.ordinal() + regsOffs); } public static int getProcIntS(final int pioNum, final int intsNum) { Constants.checkPioNum(pioNum, "PIO number"); Constants.checkIntrNum(intsNum, "INTS number"); final int regsOffs = pioNum * PROC_INT_DATA_SIZE + intsNum; return IO_BANK0_BASE + 0x4 * (Regs.PROC0_INTS0.ordinal() + regsOffs); } public static int getDormantWakeIntE(final int inteNum) { Constants.checkIntrNum(inteNum, "Dormant Wake INTE number"); return IO_BANK0_BASE + 0x4 * (Regs.DORMANT_WAKE_INTE0.ordinal() + inteNum); } public static int getDormantWakeIntF(final int intfNum) { Constants.checkIntrNum(intfNum, "Dormant Wake INTF number"); return IO_BANK0_BASE + 0x4 * (Regs.DORMANT_WAKE_INTF0.ordinal() + intfNum); } public static int getDormantWakeIntS(final int intsNum) { Constants.checkIntrNum(intsNum, "Dormant Wake INTS number"); return IO_BANK0_BASE + 0x4 * (Regs.DORMANT_WAKE_INTS0.ordinal() + intsNum); } public GPIOIOBank0Registers() { super("GPIOIOBank0", IO_BANK0_BASE); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/GPIOIOBank0RegistersImpl.java ================================================ /* * @(#)GPIOIOBank0RegistersImpl.java 1.00 21/03/20 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * Facade to the internal GPIO IO Bank 0 subsystem. The layout of * registers follows the list of registers in Sect. 2.19.6 of the * RP2040 datasheet. The facade is in particular intended for use by * the SDK. * * Note: This class implements only a subset of the RP2040 GPIO set of * registers, focussing on those registers that are relevant for PIO * simulation. All GPIO registers specified by the RP2040 datasheet * are addressable, but writing to non-relevant registers or register * bits will have no effect, and reading from non-relevant registers * or register bits will return a constant value of 0. */ public class GPIOIOBank0RegistersImpl extends GPIOIOBank0Registers { public static final int GPIO_DATA_SIZE = Regs.GPIO1_STATUS.ordinal() - Regs.GPIO0_STATUS.ordinal(); public static final int PROC_INT_DATA_SIZE = Regs.PROC1_INTE0.ordinal() - Regs.PROC0_INTE0.ordinal(); private final GPIO gpio; public GPIOIOBank0RegistersImpl(final GPIO gpio) { if (gpio == null) { throw new NullPointerException("gpio"); } this.gpio = gpio; } public GPIO getGPIO() { return gpio; } @Override public void writeRegister(final int regNum, final int value, final int mask, final boolean xor) { checkRegNum(regNum); final Regs register = REGS[regNum]; switch (register) { case GPIO0_STATUS: case GPIO1_STATUS: case GPIO2_STATUS: case GPIO3_STATUS: case GPIO4_STATUS: case GPIO5_STATUS: case GPIO6_STATUS: case GPIO7_STATUS: case GPIO8_STATUS: case GPIO9_STATUS: case GPIO10_STATUS: case GPIO11_STATUS: case GPIO12_STATUS: case GPIO13_STATUS: case GPIO14_STATUS: case GPIO15_STATUS: case GPIO16_STATUS: case GPIO17_STATUS: case GPIO18_STATUS: case GPIO19_STATUS: case GPIO20_STATUS: case GPIO21_STATUS: case GPIO22_STATUS: case GPIO23_STATUS: case GPIO24_STATUS: case GPIO25_STATUS: case GPIO26_STATUS: case GPIO27_STATUS: case GPIO28_STATUS: case GPIO29_STATUS: break; // read-only address case GPIO0_CTRL: case GPIO1_CTRL: case GPIO2_CTRL: case GPIO3_CTRL: case GPIO4_CTRL: case GPIO5_CTRL: case GPIO6_CTRL: case GPIO7_CTRL: case GPIO8_CTRL: case GPIO9_CTRL: case GPIO10_CTRL: case GPIO11_CTRL: case GPIO12_CTRL: case GPIO13_CTRL: case GPIO14_CTRL: case GPIO15_CTRL: case GPIO16_CTRL: case GPIO17_CTRL: case GPIO18_CTRL: case GPIO19_CTRL: case GPIO20_CTRL: case GPIO21_CTRL: case GPIO22_CTRL: case GPIO23_CTRL: case GPIO24_CTRL: case GPIO25_CTRL: case GPIO26_CTRL: case GPIO27_CTRL: case GPIO28_CTRL: case GPIO29_CTRL: gpio.setCTRL((regNum - Regs.GPIO0_CTRL.ordinal()) / GPIO_DATA_SIZE, value, mask, xor); break; case INTR0: case INTR1: case INTR2: case INTR3: // TODO break; case PROC0_INTE0: case PROC0_INTE1: case PROC0_INTE2: case PROC0_INTE3: case PROC1_INTE0: case PROC1_INTE1: case PROC1_INTE2: case PROC1_INTE3: // TODO break; case PROC0_INTF0: case PROC0_INTF1: case PROC0_INTF2: case PROC0_INTF3: case PROC1_INTF0: case PROC1_INTF1: case PROC1_INTF2: case PROC1_INTF3: // TODO break; case PROC0_INTS0: case PROC0_INTS1: case PROC0_INTS2: case PROC0_INTS3: case PROC1_INTS0: case PROC1_INTS1: case PROC1_INTS2: case PROC1_INTS3: // TODO break; case DORMANT_WAKE_INTE0: case DORMANT_WAKE_INTE1: case DORMANT_WAKE_INTE2: case DORMANT_WAKE_INTE3: // TODO break; case DORMANT_WAKE_INTF0: case DORMANT_WAKE_INTF1: case DORMANT_WAKE_INTF2: case DORMANT_WAKE_INTF3: // TODO break; case DORMANT_WAKE_INTS0: case DORMANT_WAKE_INTS1: case DORMANT_WAKE_INTS2: case DORMANT_WAKE_INTS3: // TODO break; default: throw new InternalError("unexpected case fall-through"); } } @Override public synchronized int readRegister(final int regNum) { checkRegNum(regNum); final Regs register = REGS[regNum]; switch (register) { case GPIO0_STATUS: case GPIO1_STATUS: case GPIO2_STATUS: case GPIO3_STATUS: case GPIO4_STATUS: case GPIO5_STATUS: case GPIO6_STATUS: case GPIO7_STATUS: case GPIO8_STATUS: case GPIO9_STATUS: case GPIO10_STATUS: case GPIO11_STATUS: case GPIO12_STATUS: case GPIO13_STATUS: case GPIO14_STATUS: case GPIO15_STATUS: case GPIO16_STATUS: case GPIO17_STATUS: case GPIO18_STATUS: case GPIO19_STATUS: case GPIO20_STATUS: case GPIO21_STATUS: case GPIO22_STATUS: case GPIO23_STATUS: case GPIO24_STATUS: case GPIO25_STATUS: case GPIO26_STATUS: case GPIO27_STATUS: case GPIO28_STATUS: case GPIO29_STATUS: return gpio.getSTATUS((regNum - Regs.GPIO0_STATUS.ordinal()) / GPIO_DATA_SIZE); case GPIO0_CTRL: case GPIO1_CTRL: case GPIO2_CTRL: case GPIO3_CTRL: case GPIO4_CTRL: case GPIO5_CTRL: case GPIO6_CTRL: case GPIO7_CTRL: case GPIO8_CTRL: case GPIO9_CTRL: case GPIO10_CTRL: case GPIO11_CTRL: case GPIO12_CTRL: case GPIO13_CTRL: case GPIO14_CTRL: case GPIO15_CTRL: case GPIO16_CTRL: case GPIO17_CTRL: case GPIO18_CTRL: case GPIO19_CTRL: case GPIO20_CTRL: case GPIO21_CTRL: case GPIO22_CTRL: case GPIO23_CTRL: case GPIO24_CTRL: case GPIO25_CTRL: case GPIO26_CTRL: case GPIO27_CTRL: case GPIO28_CTRL: case GPIO29_CTRL: return gpio.getCTRL((regNum - Regs.GPIO0_CTRL.ordinal()) / GPIO_DATA_SIZE); case INTR0: case INTR1: case INTR2: case INTR3: return 0; // TODO case PROC0_INTE0: case PROC0_INTE1: case PROC0_INTE2: case PROC0_INTE3: case PROC1_INTE0: case PROC1_INTE1: case PROC1_INTE2: case PROC1_INTE3: return 0; // TODO case PROC0_INTF0: case PROC0_INTF1: case PROC0_INTF2: case PROC0_INTF3: case PROC1_INTF0: case PROC1_INTF1: case PROC1_INTF2: case PROC1_INTF3: return 0; // TODO case PROC0_INTS0: case PROC0_INTS1: case PROC0_INTS2: case PROC0_INTS3: case PROC1_INTS0: case PROC1_INTS1: case PROC1_INTS2: case PROC1_INTS3: return 0; // TODO case DORMANT_WAKE_INTE0: case DORMANT_WAKE_INTE1: case DORMANT_WAKE_INTE2: case DORMANT_WAKE_INTE3: return 0; // TODO case DORMANT_WAKE_INTF0: case DORMANT_WAKE_INTF1: case DORMANT_WAKE_INTF2: case DORMANT_WAKE_INTF3: return 0; // TODO case DORMANT_WAKE_INTS0: case DORMANT_WAKE_INTS1: case DORMANT_WAKE_INTS2: case DORMANT_WAKE_INTS3: return 0; // TODO default: throw new InternalError("unexpected case fall-through"); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/GPIOPadsBank0Registers.java ================================================ /* * @(#)GPIOPadsBank0Registers.java 1.00 21/03/20 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.util.List; import org.soundpaint.rp2040pio.doctool.RegistersDocs; /** * Facade to the internal GPIO Pads Bank 0 subsystem. The layout of * registers follows the list of registers in Sect. 2.19.6 of the * RP2040 datasheet. The facade is in particular intended for use by * the SDK. * * Note: This class implements only a subset of the RP2040 GPIO set of * registers, focussing on those registers that are relevant for PIO * simulation. All GPIO registers specified by the RP2040 datasheet * are addressable, but writing to non-relevant registers or register * bits will have no effect, and reading from non-relevant registers * or register bits will return a constant value of 0. */ public abstract class GPIOPadsBank0Registers extends RegisterSet { public enum Regs implements RegistersDocs { VOLTAGE_SELECT("Voltage select.", new BitsInfo[] { new BitsInfo(null, 31, 1, null, BitsType.RESERVED, null), new BitsInfo(null, 0, 0, "3.3V / 1.8V", BitsType.RW, null) }), GPIO0("Pad control register.", new BitsInfo[] { new BitsInfo(null, 31, 8, null, BitsType.RESERVED, null), new BitsInfo("OD", 7, 7, "Output disable.", BitsType.RW, 0), new BitsInfo("IE", 6, 6, "Input enable.", BitsType.RW, 1), new BitsInfo("DRIVE", 5, 4, "Drive strength.", BitsType.RW, 1), new BitsInfo("PUE", 3, 3, "Pull up enable.", BitsType.RW, 0), new BitsInfo("PDE", 2, 2, "Pull down enable.", BitsType.RW, 1), new BitsInfo("SCHMITT", 1, 1, "Enable schmitt trigger", BitsType.RW, 1), new BitsInfo("SLEWFAST", 0, 0, "Slew rate control.", BitsType.RW, 0) }), GPIO1(Regs.GPIO0), GPIO2(Regs.GPIO0), GPIO3(Regs.GPIO0), GPIO4(Regs.GPIO0), GPIO5(Regs.GPIO0), GPIO6(Regs.GPIO0), GPIO7(Regs.GPIO0), GPIO8(Regs.GPIO0), GPIO9(Regs.GPIO0), GPIO10(Regs.GPIO0), GPIO11(Regs.GPIO0), GPIO12(Regs.GPIO0), GPIO13(Regs.GPIO0), GPIO14(Regs.GPIO0), GPIO15(Regs.GPIO0), GPIO16(Regs.GPIO0), GPIO17(Regs.GPIO0), GPIO18(Regs.GPIO0), GPIO19(Regs.GPIO0), GPIO20(Regs.GPIO0), GPIO21(Regs.GPIO0), GPIO22(Regs.GPIO0), GPIO23(Regs.GPIO0), GPIO24(Regs.GPIO0), GPIO25(Regs.GPIO0), GPIO26(Regs.GPIO0), GPIO27(Regs.GPIO0), GPIO28(Regs.GPIO0), GPIO29(Regs.GPIO0), SWCLK("Pad control register.", new BitsInfo[] { new BitsInfo(null, 31, 8, null, BitsType.RESERVED, null), new BitsInfo("OD", 7, 7, "Output disable.", BitsType.RW, 1), new BitsInfo("IE", 6, 6, "Input enable.", BitsType.RW, 1), new BitsInfo("DRIVE", 5, 4, "Drive strength.", BitsType.RW, 1), new BitsInfo("PUE", 3, 3, "Pull up enable.", BitsType.RW, 1), new BitsInfo("PDE", 2, 2, "Pull down enable.", BitsType.RW, 0), new BitsInfo("SCHMITT", 1, 1, "Enable schmitt trigger", BitsType.RW, 1), new BitsInfo("SLEWFAST", 0, 0, "Slew rate control.", BitsType.RW, 0) }), SWD("Pad control register.", new BitsInfo[] { new BitsInfo(null, 31, 8, null, BitsType.RESERVED, null), new BitsInfo("OD", 7, 7, "Output disable.", BitsType.RW, 0), new BitsInfo("IE", 6, 6, "Input enable.", BitsType.RW, 1), new BitsInfo("DRIVE", 5, 4, "Drive strength.", BitsType.RW, 1), new BitsInfo("PUE", 3, 3, "Pull up enable.", BitsType.RW, 1), new BitsInfo("PDE", 2, 2, "Pull down enable.", BitsType.RW, 0), new BitsInfo("SCHMITT", 1, 1, "Enable schmitt trigger", BitsType.RW, 1), new BitsInfo("SLEWFAST", 0, 0, "Slew rate control.", BitsType.RW, 0) }); public static String getRegisterSetLabel() { return "GPIO User Bank Pad Control Registers"; } public static String getRegisterSetDescription() { return "The GPIO user bank pad control registers as described in%n" + "Sect. 2.19.6.3 of the RP2040 datasheet.%n" + "Base address for this register set is%n" + String.format("0x%08x.%n", PADS_BANK0_BASE); } private final RegisterDetails registerDetails; private Regs() { throw new UnsupportedOperationException("unsupported empty constructor"); } private Regs(final Regs ref) { this(ref.registerDetails); } private Regs(final Regs ref, final int smNum) { this(ref.registerDetails.createCopyForDifferentSm(smNum)); } private Regs(final String info, final BitsInfo[] bitsInfos) { this(new RegisterDetails(info, bitsInfos)); } private Regs(final String info, final List bitsInfos) { this(new RegisterDetails(info, bitsInfos)); } private Regs(final String info, final int smNum, final BitsInfo[] bitsInfos) { this(new RegisterDetails(info, smNum, bitsInfos)); } private Regs(final String info, final int smNum, final List bitsInfos) { this(new RegisterDetails(info, smNum, bitsInfos)); } private Regs(final RegisterDetails registerDetails) { this.registerDetails = registerDetails; } @Override public String getInfo() { return registerDetails.getInfo(); } @Override public RegisterDetails getRegisterDetails() { return registerDetails; } } protected static final Regs[] REGS = Regs.values(); @Override @SuppressWarnings("unchecked") protected > T[] getRegs() { return (T[])REGS; } public static int getAddress(final GPIOPadsBank0Registers.Regs register) { if (register == null) { throw new NullPointerException("register"); } return PADS_BANK0_BASE + 0x4 * register.ordinal(); } public static int getGPIOAddress(final int gpioNum) { Constants.checkGpioPin(gpioNum, "GPIO pin number"); return PADS_BANK0_BASE + 0x4 * (Regs.GPIO0.ordinal() + gpioNum); } public GPIOPadsBank0Registers() { super("GPIOPadsBank0", PADS_BANK0_BASE); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/GPIOPadsBank0RegistersImpl.java ================================================ /* * @(#)GPIOPadsBank0RegistersImpl.java 1.00 21/03/20 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * Facade to the internal GPIO Pads Bank 0 subsystem. The layout of * registers follows the list of registers in Sect. 2.19.6 of the * RP2040 datasheet. The facade is in particular intended for use by * the SDK. * * Note: This class implements only a subset of the RP2040 GPIO set of * registers, focussing on those registers that are relevant for PIO * simulation. All GPIO registers specified by the RP2040 datasheet * are addressable, but writing to non-relevant registers or register * bits will have no effect, and reading from non-relevant registers * or register bits will return a constant value of 0. */ public class GPIOPadsBank0RegistersImpl extends GPIOPadsBank0Registers { private final GPIO gpio; public GPIOPadsBank0RegistersImpl(final GPIO gpio) { if (gpio == null) { throw new NullPointerException("gpio"); } this.gpio = gpio; } public GPIO getGPIO() { return gpio; } @Override public void writeRegister(final int regNum, final int value, final int mask, final boolean xor) { checkRegNum(regNum); final Regs register = REGS[regNum]; switch (register) { case VOLTAGE_SELECT: // TODO break; case GPIO0: case GPIO1: case GPIO2: case GPIO3: case GPIO4: case GPIO5: case GPIO6: case GPIO7: case GPIO8: case GPIO9: case GPIO10: case GPIO11: case GPIO12: case GPIO13: case GPIO14: case GPIO15: case GPIO16: case GPIO17: case GPIO18: case GPIO19: case GPIO20: case GPIO21: case GPIO22: case GPIO23: case GPIO24: case GPIO25: case GPIO26: case GPIO27: case GPIO28: case GPIO29: // TODO break; case SWCLK: // TODO break; case SWD: // TODO break; default: throw new InternalError("unexpected case fall-through"); } } @Override public synchronized int readRegister(final int regNum) { checkRegNum(regNum); final Regs register = REGS[regNum]; switch (register) { case VOLTAGE_SELECT: return 0; // TODO case GPIO0: case GPIO1: case GPIO2: case GPIO3: case GPIO4: case GPIO5: case GPIO6: case GPIO7: case GPIO8: case GPIO9: case GPIO10: case GPIO11: case GPIO12: case GPIO13: case GPIO14: case GPIO15: case GPIO16: case GPIO17: case GPIO18: case GPIO19: case GPIO20: case GPIO21: case GPIO22: case GPIO23: case GPIO24: case GPIO25: case GPIO26: case GPIO27: case GPIO28: case GPIO29: return 0; // TODO case SWCLK: return 0; // TODO case SWD: return 0; // TODO default: throw new InternalError("unexpected case fall-through"); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/IOUtils.java ================================================ /* * @(#)IOUtils.java 1.00 21/04/08 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.File; import java.io.FileNotFoundException; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.LineNumberReader; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class IOUtils { /** * @param resourcePath absolute Path within root package, i.e. with * leading "/". */ public static InputStream getStreamForResourcePath(final String resourcePath) throws IOException { final InputStream fromFile; try { fromFile = new FileInputStream(resourcePath); } catch (final FileNotFoundException e) { final InputStream fromResource = Constants.class.getResourceAsStream(resourcePath); if (fromResource == null) { throw new IOException("resource not found: " + resourcePath); } return fromResource; } return fromFile; } public static LineNumberReader getReaderForResourcePath(final String resourcePath) throws IOException { final InputStream in = getStreamForResourcePath(resourcePath); return new LineNumberReader(new InputStreamReader(in)); } /** * @param resourcePath Path relative to root package, i.e. without * leading "/". */ public static List list(final String resourcePath) throws IOException { final List paths = new ArrayList(); final File resourceFile = new File(IOUtils.class.getProtectionDomain(). getCodeSource().getLocation().getPath()); if (resourceFile.isFile()) { final JarFile jarFile = new JarFile(resourceFile); final Enumeration entries = jarFile.entries(); final String prefix = resourcePath + "/"; while (entries.hasMoreElements()) { final String path = entries.nextElement().getName(); if (path.startsWith(prefix) && (path.length() > prefix.length())) { paths.add(path.substring(prefix.length())); } } jarFile.close(); } else { final URL url = IOUtils.class.getResource("/" + resourcePath); if (url != null) { final File directoryPath; try { directoryPath = new File(url.toURI()); } catch (final URISyntaxException e) { throw new InternalError("unexpected exception", e); } final String prefix = directoryPath + "/"; for (final File file : directoryPath.listFiles()) { final String path = file.getPath(); if (path.startsWith(prefix) && (path.length() > prefix.length())) { paths.add(path.substring(prefix.length())); } } } } return paths; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/IRQ.java ================================================ /* * @(#)IRQ.java 1.00 21/02/06 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * IRQ Register Set */ public class IRQ implements Constants { private int regIRQ; // bits 0…7 of IRQ private int regIRQ0_INTE; // bits 0…11 of IRQ0_INTE private int regIRQ0_INTF; // bits 0…11 of IRQ0_INTF private int regIRQ1_INTE; // bits 0…11 of IRQ1_INTE private int regIRQ1_INTF; // bits 0…11 of IRQ1_INTF private int fifoStatus; public IRQ() { reset(); } public void reset() { regIRQ = 0; regIRQ0_INTE = 0; regIRQ0_INTF = 0; regIRQ1_INTE = 0; regIRQ1_INTF = 0; fifoStatus = 0; } public void setTxNFull(final int smNum, final boolean nFull) { Constants.checkSmNum(smNum); if (nFull) { fifoStatus |= 0x10 << smNum; } else { fifoStatus &= ~(0x10 << smNum); } } public void setRxNEmpty(final int smNum, final boolean nEmpty) { Constants.checkSmNum(smNum); if (nEmpty) { fifoStatus |= 0x1 << smNum; } else { fifoStatus &= ~(0x1 << smNum); } } public void writeRegIRQ(final int value) { regIRQ &= (~value) & 0xff; // ignore reserved bits 31:8 } public void writeRegIRQ_FORCE(final int value) { regIRQ |= value & 0xff; // ignore reserved bits 31:8 } public Bit get(final int index) { if (index < 0) { throw new IllegalArgumentException("IRQ index < 0: " + index); } if (index > 7) { throw new IllegalArgumentException("IRQ index > 7: " + index); } return ((regIRQ >> index) & 0x1) == 0x0 ? Bit.LOW : Bit.HIGH; } public void clear(final int index) { if (index < 0) { throw new IllegalArgumentException("IRQ index < 0: " + index); } if (index > 7) { throw new IllegalArgumentException("IRQ index > 7: " + index); } regIRQ &= ~(0x1 << index); } public void set(final int index) { if (index < 0) { throw new IllegalArgumentException("IRQ index < 0: " + index); } if (index > 7) { throw new IllegalArgumentException("IRQ index > 7: " + index); } regIRQ |= 0x1 << index; } public int getIRQ() { return regIRQ; } public int getIRQ0_INTE() { return regIRQ0_INTE; } public void setIRQ0_INTE(final int value, final int mask, final boolean xor) { setIRQ0_INTE(Constants.hwSetBits(regIRQ0_INTE, value, mask, xor)); } private void setIRQ0_INTE(final int value) { regIRQ0_INTE = value & 0xfff; // ignore reserved bits 31:12 } public int getIRQ1_INTE() { return regIRQ1_INTE; } public void setIRQ1_INTE(final int value, final int mask, final boolean xor) { setIRQ1_INTE(Constants.hwSetBits(regIRQ1_INTE, value, mask, xor)); } private void setIRQ1_INTE(final int value) { regIRQ1_INTE = value & 0xfff; // ignore reserved bits 31:12 } public int getIRQ0_INTF() { return regIRQ0_INTF; } public void setIRQ0_INTF(final int value, final int mask, final boolean xor) { setIRQ0_INTF(Constants.hwSetBits(regIRQ0_INTF, value, mask, xor)); } private void setIRQ0_INTF(final int value) { regIRQ0_INTF = value & 0xfff; // ignore reserved bits 31:12 } public int getIRQ1_INTF() { return regIRQ1_INTF; } public void setIRQ1_INTF(final int value, final int mask, final boolean xor) { setIRQ1_INTF(Constants.hwSetBits(regIRQ1_INTF, value, mask, xor)); } private void setIRQ1_INTF(final int value) { regIRQ1_INTF = value & 0xfff; // ignore reserved bits 31:12 } public int readINTR() { return ((regIRQ & 0x7) << 8) | fifoStatus; } public int readIRQ0_INTS() { return (readINTR() & regIRQ0_INTE) | regIRQ0_INTF; } public int readIRQ1_INTS() { return (readINTR() & regIRQ1_INTE) | regIRQ1_INTF; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/Instruction.java ================================================ /* * @(#)Instruction.java 1.00 21/01/31 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.util.HashMap; import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.IntConsumer; /** * Instruction */ public abstract class Instruction { private int delay; private int sideSet; private int sideSetCount; private boolean sideSetEnabled; private int opCode; public enum ResultState { /** * The instruction leaves it to the program control to ordinarily * update the program counter (PC) by increasing it by one and * perform any delay. */ COMPLETE, /** * The PC must be kept unmodified (e.g. as the result of a wait * instruction keeping unfulfilled or an additional synthetic * instruction being inserted), and any delay must not yet be * performed. */ STALL, /** * The instruction has modified the PC by itself. Delay must be * ordinarily performed. */ JUMP }; public Instruction() { reset(); } public int getDelay() { return delay; } private String getDelayDisplayValue() { return delay > 0 ? "[" + delay + "]" : ""; } public int getOpCode() { return opCode; } private String getSideSetDisplayValue() { final boolean printSideSet = sideSetEnabled && sideSetCount > 0; return printSideSet ? "side " + Integer.toString(sideSet) : ""; } public void reset() { delay = 0; sideSet = 0; sideSetCount = 0; sideSetEnabled = false; resetParams(); } abstract protected void resetParams(); public Instruction decode(final short opCode, final int pinCtrlSidesetCount, final boolean execCtrlSideEn) throws Decoder.DecodeException { final int delayAndSideSet = (opCode >>> 0x8) & 0x1f; final int delayMask = (0x1 << (5 - pinCtrlSidesetCount)) - 1; this.opCode = opCode; delay = delayAndSideSet & delayMask; final int delayBitCount = 5 - pinCtrlSidesetCount; final boolean haveSideSetEnableBit = execCtrlSideEn && (pinCtrlSidesetCount > 0); sideSetEnabled = !haveSideSetEnableBit || (delayAndSideSet & 0x10) != 0x0; sideSetCount = pinCtrlSidesetCount - (haveSideSetEnableBit ? 1 : 0); final int delayAndSideSetWithoutSideEn = execCtrlSideEn ? delayAndSideSet & 0xf : delayAndSideSet; sideSet = delayAndSideSetWithoutSideEn >>> delayBitCount; decodeLSB(opCode & 0xff); return this; } protected int getDelayAndSideSetBits(final int pinCtrlSidesetCount, final boolean execCtrlSideEn) { final int delayBitCount = 5 - pinCtrlSidesetCount; final boolean haveSideSetEnableBit = execCtrlSideEn && (pinCtrlSidesetCount > 0); final int delayAndSideSet = (haveSideSetEnableBit && (sideSet > 0) ? 0x10 : 0x00) | (sideSet << delayBitCount) | delay; return (delayAndSideSet & 0x1f) << 8; } /** * Updates all internal data of this instruction according to the * argument bits of the instruction word. */ abstract void decodeLSB(final int lsb) throws Decoder.DecodeException; private void executeSideSet(final SM.Status smStatus) { final int pinCtrlSidesetBase = smStatus.regPINCTRL_SIDESET_BASE; final PIO.PinDir execCtrlSidePinDir = smStatus.regEXECCTRL_SIDE_PINDIR; if (sideSetCount > 0) { if (execCtrlSidePinDir == PIO.PinDir.GPIO_LEVELS) { smStatus.collatePins(sideSet, pinCtrlSidesetBase, sideSetCount, true); } else { smStatus.collatePinDirs(sideSet, pinCtrlSidesetBase, sideSetCount); } } } abstract ResultState executeOperation(final SM sm); public ResultState execute(final SM sm) { final ResultState resultState = executeOperation(sm); if (sideSetEnabled) executeSideSet(sm.getStatus()); return resultState; } public abstract String getMnemonic(); abstract String getParamsDisplay(); protected void checkIRQIndex(final int irqIndex) throws Decoder.DecodeException { if ((irqIndex & 0x08) != 0) { throw new Decoder.DecodeException(this, getOpCode()); } } protected static int getIRQNum(final int smNum, final int index) { final boolean isRel = (index & 0x10) != 0; return isRel ? (index & 0x4) | ((smNum + index) & 0x3) : index & 0x7; } protected static String getIRQNumDisplay(final int index) { return String.format("%1x%s", index & 0x7, (index & 0x10) != 0 ? "_rel" : ""); } @Override public String toString() { final String mnemonic = getMnemonic(); final String paramsDisplay = getParamsDisplay(); final String sideSetDisplayValue = getSideSetDisplayValue(); final String delayDisplayValue = getDelayDisplayValue(); return String.format("%-16s%s", mnemonic + (!paramsDisplay.isEmpty() ? " " + paramsDisplay : ""), sideSetDisplayValue + (!sideSetDisplayValue.isEmpty() && !delayDisplayValue.isEmpty() ? " " : "") + delayDisplayValue); } public static class Jmp extends Instruction { private static final Map code2cond = new HashMap(); public enum Condition { ALWAYS(0b000, "", (smStatus) -> true), NOT_X(0b001, "!x", (smStatus) -> smStatus.regX == 0), DEC_X(0b010, "x--", (smStatus) -> smStatus.regX-- != 0), NOT_Y(0b011, "!y", (smStatus) -> smStatus.regY == 0), DEC_Y(0b100, "y--", (smStatus) -> smStatus.regY-- != 0), X_NEQ_Y(0b101, "x!=y", (smStatus) -> smStatus.regX != smStatus.regY), PIN(0b110, "pin", (smStatus) -> smStatus.jmpPin() == Bit.HIGH), NOT_OSRE(0b111, "!osre", (smStatus) -> !smStatus.isOsrCountBeyondThreshold()); private final int code; private final String mnemonic; private final Function eval; private Condition(final int code, final String mnemonic, final Function eval) { this.code = code; this.mnemonic = mnemonic; this.eval = eval; code2cond.put(code, this); } public boolean fulfilled(final SM.Status smStatus) { return eval.apply(smStatus); } @Override public String toString() { return mnemonic; } } private int address; private Condition condition; @Override protected void resetParams() { address = 0; condition = Condition.ALWAYS; } public void setCondition(final Condition condition) { if (condition == null) { throw new NullPointerException("condition"); } this.condition = condition; } public void setAddress(final int address) { if (address < 0) { throw new IllegalArgumentException("address < 0: " + address); } if (address > 31) { throw new IllegalArgumentException("address > 31: " + address); } this.address = address; } public int encode(final int pinCtrlSidesetCount, final boolean execCtrlSideEn) { return 0x0000 | getDelayAndSideSetBits(pinCtrlSidesetCount, execCtrlSideEn) | (condition.ordinal() << 5) | (address & 0x1f); } @Override public void decodeLSB(final int lsb) { address = lsb & 0x1f; condition = code2cond.get((lsb >>> 5) & 0x7); } @Override public ResultState executeOperation(final SM sm) { final SM.Status smStatus = sm.getStatus(); final boolean doJump = condition.fulfilled(smStatus); if (doJump) smStatus.regADDR = address; return doJump ? ResultState.JUMP : ResultState.COMPLETE; } @Override public String getMnemonic() { return "jmp"; } @Override public String getParamsDisplay() { final String conditionDisplay = condition.toString(); return (!conditionDisplay.isEmpty() ? conditionDisplay + ", " : "") + String.format("%02x", address); } } public static class Wait extends Instruction { private static final Map code2src = new HashMap(); private enum Source { GPIO_(0b00, "gpio", (wait, sm) -> sm.getPIOGPIO().getGPIO().getInToPeri(wait.index)), PIN(0b01, "pin", (wait, sm) -> { final int gpioNum = (wait.index + sm.getStatus().regPINCTRL_IN_BASE) & (Constants.GPIO_NUM - 1); return sm.getPIOGPIO().getGPIO().getInToPeri(gpioNum); }), IRQ(0b10, "irq", (wait, sm) -> { final int irqNum = getIRQNum(sm.getNum(), wait.index); final Bit bit = sm.getIRQ(irqNum); if ((wait.polarity == Bit.HIGH) && (bit == wait.polarity)) sm.clearIRQ(irqNum); return bit; }), RESERVED_3(0b11, "???", null); private final int code; private final String mnemonic; private final BiFunction eval; private Source(final int code, final String mnemonic, final BiFunction eval) { this.code = code; this.mnemonic = mnemonic; this.eval = eval; code2src.put(code, this); } public Bit getBit(final Wait wait, SM sm) { return eval.apply(wait, sm); } @Override public String toString() { return mnemonic; } } private Bit polarity; private Source src; private int index; @Override protected void resetParams() { polarity = Bit.LOW; src = Source.GPIO_; index = 0; } @Override public void decodeLSB(final int lsb) throws Decoder.DecodeException { polarity = (lsb & 0x80) != 0 ? Bit.HIGH : Bit.LOW; src = code2src.get((lsb & 0x60) >>> 5); if (src == Source.RESERVED_3) { throw new Decoder.DecodeException(this, getOpCode()); } index = lsb & 0x1f; checkIRQIndex(index); } @Override public ResultState executeOperation(final SM sm) { final boolean doStall = src.getBit(this, sm) != polarity; return doStall ? ResultState.STALL : ResultState.COMPLETE; } @Override public String getMnemonic() { return "wait"; } @Override public String getParamsDisplay() { final int maskedIndex; final String num = src == Source.IRQ ? getIRQNumDisplay(index) : String.format("%02x", index); return polarity + " " + src + " " + num; } } public static class In extends Instruction { private static final Map code2src = new HashMap(); private enum Source { PINS(0b000, "pins", (sm) -> { final int base = sm.getStatus().regPINCTRL_IN_BASE; return sm.getPIOGPIO().getGPIO().getPinsToPeri(base, Constants.GPIO_NUM); }), X(0b001, "x", (sm) -> sm.getX()), Y(0b010, "y", (sm) -> sm.getY()), NULL(0b011, "null", (sm) -> 0), RESERVED_4(0b100, "???", null), RESERVED_5(0b101, "???", null), ISR(0b110, "ISR", (sm) -> sm.getISRValue()), OSR(0b111, "OSR", (sm) -> sm.getOSRValue()); private final int code; private final String mnemonic; private final Function eval; private Source(final int code, final String mnemonic, final Function eval) { this.code = code; this.mnemonic = mnemonic; this.eval = eval; code2src.put(code, this); } public Integer getData(final SM sm) { return eval.apply(sm); } @Override public String toString() { return mnemonic; } } private Source src; private int bitCount; @Override protected void resetParams() { src = Source.PINS; bitCount = 0; } @Override public void decodeLSB(final int lsb) throws Decoder.DecodeException { src = code2src.get((lsb & 0xe0) >>> 5); if ((src == Source.RESERVED_4) || (src == Source.RESERVED_5)) { throw new Decoder.DecodeException(this, getOpCode()); } bitCount = lsb & 0x1f; } private void shiftIn(final SM sm, final SM.Status smStatus, final int data, final int bitsToShift) { if (bitsToShift < 32) { if (sm.getInShiftDir() == PIO.ShiftDir.SHIFT_LEFT) { smStatus.isrValue <<= bitsToShift; smStatus.isrValue |= data & ((0x1 << bitsToShift) - 1); } else /* SHIFT RIGHT */ { smStatus.isrValue >>>= bitsToShift; smStatus.isrValue |= (data & ((0x1 << bitsToShift) - 1)) << (32 - bitsToShift); } } else { smStatus.isrValue = data; } } private void saturate(final SM sm, final SM.Status smStatus, final int bitsToShift) { smStatus.isrShiftCount = SM.saturate(smStatus.isrShiftCount, bitsToShift, 32); } @Override public ResultState executeOperation(final SM sm) { final int bitsToShift = Constants.checkBitCount(bitCount, "shift ISR bitCount"); final SM.Status smStatus = sm.getStatus(); /* * TODO: Clarify: Do we need to stall if ISR and RX FIFO are * both full, prior to call shiftIn() (see built-in example * "logic-analyser" as test case)? The spec does not say so * (see Sect. 3.5.4.1.), but I would expect it. In the latter * case, we need the following additional code te be executed * first, prior to the call to shiftIn(): */ /* if (smStatus.isIsrCountBeyondThreshold()) { if (sm.isRXFIFOFull()) { return ResultState.STALL; } } */ shiftIn(sm, smStatus, src.getData(sm), bitsToShift); saturate(sm, smStatus, bitsToShift); final boolean stall; if (smStatus.regSHIFTCTRL_AUTOPUSH) { // Cp. pseudocode sequence for "IN" cycle in RP2040 datasheet, // Sect. 3.5.4.1. "Autopush Details". if (smStatus.isIsrCountBeyondThreshold()) { if (sm.isRXFIFOFull()) { stall = true; } else { stall = sm.rxPush(false, true); } } else { stall = false; } } else { stall = false; } return stall ? ResultState.STALL : ResultState.COMPLETE; } @Override public String getMnemonic() { return "in"; } @Override public String getParamsDisplay() { return src + ", " + String.format("%02x", bitCount != 0 ? bitCount : 32); } } public static class Out extends Instruction { private static final Map code2dst = new HashMap(); public enum Destination { PINS(0b000, "pins", (sm, data) -> { SM.IOMapping.OUT.collatePins(sm, data); return null; }), X(0b001, "x", (sm, data) -> { sm.setX(data); return null; }), Y(0b010, "y", (sm, data) -> { sm.setY(data); return null; }), NULL(0b011, "null", (sm, data) -> { return null; }), PINDIRS(0b100, "pindirs", (sm, data) -> { SM.IOMapping.OUT.collatePinDirs(sm, data); return null; }), PC(0b101, "pc", (sm, data) -> { sm.setPC(data & 0x1f); return null; }), ISR(0b110, "isr", (sm, data) -> { sm.setISRValue(data); return null; }), EXEC(0b111, "exec", (sm, data) -> { sm.execInstruction(data); return null; }); private final int code; private final String mnemonic; private final BiFunction eval; private Destination(final int code, final String mnemonic, final BiFunction eval) { this.code = code; this.mnemonic = mnemonic; this.eval = eval; code2dst.put(code, this); } public IntConsumer getConsumer(final SM sm) { return (data) -> { eval.apply(sm, data); }; } @Override public String toString() { return mnemonic; } } private Destination dst; private int bitCount; @Override protected void resetParams() { dst = Destination.PINS; bitCount = 0; } public void setDestination(final Destination dst) { if (dst == null) { throw new NullPointerException("dst"); } this.dst = dst; } public void setBitCount(final int bitCount) { if (bitCount < 0) { throw new IllegalArgumentException("bit count < 0: " + bitCount); } if (bitCount > 31) { throw new IllegalArgumentException("bit count > 31: " + bitCount); } this.bitCount = bitCount; } public int encode(final int pinCtrlSidesetCount, final boolean execCtrlSideEn) { return 0x6000 | getDelayAndSideSetBits(pinCtrlSidesetCount, execCtrlSideEn) | (dst.ordinal() << 5) | bitCount; } @Override public void decodeLSB(final int lsb) { dst = code2dst.get((lsb & 0xe0) >>> 5); bitCount = lsb & 0x1f; } private void outputOsr(final SM sm, final SM.Status smStatus, final int bitsToShift) { final int shiftOutBits; if (bitsToShift < 32) { if (sm.getOutShiftDir() == PIO.ShiftDir.SHIFT_LEFT) { shiftOutBits = (smStatus.osrValue >>> (32 - bitsToShift)) & ((0x1 << bitsToShift) - 1); } else /* SHIFT_RIGHT */ { shiftOutBits = smStatus.osrValue & ((0x1 << bitsToShift) - 1); } } else { shiftOutBits = smStatus.osrValue; } dst.getConsumer(sm).accept(shiftOutBits); } private void shiftOsr(final SM sm, final SM.Status smStatus, final int bitsToShift) { if (bitsToShift < 32) { if (sm.getOutShiftDir() == PIO.ShiftDir.SHIFT_LEFT) { smStatus.osrValue <<= bitsToShift; } else /* SHIFT_RIGHT */ { smStatus.osrValue >>>= bitsToShift; } } else { smStatus.osrValue = 0; } } private void saturate(final SM sm, final SM.Status smStatus, final int bitsToShift) { smStatus.osrShiftCount = SM.saturate(smStatus.osrShiftCount, bitsToShift, 32); } @Override public ResultState executeOperation(final SM sm) { // Cp. pseudocode sequence for "OUT" cycles in RP2040 datasheet, // Sect. 3.5.4.2. "Autopull Details". final SM.Status smStatus = sm.getStatus(); final boolean stall; if (smStatus.regSHIFTCTRL_AUTOPULL && smStatus.isOsrCountBeyondThreshold()) { // block=true to avoid loading regX sm.txPull(false, true); // also sets osr count = 0 // RP2040 cannot fill empty OSR and "OUT" in same cycle => // always stall regardless of TX state stall = true; } else { final int bitsToShift = Constants.checkBitCount(bitCount, "shift OSR bitCount"); outputOsr(sm, smStatus, bitsToShift); shiftOsr(sm, smStatus, bitsToShift); saturate(sm, smStatus, bitsToShift); if (smStatus.regSHIFTCTRL_AUTOPULL) { if (smStatus.isOsrCountBeyondThreshold()) { // block=true to avoid loading regX sm.txPull(false, true); // also sets osr count = 0 } } // stall always false, since we *did* output from OSR stall = false; } return (stall || dst == Destination.EXEC) ? ResultState.STALL : (dst == Destination.PC ? ResultState.JUMP : ResultState.COMPLETE); } @Override public String getMnemonic() { return "out"; } @Override public String getParamsDisplay() { return dst + ", " + String.format("%02x", bitCount != 0 ? bitCount : 32); } } public static class Push extends Instruction { private boolean ifFull; private boolean block; @Override protected void resetParams() { ifFull = false; block = false; } @Override public void decodeLSB(final int lsb) throws Decoder.DecodeException { ifFull = (lsb & 0x40) != 0; block = (lsb & 0x20) != 0; if ((lsb & 0x1f) != 0) { throw new Decoder.DecodeException(this, getOpCode()); } } @Override public ResultState executeOperation(final SM sm) { return sm.rxPush(ifFull, block) ? ResultState.STALL : ResultState.COMPLETE; } @Override public String getMnemonic() { return "push"; } @Override public String getParamsDisplay() { return (ifFull ? "iffull" : "") + (ifFull && !block ? " " : "") + (block ? "" : "noblock"); } } public static class Pull extends Instruction { private boolean ifEmpty; private boolean block; @Override protected void resetParams() { ifEmpty = false; block = false; } public void setIfEmpty(final boolean ifEmpty) { this.ifEmpty = ifEmpty; } public void setBlock(final boolean block) { this.block = block; } public int encode(final int pinCtrlSidesetCount, final boolean execCtrlSideEn) { return 0x8080 | getDelayAndSideSetBits(pinCtrlSidesetCount, execCtrlSideEn) | (ifEmpty ? 0x1 << 6 : 0) | (block ? 0x1 << 5 : 0); } @Override public void decodeLSB(final int lsb) throws Decoder.DecodeException { ifEmpty = (lsb & 0x40) != 0; block = (lsb & 0x20) != 0; if ((lsb & 0x1f) != 0) { throw new Decoder.DecodeException(this, getOpCode()); } } @Override public ResultState executeOperation(final SM sm) { return sm.txPull(ifEmpty, block) ? ResultState.STALL : ResultState.COMPLETE; } @Override public String getMnemonic() { return "pull"; } @Override public String getParamsDisplay() { return (ifEmpty ? "ifempty" : "") + (ifEmpty && !block ? " " : "") + (block ? "" : "noblock"); } } public static class Mov extends Instruction { private static final Map code2src = new HashMap(); private static final Map code2dst = new HashMap(); private static final Map code2op = new HashMap(); private enum Source { PINS(0b000, "pins", (sm) -> { final int base = sm.getStatus().regPINCTRL_IN_BASE; return sm.getPIOGPIO().getGPIO().getPinsToPeri(base, Constants.GPIO_NUM); }), X(0b001, "x", (sm) -> sm.getX()), Y(0b010, "y", (sm) -> sm.getY()), NULL(0b011, "null", (sm) -> 0), RESERVED_4(0b100, "???", null), STATUS(0b101, "status", (sm) -> (sm.getStatus().getFIFOStatus())), ISR(0b110, "isr", (sm) -> sm.getISRValue()), OSR(0b111, "osr", (sm) -> sm.getOSRValue()); private final int code; private final String mnemonic; private final Function eval; private Source(final int code, final String mnemonic, final Function eval) { this.code = code; this.mnemonic = mnemonic; this.eval = eval; code2src.put(code, this); } public Integer read(final SM sm) { return eval.apply(sm); } @Override public String toString() { return mnemonic; } } private enum Destination { PINS(0b000, "pins", (sm, data) -> { SM.IOMapping.OUT.collatePins(sm, data); return null; }), X(0b001, "x", (sm, data) -> { sm.setX(data); return null; }), Y(0b010, "y", (sm, data) -> { sm.setY(data); return null; }), RESERVED_3(0b011, "???", null), EXEC(0b100, "exec", (sm, data) -> { sm.execInstruction(data); return null; }), PC(0b101, "pc", (sm, data) -> { sm.setPC(data & 0x1f); return null; }), ISR(0b110, "isr", (sm, data) -> { sm.setISRValue(data); return null; }), OSR(0b111, "osr", (sm, data) -> { sm.setOSRValue(data); return null; }); private final int code; private final String mnemonic; private final BiFunction eval; private Destination(final int code, final String mnemonic, final BiFunction eval) { this.code = code; this.mnemonic = mnemonic; this.eval = eval; code2dst.put(code, this); } public void write(final SM sm, final int data) { eval.apply(sm, data); } @Override public String toString() { return mnemonic; } } private enum Operation { NONE(0b00, "", (data) -> data), INVERT(0b01, "~", (data) -> ~data), BIT_REVERSE(0b10, "::", (data) -> Integer.reverse(data)), RESERVED_3(0b11, "???", null); private final int code; private final String mnemonic; private final Function eval; private Operation(final int code, final String mnemonic, final Function eval) { this.code = code; this.mnemonic = mnemonic; this.eval = eval; code2op.put(code, this); } private int apply(final int data) { return eval.apply(data); } @Override public String toString() { return mnemonic; } } private Source src; private Destination dst; private Operation op; @Override protected void resetParams() { src = Source.PINS; dst = Destination.PINS; op = Operation.NONE; } private boolean isNop() { return (src == Source.Y) && (dst == Destination.Y) && (op == Operation.NONE); } @Override public void decodeLSB(final int lsb) throws Decoder.DecodeException { src = code2src.get(lsb & 0x7); if (src == Source.RESERVED_4) { throw new Decoder.DecodeException(this, getOpCode()); } dst = code2dst.get((lsb & 0xe0) >>> 5); if (dst == Destination.RESERVED_3) { throw new Decoder.DecodeException(this, getOpCode()); } op = code2op.get((lsb & 0x18) >>> 3); if (op == Operation.RESERVED_3) { throw new Decoder.DecodeException(this, getOpCode()); } } @Override public ResultState executeOperation(final SM sm) { dst.write(sm, op.apply(src.read(sm))); return dst == Destination.EXEC ? ResultState.STALL : (dst == Destination.PC ? ResultState.JUMP : ResultState.COMPLETE); } @Override public String getMnemonic() { return isNop() ? "nop" : "mov"; } @Override public String getParamsDisplay() { if (isNop()) return ""; final String strOp = op.toString(); return dst + ", " + (!strOp.isEmpty() ? strOp : "") + src; } } public static class Irq extends Instruction { private boolean clr; private boolean wait; private int index; @Override protected void resetParams() { clr = false; wait = false; index = 0; } @Override public void decodeLSB(final int lsb) throws Decoder.DecodeException { if ((lsb & 0x80) != 0) { throw new Decoder.DecodeException(this, getOpCode()); } clr = (lsb & 0x40) != 0; wait = (lsb & 0x20) != 0; index = lsb & 0x1f; checkIRQIndex(index); } @Override public ResultState executeOperation(final SM sm) { final boolean stall; final int irqNum = getIRQNum(sm.getNum(), index); if (clr) { sm.clearIRQ(irqNum); stall = false; } else { sm.setIRQ(irqNum); stall = wait; } return stall ? ResultState.STALL : ResultState.COMPLETE; } @Override public String getMnemonic() { return "irq"; } @Override public String getParamsDisplay() { /* * Note: Modes "", "set" and "nowait" are all synonyms for the * same thing, namely that both flags (clr, wait) are not set. * For display, we deliberately choose "". */ final String mode = clr ? "clear " : (wait ? "wait " : ""); return mode + getIRQNumDisplay(index); } } public static class Set extends Instruction { private static final Map code2dst = new HashMap(); public enum Destination { PINS(0b000, "pins", (sm, data) -> { SM.IOMapping.SET.collatePins(sm, data); return null; }), X(0b001, "x", (sm, data) -> { sm.setX(data); return null; }), Y(0b010, "y", (sm, data) -> { sm.setY(data); return null; }), RESERVED_3(0b011, "???", null), PINDIRS(0b100, "pindirs", (sm, data) -> { SM.IOMapping.SET.collatePinDirs(sm, data); return null; }), RESERVED_5(0b101, "???", null), RESERVED_6(0b110, "???", null), RESERVED_7(0b111, "???", null); private final int code; private final String mnemonic; private final BiFunction eval; private Destination(final int code, final String mnemonic, final BiFunction eval) { this.code = code; this.mnemonic = mnemonic; this.eval = eval; code2dst.put(code, this); } public void write(final SM sm, final int data) { eval.apply(sm, data); } @Override public String toString() { return mnemonic; } } private Destination dst; private int data; @Override protected void resetParams() { dst = Destination.PINS; data = 0; } public void setDestination(final Destination dst) { if (dst == null) { throw new NullPointerException("dst"); } this.dst = dst; } public void setData(final int data) { if (data < 0) { throw new IllegalArgumentException("data < 0: " + data); } if (data > 31) { throw new IllegalArgumentException("data > 31: " + data); } this.data = data; } public int encode(final int pinCtrlSidesetCount, final boolean execCtrlSideEn) { return 0xe000 | getDelayAndSideSetBits(pinCtrlSidesetCount, execCtrlSideEn) | (dst.ordinal() << 5) | (data & 0x1f); } @Override public void decodeLSB(final int lsb) throws Decoder.DecodeException { dst = code2dst.get((lsb & 0xe0) >>> 5); if ((dst == Destination.RESERVED_3) || (dst == Destination.RESERVED_5) || (dst == Destination.RESERVED_6) || (dst == Destination.RESERVED_7)) { throw new Decoder.DecodeException(this, getOpCode()); } data = lsb & 0x1f; } @Override public ResultState executeOperation(final SM sm) { dst.write(sm, data); return ResultState.COMPLETE; } @Override public String getMnemonic() { return "set"; } @Override public String getParamsDisplay() { return dst + ", " + String.format("%02x", data); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/LocalAddressSpace.java ================================================ /* * @(#)LocalAddressSpace.java 1.00 21/03/25 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class LocalAddressSpace extends AddressSpace { private final Emulator emulator; private final PicoEmuRegistersImpl picoEmuRegisters; private final GPIOIOBank0RegistersImpl gpioIOBank0Registers; private final GPIOPadsBank0RegistersImpl gpioPadsBank0Registers; private final PIORegistersImpl pio0Registers; private final PIOEmuRegistersImpl pio0EmuRegisters; private final PIORegistersImpl pio1Registers; private final PIOEmuRegistersImpl pio1EmuRegisters; /* * TODO: Really should replace this simple-minded list approach with * either a responsibility chain or a composite design pattern, as * soon as the number of registers interfaces grows. */ private final List registerSetList; public LocalAddressSpace(final Emulator emulator) { this.emulator = emulator; registerSetList = new ArrayList(); picoEmuRegisters = new PicoEmuRegistersImpl(emulator); registerSetList.add(picoEmuRegisters); final GPIO gpio = emulator.getGPIO(); gpioIOBank0Registers = new GPIOIOBank0RegistersImpl(gpio); registerSetList.add(gpioIOBank0Registers); gpioPadsBank0Registers = new GPIOPadsBank0RegistersImpl(gpio); registerSetList.add(gpioPadsBank0Registers); final PIO pio0 = emulator.getPIO0(); pio0Registers = new PIORegistersImpl(pio0); registerSetList.add(pio0Registers); pio0EmuRegisters = new PIOEmuRegistersImpl(pio0); registerSetList.add(pio0EmuRegisters); final PIO pio1 = emulator.getPIO1(); pio1Registers = new PIORegistersImpl(pio1); registerSetList.add(pio1Registers); pio1EmuRegisters = new PIOEmuRegistersImpl(pio1); registerSetList.add(pio1EmuRegisters); } @Override public String getEmulatorInfo() throws IOException { return Constants.getEmulatorIdAndVersionWithOs(); } public int getGPIOAddress(final GPIOIOBank0RegistersImpl.Regs register) { return GPIOIOBank0RegistersImpl.getAddress(register); } public int getGPIOAddress(final GPIOPadsBank0RegistersImpl.Regs register) { return GPIOPadsBank0RegistersImpl.getAddress(register); } public int getPIO0Address(final PIORegistersImpl.Regs register) { return pio0Registers.getAddress(register); } public int getPIO1Address(final PIORegistersImpl.Regs register) { return pio1Registers.getAddress(register); } public int getPIO0Address(final PIOEmuRegistersImpl.Regs register) { return pio0EmuRegisters.getAddress(register); } public int getPIO1Address(final PIOEmuRegistersImpl.Regs register) { return pio1EmuRegisters.getAddress(register); } private static int address2register(final RegisterSet registers, final int address) { checkAddressAligned(address); return ((address - registers.getBaseAddress()) & ~0x3000) >>> 2; } private RegisterSet getProvidingRegisters(final int address) throws IOException { for (final RegisterSet registers : registerSetList) { final int regNum = address2register(registers, address); if (regNum < registers.getSize()) { return registers; } } return null; } @Override public boolean providesAddress(final int address) throws IOException { return getProvidingRegisters(address) != null; } @Override public String getRegisterSetId(final int address) throws IOException { final RegisterSet registers = getProvidingRegisters(address); if (registers != null) { return registers.getId(); } final String message = String.format("requesting register set ID for unsupported address: %08x", address); throw new IOException(message); } @Override public String getAddressLabel(final int address) throws IOException { final RegisterSet registers = getProvidingRegisters(address); if (registers != null) { final int regNum = address2register(registers, address); return registers.getRegisterLabel(regNum); } final String message = String.format("requesting label for unsupported address: %08x", address); throw new IOException(message); } @Override public synchronized void writeAddressMasked(final int address, final int bits, final int mask, final boolean xor) throws IOException { if ((address & 0x3000) != 0x0000) { final String message = String.format("writeAddressMasked(): " + "address not in base address range: 0x%8x", address); throw new IOException(message); } final RegisterSet registers = getProvidingRegisters(address); if (registers != null) { final int regNum = address2register(registers, address); try { registers.writeRegister(regNum, bits, mask, xor); } catch (final Throwable t) { final String message = t.getMessage(); emulator.getConsole(). printf("warning: internal error occurred: %s%n", message); t.printStackTrace(emulator.getConsole()); throw new IOException(message); } return; } final String message = String.format("write on unsupported address: %08x", address); throw new IOException(message); } @Override public synchronized int readAddress(final int address) throws IOException { final RegisterSet registers = getProvidingRegisters(address); if (registers != null) { final int regNum = address2register(registers, address); try { return registers.readRegister(regNum); } catch (final Throwable t) { final String message = t.getMessage(); emulator.getConsole(). printf("warning: internal error occurred: %s%n", message); t.printStackTrace(emulator.getConsole()); throw new IOException(message); } } final String message = String.format("read from unsupported address: %08x", address); throw new IOException(message); } private static boolean timedOut(final long startWallClock, final long stopWallClock, final long wallClock) { return (startWallClock < stopWallClock) ? (wallClock < startWallClock) || (wallClock >= stopWallClock) : (wallClock < startWallClock) && (wallClock >= stopWallClock); } @Override public int waitAddress(final int address, final int expectedValue, final int mask, final long cyclesTimeout, final long millisTimeout) throws IOException { if (cyclesTimeout < 0) { throw new IllegalArgumentException("cyclesTimeout < 0: " + cyclesTimeout); } if (millisTimeout < 0) { throw new IllegalArgumentException("millisTimeout < 0: " + millisTimeout); } final MasterClock masterClock = emulator.getMasterClock(); final long startWallClock = masterClock.getWallClock(); final long stopWallClock = startWallClock + cyclesTimeout; final long startTime = System.currentTimeMillis(); final long stopTime = startTime + millisTimeout; int receivedValue; while (((receivedValue = readAddress(address) & mask) != expectedValue)) { final long wallClock = masterClock.getWallClock(); if (timedOut(startWallClock, stopWallClock, wallClock)) break; try { if (millisTimeout != 0) { final long time = System.currentTimeMillis(); if (timedOut(startTime, stopTime, time)) break; masterClock.awaitPhaseChange(stopTime - time); } else { masterClock.awaitPhaseChange(); } } catch (final InterruptedException e) { // ignore here, since check in while condition } } return receivedValue; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/MasterClock.java ================================================ /* * @(#)MasterClock.java 1.00 21/02/05 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; /** * System Master Clock */ public class MasterClock implements Clock, Constants { public enum Mode { TARGET_FREQUENCY, SINGLE_STEP; public static Mode fromValue(final int value) { if ((value < 0) || (value >= MODES.length)) { throw new IllegalArgumentException("value: " + value); } return MODES[value]; } }; private static final Mode[] MODES = Mode.values(); private class DrivingGear extends Thread { public DrivingGear() { super("Emulation Thread"); } private void runSingleStep() { synchronized(this) { while ((mode == Mode.SINGLE_STEP) && (phase == Phase.PHASE_1_STABLE)) { try { wait(); } catch (final InterruptedException e) { // ignore here, since check in while condition } if (terminate) return; } if (phase == Phase.PHASE_0_IN_PROGRESS) { syncWithRealTime(); cyclePhase0(); } while ((mode == Mode.SINGLE_STEP) && (phase == Phase.PHASE_0_STABLE)) { try { wait(); } catch (final InterruptedException e) { // ignore here, since check in while condition } if (terminate) return; } if (phase == Phase.PHASE_1_IN_PROGRESS) { cyclePhase1(); } } } private void runTargetFrequency() { syncWithRealTime(); phase = Phase.PHASE_0_IN_PROGRESS; cyclePhase0(); phase = Phase.PHASE_1_IN_PROGRESS; cyclePhase1(); } @Override public void run() { while (true) { while (mode == Mode.SINGLE_STEP) { runSingleStep(); if (terminate) return; } while (mode == Mode.TARGET_FREQUENCY) { runTargetFrequency(); if (terminate) return; } } } } private final PrintStream console; /** * Insure access to the following set of variables is atomic: * (refWallClock, refRealTime, frequency, milliSecondsPerCycle). */ private final Object accountingLock; /** * Emulator-wide lock for synchronizing register reads waiting for a * specific masked value to match. */ private final Object registerWaitLock; private final DrivingGear drivingGear; private final List listeners; private long frequency; private double milliSecondsPerCycle; private Mode mode; private Phase phase; private long wallClock; private long refWallClock; private long refRealTime; private boolean terminate; private MasterClock() { throw new UnsupportedOperationException("unsupported empty constructor"); } public MasterClock(final PrintStream console) { if (console == null) { throw new NullPointerException("console"); } this.console = console; accountingLock = new Object(); registerWaitLock = new Object(); drivingGear = new DrivingGear(); listeners = new ArrayList(); reset(); start(); } public void reset() { setFrequency(DEFAULT_FREQUENCY); setMode(Mode.SINGLE_STEP); phase = Phase.PHASE_1_STABLE; wallClock = 0; } private void start() { terminate = false; drivingGear.start(); } public void terminate() { synchronized(drivingGear) { terminate = true; drivingGear.notify(); } } private void resetRef() { synchronized(accountingLock) { refWallClock = wallClock; refRealTime = System.currentTimeMillis(); } } private long getMilliSecondsAhead() { synchronized(accountingLock) { if (frequency == 0) { return 0; } final long wallTimeMillisSinceRef = Math.round(milliSecondsPerCycle * (wallClock - refWallClock)); final long realTimeMillisSinceRef = System.currentTimeMillis() - refRealTime; final long milliSecondsAhead = wallTimeMillisSinceRef - realTimeMillisSinceRef; return milliSecondsAhead >= 0 ? milliSecondsAhead : 0; } } private void syncWithRealTime() { if (mode != Mode.TARGET_FREQUENCY) return; final long milliSecondsAhead = getMilliSecondsAhead(); if (milliSecondsAhead > 0) { try { Thread.sleep(milliSecondsAhead); } catch (final InterruptedException e) { // ignore } } } private void setFrequency(final int frequency) { synchronized(accountingLock) { this.frequency = frequency & 0xffffffff; milliSecondsPerCycle = frequency != 0 ? 8000.0 / this.frequency : Double.POSITIVE_INFINITY; resetRef(); } } public Object getRegisterWaitLock() { return registerWaitLock; } public void setMASTERCLK_FREQ(final int frequency) { synchronized(drivingGear) { setFrequency(frequency); drivingGear.notify(); } } public int getMASTERCLK_FREQ() { return (int)frequency; } public void setMode(final Mode mode) { synchronized(drivingGear) { this.mode = mode; drivingGear.notify(); resetRef(); } } public Mode getMode() { return mode; } public void setMASTERCLK_MODE(final int value) { setMode(Mode.fromValue(value & 0x1)); } public int getMASTERCLK_MODE() { return mode.ordinal(); } @Override public void addTransitionListener(final TransitionListener listener) { listeners.add(listener); } @Override public boolean removeTransitionListener(final TransitionListener listener) { return listeners.remove(listener); } @Override public long getWallClock() { return wallClock; } private void announceRisingEdge() { for (final TransitionListener listener : listeners) { listener.risingEdge(wallClock); } } private void announceFallingEdge() { for (final TransitionListener listener : listeners) { listener.fallingEdge(wallClock); } } public Phase getPhase() { return phase; } public void triggerPhase0() { synchronized(accountingLock) { if (mode != Mode.SINGLE_STEP) return; synchronized(drivingGear) { if (phase == Phase.PHASE_1_STABLE) { phase = Phase.PHASE_0_IN_PROGRESS; drivingGear.notify(); } } } } private void cyclePhase0() { if (phase != Phase.PHASE_0_IN_PROGRESS) { console.println("warning: cyclePhase0: unexpected phase: " + phase); return; } announceRisingEdge(); phase = Phase.PHASE_0_STABLE; synchronized(registerWaitLock) { registerWaitLock.notifyAll(); } } public void triggerPhase1() { synchronized(accountingLock) { if (mode != Mode.SINGLE_STEP) return; synchronized(drivingGear) { if (phase == Phase.PHASE_0_STABLE) { phase = Phase.PHASE_1_IN_PROGRESS; drivingGear.notify(); } } } } private void cyclePhase1() { if (phase != Phase.PHASE_1_IN_PROGRESS) { console.println("warning: cyclePhase1: unexpected phase: " + phase); return; } announceFallingEdge(); wallClock++; phase = Phase.PHASE_1_STABLE; synchronized(registerWaitLock) { registerWaitLock.notifyAll(); } } public void awaitPhaseChange() throws InterruptedException { synchronized(registerWaitLock) { registerWaitLock.wait(); } } public void awaitPhaseChange(final long millisTimeout) throws InterruptedException { synchronized(registerWaitLock) { registerWaitLock.wait(millisTimeout); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/Memory.java ================================================ /* * @(#)Memory.java 1.00 21/01/31 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * 32 32-Bit Words of Shared Instruction Memory */ public class Memory implements Constants { public final Object FETCH_LOCK; private final short[] code; public Memory() { FETCH_LOCK = new Object(); code = new short[MEMORY_SIZE]; } public void reset() { synchronized(FETCH_LOCK) { for (int address = 0; address < MEMORY_SIZE; address++) { set(address, (short)0); } } } public void set(final int address, final int value, final int mask, final boolean xor) { set(address, (short)Constants.hwSetBits(get(address), value, mask, xor)); } private void set(final int address, final short value) { Constants.checkSmMemAddr(address, "write address"); code[address] = value; } public short get(final int address) { Constants.checkSmMemAddr(address, "read address"); return code[address]; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/PIO.java ================================================ /* * @(#)PIO.java 1.00 21/01/31 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * Peripheral I/O Unit */ public class PIO implements Constants, Clock.TransitionListener { private final int index; private final PrintStream console; private final MasterClock masterClock; private final GPIO gpio; private final PIOGPIO pioGpio; private final Memory memory; private final IRQ irq; private final SM[] sms; private int smEnabled; // bits 0…3 of CTRL_SM_ENABLE public enum PinDir { GPIO_LEVELS(0, "levels"), GPIO_DIRECTIONS(1, "directions"); private final int value; private final String label; private PinDir(final int value, final String label) { this.value = value; this.label = label; } public int getValue() { return value; } public static PinDir fromValue(final int value) { if (value == 0) return GPIO_LEVELS; if (value == 1) return GPIO_DIRECTIONS; throw new IllegalArgumentException("value neither 0 nor 1"); } public static PinDir fromValue(final int value, final PinDir defaultValue) { if (value == 0) return GPIO_LEVELS; if (value == 1) return GPIO_DIRECTIONS; return defaultValue; } }; public enum ShiftDir { SHIFT_LEFT(0, "shift left"), SHIFT_RIGHT(1, "shift right"); private final int value; private final String label; private ShiftDir(final int value, final String label) { this.value = value; this.label = label; } public int getValue() { return value; } public static ShiftDir fromValue(final int value) { if (value == 0) return SHIFT_LEFT; if (value == 1) return SHIFT_RIGHT; throw new IllegalArgumentException("value neither 0 nor 1"); } }; private PIO() { throw new UnsupportedOperationException("unsupported empty constructor"); } public PIO(final int index, final PrintStream console, final MasterClock masterClock, final GPIO gpio) { if (index < 0) { throw new IllegalArgumentException("PIO index < 0: " + index); } if (index > 1) { throw new IllegalArgumentException("PIO index > 1: " + index); } Objects.requireNonNull(console); Objects.requireNonNull(masterClock); Objects.requireNonNull(gpio); this.index = index; this.console = console; this.masterClock = masterClock; this.gpio = gpio; masterClock.addTransitionListener(this); pioGpio = new PIOGPIO(gpio); memory = new Memory(); irq = new IRQ(); sms = new SM[SM_COUNT]; for (int smNum = 0; smNum < SM_COUNT; smNum++) { sms[smNum] = new SM(smNum, console, masterClock, pioGpio, memory, irq); } smEnabled = 0x0; } public void reset() { pioGpio.reset(); memory.reset(); irq.reset(); for (final SM sm : sms) sm.reset(); smEnabled = 0x0; } public int getIndex() { return index; } public int getDBG_CFGINFO_IMEM_SIZE() { return MEMORY_SIZE; } public int getDBG_CFGINFO_SM_COUNT() { return SM_COUNT; } public int getDBG_CFGINFO_FIFO_DEPTH() { return FIFO_DEPTH; } public PrintStream getConsole() { return console; } public MasterClock getMasterClock() { return masterClock; } public GPIO getGPIO() { return gpio; } public PIOGPIO getPIOGPIO() { return pioGpio; } public Memory getMemory() { return memory; } public SM getSM(final int index) { if (index < 0) { throw new IllegalArgumentException("state machine index < 0"); } if (index > SM_COUNT) { throw new IllegalArgumentException("state machine index > " + SM_COUNT); } return sms[index]; } public IRQ getIRQ() { return irq; } public int getSM_ENABLED() { return smEnabled; } public void setSM_ENABLED(final int smEnabled) { if (smEnabled < 0) { throw new IllegalArgumentException("SM_ENABLED < 0: " + smEnabled); } if (smEnabled > 15) { throw new IllegalArgumentException("SM_ENABLED > 15:" + smEnabled); } this.smEnabled = smEnabled; } public int getCtrl() { return getSM_ENABLED(); } public void setCtrl(final int ctrl, final int mask) { synchronized(sms) { smEnabled = Constants.hwSetBits(smEnabled, ctrl, mask, false) & 0xf; for (int smNum = 0; smNum < SM_COUNT; smNum++) { final boolean clkDivRestart = ((ctrl >> (8 + smNum)) & 0x1) != 0x0 && ((mask >> (8 + smNum)) & 0x1) != 0x0; final boolean smRestart = ((ctrl >> (4 + smNum)) & 0x1) != 0x0 && ((mask >> (4 + smNum)) & 0x1) != 0x0; final SM sm = getSM(smNum); if (clkDivRestart) { sm.resetCLKDIV(); } if (smRestart) { sm.restart(); } } } } public void setSideSetCount(final int count) { if (count < 0) { throw new IllegalArgumentException("side set count < 0"); } if (count > 5) { throw new IllegalArgumentException("side set count > 5"); } for (final SM sm : sms) { sm.setSideSetCount(count); } } private boolean smIsEnabled(final int smNum) { if (smNum < 0) { throw new IllegalArgumentException("smNum < 0: " + smNum); } if (smNum > SM_COUNT - 1) { throw new IllegalArgumentException("smNum > " + (SM_COUNT - 1) + ": " + smNum); } return (smEnabled & (0x1 << smNum)) != 0x0; } public Direction getDirection(final int gpio) { return pioGpio.getDirection(gpio); } public Bit getLevel(final int gpio) { return pioGpio.getLevel(gpio); } @Override public void risingEdge(final long wallClock) { synchronized(sms) { for (int smNum = 0; smNum < SM_COUNT; smNum++) { final SM sm = getSM(smNum); sm.clockRisingEdge(smIsEnabled(smNum), wallClock); } } } @Override public void fallingEdge(final long wallClock) { synchronized(sms) { for (int smNum = 0; smNum < SM_COUNT; smNum++) { final SM sm = getSM(smNum); sm.clockFallingEdge(wallClock); } pioGpio.applyCollatedWrites(); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/PIOEmuRegisters.java ================================================ /* * @(#)PIOEmuRegisters.java 1.00 21/03/06 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.soundpaint.rp2040pio.doctool.RegistersDocs; /** * Facade to additonal emulator properties of the internal subsystems * of a PIO that are not available via the PIORegisters facade. This * facade is in particular intended for use by software that wants to * exploit the emulator's debug facilities. */ public abstract class PIOEmuRegisters extends RegisterSet { public enum Regs implements RegistersDocs { SM0_REGX("Direct read / write access to the SM's%n" + "scratch register X.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RW, 0) }), SM0_REGY("Direct read / write access to the SM's%n" + "scratch register Y.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RW, 0) }), SM0_PC("Direct read / write access to the SM's%n" + "instruction pointer / program counter.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RW, 0) }), SM0_ISR("Direct read / write access to the SM's%n" + "input shift register.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RW, 0) }), SM0_ISR_SHIFT_COUNT("Direct read / write access to the SM's%n" + "input shift count register.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RW, 0) }), SM0_OSR("Direct read / write access to all of the SM's%n" + "output shift register.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RW, 0) }), SM0_OSR_SHIFT_COUNT("Direct read / write access to the SM's%n" + "output shift count register.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RW, 0) }), SM0_FIFO_MEM0("Read / write access to FIFO memory word.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RW, 0) }), SM0_FIFO_MEM1(Regs.SM0_FIFO_MEM0, 0), SM0_FIFO_MEM2(Regs.SM0_FIFO_MEM0, 0), SM0_FIFO_MEM3(Regs.SM0_FIFO_MEM0, 0), SM0_FIFO_MEM4(Regs.SM0_FIFO_MEM0, 0), SM0_FIFO_MEM5(Regs.SM0_FIFO_MEM0, 0), SM0_FIFO_MEM6(Regs.SM0_FIFO_MEM0, 0), SM0_FIFO_MEM7(Regs.SM0_FIFO_MEM0, 0), SM0_CLEAR_FORCED("When writing to this address, any pending%n" + "forced instruction is cancelled, provided that%n" + "instruction fetch & decode has not yet been started.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.WF, 0) }), SM0_CLEAR_EXECD("When writing to this address, any pending%n" + "EXEC'd instruction is cancelled, provided that%n" + "instruction fetch & decode has not yet been started.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.WF, 0) }), SM0_INSTR_ORIGIN("Direct read-only access to the origin of the SM's%n" + "currently executed instruction. The mode bits%n" + "determine the origin category. If the origin%n" + "category is memory address, the memory address bits%n" + "will contain the memory instruction's address.%n" + "Otherwise, the bits of the memory address are%n" + "undefined.%n" + "Note that for memory instructions, the address may%n" + "differ from the value of the instruction pointer PC,%n" + "if the PC has already been updated while the%n" + "instruction is still in progress.", 0, new BitsInfo[] { new BitsInfo(null, 31, 7, null, BitsType.RESERVED, null), new BitsInfo("CATEGORY", 6, 5, "For forced instructions,%n" + "this is the value " + (INSTR_ORIGIN_FORCED & 0x3) + ".%n" + "For EXEC'd instructions,%n" + "this is the value " + (INSTR_ORIGIN_EXECD & 0x3) + ".%n" + "Otherwise (e.g. after reset),%n" + "this is the value " + (INSTR_ORIGIN_UNKNOWN & 0x3) + ".%n", BitsType.RO, INSTR_ORIGIN_UNKNOWN & 0x3), new BitsInfo("MEMORY_ADDRESS", 4, 0, "memory address value (0x00…0x1f)", BitsType.RO, 0) }), SM0_DELAY("Direct read-only access to the SM's%n" + "currently executed instruction's number of delay cycles.", 0, new BitsInfo[] { new BitsInfo(null, 31, 5, null, BitsType.RESERVED, null), new BitsInfo(null, 4, 0, null, BitsType.RO, 0) }), SM0_DELAY_CYCLE("Read-only access to the SM's delay status.", 0, new BitsInfo[] { new BitsInfo(null, 31, 1, null, BitsType.UNUSED, null), new BitsInfo("DELAY_CYCLE", 0, 0, "0x1, if the currently executed cycles%n" + "is a delay cycle.", BitsType.RO, 0) }), SM0_PENDING_DELAY("Direct read-only access to the SM's%n" + "number of pending delay cycles.", 0, new BitsInfo[] { new BitsInfo(null, 31, 5, null, BitsType.RESERVED, null), new BitsInfo("PENDING_DELAY", 4, 0, "Number (0x00…0x1f) of pending delays%n" + "of the currently executed instruction.", BitsType.RO, 0) }), SM0_FORCED_INSTR("Direct read-only access to the op-code of a forced%n" + "instruction that is awaiting execution during the%n" + "next clock cycle. For writing a forced instruction,%n" + "use SMx_INSTR of PIORegisters instead.", 0, new BitsInfo[] { new BitsInfo(null, 31, 17, null, BitsType.RESERVED, null), new BitsInfo("PENDING", 16, 16, "0x1, if a forced instruction is%n" + "awaiting execution, otherwise 0x0.", BitsType.RO, 0), new BitsInfo("INSTR", 15, 0, "Instruction op-code, if any;%n" + "otherwise, 0x0000.", BitsType.RO, 0) }), SM0_EXECD_INSTR("Direct read/write access to the op-code of an EXEC'd%n" + "instruction that is awaiting execution during the%n" + "next clock cycle, unless the state machine's clock%n" + "signal does not evaluate to true, or there is a%n" + "pending forced instruction, in which case the forced%n" + "instruction will be executed first.", 0, new BitsInfo[] { new BitsInfo(null, 31, 17, null, BitsType.RESERVED, null), new BitsInfo("PENDING", 16, 16, "0x1, if an EXEC'd instruction is%n" + "awaiting execution, otherwise 0x0.", BitsType.RO, 0), new BitsInfo("INSTR", 15, 0, "Instruction op-code, if any;%n" + "otherwise, 0x0000.", BitsType.RW, 0) }), SM0_CLK_ENABLE("Read-only access to the SM's current clock enable status.", 0, new BitsInfo[] { new BitsInfo(null, 31, 1, null, BitsType.UNUSED, null), new BitsInfo("CLK_ENABLE", 0, 0, "0x1, if in the current cycle the clock%n" + "enable signal evaluates to 0x1.", BitsType.RO, 0) }), SM0_NEXT_CLK_ENABLE("Read-only access to the SM's next clock enable%n" + "status. May differ from current clock enable%n" + "when master clock phase 1 has been completed,%n" + "otherwise identical with current clock enable.", 0, new BitsInfo[] { new BitsInfo(null, 31, 1, null, BitsType.UNUSED, null), new BitsInfo("CLK_ENABLE", 0, 0, "0x1, if in the pre-computed clock%n" + "enable signal for the upcoming master%n" + "clock cycle evaluates to 0x1.", BitsType.RO, 0) }), SM0_BREAKPOINTS("Each bit of this value corresponds to each of the%n" + "32 memory locations of the PIO instruction memory%n" + "(with the LSB of the word corresponding to the lowest%n" + "memory address). Setting a bit to 1 marks the%n" + "corresponding memory address as location of a%n" + "breakpoint. Setting a bit to 0 removes the%n" + "breakpoint.%n" + "%n" + "As soon as the program counter of the state machine%n" + "reaches an address that is marked as a breakpoint,%n" + "master clock MASTERCLK_MODE will be automatically set%n" + "to single step mode.", 0, IntStream.rangeClosed(0, 31).boxed() .map(n -> new BitsInfo("BP_MEM" + (31 - n), 31 - n, 31 - n, "0x1, if the memory address is " + "marked as breakpoint.", BitsType.RW, 0)) .collect(Collectors.toList())), SM0_TRACEPOINTS("Tracepoints work like breakpoints with the difference%n" + "that master clock MASTERCLK_MODE it not automatically%n" + "set to single step mode, but instead a message is%n" + "typically printed to console output (depending on%n" + "the specific client application). The message may,%n" + "for example, caontain the state machine's number and%n" + "disassembled instruction with prefixed instruction%n" + "memory address. Tracepoints work in all master clock%n" + "MASTERCLK_MODE modes.", 0, IntStream.rangeClosed(0, 31).boxed() .map(n -> new BitsInfo("TP_MEM" + (31 - n), 31 - n, 31 - n, "0x1, if the memory address is " + "marked as tracepoint.", BitsType.RW, 0)) .collect(Collectors.toList())), SM1_REGX(Regs.SM0_REGX, 1), SM1_REGY(Regs.SM0_REGY, 1), SM1_PC(Regs.SM0_PC, 1), SM1_ISR(Regs.SM0_ISR, 1), SM1_ISR_SHIFT_COUNT(Regs.SM0_ISR_SHIFT_COUNT, 1), SM1_OSR(Regs.SM0_OSR, 1), SM1_OSR_SHIFT_COUNT(Regs.SM0_OSR_SHIFT_COUNT, 1), SM1_FIFO_MEM0(Regs.SM0_FIFO_MEM0, 1), SM1_FIFO_MEM1(Regs.SM0_FIFO_MEM0, 1), SM1_FIFO_MEM2(Regs.SM0_FIFO_MEM0, 1), SM1_FIFO_MEM3(Regs.SM0_FIFO_MEM0, 1), SM1_FIFO_MEM4(Regs.SM0_FIFO_MEM0, 1), SM1_FIFO_MEM5(Regs.SM0_FIFO_MEM0, 1), SM1_FIFO_MEM6(Regs.SM0_FIFO_MEM0, 1), SM1_FIFO_MEM7(Regs.SM0_FIFO_MEM0, 1), SM1_CLEAR_FORCED(Regs.SM0_CLEAR_FORCED, 1), SM1_CLEAR_EXECD(Regs.SM0_CLEAR_EXECD, 1), SM1_INSTR_ORIGIN(Regs.SM0_INSTR_ORIGIN, 1), SM1_DELAY(Regs.SM0_DELAY, 1), SM1_DELAY_CYCLE(Regs.SM0_DELAY_CYCLE, 1), SM1_PENDING_DELAY(Regs.SM0_PENDING_DELAY, 1), SM1_FORCED_INSTR(Regs.SM0_FORCED_INSTR, 1), SM1_EXECD_INSTR(Regs.SM0_EXECD_INSTR, 1), SM1_CLK_ENABLE(Regs.SM0_CLK_ENABLE, 1), SM1_NEXT_CLK_ENABLE(Regs.SM0_NEXT_CLK_ENABLE, 1), SM1_BREAKPOINTS(Regs.SM0_BREAKPOINTS, 1), SM1_TRACEPOINTS(Regs.SM0_TRACEPOINTS, 1), SM2_REGX(Regs.SM0_REGX, 2), SM2_REGY(Regs.SM0_REGY, 2), SM2_PC(Regs.SM0_PC, 2), SM2_ISR(Regs.SM0_ISR, 2), SM2_ISR_SHIFT_COUNT(Regs.SM0_ISR_SHIFT_COUNT, 2), SM2_OSR(Regs.SM0_OSR, 2), SM2_OSR_SHIFT_COUNT(Regs.SM0_OSR_SHIFT_COUNT, 2), SM2_FIFO_MEM0(Regs.SM0_FIFO_MEM0, 2), SM2_FIFO_MEM1(Regs.SM0_FIFO_MEM0, 2), SM2_FIFO_MEM2(Regs.SM0_FIFO_MEM0, 2), SM2_FIFO_MEM3(Regs.SM0_FIFO_MEM0, 2), SM2_FIFO_MEM4(Regs.SM0_FIFO_MEM0, 2), SM2_FIFO_MEM5(Regs.SM0_FIFO_MEM0, 2), SM2_FIFO_MEM6(Regs.SM0_FIFO_MEM0, 2), SM2_FIFO_MEM7(Regs.SM0_FIFO_MEM0, 2), SM2_CLEAR_FORCED(Regs.SM0_CLEAR_FORCED, 2), SM2_CLEAR_EXECD(Regs.SM0_CLEAR_EXECD, 2), SM2_INSTR_ORIGIN(Regs.SM0_INSTR_ORIGIN, 2), SM2_DELAY(Regs.SM0_DELAY, 2), SM2_DELAY_CYCLE(Regs.SM0_DELAY_CYCLE, 2), SM2_PENDING_DELAY(Regs.SM0_PENDING_DELAY, 2), SM2_FORCED_INSTR(Regs.SM0_FORCED_INSTR, 2), SM2_EXECD_INSTR(Regs.SM0_EXECD_INSTR, 2), SM2_CLK_ENABLE(Regs.SM0_CLK_ENABLE, 2), SM2_NEXT_CLK_ENABLE(Regs.SM0_NEXT_CLK_ENABLE, 2), SM2_BREAKPOINTS(Regs.SM0_BREAKPOINTS, 2), SM2_TRACEPOINTS(Regs.SM0_TRACEPOINTS, 2), SM3_REGX(Regs.SM0_REGX, 3), SM3_REGY(Regs.SM0_REGY, 3), SM3_PC(Regs.SM0_PC, 3), SM3_ISR(Regs.SM0_ISR, 3), SM3_ISR_SHIFT_COUNT(Regs.SM0_ISR_SHIFT_COUNT, 3), SM3_OSR(Regs.SM0_OSR, 3), SM3_OSR_SHIFT_COUNT(Regs.SM0_OSR_SHIFT_COUNT, 3), SM3_FIFO_MEM0(Regs.SM0_FIFO_MEM0, 3), SM3_FIFO_MEM1(Regs.SM0_FIFO_MEM0, 3), SM3_FIFO_MEM2(Regs.SM0_FIFO_MEM0, 3), SM3_FIFO_MEM3(Regs.SM0_FIFO_MEM0, 3), SM3_FIFO_MEM4(Regs.SM0_FIFO_MEM0, 3), SM3_FIFO_MEM5(Regs.SM0_FIFO_MEM0, 3), SM3_FIFO_MEM6(Regs.SM0_FIFO_MEM0, 3), SM3_FIFO_MEM7(Regs.SM0_FIFO_MEM0, 3), SM3_CLEAR_FORCED(Regs.SM0_CLEAR_FORCED, 3), SM3_CLEAR_EXECD(Regs.SM0_CLEAR_EXECD, 3), SM3_INSTR_ORIGIN(Regs.SM0_INSTR_ORIGIN, 3), SM3_DELAY(Regs.SM0_DELAY, 3), SM3_DELAY_CYCLE(Regs.SM0_DELAY_CYCLE, 3), SM3_PENDING_DELAY(Regs.SM0_PENDING_DELAY, 3), SM3_FORCED_INSTR(Regs.SM0_FORCED_INSTR, 3), SM3_EXECD_INSTR(Regs.SM0_EXECD_INSTR, 3), SM3_CLK_ENABLE(Regs.SM0_CLK_ENABLE, 3), SM3_NEXT_CLK_ENABLE(Regs.SM0_NEXT_CLK_ENABLE, 3), SM3_BREAKPOINTS(Regs.SM0_BREAKPOINTS, 3), SM3_TRACEPOINTS(Regs.SM0_TRACEPOINTS, 3), INSTR_MEM0("Read / write access to instruction memory word.", new BitsInfo[] { new BitsInfo(null, 31, 16, null, BitsType.UNUSED, null), new BitsInfo(null, 15, 0, null, BitsType.RW, 0) }), INSTR_MEM1(Regs.INSTR_MEM0), INSTR_MEM2(Regs.INSTR_MEM0), INSTR_MEM3(Regs.INSTR_MEM0), INSTR_MEM4(Regs.INSTR_MEM0), INSTR_MEM5(Regs.INSTR_MEM0), INSTR_MEM6(Regs.INSTR_MEM0), INSTR_MEM7(Regs.INSTR_MEM0), INSTR_MEM8(Regs.INSTR_MEM0), INSTR_MEM9(Regs.INSTR_MEM0), INSTR_MEM10(Regs.INSTR_MEM0), INSTR_MEM11(Regs.INSTR_MEM0), INSTR_MEM12(Regs.INSTR_MEM0), INSTR_MEM13(Regs.INSTR_MEM0), INSTR_MEM14(Regs.INSTR_MEM0), INSTR_MEM15(Regs.INSTR_MEM0), INSTR_MEM16(Regs.INSTR_MEM0), INSTR_MEM17(Regs.INSTR_MEM0), INSTR_MEM18(Regs.INSTR_MEM0), INSTR_MEM19(Regs.INSTR_MEM0), INSTR_MEM20(Regs.INSTR_MEM0), INSTR_MEM21(Regs.INSTR_MEM0), INSTR_MEM22(Regs.INSTR_MEM0), INSTR_MEM23(Regs.INSTR_MEM0), INSTR_MEM24(Regs.INSTR_MEM0), INSTR_MEM25(Regs.INSTR_MEM0), INSTR_MEM26(Regs.INSTR_MEM0), INSTR_MEM27(Regs.INSTR_MEM0), INSTR_MEM28(Regs.INSTR_MEM0), INSTR_MEM29(Regs.INSTR_MEM0), INSTR_MEM30(Regs.INSTR_MEM0), INSTR_MEM31(Regs.INSTR_MEM0), TXF0("Direct read access to the TX FIFO for the corresponding state%n" + "machine. Each read pops one word from the FIFO. Attempting to%n" + "read from an empty FIFO has no effect on the FIFO state,%n" + "and sets the sticky FDEBUG_TXUNDER error flag for this FIFO.%n" + "The data returned to the system on a read from an empty FIFO%n" + "is undefined.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RF, null) }), TXF1(Regs.TXF0, 1), TXF2(Regs.TXF0, 2), TXF3(Regs.TXF0, 3), RXF0("Direct write access to the RX FIFO for the corresponding state%n" + "machine. Each write pushes one word to the FIFO. Attempting to%n" + "write to a full FIFO has no effect on the FIFO state or contents,%n" + "and sets the sticky FDEBUG_RXOVER error flag for this FIFO.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.WF, 0) }), RXF1(Regs.RXF0, 1), RXF2(Regs.RXF0, 2), RXF3(Regs.RXF0, 3), FREAD_PTR("Read pointers of all of the SM's TX and RX FIFOs.", IntStream.rangeClosed(0, 7).boxed() .map(n -> new BitsInfo(((n & 0x1) == 0 ? "TX" : "RX") + "F" + (n >> 1) + "_READ_PTR", 31 - (n << 2), 28 - (n << 2), "Offset (0…7) within FIFO memory for%n" + "the next FIFO read operation", BitsType.RO, 0)) .collect(Collectors.toList())), GPIO_PINS("Direct read / write access to all of the 32 GPIO pins%n" + "that PIO is currently driving to the GPIOs.", IntStream.rangeClosed(0, 31).boxed() .map(n -> new BitsInfo("GPIO_PIN" + (31 - n), 31 - n, 31 - n, "0x1 for HIGH or 0x0 for LOW", BitsType.RW, 0)) .collect(Collectors.toList())), GPIO_PINDIRS("Direct read / write access to all of the 32 GPIO pin%n" + "directions that PIO is currently driving to the GPIOs.", IntStream.rangeClosed(0, 31).boxed() .map(n -> new BitsInfo("GPIO_PINDIR" + (31 - n), 31 - n, 31 - n, "0x1 for pin direction out or%n" + "0x0 for pin direction in", BitsType.RW, 0)) .collect(Collectors.toList())), IRQ("Direct read access of all PIO IRQ.", IntStream.rangeClosed(0, 7).boxed() .map(n -> new BitsInfo("IRQ" + (7 - n), 7 - n, 7 - n, "0x1 for HIGH or 0x0 for LOW", BitsType.RO, 0)) .collect(Collectors.toList())); public static String getRegisterSetLabel() { return "Emulator PIO Registers"; } public static String getRegisterSetDescription() { return "The PIO emulator provides registers in addition to those%n" + "of the PIO as specified in the RP2040 datasheet to allow%n" + "for inspection of more details of the PIO's internal state%n" + "such as its scratch registers X and Y, its shift registers%n" + "ISR, OSR, FIFO memory, and read access to PIO instruction%n" + "memory for enhanced debugging of programs.%n" + "Base address for the two emulator PIO register sets (one %n" + "register set for each of the two PIOs) is%n" + String.format("0x%08x and 0x%08x for PIO0 and PIO1, respectively.%n", PIO0_EMU_BASE, PIO1_EMU_BASE); } private final RegisterDetails registerDetails; private Regs() { throw new UnsupportedOperationException("unsupported empty constructor"); } private Regs(final Regs ref) { this(ref.registerDetails); } private Regs(final Regs ref, final int smNum) { this(ref.registerDetails.createCopyForDifferentSm(smNum)); } private Regs(final String info, final BitsInfo[] bitsInfos) { this(new RegisterDetails(info, bitsInfos)); } private Regs(final String info, final List bitsInfos) { this(new RegisterDetails(info, bitsInfos)); } private Regs(final String info, final int smNum, final BitsInfo[] bitsInfos) { this(new RegisterDetails(info, smNum, bitsInfos)); } private Regs(final String info, final int smNum, final List bitsInfos) { this(new RegisterDetails(info, smNum, bitsInfos)); } private Regs(final RegisterDetails registerDetails) { this.registerDetails = registerDetails; } @Override public String getInfo() { return registerDetails.getInfo(); } @Override public RegisterDetails getRegisterDetails() { return registerDetails; } } protected static final Regs[] REGS = Regs.values(); @Override @SuppressWarnings("unchecked") protected > T[] getRegs() { return (T[])REGS; } protected static final int SM_SIZE = Regs.SM1_REGX.ordinal() - Regs.SM0_REGX.ordinal(); public static int getAddress(final int pioNum, final PIOEmuRegisters.Regs register) { Constants.checkPioNum(pioNum, "PIO index number"); if (register == null) { throw new NullPointerException("register"); } return Constants.getPIOEmuBaseAddress(pioNum) + 0x4 * register.ordinal(); } public static int getSMAddress(final int pioNum, final int smNum, final PIOEmuRegisters.Regs register) { Constants.checkPioNum(pioNum, "PIO index number"); Constants.checkSmNum(smNum); if (register == null) { throw new NullPointerException("register"); } switch (register) { case SM0_REGX: case SM0_REGY: case SM0_PC: case SM0_ISR: case SM0_ISR_SHIFT_COUNT: case SM0_OSR: case SM0_OSR_SHIFT_COUNT: case SM0_FIFO_MEM0: case SM0_FIFO_MEM1: case SM0_FIFO_MEM2: case SM0_FIFO_MEM3: case SM0_FIFO_MEM4: case SM0_FIFO_MEM5: case SM0_FIFO_MEM6: case SM0_FIFO_MEM7: case SM0_CLEAR_FORCED: case SM0_CLEAR_EXECD: case SM0_INSTR_ORIGIN: case SM0_DELAY: case SM0_DELAY_CYCLE: case SM0_PENDING_DELAY: case SM0_FORCED_INSTR: case SM0_EXECD_INSTR: case SM0_CLK_ENABLE: case SM0_NEXT_CLK_ENABLE: case SM0_BREAKPOINTS: case SM0_TRACEPOINTS: break; // ok default: throw new IllegalArgumentException("register not one of SM0_*: " + register); } return Constants.getPIOEmuBaseAddress(pioNum) + 0x4 * (register.ordinal() + smNum * SM_SIZE); } public static int getFIFOMemAddress(final int pioNum, final int smNum, final int address) { Constants.checkPioNum(pioNum, "PIO index number"); Constants.checkSmNum(smNum); Constants.checkFIFOAddr(address, "FIFO address"); return Constants.getPIOEmuBaseAddress(pioNum) + 0x4 * (Regs.SM0_FIFO_MEM0.ordinal() + smNum * SM_SIZE + address); } public static int getMemoryAddress(final int pioNum, final int memoryAddress) { Constants.checkPioNum(pioNum, "PIO index number"); Constants.checkSmMemAddr(memoryAddress, "memory address"); return Constants.getPIOEmuBaseAddress(pioNum) + 0x4 * (Regs.INSTR_MEM0.ordinal() + memoryAddress); } public static int getTXFAddress(final int pioNum, final int smNum) { Constants.checkPioNum(pioNum, "PIO index number"); Constants.checkSmNum(smNum); return Constants.getPIOEmuBaseAddress(pioNum) + 0x4 * (Regs.TXF0.ordinal() + smNum); } public static int getRXFAddress(final int pioNum, final int smNum) { Constants.checkPioNum(pioNum, "PIO index number"); Constants.checkSmNum(smNum); return Constants.getPIOEmuBaseAddress(pioNum) + 0x4 * (Regs.RXF0.ordinal() + smNum); } public PIOEmuRegisters(final String id, final int baseAddress) { super(id, baseAddress); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/PIOEmuRegistersImpl.java ================================================ /* * @(#)PIOEmuRegistersImpl.java 1.00 21/03/06 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * Facade to additonal emulator properties of the internal subsystems * of a PIO that are not available via the PIORegisters facade. This * facade is in particular intended for use by software that wants to * exploit the emulator's debug facilities. */ public class PIOEmuRegistersImpl extends PIOEmuRegisters { private final PIO pio; public PIOEmuRegistersImpl(final PIO pio) { super("PIOEmu" + pio.getIndex(), Constants.getPIOEmuBaseAddress(pio.getIndex())); this.pio = pio; } public PIO getPIO() { return pio; } public int getPIOIndex() { return pio.getIndex(); } public int getAddress(final PIOEmuRegisters.Regs register) { return getAddress(getPIOIndex(), register); } public int getSMAddress(final PIOEmuRegisters.Regs register, final int smNum) { return getSMAddress(getPIOIndex(), smNum, register); } public int getFIFOMemAddress(final int smNum, final int address) { return getFIFOMemAddress(getPIOIndex(), smNum, address); } public int getMemoryAddress(final int memoryAddress) { return getMemoryAddress(getPIOIndex(), memoryAddress); } private void setFIFOMemValue(final int regsOffset, final int value, final int mask, final boolean xor) { final int smNum = regsOffset / SM_SIZE; Constants.checkSmNum(smNum); final int address = regsOffset - SM_SIZE * smNum; final FIFO fifo = pio.getSM(smNum).getFIFO(); fifo.setMemValue(address, Constants.hwSetBits(fifo.getMemValue(address), value, mask, xor)); } @Override public void writeRegister(final int regNum, final int value, final int mask, final boolean xor) { checkRegNum(regNum); final Regs register = REGS[regNum]; switch (register) { case SM0_REGX: case SM1_REGX: case SM2_REGX: case SM3_REGX: pio.getSM((regNum - Regs.SM0_REGX.ordinal()) / SM_SIZE). setX(value, mask, xor); break; case SM0_REGY: case SM1_REGY: case SM2_REGY: case SM3_REGY: pio.getSM((regNum - Regs.SM0_REGY.ordinal()) / SM_SIZE). setY(value, mask, xor); break; case SM0_PC: case SM1_PC: case SM2_PC: case SM3_PC: pio.getSM((regNum - Regs.SM0_PC.ordinal()) / SM_SIZE). setPC(value, mask, xor); break; case SM0_ISR: case SM1_ISR: case SM2_ISR: case SM3_ISR: pio.getSM((regNum - Regs.SM0_ISR.ordinal()) / SM_SIZE). setISRValue(value, mask, xor); break; case SM0_ISR_SHIFT_COUNT: case SM1_ISR_SHIFT_COUNT: case SM2_ISR_SHIFT_COUNT: case SM3_ISR_SHIFT_COUNT: pio.getSM((regNum - Regs.SM0_ISR_SHIFT_COUNT.ordinal()) / SM_SIZE). setISRShiftCount(value, mask, xor); break; case SM0_OSR: case SM1_OSR: case SM2_OSR: case SM3_OSR: pio.getSM((regNum - Regs.SM0_OSR.ordinal()) / SM_SIZE). setOSRValue(value, mask, xor); break; case SM0_OSR_SHIFT_COUNT: case SM1_OSR_SHIFT_COUNT: case SM2_OSR_SHIFT_COUNT: case SM3_OSR_SHIFT_COUNT: pio.getSM((regNum - Regs.SM0_OSR_SHIFT_COUNT.ordinal()) / SM_SIZE). setOSRShiftCount(value, mask, xor); break; case SM0_FIFO_MEM0: case SM0_FIFO_MEM1: case SM0_FIFO_MEM2: case SM0_FIFO_MEM3: case SM0_FIFO_MEM4: case SM0_FIFO_MEM5: case SM0_FIFO_MEM6: case SM0_FIFO_MEM7: case SM1_FIFO_MEM0: case SM1_FIFO_MEM1: case SM1_FIFO_MEM2: case SM1_FIFO_MEM3: case SM1_FIFO_MEM4: case SM1_FIFO_MEM5: case SM1_FIFO_MEM6: case SM1_FIFO_MEM7: case SM2_FIFO_MEM0: case SM2_FIFO_MEM1: case SM2_FIFO_MEM2: case SM2_FIFO_MEM3: case SM2_FIFO_MEM4: case SM2_FIFO_MEM5: case SM2_FIFO_MEM6: case SM2_FIFO_MEM7: case SM3_FIFO_MEM0: case SM3_FIFO_MEM1: case SM3_FIFO_MEM2: case SM3_FIFO_MEM3: case SM3_FIFO_MEM4: case SM3_FIFO_MEM5: case SM3_FIFO_MEM6: case SM3_FIFO_MEM7: setFIFOMemValue(regNum - Regs.SM0_FIFO_MEM0.ordinal(), value, mask, xor); break; case SM0_CLEAR_FORCED: case SM1_CLEAR_FORCED: case SM2_CLEAR_FORCED: case SM3_CLEAR_FORCED: pio.getSM((regNum - Regs.SM0_CLEAR_FORCED.ordinal()) / SM_SIZE). clearPendingForcedInstruction(); break; case SM0_CLEAR_EXECD: case SM1_CLEAR_EXECD: case SM2_CLEAR_EXECD: case SM3_CLEAR_EXECD: pio.getSM((regNum - Regs.SM0_CLEAR_EXECD.ordinal()) / SM_SIZE). clearPendingExecdInstruction(); break; case SM0_INSTR_ORIGIN: case SM1_INSTR_ORIGIN: case SM2_INSTR_ORIGIN: case SM3_INSTR_ORIGIN: break; // (for now) read-only address case SM0_DELAY: case SM1_DELAY: case SM2_DELAY: case SM3_DELAY: break; // (for now) read-only address case SM0_DELAY_CYCLE: case SM1_DELAY_CYCLE: case SM2_DELAY_CYCLE: case SM3_DELAY_CYCLE: break; // (for now) read-only address case SM0_PENDING_DELAY: case SM1_PENDING_DELAY: case SM2_PENDING_DELAY: case SM3_PENDING_DELAY: break; // (for now) read-only address case SM0_FORCED_INSTR: case SM1_FORCED_INSTR: case SM2_FORCED_INSTR: case SM3_FORCED_INSTR: break; // (for now) read-only address case SM0_EXECD_INSTR: case SM1_EXECD_INSTR: case SM2_EXECD_INSTR: case SM3_EXECD_INSTR: pio.getSM((regNum - Regs.SM0_EXECD_INSTR.ordinal()) / SM_SIZE). execInstruction(value & mask); break; case SM0_CLK_ENABLE: case SM1_CLK_ENABLE: case SM2_CLK_ENABLE: case SM3_CLK_ENABLE: break; // (for now) read-only address case SM0_NEXT_CLK_ENABLE: case SM1_NEXT_CLK_ENABLE: case SM2_NEXT_CLK_ENABLE: case SM3_NEXT_CLK_ENABLE: break; // (for now) read-only address case SM0_BREAKPOINTS: case SM1_BREAKPOINTS: case SM2_BREAKPOINTS: case SM3_BREAKPOINTS: pio.getSM((regNum - Regs.SM0_BREAKPOINTS.ordinal()) / SM_SIZE). setBreakPoints(value, mask, xor); break; case SM0_TRACEPOINTS: case SM1_TRACEPOINTS: case SM2_TRACEPOINTS: case SM3_TRACEPOINTS: pio.getSM((regNum - Regs.SM0_TRACEPOINTS.ordinal()) / SM_SIZE). setTracePoints(value, mask, xor); break; case INSTR_MEM0: case INSTR_MEM1: case INSTR_MEM2: case INSTR_MEM3: case INSTR_MEM4: case INSTR_MEM5: case INSTR_MEM6: case INSTR_MEM7: case INSTR_MEM8: case INSTR_MEM9: case INSTR_MEM10: case INSTR_MEM11: case INSTR_MEM12: case INSTR_MEM13: case INSTR_MEM14: case INSTR_MEM15: case INSTR_MEM16: case INSTR_MEM17: case INSTR_MEM18: case INSTR_MEM19: case INSTR_MEM20: case INSTR_MEM21: case INSTR_MEM22: case INSTR_MEM23: case INSTR_MEM24: case INSTR_MEM25: case INSTR_MEM26: case INSTR_MEM27: case INSTR_MEM28: case INSTR_MEM29: case INSTR_MEM30: case INSTR_MEM31: pio.getMemory().set(regNum - Regs.INSTR_MEM0.ordinal(), value, mask, xor); break; case RXF0: case RXF1: case RXF2: case RXF3: pio.getSM(regNum - Regs.RXF0.ordinal()).putRXF(value & mask); break; case TXF0: case TXF1: case TXF2: case TXF3: break; // read-only address case FREAD_PTR: break; // read-only address case GPIO_PINS: pio.getPIOGPIO().setPinsMask(value, mask, xor); break; case GPIO_PINDIRS: pio.getPIOGPIO().setPinDirsMask(value, mask, xor); break; case IRQ: break; // read-only address default: throw new InternalError("unexpected case fall-through"); } } private int getClockEnable(final int smNum) { Constants.checkSmNum(smNum); final boolean clockEnable = pio.getSM(smNum).getPLL().getClockEnable(); return clockEnable ? 0x1 : 0x0; } private int getNextClockEnable(final int smNum) { Constants.checkSmNum(smNum); final boolean nextClockEnable = pio.getSM(smNum).getPLL().getNextClockEnable(); return nextClockEnable ? 0x1 : 0x0; } private int getFIFOMemValue(final int regsOffset) { final int smNum = regsOffset / SM_SIZE; final int address = regsOffset - SM_SIZE * smNum; Constants.checkSmNum(smNum); return pio.getSM(smNum).getFIFO().getMemValue(address); } private int getFIFOReadPointers() { int readPointers = 0; for (int smNum = SM_COUNT - 1; smNum >= 0; smNum--) { final FIFO fifo = pio.getSM(smNum).getFIFO(); readPointers <<= 8; readPointers |= (fifo.getRXReadPointer() & 0x7) << 4; readPointers |= fifo.getTXReadPointer() & 0x7; } return readPointers; } @Override public synchronized int readRegister(final int regNum) { checkRegNum(regNum); final Regs register = REGS[regNum]; switch (register) { case SM0_REGX: case SM1_REGX: case SM2_REGX: case SM3_REGX: return pio.getSM((regNum - Regs.SM0_REGX.ordinal()) / SM_SIZE).getX(); case SM0_REGY: case SM1_REGY: case SM2_REGY: case SM3_REGY: return pio.getSM((regNum - Regs.SM0_REGY.ordinal()) / SM_SIZE).getY(); case SM0_PC: case SM1_PC: case SM2_PC: case SM3_PC: return pio.getSM((regNum - Regs.SM0_PC.ordinal()) / SM_SIZE).getPC(); case SM0_ISR: case SM1_ISR: case SM2_ISR: case SM3_ISR: return pio.getSM((regNum - Regs.SM0_ISR.ordinal()) / SM_SIZE).getISRValue(); case SM0_ISR_SHIFT_COUNT: case SM1_ISR_SHIFT_COUNT: case SM2_ISR_SHIFT_COUNT: case SM3_ISR_SHIFT_COUNT: return pio.getSM((regNum - Regs.SM0_ISR_SHIFT_COUNT.ordinal()) / SM_SIZE). getISRShiftCount(); case SM0_OSR: case SM1_OSR: case SM2_OSR: case SM3_OSR: return pio.getSM((regNum - Regs.SM0_OSR.ordinal()) / SM_SIZE).getOSRValue(); case SM0_OSR_SHIFT_COUNT: case SM1_OSR_SHIFT_COUNT: case SM2_OSR_SHIFT_COUNT: case SM3_OSR_SHIFT_COUNT: return pio.getSM((regNum - Regs.SM0_OSR_SHIFT_COUNT.ordinal()) / SM_SIZE). getOSRShiftCount(); case SM0_FIFO_MEM0: case SM0_FIFO_MEM1: case SM0_FIFO_MEM2: case SM0_FIFO_MEM3: case SM0_FIFO_MEM4: case SM0_FIFO_MEM5: case SM0_FIFO_MEM6: case SM0_FIFO_MEM7: case SM1_FIFO_MEM0: case SM1_FIFO_MEM1: case SM1_FIFO_MEM2: case SM1_FIFO_MEM3: case SM1_FIFO_MEM4: case SM1_FIFO_MEM5: case SM1_FIFO_MEM6: case SM1_FIFO_MEM7: case SM2_FIFO_MEM0: case SM2_FIFO_MEM1: case SM2_FIFO_MEM2: case SM2_FIFO_MEM3: case SM2_FIFO_MEM4: case SM2_FIFO_MEM5: case SM2_FIFO_MEM6: case SM2_FIFO_MEM7: case SM3_FIFO_MEM0: case SM3_FIFO_MEM1: case SM3_FIFO_MEM2: case SM3_FIFO_MEM3: case SM3_FIFO_MEM4: case SM3_FIFO_MEM5: case SM3_FIFO_MEM6: case SM3_FIFO_MEM7: return getFIFOMemValue(regNum - Regs.SM0_FIFO_MEM0.ordinal()); case SM0_CLEAR_FORCED: case SM1_CLEAR_FORCED: case SM2_CLEAR_FORCED: case SM3_CLEAR_FORCED: return 0; // write-only address case SM0_CLEAR_EXECD: case SM1_CLEAR_EXECD: case SM2_CLEAR_EXECD: case SM3_CLEAR_EXECD: return 0; // write-only address case SM0_INSTR_ORIGIN: case SM1_INSTR_ORIGIN: case SM2_INSTR_ORIGIN: case SM3_INSTR_ORIGIN: return pio.getSM((regNum - Regs.SM0_INSTR_ORIGIN.ordinal()) / SM_SIZE). getINSTR_ORIGIN(); case SM0_DELAY: case SM1_DELAY: case SM2_DELAY: case SM3_DELAY: return pio.getSM((regNum - Regs.SM0_DELAY.ordinal()) / SM_SIZE). getTotalDelay(); case SM0_DELAY_CYCLE: case SM1_DELAY_CYCLE: case SM2_DELAY_CYCLE: case SM3_DELAY_CYCLE: return pio.getSM((regNum - Regs.SM0_DELAY_CYCLE.ordinal()) / SM_SIZE). isDelayCycle() ? 0x1 : 0x0; case SM0_PENDING_DELAY: case SM1_PENDING_DELAY: case SM2_PENDING_DELAY: case SM3_PENDING_DELAY: return pio.getSM((regNum - Regs.SM0_PENDING_DELAY.ordinal()) / SM_SIZE). getPendingDelay(); case SM0_FORCED_INSTR: case SM1_FORCED_INSTR: case SM2_FORCED_INSTR: case SM3_FORCED_INSTR: return pio.getSM((regNum - Regs.SM0_FORCED_INSTR.ordinal()) / SM_SIZE). getFORCED_INSTR(); case SM0_EXECD_INSTR: case SM1_EXECD_INSTR: case SM2_EXECD_INSTR: case SM3_EXECD_INSTR: return pio.getSM((regNum - Regs.SM0_EXECD_INSTR.ordinal()) / SM_SIZE). getEXECD_INSTR(); case SM0_CLK_ENABLE: case SM1_CLK_ENABLE: case SM2_CLK_ENABLE: case SM3_CLK_ENABLE: return getClockEnable((regNum - Regs.SM0_CLK_ENABLE.ordinal()) / SM_SIZE); case SM0_NEXT_CLK_ENABLE: case SM1_NEXT_CLK_ENABLE: case SM2_NEXT_CLK_ENABLE: case SM3_NEXT_CLK_ENABLE: return getNextClockEnable((regNum - Regs.SM0_CLK_ENABLE.ordinal()) / SM_SIZE); case SM0_BREAKPOINTS: case SM1_BREAKPOINTS: case SM2_BREAKPOINTS: case SM3_BREAKPOINTS: return pio.getSM((regNum - Regs.SM0_BREAKPOINTS.ordinal()) / SM_SIZE). getBreakPoints(); case SM0_TRACEPOINTS: case SM1_TRACEPOINTS: case SM2_TRACEPOINTS: case SM3_TRACEPOINTS: return pio.getSM((regNum - Regs.SM0_TRACEPOINTS.ordinal()) / SM_SIZE). getTracePoints(); case INSTR_MEM0: case INSTR_MEM1: case INSTR_MEM2: case INSTR_MEM3: case INSTR_MEM4: case INSTR_MEM5: case INSTR_MEM6: case INSTR_MEM7: case INSTR_MEM8: case INSTR_MEM9: case INSTR_MEM10: case INSTR_MEM11: case INSTR_MEM12: case INSTR_MEM13: case INSTR_MEM14: case INSTR_MEM15: case INSTR_MEM16: case INSTR_MEM17: case INSTR_MEM18: case INSTR_MEM19: case INSTR_MEM20: case INSTR_MEM21: case INSTR_MEM22: case INSTR_MEM23: case INSTR_MEM24: case INSTR_MEM25: case INSTR_MEM26: case INSTR_MEM27: case INSTR_MEM28: case INSTR_MEM29: case INSTR_MEM30: case INSTR_MEM31: return pio.getMemory().get(regNum - Regs.INSTR_MEM0.ordinal()) & 0xffff; case TXF0: case TXF1: case TXF2: case TXF3: return pio.getSM(regNum - Regs.TXF0.ordinal()).getTXF(); case RXF0: case RXF1: case RXF2: case RXF3: return 0; // write-only address case FREAD_PTR: return getFIFOReadPointers(); case GPIO_PINS: return pio.getPIOGPIO().getPins(0, GPIO_NUM); case GPIO_PINDIRS: return pio.getPIOGPIO().getPinDirs(0, GPIO_NUM); case IRQ: return pio.getIRQ().getIRQ(); default: throw new InternalError("unexpected case fall-through"); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/PIOGPIO.java ================================================ /* * @(#)PIOGPIO.java 1.00 21/03/19 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * General-Purpose Set of 32 Peripheral I/O Terminals */ public class PIOGPIO implements Constants { private final GPIO gpio; private final Bit[] collatedLevels; private final Direction[] collatedDirections; private final PinState[] appliedStates; private PIOGPIO() { throw new UnsupportedOperationException("unsupported empty constructor"); } public PIOGPIO(final GPIO gpio) { if (gpio == null) { throw new NullPointerException("gpio"); } this.gpio = gpio; collatedLevels = new Bit[GPIO_NUM]; collatedDirections = new Direction[GPIO_NUM]; appliedStates = new PinState[GPIO_NUM]; reset(); } public void reset() { for (int gpioNum = 0; gpioNum < GPIO_NUM; gpioNum++) { collatedLevels[gpioNum] = null; collatedDirections[gpioNum] = null; appliedStates[gpioNum] = PinState.IN_LOW; } } public GPIO getGPIO() { return gpio; } private void setLevel(final int gpioNum, final Bit level) { if (level == null) { throw new NullPointerException("level"); } Constants.checkGpioPin(gpioNum, "GPIO pin number"); final PinState pinState = appliedStates[gpioNum]; appliedStates[gpioNum] = PinState.fromValues(pinState.getDirection(), level); } public Bit getLevel(final int gpioNum) { Constants.checkGpioPin(gpioNum, "GPIO pin number"); return appliedStates[gpioNum].getLevel(); } private void setDirection(final int gpioNum, final Direction direction) { if (direction == null) { throw new NullPointerException("direction"); } Constants.checkGpioPin(gpioNum, "GPIO pin number"); final PinState pinState = appliedStates[gpioNum]; appliedStates[gpioNum] = PinState.fromValues(direction, pinState.getLevel()); } public Direction getDirection(final int gpioNum) { Constants.checkGpioPin(gpioNum, "GPIO pin number"); return appliedStates[gpioNum].getDirection(); } public int getPins(final int base, final int count) { Constants.checkGpioPin(base, "GPIO pin base"); Constants.checkGpioPinsCount(count, "GPIO pin count"); int pins = 0; for (int pin = 0; pin < count; pin++) { pins = (pins << 0x1) | getLevel((base - pin - 1) & 0x1f).getValue(); } return pins; } private void collateLevel(final int gpioNum, final Bit bit) { // As of now, SMs do not run parallel in separate threads, but one // after the other with ascending SM number. Therefore, no // further action / writer tracking needs to be taken for assuring // output priority (cp. Sect. 3.5.6.1 of RP2040 datasheet). collatedLevels[gpioNum] = bit; } public void collatePins(final int pins, final int base, final int count) { for (int pin = 0; pin < count; pin++) { collateLevel((base + pin) & (GPIO_NUM - 1), Bit.fromValue((pins >>> pin) & 0x1)); } } public void setPins(final int pins, final int base, final int count) { Constants.checkGpioPin(base, "GPIO pin base"); Constants.checkGpioPinsCount(count, "GPIO pin count"); for (int pin = 0; pin < count; pin++) { setLevel((base + pin) & 0x1f, Bit.fromValue((pins >>> pin) & 0x1)); } } public void setPinsMask(final int pins, final int mask, final boolean xor) { for (int gpioNum = 0; gpioNum < GPIO_NUM; gpioNum++) { final int oldLevel = getLevel(gpioNum).getValue(); final int pin = pins >>> gpioNum & 0x1; final int maskBit = mask >>> gpioNum & 0x1; final int newLevel = Constants.hwSetBits(oldLevel, pin, maskBit, xor); setLevel(gpioNum, Bit.fromValue(newLevel)); } } public int getPinDirs(final int base, final int count) { Constants.checkGpioPin(base, "GPIO pin base"); Constants.checkGpioPinsCount(count, "GPIO pin count"); int pinDirs = 0; for (int pin = 0; pin < count; pin++) { pinDirs = (pinDirs << 0x1) | getDirection((base - pin - 1) & 0x1f).getValue(); } return pinDirs; } private void collatePinDir(final int gpioNum, final Direction direction) { // As of now, SMs do not run parallel in separate threads, but one // after the other with ascending SM number. Therefore, no // further action / writer tracking needs to be taken for assuring // output priority (cp. Sect. 3.5.6.1 of RP2040 datasheet). collatedDirections[gpioNum] = direction; } public void collatePinDirs(final int pinDirs, final int base, final int count) { for (int pin = 0; pin < count; pin++) { collatePinDir((base + pin) & 0x1f, Direction.fromValue((pinDirs >>> pin) & 0x1)); } } public void setPinDirs(final int pinDirs, final int base, final int count) { Constants.checkGpioPin(base, "GPIO pin base"); Constants.checkGpioPinsCount(count, "GPIO pin count"); for (int pin = 0; pin < count; pin++) { setDirection((base + pin) & 0x1f, Direction.fromValue((pinDirs >>> pin) & 0x1)); } } public void setPinDirsMask(final int pinDirs, final int mask, final boolean xor) { for (int gpioNum = 0; gpioNum < GPIO_NUM; gpioNum++) { final int oldDirection = getDirection(gpioNum).getValue(); final int pinDir = pinDirs >>> gpioNum & 0x1; final int maskBit = mask >>> gpioNum & 0x1; final int newDirection = Constants.hwSetBits(oldDirection, pinDir, maskBit, xor); setDirection(gpioNum, Direction.fromValue(newDirection)); } } public void applyCollatedWrites() { for (int gpioNum = 0; gpioNum < GPIO_NUM; gpioNum++) { final PinState state = appliedStates[gpioNum]; final Bit collatedLevel = collatedLevels[gpioNum]; final Direction collatedDirection = collatedDirections[gpioNum]; final Bit level = collatedLevel == null ? state.getLevel() : collatedLevel; final Direction direction = collatedDirection == null ? state.getDirection() : collatedDirection; appliedStates[gpioNum] = PinState.fromValues(direction, level); collatedLevels[gpioNum] = null; collatedDirections[gpioNum] = null; } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/PIORegisters.java ================================================ /* * @(#)PIORegisters.java 1.00 21/02/25 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.soundpaint.rp2040pio.doctool.RegistersDocs; /** * Facade to the internal subsystems of a PIO. The layout of * registers follows the list of registers in Sect. 3.7 of the RP2040 * datasheet. The facade is in particular intended for use by the * SDK. */ public abstract class PIORegisters extends RegisterSet { public enum Regs implements RegistersDocs { CTRL("PIO control register.", new BitsInfo[] { new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null), new BitsInfo("CLKDIV3_RESTART", 11, 11, null, BitsType.SC, 0), new BitsInfo("CLKDIV2_RESTART", 10, 10, null, BitsType.SC, 0), new BitsInfo("CLKDIV1_RESTART", 9, 9, null, BitsType.SC, 0), new BitsInfo("CLKDIV0_RESTART", 8, 8, null, BitsType.SC, 0), new BitsInfo("SM3_RESTART", 7, 7, null, BitsType.SC, 0), new BitsInfo("SM2_RESTART", 6, 6, null, BitsType.SC, 0), new BitsInfo("SM1_RESTART", 5, 5, null, BitsType.SC, 0), new BitsInfo("SM0_RESTART", 4, 4, null, BitsType.SC, 0), new BitsInfo("SM3_ENABLE", 3, 3, null, BitsType.RW, 0), new BitsInfo("SM2_ENABLE", 2, 2, null, BitsType.RW, 0), new BitsInfo("SM1_ENABLE", 1, 1, null, BitsType.RW, 0), new BitsInfo("SM0_ENABLE", 0, 0, null, BitsType.RW, 0) }), FSTAT("FIFO status register.", new BitsInfo[] { new BitsInfo(null, 31, 28, null, BitsType.RESERVED, null), new BitsInfo("SM3_TXEMPTY", 27, 27, null, BitsType.RO, 1), new BitsInfo("SM2_TXEMPTY", 26, 26, null, BitsType.RO, 1), new BitsInfo("SM1_TXEMPTY", 25, 25, null, BitsType.RO, 1), new BitsInfo("SM0_TXEMPTY", 24, 24, null, BitsType.RO, 1), new BitsInfo(null, 23, 20, null, BitsType.RESERVED, null), new BitsInfo("SM3_TXFULL", 19, 19, null, BitsType.RO, 0), new BitsInfo("SM2_TXFULL", 18, 18, null, BitsType.RO, 0), new BitsInfo("SM1_TXFULL", 17, 17, null, BitsType.RO, 0), new BitsInfo("SM0_TXFULL", 16, 16, null, BitsType.RO, 0), new BitsInfo(null, 15, 12, null, BitsType.RESERVED, null), new BitsInfo("SM3_RXEMPTY", 11, 11, null, BitsType.RO, 1), new BitsInfo("SM2_RXEMPTY", 10, 10, null, BitsType.RO, 1), new BitsInfo("SM1_RXEMPTY", 9, 9, null, BitsType.RO, 1), new BitsInfo("SM0_RXEMPTY", 8, 8, null, BitsType.RO, 1), new BitsInfo(null, 7, 4, null, BitsType.RESERVED, null), new BitsInfo("SM3_RXFULL", 3, 3, null, BitsType.RO, 0), new BitsInfo("SM2_RXFULL", 2, 2, null, BitsType.RO, 0), new BitsInfo("SM1_RXFULL", 1, 1, null, BitsType.RO, 0), new BitsInfo("SM0_RXFULL", 0, 0, null, BitsType.RO, 0) }), FDEBUG("FIFO debug register.", new BitsInfo[] { new BitsInfo(null, 31, 28, null, BitsType.RESERVED, null), new BitsInfo("SM3_TXSTALL", 27, 27, null, BitsType.WC, 0), new BitsInfo("SM2_TXSTALL", 26, 26, null, BitsType.WC, 0), new BitsInfo("SM1_TXSTALL", 25, 25, null, BitsType.WC, 0), new BitsInfo("SM0_TXSTALL", 24, 24, null, BitsType.WC, 0), new BitsInfo(null, 23, 20, null, BitsType.RESERVED, null), new BitsInfo("SM3_TXOVER", 19, 19, null, BitsType.WC, 0), new BitsInfo("SM2_TXOVER", 18, 18, null, BitsType.WC, 0), new BitsInfo("SM1_TXOVER", 17, 17, null, BitsType.WC, 0), new BitsInfo("SM0_TXOVER", 16, 16, null, BitsType.WC, 0), new BitsInfo(null, 15, 12, null, BitsType.RESERVED, null), new BitsInfo("SM3_RXUNDER", 11, 11, null, BitsType.WC, 0), new BitsInfo("SM2_RXUNDER", 10, 10, null, BitsType.WC, 0), new BitsInfo("SM1_RXUNDER", 9, 9, null, BitsType.WC, 0), new BitsInfo("SM0_RXUNDER", 8, 8, null, BitsType.WC, 0), new BitsInfo(null, 7, 4, null, BitsType.RESERVED, null), new BitsInfo("SM3_RXSTALL", 3, 3, null, BitsType.WC, 0), new BitsInfo("SM2_RXSTALL", 2, 2, null, BitsType.WC, 0), new BitsInfo("SM1_RXSTALL", 1, 1, null, BitsType.WC, 0), new BitsInfo("SM0_RXSTALL", 0, 0, null, BitsType.WC, 0) }), FLEVEL("FIFO levels.", IntStream.rangeClosed(0, 7).boxed() .map(n -> new BitsInfo(((n & 1) == 0 ? "R" : "T") + "X" + (3 - (n / 2)), 31 - (n << 2), 28 - (n << 2), null, BitsType.RO, 0)) .collect(Collectors.toList())), TXF0("Direct write access to the TX FIFO for state machine *N*.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.WF, 0) }), TXF1(Regs.TXF0, 1), TXF2(Regs.TXF0, 2), TXF3(Regs.TXF0, 3), RXF0("Direct read access to the RX FIFO for state machine *N*.", 0, new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RF, 0) }), RXF1(Regs.RXF0, 1), RXF2(Regs.RXF0, 2), RXF3(Regs.RXF0, 3), IRQ("State machine IRQ flags register. Write 1 to clear.", new BitsInfo[] { new BitsInfo(null, 31, 8, null, BitsType.RESERVED, null), new BitsInfo("IRQ7", 7, 7, null, BitsType.WC, 0), new BitsInfo("IRQ6", 6, 6, null, BitsType.WC, 0), new BitsInfo("IRQ5", 5, 5, null, BitsType.WC, 0), new BitsInfo("IRQ4", 4, 4, null, BitsType.WC, 0), new BitsInfo("IRQ3", 3, 3, null, BitsType.WC, 0), new BitsInfo("IRQ2", 2, 2, null, BitsType.WC, 0), new BitsInfo("IRQ1", 1, 1, null, BitsType.WC, 0), new BitsInfo("IRQ0", 0, 0, null, BitsType.WC, 0), }), IRQ_FORCE("Writing a 1 to each the bit will forcibly assert " + "the corresponding IRQ.", new BitsInfo[] { new BitsInfo(null, 31, 8, null, BitsType.RESERVED, null), new BitsInfo("IRQ7", 7, 7, null, BitsType.WF, 0), new BitsInfo("IRQ6", 6, 6, null, BitsType.WF, 0), new BitsInfo("IRQ5", 5, 5, null, BitsType.WF, 0), new BitsInfo("IRQ4", 4, 4, null, BitsType.WF, 0), new BitsInfo("IRQ3", 3, 3, null, BitsType.WF, 0), new BitsInfo("IRQ2", 2, 2, null, BitsType.WF, 0), new BitsInfo("IRQ1", 1, 1, null, BitsType.WF, 0), new BitsInfo("IRQ0", 0, 0, null, BitsType.WF, 0), }), INPUT_SYNC_BYPASS("GPIO input synchronizer policy.", IntStream.rangeClosed(0, 31).boxed() .map(n -> new BitsInfo("GPIO" + (31 - n), 31 - n, 31 - n, null, BitsType.RW, 0)) .collect(Collectors.toList())), DBG_PADOUT("Read to sample pad output value from PIO.", IntStream.rangeClosed(0, 31).boxed() .map(n -> new BitsInfo("GPIO" + (31 - n), 31 - n, 31 - n, null, BitsType.RO, 0)) .collect(Collectors.toList())), DBG_PADOE("Read to sample pad output enable from PIO.", IntStream.rangeClosed(0, 31).boxed() .map(n -> new BitsInfo("GPIO" + (31 - n), 31 - n, 31 - n, null, BitsType.RO, 0)) .collect(Collectors.toList())), DBG_CFGINFO("PIO hardware free parameters.", new BitsInfo[] { new BitsInfo(null, 31, 22, null, BitsType.RESERVED, null), new BitsInfo("IMEM_SIZE", 21, 16, null, BitsType.RO, null), new BitsInfo(null, 15, 12, null, BitsType.RESERVED, null), new BitsInfo("SM_COUNT", 11, 8, null, BitsType.RO, null), new BitsInfo(null, 7, 6, null, BitsType.RESERVED, null), new BitsInfo("FIFO_DEPTH", 5, 0, null, BitsType.RO, null) }), INSTR_MEM0("Write-only access to instruction memory location *N*.", new BitsInfo[] { new BitsInfo(null, 31, 16, null, BitsType.RESERVED, null), new BitsInfo(null, 15, 0, null, BitsType.RO, 0) }), INSTR_MEM1(Regs.INSTR_MEM0), INSTR_MEM2(Regs.INSTR_MEM0), INSTR_MEM3(Regs.INSTR_MEM0), INSTR_MEM4(Regs.INSTR_MEM0), INSTR_MEM5(Regs.INSTR_MEM0), INSTR_MEM6(Regs.INSTR_MEM0), INSTR_MEM7(Regs.INSTR_MEM0), INSTR_MEM8(Regs.INSTR_MEM0), INSTR_MEM9(Regs.INSTR_MEM0), INSTR_MEM10(Regs.INSTR_MEM0), INSTR_MEM11(Regs.INSTR_MEM0), INSTR_MEM12(Regs.INSTR_MEM0), INSTR_MEM13(Regs.INSTR_MEM0), INSTR_MEM14(Regs.INSTR_MEM0), INSTR_MEM15(Regs.INSTR_MEM0), INSTR_MEM16(Regs.INSTR_MEM0), INSTR_MEM17(Regs.INSTR_MEM0), INSTR_MEM18(Regs.INSTR_MEM0), INSTR_MEM19(Regs.INSTR_MEM0), INSTR_MEM20(Regs.INSTR_MEM0), INSTR_MEM21(Regs.INSTR_MEM0), INSTR_MEM22(Regs.INSTR_MEM0), INSTR_MEM23(Regs.INSTR_MEM0), INSTR_MEM24(Regs.INSTR_MEM0), INSTR_MEM25(Regs.INSTR_MEM0), INSTR_MEM26(Regs.INSTR_MEM0), INSTR_MEM27(Regs.INSTR_MEM0), INSTR_MEM28(Regs.INSTR_MEM0), INSTR_MEM29(Regs.INSTR_MEM0), INSTR_MEM30(Regs.INSTR_MEM0), INSTR_MEM31(Regs.INSTR_MEM0), SM0_CLKDIV("Clock divisor register for state machine *N*.", 0, new BitsInfo[] { new BitsInfo("INT", 31, 16, null, BitsType.RW, 1), new BitsInfo("FRAC", 15, 8, null, BitsType.RW, 0), new BitsInfo(null, 7, 0, null, BitsType.RESERVED, null) }), SM0_EXECCTRL("Execution/behavioural settings for state machine *N*.", 0, new BitsInfo[] { new BitsInfo("EXEC_STALLED", 31, 31, null, BitsType.RO, 0), new BitsInfo("SIDE_EN", 30, 30, null, BitsType.RW, 0), new BitsInfo("SIDE_PINDIR", 29, 29, null, BitsType.RW, 0), new BitsInfo("JMP_PIN", 28, 24, null, BitsType.RW, 0), new BitsInfo("OUT_EN_SEL", 23, 19, null, BitsType.RW, 0), new BitsInfo("INLINE_OUT_EN", 18, 18, null, BitsType.RW, 0), new BitsInfo("OUT_STICKY", 17, 17, null, BitsType.RW, 0), new BitsInfo("WRAP_TOP", 16, 12, null, BitsType.RW, 0x1f), new BitsInfo("WRAP_BOTTOM", 11, 7, null, BitsType.RW, 0), new BitsInfo(null, 6, 5, null, BitsType.RESERVED, null), new BitsInfo("STATUS_SEL", 4, 4, null, BitsType.RW, 0), new BitsInfo("STATUS_N", 3, 0, null, BitsType.RW, 0) }), SM0_SHIFTCTRL("Control behaviour of the input/output shift registers " + "for state machine *N*.", 0, new BitsInfo[] { new BitsInfo("FJOIN_RX", 31, 31, null, BitsType.RW, 0), new BitsInfo("FJOIN_TX", 30, 30, null, BitsType.RW, 0), new BitsInfo("PULL_THRESH", 29, 25, null, BitsType.RW, 0), new BitsInfo("PUSH_THRESH", 24, 20, null, BitsType.RW, 0), new BitsInfo("OUT_SHIFTDIR", 19, 19, null, BitsType.RW, 1), new BitsInfo("IN_SHIFTDIR", 18, 18, null, BitsType.RW, 1), new BitsInfo("AUTOPULL", 17, 17, null, BitsType.RW, 0), new BitsInfo("AUTOPUSH", 16, 16, null, BitsType.RW, 0), new BitsInfo(null, 15, 0, null, BitsType.RESERVED, null) }), SM0_ADDR("Current instruction address of state machine *N*.", 0, new BitsInfo[] { new BitsInfo(null, 31, 5, null, BitsType.RESERVED, null), new BitsInfo(null, 4, 0, null, BitsType.RO, 0) }), SM0_INSTR("Read to see current instruction on state machine *N*. Write " + "to execute instruction immediately on state machine *N*.", 0, new BitsInfo[] { new BitsInfo(null, 31, 16, null, BitsType.RESERVED, null), new BitsInfo(null, 15, 0, null, BitsType.RW, null) }), SM0_PINCTRL("State machine pin control for state machine *N*.", 0, new BitsInfo[] { new BitsInfo("SIDESET_COUNT", 31, 29, null, BitsType.RW, 0), new BitsInfo("SET_COUNT", 28, 26, null, BitsType.RW, 5), new BitsInfo("OUT_COUNT", 25, 20, null, BitsType.RW, 0), new BitsInfo("IN_BASE", 19, 15, null, BitsType.RW, 0), new BitsInfo("SIDESET_BASE", 14, 10, null, BitsType.RW, 0), new BitsInfo("SET_BASE", 9, 5, null, BitsType.RW, 0), new BitsInfo("OUT_BASE", 4, 0, null, BitsType.RW, 0) }), SM1_CLKDIV(Regs.SM0_CLKDIV, 1), SM1_EXECCTRL(Regs.SM0_EXECCTRL, 1), SM1_SHIFTCTRL(Regs.SM0_SHIFTCTRL, 1), SM1_ADDR(Regs.SM0_ADDR, 1), SM1_INSTR(Regs.SM0_INSTR, 1), SM1_PINCTRL(Regs.SM0_PINCTRL, 1), SM2_CLKDIV(Regs.SM0_CLKDIV, 2), SM2_EXECCTRL(Regs.SM0_EXECCTRL, 2), SM2_SHIFTCTRL(Regs.SM0_SHIFTCTRL, 2), SM2_ADDR(Regs.SM0_ADDR, 2), SM2_INSTR(Regs.SM0_INSTR, 2), SM2_PINCTRL(Regs.SM0_PINCTRL, 2), SM3_CLKDIV(Regs.SM0_CLKDIV, 3), SM3_EXECCTRL(Regs.SM0_EXECCTRL, 3), SM3_SHIFTCTRL(Regs.SM0_SHIFTCTRL, 3), SM3_ADDR(Regs.SM0_ADDR, 3), SM3_INSTR(Regs.SM0_INSTR, 3), SM3_PINCTRL(Regs.SM0_PINCTRL, 3), INTR("Raw Interrupts.", new BitsInfo[] { new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null), new BitsInfo("SM3", 11, 11, null, BitsType.RO, 0), new BitsInfo("SM2", 10, 10, null, BitsType.RO, 0), new BitsInfo("SM1", 9, 9, null, BitsType.RO, 0), new BitsInfo("SM0", 8, 8, null, BitsType.RO, 0), new BitsInfo("SM3_TXNFULL", 7, 7, null, BitsType.RO, 0), new BitsInfo("SM2_TXNFULL", 6, 6, null, BitsType.RO, 0), new BitsInfo("SM1_TXNFULL", 5, 5, null, BitsType.RO, 0), new BitsInfo("SM0_TXNFULL", 4, 4, null, BitsType.RO, 0), new BitsInfo("SM3_RXNEMPTY", 3, 3, null, BitsType.RO, 0), new BitsInfo("SM2_RXNEMPTY", 2, 2, null, BitsType.RO, 0), new BitsInfo("SM1_RXNEMPTY", 1, 1, null, BitsType.RO, 0), new BitsInfo("SM0_RXNEMPTY", 0, 0, null, BitsType.RO, 0) }), IRQ0_INTE("Interrupt enable for IRQ0.", new BitsInfo[] { new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null), new BitsInfo("SM3", 11, 11, null, BitsType.RW, 0), new BitsInfo("SM2", 10, 10, null, BitsType.RW, 0), new BitsInfo("SM1", 9, 9, null, BitsType.RW, 0), new BitsInfo("SM0", 8, 8, null, BitsType.RW, 0), new BitsInfo("SM3_TXNFULL", 7, 7, null, BitsType.RW, 0), new BitsInfo("SM2_TXNFULL", 6, 6, null, BitsType.RW, 0), new BitsInfo("SM1_TXNFULL", 5, 5, null, BitsType.RW, 0), new BitsInfo("SM0_TXNFULL", 4, 4, null, BitsType.RW, 0), new BitsInfo("SM3_RXNEMPTY", 3, 3, null, BitsType.RW, 0), new BitsInfo("SM2_RXNEMPTY", 2, 2, null, BitsType.RW, 0), new BitsInfo("SM1_RXNEMPTY", 1, 1, null, BitsType.RW, 0), new BitsInfo("SM0_RXNEMPTY", 0, 0, null, BitsType.RW, 0) }), IRQ0_INTF("Interrupt force for IRQ0.", new BitsInfo[] { new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null), new BitsInfo("SM3", 11, 11, null, BitsType.RW, 0), new BitsInfo("SM2", 10, 10, null, BitsType.RW, 0), new BitsInfo("SM1", 9, 9, null, BitsType.RW, 0), new BitsInfo("SM0", 8, 8, null, BitsType.RW, 0), new BitsInfo("SM3_TXNFULL", 7, 7, null, BitsType.RW, 0), new BitsInfo("SM2_TXNFULL", 6, 6, null, BitsType.RW, 0), new BitsInfo("SM1_TXNFULL", 5, 5, null, BitsType.RW, 0), new BitsInfo("SM0_TXNFULL", 4, 4, null, BitsType.RW, 0), new BitsInfo("SM3_RXNEMPTY", 3, 3, null, BitsType.RW, 0), new BitsInfo("SM2_RXNEMPTY", 2, 2, null, BitsType.RW, 0), new BitsInfo("SM1_RXNEMPTY", 1, 1, null, BitsType.RW, 0), new BitsInfo("SM0_RXNEMPTY", 0, 0, null, BitsType.RW, 0) }), IRQ0_INTS("Interrupt status after masking & forcing for IRQ0.", new BitsInfo[] { new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null), new BitsInfo("SM3", 11, 11, null, BitsType.RO, 0), new BitsInfo("SM2", 10, 10, null, BitsType.RO, 0), new BitsInfo("SM1", 9, 9, null, BitsType.RO, 0), new BitsInfo("SM0", 8, 8, null, BitsType.RO, 0), new BitsInfo("SM3_TXNFULL", 7, 7, null, BitsType.RO, 0), new BitsInfo("SM2_TXNFULL", 6, 6, null, BitsType.RO, 0), new BitsInfo("SM1_TXNFULL", 5, 5, null, BitsType.RO, 0), new BitsInfo("SM0_TXNFULL", 4, 4, null, BitsType.RO, 0), new BitsInfo("SM3_RXNEMPTY", 3, 3, null, BitsType.RO, 0), new BitsInfo("SM2_RXNEMPTY", 2, 2, null, BitsType.RO, 0), new BitsInfo("SM1_RXNEMPTY", 1, 1, null, BitsType.RO, 0), new BitsInfo("SM0_RXNEMPTY", 0, 0, null, BitsType.RO, 0) }), IRQ1_INTE("Interrupt enable for IRQ1.", new BitsInfo[] { new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null), new BitsInfo("SM3", 11, 11, null, BitsType.RW, 0), new BitsInfo("SM2", 10, 10, null, BitsType.RW, 0), new BitsInfo("SM1", 9, 9, null, BitsType.RW, 0), new BitsInfo("SM0", 8, 8, null, BitsType.RW, 0), new BitsInfo("SM3_TXNFULL", 7, 7, null, BitsType.RW, 0), new BitsInfo("SM2_TXNFULL", 6, 6, null, BitsType.RW, 0), new BitsInfo("SM1_TXNFULL", 5, 5, null, BitsType.RW, 0), new BitsInfo("SM0_TXNFULL", 4, 4, null, BitsType.RW, 0), new BitsInfo("SM3_RXNEMPTY", 3, 3, null, BitsType.RW, 0), new BitsInfo("SM2_RXNEMPTY", 2, 2, null, BitsType.RW, 0), new BitsInfo("SM1_RXNEMPTY", 1, 1, null, BitsType.RW, 0), new BitsInfo("SM0_RXNEMPTY", 0, 0, null, BitsType.RW, 0) }), IRQ1_INTF("Interrupt force for IRQ1.", new BitsInfo[] { new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null), new BitsInfo("SM3", 11, 11, null, BitsType.RW, 0), new BitsInfo("SM2", 10, 10, null, BitsType.RW, 0), new BitsInfo("SM1", 9, 9, null, BitsType.RW, 0), new BitsInfo("SM0", 8, 8, null, BitsType.RW, 0), new BitsInfo("SM3_TXNFULL", 7, 7, null, BitsType.RW, 0), new BitsInfo("SM2_TXNFULL", 6, 6, null, BitsType.RW, 0), new BitsInfo("SM1_TXNFULL", 5, 5, null, BitsType.RW, 0), new BitsInfo("SM0_TXNFULL", 4, 4, null, BitsType.RW, 0), new BitsInfo("SM3_RXNEMPTY", 3, 3, null, BitsType.RW, 0), new BitsInfo("SM2_RXNEMPTY", 2, 2, null, BitsType.RW, 0), new BitsInfo("SM1_RXNEMPTY", 1, 1, null, BitsType.RW, 0), new BitsInfo("SM0_RXNEMPTY", 0, 0, null, BitsType.RW, 0) }), IRQ1_INTS("Interrupt status after masking & forcing for IRQ1.", new BitsInfo[] { new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null), new BitsInfo("SM3", 11, 11, null, BitsType.RO, 0), new BitsInfo("SM2", 10, 10, null, BitsType.RO, 0), new BitsInfo("SM1", 9, 9, null, BitsType.RO, 0), new BitsInfo("SM0", 8, 8, null, BitsType.RO, 0), new BitsInfo("SM3_TXNFULL", 7, 7, null, BitsType.RO, 0), new BitsInfo("SM2_TXNFULL", 6, 6, null, BitsType.RO, 0), new BitsInfo("SM1_TXNFULL", 5, 5, null, BitsType.RO, 0), new BitsInfo("SM0_TXNFULL", 4, 4, null, BitsType.RO, 0), new BitsInfo("SM3_RXNEMPTY", 3, 3, null, BitsType.RO, 0), new BitsInfo("SM2_RXNEMPTY", 2, 2, null, BitsType.RO, 0), new BitsInfo("SM1_RXNEMPTY", 1, 1, null, BitsType.RO, 0), new BitsInfo("SM0_RXNEMPTY", 0, 0, null, BitsType.RO, 0) }); public static String getRegisterSetLabel() { return "PIO Registers"; } public static String getRegisterSetDescription() { return "The PIO registers as described in Sect. 3.7 of the RP2040%n" + "datasheet.%n" + "Base address for the two emulator PIO register sets (one %n" + "register set for each of the two PIOs) is%n" + String.format("0x%08x and 0x%08x for PIO0 and PIO1, respectively.%n", PIO0_BASE, PIO1_BASE); } private final RegisterDetails registerDetails; private Regs() { throw new UnsupportedOperationException("unsupported empty constructor"); } private Regs(final Regs ref) { this(ref.registerDetails); } private Regs(final Regs ref, final int smNum) { this(ref.registerDetails.createCopyForDifferentSm(smNum)); } private Regs(final String info, final BitsInfo[] bitsInfos) { this(new RegisterDetails(info, bitsInfos)); } private Regs(final String info, final List bitsInfos) { this(new RegisterDetails(info, bitsInfos)); } private Regs(final String info, final int smNum, final BitsInfo[] bitsInfos) { this(new RegisterDetails(info, smNum, bitsInfos)); } private Regs(final String info, final int smNum, final List bitsInfos) { this(new RegisterDetails(info, smNum, bitsInfos)); } private Regs(final RegisterDetails registerDetails) { this.registerDetails = registerDetails; } @Override public String getInfo() { return registerDetails.getInfo(); } @Override public RegisterDetails getRegisterDetails() { return registerDetails; } } protected static final Regs[] REGS = Regs.values(); @Override @SuppressWarnings("unchecked") protected > T[] getRegs() { return (T[])REGS; } protected static final int SM_SIZE = Regs.SM1_CLKDIV.ordinal() - Regs.SM0_CLKDIV.ordinal(); public static int getAddress(final int pioNum, final PIORegisters.Regs register) { Constants.checkPioNum(pioNum, "PIO index number"); if (register == null) { throw new NullPointerException("register"); } return Constants.getPIOBaseAddress(pioNum) + 0x4 * register.ordinal(); } public static int getSMAddress(final int pioNum, final int smNum, final PIORegisters.Regs register) { Constants.checkPioNum(pioNum, "PIO index number"); Constants.checkSmNum(smNum); if (register == null) { throw new NullPointerException("register"); } switch (register) { case SM0_CLKDIV: case SM0_EXECCTRL: case SM0_SHIFTCTRL: case SM0_ADDR: case SM0_INSTR: case SM0_PINCTRL: break; // ok default: throw new IllegalArgumentException("register not one of SM0_*: " + register); } return Constants.getPIOBaseAddress(pioNum) + 0x4 * (register.ordinal() + smNum * SM_SIZE); } public static int getMemoryAddress(final int pioNum, final int memoryAddress) { Constants.checkPioNum(pioNum, "PIO index number"); Constants.checkSmMemAddr(memoryAddress, "memory address"); return Constants.getPIOBaseAddress(pioNum) + 0x4 * (Regs.INSTR_MEM0.ordinal() + memoryAddress); } public static int getTXFAddress(final int pioNum, final int smNum) { Constants.checkPioNum(pioNum, "PIO index number"); Constants.checkSmNum(smNum); return Constants.getPIOBaseAddress(pioNum) + 0x4 * (Regs.TXF0.ordinal() + smNum); } public static int getRXFAddress(final int pioNum, final int smNum) { Constants.checkPioNum(pioNum, "PIO index number"); Constants.checkSmNum(smNum); return Constants.getPIOBaseAddress(pioNum) + 0x4 * (Regs.RXF0.ordinal() + smNum); } public PIORegisters(final String id, final int baseAddress) { super(id, baseAddress); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/PIORegistersImpl.java ================================================ /* * @(#)PIORegistersImpl.java 1.00 21/02/25 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * Facade to the internal subsystems of a PIO. The layout of * registers follows the list of registers in Sect. 3.7 of the RP2040 * datasheet. The facade is in particular intended for use by the * SDK. */ public class PIORegistersImpl extends PIORegisters { private final PIO pio; public PIORegistersImpl(final PIO pio) { super("PIO" + pio.getIndex(), Constants.getPIOBaseAddress(pio.getIndex())); this.pio = pio; } public PIO getPIO() { return pio; } public int getPIOIndex() { return pio.getIndex(); } public int getAddress(final PIORegisters.Regs register) { return getAddress(getPIOIndex(), register); } public int getSMAddress(final PIORegisters.Regs register, final int smNum) { return getSMAddress(getPIOIndex(), smNum, register); } public int getMemoryAddress(final int memoryAddress) { return getMemoryAddress(getPIOIndex(), memoryAddress); } public int getTXFAddress(final int smNum) { return getTXFAddress(getPIOIndex(), smNum); } public int getRXFAddress(final int smNum) { return getRXFAddress(getPIOIndex(), smNum); } /* * TODO: In all of the following methods, use constants declared in * class Constants for bit shifting & masking. */ private void writeFDebug(final int value, final int mask) { for (int smNum = 0; smNum < SM_COUNT; smNum++) { final SM sm = pio.getSM(smNum); final FIFO fifo = sm.getFIFO(); if (((value >>> (24 + smNum)) & 0x1) != 0x0 && ((mask >>> (24 + smNum)) & 0x1) != 0x0) { fifo.clearTXStall(); } if (((value >>> (16 + smNum)) & 0x1) != 0x0 && ((mask >>> (16 + smNum)) & 0x1) != 0x0) { fifo.clearTXOver(); } if (((value >>> (8 + smNum)) & 0x1) != 0x0 && ((mask >>> (8 + smNum)) & 0x1) != 0x0) { fifo.clearRXUnder(); } if (((value >>> smNum) & 0x1) != 0x0 && ((mask >>> smNum) & 0x1) != 0x0) { fifo.clearRXStall(); } } } @Override public void writeRegister(final int regNum, final int value, final int mask, final boolean xor) { checkRegNum(regNum); final Regs register = REGS[regNum]; switch (register) { case CTRL: pio.setCtrl(value, mask); break; case FSTAT: break; // read-only address case FDEBUG: writeFDebug(value, mask); break; case FLEVEL: break; // read-only address case TXF0: case TXF1: case TXF2: case TXF3: pio.getSM(regNum - Regs.TXF0.ordinal()).put(value & mask); break; case RXF0: case RXF1: case RXF2: case RXF3: break; // read-only address case IRQ: pio.getIRQ().writeRegIRQ(value & mask); break; case IRQ_FORCE: pio.getIRQ().writeRegIRQ_FORCE(value & mask); break; case INPUT_SYNC_BYPASS: pio.getPIOGPIO().getGPIO().setInputSyncByPass(value, mask, xor); break; case DBG_PADOUT: break; // read-only address case DBG_PADOE: break; // read-only address case DBG_CFGINFO: break; // read-only address case INSTR_MEM0: case INSTR_MEM1: case INSTR_MEM2: case INSTR_MEM3: case INSTR_MEM4: case INSTR_MEM5: case INSTR_MEM6: case INSTR_MEM7: case INSTR_MEM8: case INSTR_MEM9: case INSTR_MEM10: case INSTR_MEM11: case INSTR_MEM12: case INSTR_MEM13: case INSTR_MEM14: case INSTR_MEM15: case INSTR_MEM16: case INSTR_MEM17: case INSTR_MEM18: case INSTR_MEM19: case INSTR_MEM20: case INSTR_MEM21: case INSTR_MEM22: case INSTR_MEM23: case INSTR_MEM24: case INSTR_MEM25: case INSTR_MEM26: case INSTR_MEM27: case INSTR_MEM28: case INSTR_MEM29: case INSTR_MEM30: case INSTR_MEM31: pio.getMemory().set(regNum - Regs.INSTR_MEM0.ordinal(), value, mask, xor); break; case SM0_CLKDIV: case SM1_CLKDIV: case SM2_CLKDIV: case SM3_CLKDIV: pio.getSM((regNum - Regs.SM0_CLKDIV.ordinal()) / SM_SIZE). setCLKDIV(value, mask, xor); break; case SM0_EXECCTRL: case SM1_EXECCTRL: case SM2_EXECCTRL: case SM3_EXECCTRL: pio.getSM((regNum - Regs.SM0_EXECCTRL.ordinal()) / SM_SIZE). setEXECCTRL(value, mask, xor); break; case SM0_SHIFTCTRL: case SM1_SHIFTCTRL: case SM2_SHIFTCTRL: case SM3_SHIFTCTRL: pio.getSM((regNum - Regs.SM0_SHIFTCTRL.ordinal()) / SM_SIZE). setSHIFTCTRL(value, mask, xor); break; case SM0_ADDR: case SM1_ADDR: case SM2_ADDR: case SM3_ADDR: break; // read-only address case SM0_INSTR: case SM1_INSTR: case SM2_INSTR: case SM3_INSTR: pio.getSM((regNum - Regs.SM0_INSTR.ordinal()) / SM_SIZE). forceInstruction(value & mask); break; case SM0_PINCTRL: case SM1_PINCTRL: case SM2_PINCTRL: case SM3_PINCTRL: pio.getSM((regNum - Regs.SM0_PINCTRL.ordinal()) / SM_SIZE). setPINCTRL(value, mask, xor); break; case INTR: break; // read-only address case IRQ0_INTE: pio.getIRQ().setIRQ0_INTE(value, mask, xor); break; case IRQ1_INTE: pio.getIRQ().setIRQ1_INTE(value, mask, xor); break; case IRQ0_INTF: pio.getIRQ().setIRQ0_INTF(value, mask, xor); break; case IRQ1_INTF: pio.getIRQ().setIRQ1_INTF(value, mask, xor); break; case IRQ0_INTS: case IRQ1_INTS: break; // read-only address default: throw new InternalError("unexpected case fall-through"); } } private int readFStat() { return ((pio.getSM(3).isTXFIFOEmpty() ? 0x1 : 0x0) << 27) | ((pio.getSM(2).isTXFIFOEmpty() ? 0x1 : 0x0) << 26) | ((pio.getSM(1).isTXFIFOEmpty() ? 0x1 : 0x0) << 25) | ((pio.getSM(0).isTXFIFOEmpty() ? 0x1 : 0x0) << 24) | ((pio.getSM(3).isTXFIFOFull() ? 0x1 : 0x0) << 19) | ((pio.getSM(2).isTXFIFOFull() ? 0x1 : 0x0) << 18) | ((pio.getSM(1).isTXFIFOFull() ? 0x1 : 0x0) << 17) | ((pio.getSM(0).isTXFIFOFull() ? 0x1 : 0x0) << 16) | ((pio.getSM(3).isRXFIFOEmpty() ? 0x1 : 0x0) << 11) | ((pio.getSM(2).isRXFIFOEmpty() ? 0x1 : 0x0) << 10) | ((pio.getSM(1).isRXFIFOEmpty() ? 0x1 : 0x0) << 9) | ((pio.getSM(0).isRXFIFOEmpty() ? 0x1 : 0x0) << 8) | ((pio.getSM(3).isRXFIFOFull() ? 0x1 : 0x0) << 3) | ((pio.getSM(2).isRXFIFOFull() ? 0x1 : 0x0) << 2) | ((pio.getSM(1).isRXFIFOFull() ? 0x1 : 0x0) << 1) | ((pio.getSM(0).isRXFIFOFull() ? 0x1 : 0x0) << 0); } private int readFDebug() { int value = 0; for (int smNum = 0; smNum < SM_COUNT; smNum++) { final SM sm = pio.getSM(smNum); final FIFO fifo = sm.getFIFO(); if (fifo.isTXStall()) { value |= 0x1 << (24 + smNum); } if (fifo.isTXOver()) { value |= 0x1 << (16 + smNum); } if (fifo.isRXUnder()) { value |= 0x1 << (8 + smNum); } if (fifo.isRXStall()) { value |= 0x1 << smNum; } } return value; } private int readFLevel() { return (pio.getSM(3).getRXFIFOLevel() << 28) | (pio.getSM(3).getTXFIFOLevel() << 24) | (pio.getSM(2).getRXFIFOLevel() << 20) | (pio.getSM(2).getTXFIFOLevel() << 16) | (pio.getSM(1).getRXFIFOLevel() << 12) | (pio.getSM(1).getTXFIFOLevel() << 8) | (pio.getSM(0).getRXFIFOLevel() << 4) | pio.getSM(0).getTXFIFOLevel(); } private int getCfgInfo() { return MEMORY_SIZE << 16 | SM_COUNT << 8 | FIFO_DEPTH; } @Override public synchronized int readRegister(final int regNum) { checkRegNum(regNum); final Regs register = REGS[regNum]; switch (register) { case CTRL: return pio.getCtrl(); case FSTAT: return readFStat(); case FDEBUG: return readFDebug(); case FLEVEL: return readFLevel(); case TXF0: case TXF1: case TXF2: case TXF3: return 0; // write-only address case RXF0: case RXF1: case RXF2: case RXF3: return pio.getSM(regNum - Regs.RXF0.ordinal()).get(); case IRQ: return 0; // write-only address case IRQ_FORCE: return 0; // write-only address case INPUT_SYNC_BYPASS: return pio.getPIOGPIO().getGPIO().getInputSyncByPass(); case DBG_PADOUT: return pio.getPIOGPIO().getPins(0, 32); case DBG_PADOE: return pio.getPIOGPIO().getPinDirs(0, 32); case DBG_CFGINFO: return getCfgInfo(); case INSTR_MEM0: case INSTR_MEM1: case INSTR_MEM2: case INSTR_MEM3: case INSTR_MEM4: case INSTR_MEM5: case INSTR_MEM6: case INSTR_MEM7: case INSTR_MEM8: case INSTR_MEM9: case INSTR_MEM10: case INSTR_MEM11: case INSTR_MEM12: case INSTR_MEM13: case INSTR_MEM14: case INSTR_MEM15: case INSTR_MEM16: case INSTR_MEM17: case INSTR_MEM18: case INSTR_MEM19: case INSTR_MEM20: case INSTR_MEM21: case INSTR_MEM22: case INSTR_MEM23: case INSTR_MEM24: case INSTR_MEM25: case INSTR_MEM26: case INSTR_MEM27: case INSTR_MEM28: case INSTR_MEM29: case INSTR_MEM30: case INSTR_MEM31: return 0; // write-only address case SM0_CLKDIV: case SM1_CLKDIV: case SM2_CLKDIV: case SM3_CLKDIV: return pio.getSM((regNum - Regs.SM0_CLKDIV.ordinal()) / SM_SIZE).getCLKDIV(); case SM0_EXECCTRL: case SM1_EXECCTRL: case SM2_EXECCTRL: case SM3_EXECCTRL: return pio.getSM((regNum - Regs.SM0_EXECCTRL.ordinal()) / SM_SIZE). getEXECCTRL(); case SM0_SHIFTCTRL: case SM1_SHIFTCTRL: case SM2_SHIFTCTRL: case SM3_SHIFTCTRL: return pio.getSM((regNum - Regs.SM0_SHIFTCTRL.ordinal()) / SM_SIZE). getSHIFTCTRL(); case SM0_ADDR: case SM1_ADDR: case SM2_ADDR: case SM3_ADDR: return pio.getSM((regNum - Regs.SM0_ADDR.ordinal()) / SM_SIZE).getPC(); case SM0_INSTR: case SM1_INSTR: case SM2_INSTR: case SM3_INSTR: return pio.getSM((regNum - Regs.SM0_INSTR.ordinal()) / SM_SIZE). getOpCode(); case SM0_PINCTRL: case SM1_PINCTRL: case SM2_PINCTRL: case SM3_PINCTRL: return pio.getSM((regNum - Regs.SM0_PINCTRL.ordinal()) / SM_SIZE).getPINCTRL(); case INTR: return pio.getIRQ().readINTR(); case IRQ0_INTE: return pio.getIRQ().getIRQ0_INTE(); case IRQ1_INTE: return pio.getIRQ().getIRQ1_INTE(); case IRQ0_INTF: return pio.getIRQ().getIRQ0_INTF(); case IRQ1_INTF: return pio.getIRQ().getIRQ1_INTF(); case IRQ0_INTS: return pio.getIRQ().readIRQ0_INTS(); case IRQ1_INTS: return pio.getIRQ().readIRQ1_INTS(); default: throw new InternalError("unexpected case fall-through"); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/PLL.java ================================================ /* * @(#)PLL.java 1.00 21/02/05 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; /** * Phase Locked Loop (PLL) */ public class PLL implements Clock.TransitionListener { private final PrintStream console; private int regCLKDIV_INT; // bits 16…31 of SMx_CLKDIV private int regCLKDIV_FRAC; // bits 8…15 of SMx_CLKDIV private int countIntegerBits; private int countFractionalBits; private boolean clockEnable; private boolean nextClockEnable; private PLL() { throw new UnsupportedOperationException("unsupported empty constructor"); } public PLL(final PrintStream console) { if (console == null) { throw new NullPointerException("console"); } this.console = console; reset(); } public void reset() { regCLKDIV_INT = 0x0001; regCLKDIV_FRAC = 0x00; countIntegerBits = 0x1; countFractionalBits = 0x0; clockEnable = false; nextClockEnable = false; } public int getDivIntegerBits() { return regCLKDIV_INT; } public void setDivIntegerBits(final int divIntegerBits) { if (divIntegerBits < 0) { throw new IllegalArgumentException("div integer bits < 0: " + divIntegerBits); } if (divIntegerBits > 0xffff) { throw new IllegalArgumentException("div integer bits > 65535: " + divIntegerBits); } if (divIntegerBits == 0) { if (regCLKDIV_FRAC != 0) { // RP2040 datasheet, Table 391: "If INT is 0, FRAC must also // be 0." final String message = String.format("warning: ignoring request for setting CLK int bits " + "to 0, since CLK frac bits are non-zero"); console.printf("%s%n", message); return; } // TODO: Clarify: Should we ignore the change (as implemented), // or should we silently set also FRAC to 0? } this.regCLKDIV_INT = divIntegerBits; } public int getDivFractionalBits() { return regCLKDIV_FRAC; } public void setDivFractionalBits(final int divFractionalBits) { if (divFractionalBits < 0) { throw new IllegalArgumentException("div fractional bits < 0: " + divFractionalBits); } if (divFractionalBits > 0xff) { throw new IllegalArgumentException("div fractional bits > 255: " + divFractionalBits); } if (regCLKDIV_INT == 0) { if (divFractionalBits != 0) { // RP2040 datasheet, Table 391: "If INT is 0, FRAC must also // be 0." final String message = String.format("warning: ignoring request for setting CLK frac bits " + "to non-zero value, since CLK int bits are zero"); console.printf("%s%n", message); return; } } this.regCLKDIV_FRAC = divFractionalBits; } private void setCLKDIV(final int divIntegerBits, final int divFractionalBits) { if ((divIntegerBits == 0) && (divFractionalBits == 0)) { // Special case: RP2040 datasheet, Table 391: "If INT is 0, FRAC // must also be 0." regCLKDIV_INT = 0; regCLKDIV_FRAC = 0; } else { setDivIntegerBits(divIntegerBits); setDivFractionalBits(divFractionalBits); } } public void setCLKDIV(final int clkdiv) { setCLKDIV(clkdiv >>> 16, (clkdiv >>> 8) & 0xff); } public int getCLKDIV() { return (getDivIntegerBits() << 16) | (getDivFractionalBits() << 8); } public boolean getClockEnable() { return clockEnable; } public boolean getNextClockEnable() { return nextClockEnable; } private void prepareClockEnable() { /* * TODO: Clarify: Sect. 3.5.5. "Clock Dividers", Fig. 46: "clock * divider … emits an enable pulse when it reaches 1" * * -- Really "1", not "0"? */ if (countIntegerBits <= 1) { countIntegerBits += regCLKDIV_INT; countFractionalBits += regCLKDIV_FRAC; if (countFractionalBits >= 0x100) { countFractionalBits -= 0x100; countIntegerBits++; } nextClockEnable = true; } else { nextClockEnable = false; } countIntegerBits--; } @Override public void risingEdge(final long wallClock) { clockEnable = nextClockEnable; } @Override public void fallingEdge(final long wallClock) { prepareClockEnable(); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/ParseException.java ================================================ /* * @(#)ParseException.java 1.00 21/02/16 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.IOException; public class ParseException extends IOException { private static final long serialVersionUID = -3298538004378904681L; private ParseException() { throw new UnsupportedOperationException("unsupported empty constructor"); } public ParseException(final String message) { super(message); } public ParseException(final String message, final Throwable cause) { super(message, cause); } public static ParseException create(final String message, final String resourcePath, final int lineIndex, final Throwable cause) { final String fullMessage = String.format("parse exception in %s, line %d: %s", resourcePath, lineIndex, message); return cause != null ? new ParseException(fullMessage, cause) : new ParseException(fullMessage); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/PicoEmuRegisters.java ================================================ /* * @(#)PicoEmuRegisters.java 1.00 21/03/12 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.soundpaint.rp2040pio.doctool.RegistersDocs; /** * Facade to additonal emulator properties of the internal subsystems * of a PIO that are not available via the PIORegisters facade. This * facade is in particular intended for use by software that wants to * exploit the emulator's debug facilities. */ public abstract class PicoEmuRegisters extends RegisterSet { public enum Regs implements RegistersDocs { PWR_UP("Writing the value 0xa55a5aa5 to this address will fully reset%n" + "the emulator. Writing any other value will have no effect.", new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.WF, 0) }), MASTERCLK_FREQ("Unsigned integer value that represents the%n" + "target frequency of the emulation in 1/8Hz.%n" + "That is, a value of 1 represents a frequency of%n" + "0.125 Hz, and the maximum value of 2^32 - 1 =%n" + "4294967295 represents a frequency of%n" + "536.870911875MHz.%n" + "%n" + "A value of 0 indicates that the emulation should%n" + "execute as fast as possible.%n" + "%n" + "Note that there is no guarantee at all to run at%n" + "the specified frequency. Instead, the value is%n" + "just the frequency that the emulation tries to%n" + "catch up with as close as possible. The reset%n" + "value corresponds to a target frequency of 125MHz.", new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RW, DEFAULT_FREQUENCY) }), MASTERCLK_MODE("Selects the clock mode.", new BitsInfo[] { new BitsInfo(null, 31, 1, null, BitsType.RESERVED, null), new BitsInfo(null, 0, 0, "Bit 0 = 0: Target frequency mode.%n" + "Bit 0 = 1: Single step mode.", BitsType.RW, 0) }), MASTERCLK_TRIGGER_PHASE0("When master clock is in single step%n" + "mode, writing any value to this address%n" + "will trigger the emulator to execute phase%n" + "0 of the next clock cycle. In phase%n" + "0, the emulator fetches and decodes the%n" + "next instruction. When already in phase%n" + "0, writing once more to this address will%n" + "have no effect. When master clock is in%n" + "target frequency mode, writing to this%n" + "address will have no effect. Upon reset,%n" + "the system is in phase 1.%n" + "Reading from this register will return value%n" + "0x1 if and only if the emulator is in phase%n" + "0 *and* phase 0 is settled (i.e. the emulator%n" + "has completed all operations to be performed%n" + "during this phase), and 0x0 otherwise.", new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.WF, null) }), MASTERCLK_TRIGGER_PHASE1("When master clock is in single step%n" + "mode, writing any value to this address%n" + "will trigger the emulator to execute phase%n" + "1 of the current clock cycle. In phase%n" + "1, the emulator will execute the%n" + "instruction previously decoded in%n" + "phase 0. When already in phase%n" + "1, writing once more to this address will%n" + "have no effect. When master clock is in%n" + "target frequency mode, writing to this%n" + "address will have no effect. Upon reset,%n" + "the system is in phase 1.%n" + "Reading from this register will return value%n" + "0x1 if and only if the emulator is in phase%n" + "1 *and* phase 1 is settled (i.e. the emulator%n" + "has completed all operations to be performed%n" + "during this phase), and 0x0 otherwise.", new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.WF, null) }), WALLCLOCK_LSB("LSB value (lower 32 bits) of wall clock. The%n" + "wall clock is a 64 bit counter that is initialized%n" + "to 0 and incremented whenever the master clock has%n" + "completed a cycle.", new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RO, null) }), WALLCLOCK_MSB("MSB value (upper 32 bits) of wall clock. The%n" + "wall clock is a 64 bit counter that is initialized%n" + "to 0 and incremented whenever the master clock has%n" + "completed a cycle.", new BitsInfo[] { new BitsInfo(null, 31, 0, null, BitsType.RO, null) }), GPIO_PADIN("Each bit of this value represents the corresponding%n" + "pad input state of the 32 GPIO pins, virtually provided%n" + "from some external source.", IntStream.rangeClosed(0, 31).boxed() .map(n -> new BitsInfo("INFROMPAD_GPIO" + (31 - n), 31 - n, 31 - n, "signal value 0x0 or 0x1, as%n" + "provided by some external source.", BitsType.RW, 0)) .collect(Collectors.toList())); public static String getRegisterSetLabel() { return "Emulator Global Registers"; } public static String getRegisterSetDescription() { return "The PIO emulator provides global registers, hereafter%n" + "called *Emulator Global Registers*, that are used to inspect%n" + "and control the emulator as a whole (rather than just%n" + "referring to a specifc PIO) and that are accessible through%n" + "this registers facade and provided in addition to the%n" + "registers of the original RP2040 hardware.%n" + "Base address for the emulator global register set is%n" + String.format("0x%08x.%n", EMULATOR_BASE); } private final RegisterDetails registerDetails; private Regs() { throw new UnsupportedOperationException("unsupported empty constructor"); } private Regs(final Regs ref) { this(ref.registerDetails); } private Regs(final Regs ref, final int smNum) { this(ref.registerDetails.createCopyForDifferentSm(smNum)); } private Regs(final String info, final BitsInfo[] bitsInfos) { this(new RegisterDetails(info, bitsInfos)); } private Regs(final String info, final List bitsInfos) { this(new RegisterDetails(info, bitsInfos)); } private Regs(final String info, final int smNum, final BitsInfo[] bitsInfos) { this(new RegisterDetails(info, smNum, bitsInfos)); } private Regs(final String info, final int smNum, final List bitsInfos) { this(new RegisterDetails(info, smNum, bitsInfos)); } private Regs(final RegisterDetails registerDetails) { this.registerDetails = registerDetails; } @Override public String getInfo() { return registerDetails.getInfo(); } @Override public RegisterDetails getRegisterDetails() { return registerDetails; } } protected static final Regs[] REGS = Regs.values(); @Override @SuppressWarnings("unchecked") protected > T[] getRegs() { return (T[])REGS; } public static int getAddress(final PicoEmuRegisters.Regs register) { if (register == null) { throw new NullPointerException("register"); } return EMULATOR_BASE + 0x4 * register.ordinal(); } public PicoEmuRegisters() { super("PicoEmu", EMULATOR_BASE); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/PicoEmuRegistersImpl.java ================================================ /* * @(#)PicoEmuRegistersImpl.java 1.00 21/03/12 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import org.soundpaint.rp2040pio.Clock; /** * Facade to additonal emulator properties of the internal subsystems * of a PIO that are not available via the PIORegisters facade. This * facade is in particular intended for use by software that wants to * exploit the emulator's debug facilities. */ public class PicoEmuRegistersImpl extends PicoEmuRegisters { private final Emulator emulator; public PicoEmuRegistersImpl(final Emulator emulator) { this.emulator = emulator; } public Emulator getEmulator() { return emulator; } @Override public void writeRegister(final int regNum, final int value, final int mask, final boolean xor) { checkRegNum(regNum); final Regs register = REGS[regNum]; switch (register) { case PWR_UP: if (value == PICO_PWR_UP_VALUE) emulator.reset(); break; case MASTERCLK_FREQ: emulator.getMasterClock().setMASTERCLK_FREQ(value); break; case MASTERCLK_MODE: emulator.getMasterClock().setMASTERCLK_MODE(value); break; case MASTERCLK_TRIGGER_PHASE0: emulator.getMasterClock().triggerPhase0(); break; case MASTERCLK_TRIGGER_PHASE1: emulator.getMasterClock().triggerPhase1(); break; case WALLCLOCK_LSB: case WALLCLOCK_MSB: break; // read-only address case GPIO_PADIN: emulator.getGPIO().setGPIO_PADIN(value, mask, xor); break; default: throw new InternalError("unexpected case fall-through"); } } @Override public synchronized int readRegister(final int regNum) { checkRegNum(regNum); final Regs register = REGS[regNum]; switch (register) { case PWR_UP: return 0; // write-only address case MASTERCLK_FREQ: return emulator.getMasterClock().getMASTERCLK_FREQ(); case MASTERCLK_MODE: return emulator.getMasterClock().getMASTERCLK_MODE(); case MASTERCLK_TRIGGER_PHASE0: return emulator.getMasterClock().getPhase() == Clock.Phase.PHASE_0_STABLE ? 0x1 : 0x0; case MASTERCLK_TRIGGER_PHASE1: return emulator.getMasterClock().getPhase() == Clock.Phase.PHASE_1_STABLE ? 0x1 : 0x0; case WALLCLOCK_LSB: return (int)emulator.getMasterClock().getWallClock(); case WALLCLOCK_MSB: return (int)(emulator.getMasterClock().getWallClock() >>> 32); case GPIO_PADIN: return emulator.getGPIO().getGPIO_PADIN(); default: throw new InternalError("unexpected case fall-through"); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/PinState.java ================================================ /* * @(#)PinState.java 1.00 21/04/06 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; /** * Representation of a single GPIO pin's state. */ public enum PinState { IN_LOW(Direction.IN, Bit.LOW), IN_HIGH(Direction.IN, Bit.HIGH), OUT_LOW(Direction.OUT, Bit.LOW), OUT_HIGH(Direction.OUT, Bit.HIGH); private final Direction direction; private final Bit level; private PinState(final Direction direction, final Bit level) { if (direction == null) { throw new NullPointerException("direction"); } if (level == null) { throw new NullPointerException("level"); } this.direction = direction; this.level = level; } public Direction getDirection() { return direction; } public Bit getLevel() { return level; } public static PinState fromValues(final Direction direction, final Bit level) { if (direction == null) { throw new NullPointerException("direction"); } if (level == null) { throw new NullPointerException("level"); } switch (direction) { case IN: switch (level) { case LOW: return IN_LOW; case HIGH: return IN_HIGH; default: throw new InternalError("unexpected case fall-through"); } case OUT: switch (level) { case LOW: return OUT_LOW; case HIGH: return OUT_HIGH; default: throw new InternalError("unexpected case fall-through"); } default: throw new InternalError("unexpected case fall-through"); } } @Override public String toString() { return String.format("PinState[level=%s, direction=%s]", level, direction); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/RegisterSet.java ================================================ /* * @(#)RegisterSet.java 1.00 21/03/05 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.IOException; import java.util.Objects; public abstract class RegisterSet implements Constants { private final String id; private final int baseAddress; private final short size; private RegisterSet() { throw new UnsupportedOperationException("unsupported empty constructor"); } /** * @param baseAddress The base address of the set of registers. * @param size Number of words provided by this registers interface. * The maximum allowed address computes as <code>baseAddress + * (size - 1) * 0x4</code>. */ protected RegisterSet(final String id, final int baseAddress) { Objects.requireNonNull(id); this.id = id; if ((baseAddress & 0x3fff) != 0x0) { throw new IllegalArgumentException("base address not conforming to " + "model of register access methods: " + String.format("0x%08x", baseAddress)); } this.baseAddress = baseAddress; size = (short)getRegs().length; if (size * 0x4 > 0x1000) { throw new IllegalArgumentException(String.format("size * 0x4 > 0x1000: " + "0x%08x" + size * 0x4)); } } public String getId() { return id; } public int getBaseAddress() { return baseAddress; } public int getSize() { return size; } protected void checkRegNum(final int regNum) { if ((regNum < 0) || (regNum >= size)) { final String message = String.format("regNum out of bounds: 0x%08x", regNum); throw new InternalError(message); } } /** * Returns all instance values of the subclass's REGS enum. */ protected abstract > T[] getRegs(); public > String getRegisterLabel(final int regNum) throws IOException { if (regNum < 0) { throw new IllegalArgumentException("regNum < 0: " + regNum); } if (regNum > 0xfff) { throw new IllegalArgumentException("regNum > 0xfff: " + String.format("%08x", regNum)); } final T[] regs = getRegs(); return regNum < regs.length ? regs[regNum].toString() : null; } public abstract void writeRegister(final int regNum, final int bits, final int mask, final boolean xor) throws IOException; public abstract int readRegister(final int regNum) throws IOException; @Override public String toString() { return String.format("%s@%08x, size=%08x", super.toString(), baseAddress, size); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/RemoteAddressSpaceClient.java ================================================ /* * @(#)RemoteAddressSpaceClient.java 1.00 21/03/17 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import org.soundpaint.rp2040pio.sdk.SDK; /** * TCP/IP Client that connects to a RemoteAddressSpaceServer via * socket. */ public class RemoteAddressSpaceClient extends AddressSpace { private static final String MSG_NO_CONNECTION = "no connection"; private static class Response { private final PrintStream console; private final int statusCode; private final String statusId; private final String result; private Response() { throw new UnsupportedOperationException("unsupported empty constructor"); } private Response(final PrintStream console, final int statusCode, final String statusId, final String result) { if (console == null) { throw new NullPointerException("console"); } this.console = console; this.statusCode = statusCode; this.statusId = statusId; this.result = result; } public int getStatusCode() { return statusCode; } public String getStatusId() { return statusId; } public String getResult() { return result; } public boolean isOk() { return statusCode == 101; // TODO: Use global constant. } public String getResultOrThrowOnFailure(final String errorMessage) throws IOException { if (!isOk()) { final String responseMessage = errorMessage + ": " + toString(); /* * TODO: To avoid duplicate error message display, the message * should be logged (e.g. using log4j) separately rather than * just being printed to the console, since a typical client * application (such as the Monitor application) usually will * already display the message by itself. */ console.printf("Remote Address Map Client: %s%n" ,responseMessage); throw new IOException(responseMessage); } return result; } @Override public String toString() { return statusCode + " " + statusId + (result != null ? ": " + result : ""); } } private final PrintStream console; private int port; private String host; private Socket socket; /** * Creates a register client, but does not yet connect to any * emulation server. * * @see #connect */ public RemoteAddressSpaceClient(final PrintStream console) throws IOException { if (console == null) { throw new NullPointerException("console"); } this.console = console; } /** * Creates register client and connects to the default port of the * specified host. If host is null, connects to localhost. */ public RemoteAddressSpaceClient(final PrintStream console, final String host) throws IOException { this(console); connect(host); } /** * Creates register client and connects to the specified port of the * specified host. If host is null, connects to localhost. */ public RemoteAddressSpaceClient(final PrintStream console, final String host, final int port) throws IOException { this(console); connect(host, port); } /** * Return host of most recently successfully established connection. * Return value is undefined if no connection has been successfully * established so far. To check if this is the case, use method * getPort() and check for return value of -1. */ public String getHost() { return host; } /** * Return port number of most recently successfully established * connection or -1, if no connection has been successfully * established so far. */ public int getPort() { return port; } /** * Connects this register client to the default port of the * specified host. If host is null, connects to localhost. */ public void connect(final String host) throws IOException { connect(host, Constants.REGISTER_SERVER_DEFAULT_PORT_NUMBER); } /** * Connects this register client to the specified port of * localhost. */ public void connect(final int port) throws IOException { connect(null, Constants.REGISTER_SERVER_DEFAULT_PORT_NUMBER); } /** * Connects this register client to the specified port of the * specified host. If host is null, connects to localhost. */ public void connect(final String host, final int port) throws IOException { if (socket != null) { try { socket.close(); } catch (final IOException e) { // ignore, we are throwing this connection away anyway } } socket = new Socket(); socket.connect(host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(InetAddress.getByName(null), port)); this.host = host; this.port = port; } private synchronized Response getResponse(final String request) throws IOException { final PrintWriter out = new PrintWriter(socket.getOutputStream(), true); final BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out.println(request); final String response = in.readLine(); if (response == null) { return null; } final int colonPos = response.indexOf(':'); final String statusDisplay = colonPos >= 0 ? response.substring(0, colonPos) : response; final int spacePos = statusDisplay.indexOf(' '); if (spacePos < 0) { throw new IOException("failed parsing server response status: " + statusDisplay); } final String statusCodeAsString = statusDisplay.substring(0, spacePos); final int statusCode; try { statusCode = Integer.parseInt(statusCodeAsString); } catch (final NumberFormatException e) { throw new IOException("failed parsing server response status code: " + statusCodeAsString); } final String statusId = statusDisplay.substring(spacePos + 1).trim(); final String result = colonPos >= 0 ? response.substring(colonPos + 1).trim() : null; return new Response(console, statusCode, statusId, result); } private void checkResponse(final Response response) throws IOException { if (response == null) { throw new IOException(MSG_NO_CONNECTION); } } @Override public String getEmulatorInfo() throws IOException { final Response response = getResponse("v"); checkResponse(response); return response.getResultOrThrowOnFailure("failed retreiving version"); } public String getHelp() throws IOException { final Response response = getResponse("h"); checkResponse(response); return response.getResultOrThrowOnFailure("failed retreiving help"); } public void quit() throws IOException { final Response response = getResponse("q"); if (response != null) { throw new IOException("unexpected response on quit: " + response); } } @Override public boolean providesAddress(final int address) throws IOException { final String request = String.format("p 0x%08x", address); final Response response = getResponse(request); checkResponse(response); final String retrievalMessage = String.format("failed retrieving provision info for address 0x%08x", address); final String result = response.getResultOrThrowOnFailure(retrievalMessage); if (result == null) { final String message = String.format("missing provision info for address 0x%08x", address); throw new IOException(message); } final boolean provided; try { provided = Boolean.parseBoolean(result); } catch (final NumberFormatException e) { final String message = String.format("failed parsing provision info for address 0x%08x: %s", address, result); throw new IOException(message); } return provided; } @Override public String getRegisterSetId(final int address) throws IOException { final String request = String.format("s 0x%08x", address); final Response response = getResponse(request); checkResponse(response); final String retrievalMessage = String.format("failed retrieving register set for address 0x%08x", address); final String result = response.getResultOrThrowOnFailure(retrievalMessage); if (result == null) { final String message = String.format("missing register set for address 0x%08x", address); throw new IOException(message); } return result; } @Override public String getAddressLabel(final int address) throws IOException { final String request = String.format("l 0x%08x", address); final Response response = getResponse(request); checkResponse(response); final String retrievalMessage = String.format("failed retrieving label for address 0x%08x", address); final String result = response.getResultOrThrowOnFailure(retrievalMessage); if (result == null) { final String message = String.format("missing label for address 0x%08x", address); throw new IOException(message); } return result; } @Override public void writeAddressMasked(final int address, final int bits, final int mask, final boolean xor) throws IOException { final String request = String.format("w 0x%08x 0x%08x 0x%08x %s", address, bits, mask, xor ? "t" : "f"); final Response response = getResponse(request); checkResponse(response); final String message = String.format("failed writing value 0x%08x to address 0x%08x with " + "mask 0x%08x and xor=%s", bits, address, mask, xor); response.getResultOrThrowOnFailure(message); } private int parseIntResult(final int address, final String result) throws IOException { if (result == null) { final String message = String.format("missing value for address 0x%08x", address); throw new IOException(message); } final int value; try { value = Integer.parseInt(result); } catch (final NumberFormatException e) { final String message = String.format("failed parsing value for address 0x%08x: %s", address, result); throw new IOException(message); } return value; } @Override public int readAddress(final int address) throws IOException { final String request = String.format("r 0x%08x", address); final Response response = getResponse(request); checkResponse(response); final String message = String.format("failed retrieving value for address 0x%08x", address); final String result = response.getResultOrThrowOnFailure(message); return parseIntResult(address, result); } @Override public int waitAddress(final int address, final int expectedValue, final int mask, final long cyclesTimeout, final long millisTimeout) throws IOException { final StringBuffer query = new StringBuffer(); final String request = String.format("i 0x%08x 0x%08x 0x%08x %d %d", address, expectedValue, mask, cyclesTimeout, millisTimeout); final Response response = getResponse(request); checkResponse(response); final String message = String.format("failed waiting for IRQ on address 0x%08x", address); final String result = response.getResultOrThrowOnFailure(message); return parseIntResult(address, result); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/RemoteAddressSpaceServer.java ================================================ /* * @(#)RemoteAddressSpaceServer.java 1.00 21/03/03 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; /** * The idea of the RemoteAddressSpaceServer class is to provide access * to the PIO emulator applicable even for processes other than the * JVM instance that hosts the PIO emulator, and potential integration * with other languages such as C/C++ or Python. Effectively, this * class adds an architectural layer that provides the PIO emulator as * software as a service (SaaS). Access is provided via a standard * TC/IP socket with a simple protocol for accessing the PIO * emulator's pseudo-memory-mapped registers, including the additional * emulator-specific extended set of registers (such as for accessing * the internal X and Y register or FIFO values). For example, even * an ordinary C program (like one created with the pioasm tool) may * make access the PIO emulator by compiling it against a special * extended version of the Pico C SDK, such that e.g. set up and * control of the PIO Emulator can be done directly from the C code * injected in a .pio file. Similarly, a Python library may be * developed that replaces the standard Pico Python libary with one * that accesses the emulator instead of real Pico hardware. * * The Pico Host SDL shows a specific example that draws the general * idea of how to extend the Pico C SDK in such a manner (see: * https://github.com/raspberrypi/pico-host-sdl). For this PIO * emulator, the SDK is to be extended in a way similar to the Pico * Host SDL, such that access to the PIO's registers is not performed * via direct memory access (as the default implementation of the C * SDK does), but via the socket interface that this * RemoteAddressSpaceServer class provides. */ public class RemoteAddressSpaceServer { private static final String[] NULL_ARGS = new String[0]; private final PrintStream console; private final AddressSpace memory; private final int portNumber; private final ServerSocket serverSocket; private int connectionCounter; private RemoteAddressSpaceServer() { throw new UnsupportedOperationException("unsupported empty constructor"); } public RemoteAddressSpaceServer(final PrintStream console, final AddressSpace memory) throws IOException { this(console, memory, Constants.REGISTER_SERVER_DEFAULT_PORT_NUMBER); } public RemoteAddressSpaceServer(final PrintStream console, final AddressSpace memory, final int portNumber) throws IOException { if (console == null) { throw new NullPointerException("console"); } if (memory == null) { throw new NullPointerException("memory"); } this.console = console; this.memory = memory; this.portNumber = portNumber; serverSocket = new ServerSocket(portNumber); connectionCounter = 0; // TODO: Maybe introduce pool of reusable client threads to limit // maximum number of simultaneously open connections. new Thread(() -> listen(), "RemoteAddressSpaceServer Client Thread").start(); } private void listen() { while (true) { try { final Socket clientSocket = serverSocket.accept(); new Thread(() -> serve(clientSocket), "RemoteAddressSpaceServer Server Thread").start(); } catch (final IOException e) { // establishing connection failed => abort connection } } } private String getHelp() { final String ls = System.lineSeparator(); return "available commands: " + ls + "h (help)" + ls + "v (version)" + ls + "q (quit)" + ls + "r (read address)" + ls + "w " + ls + " (write address)" + ls + "i [ [ []]]" + ls + " (await value)" + ls + "s (show address register set id)" + ls + "l (show address label)" + ls + "p (check address validity)"; } private enum ResponseStatus { BYE("bye", 100), OK("ok", 101), ERR_UNKNOWN_COMMAND("unknown command", 400), ERR_MISSING_OPERAND("missing operand", 401), ERR_UNPARSED_INPUT("unparsed input", 402), ERR_INVALID_NUMBER("invalid number", 403), ERR_INVALID_BOOL("invalid Boolean value", 404), ERR_IO("input / output error", 405), ERR_UNEXPECTED("unexpected error", 406); private final String id; private final int code; private ResponseStatus(final String id, final int code) { if (id == null) { throw new NullPointerException("id"); } this.id = id; this.code = code; } public String getId() { return id; } public int getCode() { return code; } public String getDisplayValue() { return code + " " + id.toUpperCase(); } }; private String createResponse(final ResponseStatus status) { return createResponse(status, null); } private String createResponse(final ResponseStatus status, final String message) { if (status == null) { throw new NullPointerException("status"); } final String statusDisplay = status.getDisplayValue(); return statusDisplay + (message != null ? ": " + message : ""); } private boolean parseBoolean(final String unparsed) { if (unparsed.equals("t") || unparsed.equals("T")) { return true; } else if (unparsed.equals("f") || unparsed.equals("F")) { return false; } else { final String message = String.format("expected Boolean value 't' or 'f': %s", unparsed); throw new IllegalArgumentException(message); } } private int parseInt(final String unparsed) { if (unparsed.startsWith("0x") || unparsed.startsWith("0X")) { return Integer.parseUnsignedInt(unparsed.substring(2), 16); } else { return Integer.parseInt(unparsed); } } private int parseAddress(final String unparsed) { final int address = parseInt(unparsed); if ((address & 0x3) != 0x0) { final String message = String.format("address not word-aligned: 0x%08x", address); throw new NumberFormatException(message); } return address; } private String handleGetVersion(final String[] args) throws IOException { if (args.length > 0) { return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[0]); } return createResponse(ResponseStatus.OK, memory.getEmulatorInfo()); } private String handleGetHelp(final String[] args) { if (args.length > 0) { return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[0]); } return createResponse(ResponseStatus.OK, getHelp()); } private String handleQuit(final String[] args) { if (args.length > 0) { return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[0]); } return null; } private String handleProvidesAddress(final String[] args) throws IOException { if (args.length < 1) { return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null); } if (args.length > 1) { return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[1]); } final int address; try { address = parseAddress(args[0]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } final boolean providesAddress = memory.providesAddress(address); return createResponse(ResponseStatus.OK, String.valueOf(providesAddress)); } private String handleGetRegisterSetId(final String[] args) throws IOException { if (args.length < 1) { return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null); } if (args.length > 1) { return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[1]); } final int address; try { address = parseAddress(args[0]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } final String id = memory.getRegisterSetId(address); return createResponse(ResponseStatus.OK, id); } private String handleGetLabel(final String[] args) throws IOException { if (args.length < 1) { return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null); } if (args.length > 1) { return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[1]); } final int address; try { address = parseAddress(args[0]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } final String label = memory.getAddressLabel(address); return createResponse(ResponseStatus.OK, label); } private String handleWriteAddress(final String[] args) throws IOException { if (args.length < 4) { return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null); } if (args.length > 4) { return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[4]); } final int address; try { address = parseAddress(args[0]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } final int value; try { value = parseInt(args[1]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } final int mask; try { mask = parseInt(args[2]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } final boolean xor; try { xor = parseBoolean(args[3]); } catch (final IllegalArgumentException e) { return createResponse(ResponseStatus.ERR_INVALID_BOOL, e.getMessage()); } memory.writeAddressMasked(address, value, mask, xor); return createResponse(ResponseStatus.OK); } private String handleReadAddress(final String[] args) throws IOException { if (args.length < 1) { return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null); } if (args.length > 1) { return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[1]); } final int address; try { address = parseAddress(args[0]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } final int value = memory.readAddress(address); return createResponse(ResponseStatus.OK, String.valueOf(value)); } private String handleWait(final String[] args) throws IOException { if (args.length < 2) { return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null); } if (args.length > 5) { return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[5]); } final int address; try { address = parseAddress(args[0]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } final int expectedValue; try { expectedValue = parseInt(args[1]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } final int mask; if (args.length > 2) { try { mask = parseInt(args[2]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } } else { mask = 0xffffffff; } final int cyclesTimeout; if (args.length > 3) { try { cyclesTimeout = parseInt(args[3]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } } else { cyclesTimeout = 0x0; } final int millisTimeout; if (args.length > 4) { try { millisTimeout = parseInt(args[4]); } catch (final NumberFormatException e) { return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage()); } } else { millisTimeout = 0x0; } final int value = memory.waitAddress(address, expectedValue, mask, ((long)cyclesTimeout) & 0xffffffffL, ((long)millisTimeout) & 0xffffffffL); return createResponse(ResponseStatus.OK, String.valueOf(value)); } private String handleRequest(final String request) throws IOException { if (request.isEmpty()) { return null; } final char command = request.charAt(0); final String unparsedArgs = request.substring(1).trim(); final String[] args = unparsedArgs.length() > 0 ? unparsedArgs.split(" ") : NULL_ARGS; /* * TODO: Idea: Introduce another command 's' for waiting (or * "sleeping") until the emulator runs idle (in MasterClock * SINGLE_STEP mode). This way, an external application like * TimingDiagram does not need to busy wait (via periodic polling * with 'r' read command) until a triggered clock phase has been * completed. */ switch (command) { case 'v': return handleGetVersion(args); case 'h': case '?': return handleGetHelp(args); case 'q': return handleQuit(args); case 'p': return handleProvidesAddress(args); case 's': return handleGetRegisterSetId(args); case 'l': return handleGetLabel(args); case 'w': return handleWriteAddress(args); case 'r': return handleReadAddress(args); case 'i': return handleWait(args); default: return createResponse(ResponseStatus.ERR_UNKNOWN_COMMAND, String.valueOf(command)); } } private void handleThrowable(final PrintWriter clientOut, final Throwable t, final ResponseStatus responseStatus, final int id) { if (clientOut != null) { try { clientOut.println(createResponse(responseStatus, t.getMessage())); } catch (final Throwable s) { // ignore } } t.printStackTrace(console); console.printf("connection #%d aborted: %s%n", id, t); } private void serve(final Socket clientSocket) { final int id = connectionCounter++; console.printf("connection #%d opened%n", id); PrintWriter clientOut = null; try { clientOut = new PrintWriter(clientSocket.getOutputStream(), true); final BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String request; while ((request = in.readLine()) != null) { final String response = handleRequest(request.trim()); if (response == null) { break; } clientOut.println(response); } } catch (final IOException e) { handleThrowable(clientOut, e, ResponseStatus.ERR_IO, id); } catch (final Throwable t) { handleThrowable(clientOut, t, ResponseStatus.ERR_UNEXPECTED, id); } finally { console.printf("connection #%d closed%n", id); try { clientSocket.close(); } catch (final IOException e) { console.println("warning: failed closing client socket: " + e); } } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/SM.java ================================================ /* * @(#)SM.java 1.00 21/01/31 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.io.PrintStream; import java.util.function.Function; import java.util.function.IntConsumer; /** * State Machine */ public class SM implements Constants { private final int num; private final PrintStream console; private final MasterClock masterClock; private final PIOGPIO pioGpio; private final Memory memory; private final IRQ irq; private final Status status; private final Decoder decoder; private final FIFO fifo; private final PLL pll; public enum IOMapping { SET((sm) -> sm.status.regPINCTRL_SET_BASE, (sm) -> sm.status.regPINCTRL_SET_COUNT), OUT((sm) -> sm.status.regPINCTRL_OUT_BASE, (sm) -> sm.status.regPINCTRL_OUT_COUNT); private final Function baseGetter; private final Function countGetter; private IOMapping(final Function baseGetter, final Function countGetter) { this.baseGetter = baseGetter; this.countGetter = countGetter; } public void collatePins(final SM sm, final int data) { sm.status. collatePins(data, baseGetter.apply(sm), countGetter.apply(sm), false); } public void collatePinDirs(final SM sm, final int data) { sm.status. collatePinDirs(data, baseGetter.apply(sm), countGetter.apply(sm)); } }; public class Status { public Instruction instruction; public int origin; public Instruction.ResultState resultState; public boolean processing; public boolean smEnabled; public boolean clockEnabled; public boolean isDelayCycle; public int collateSideSetPins; public int collateSideSetBase; public int collateSideSetCount; public int outStickyPins; public int outStickyBase; public int outStickyCount; public boolean havePendingOutOrSetPins; public int regX; public int regY; public int isrValue; public int isrShiftCount; public int osrValue; public int osrShiftCount; public int totalDelay; public int pendingDelay; public int pendingForcedInstruction; public boolean isForcedInstruction; public int pendingExecdInstruction; public int regADDR; // bits 0…4 of SMx_ADDR public boolean regEXECCTRL_SIDE_EN; // bit 30 of SMx_EXECCTRL public PIO.PinDir regEXECCTRL_SIDE_PINDIR; // bit 29 of SMx_EXECCTRL public int regEXECCTRL_JMP_PIN; // bits 24…28 of SMx_EXECCTRL public int regEXECCTRL_OUT_EN_SEL; // bits 19…23 of SMx_EXECCTRL public boolean regEXECCTRL_INLINE_OUT_EN; // bit 18 of SMx_EXECCTRL public boolean regEXECCTRL_OUT_STICKY; // bit 17 of SMx_EXECCTRL public int regEXECCTRL_WRAP_TOP; // bits 12…16 of SMx_EXECCTRL public int regEXECCTRL_WRAP_BOTTOM; // bits 7…11 of SMx_EXECCTRL public boolean regEXECCTRL_STATUS_SEL; // bit 4 of SMx_EXECCTRL public int regEXECCTRL_STATUS_N; // bits 0…3 of SMx_EXECCTRL public int regSHIFTCTRL_PULL_THRESH; // bits 25…29 of SMx_SHIFTCTRL public int regSHIFTCTRL_PUSH_THRESH; // bits 20…24 of SMx_SHIFTCTRL public PIO.ShiftDir regSHIFTCTRL_IN_SHIFTDIR; // bit 18 of SMx_SHIFTCTRL public boolean regSHIFTCTRL_AUTOPULL; // bit 17 of SMx_SHIFTCTRL public PIO.ShiftDir regSHIFTCTRL_OUT_SHIFTDIR; // bit 19 of SMx_SHIFTCTRL public boolean regSHIFTCTRL_AUTOPUSH; // bit 16 of SMx_SHIFTCTRL public int regPINCTRL_SIDESET_COUNT; // bits 29…31 of SMx_PINCTRL public int regPINCTRL_SET_COUNT; // bits 26…28 of SMx_PINCTRL public int regPINCTRL_OUT_COUNT; // bits 20…25 of SMx_PINCTRL public int regPINCTRL_IN_BASE; // bits 15…19 of SMx_PINCTRL public int regPINCTRL_SIDESET_BASE; // bits 10…14 of SMx_PINCTRL public int regPINCTRL_SET_BASE; // bits 5…9 of SMx_PINCTRL public int regPINCTRL_OUT_BASE; // bits 0…4 of SMx_PINCTRL // PIOEmuRegisters Status public int regBREAKPOINTS; // bits 0…31 of SMx_BREAKPOINTS public int regTRACEPOINTS; // bits 0…31 of SMx_TRACEPOINTS public Status() { reset(); } private void reset() { instruction = null; origin = INSTR_ORIGIN_UNKNOWN; resultState = null; processing = false; smEnabled = false; clockEnabled = false; isDelayCycle = false; collateSideSetPins = 0; collateSideSetBase = 0; collateSideSetCount = 0; outStickyPins = 0; outStickyBase = 0; outStickyCount = 0; havePendingOutOrSetPins = false; regX = 0; regY = 0; isrValue = 0; isrShiftCount = 0; osrValue = 0; osrShiftCount = 32; totalDelay = 0; pendingDelay = 0; pendingForcedInstruction = -1; isForcedInstruction = false; pendingExecdInstruction = -1; regADDR = 0; regEXECCTRL_STATUS_SEL = false; regEXECCTRL_STATUS_N = 0; regEXECCTRL_SIDE_EN = false; regEXECCTRL_SIDE_PINDIR = PIO.PinDir.GPIO_LEVELS; regEXECCTRL_JMP_PIN = 0; regEXECCTRL_OUT_EN_SEL = 0; regEXECCTRL_INLINE_OUT_EN = false; regEXECCTRL_OUT_STICKY = false; regEXECCTRL_WRAP_TOP = MEMORY_SIZE - 1; regEXECCTRL_WRAP_BOTTOM = 0x00; regSHIFTCTRL_PULL_THRESH = 0; regSHIFTCTRL_PUSH_THRESH = 0; regSHIFTCTRL_IN_SHIFTDIR = PIO.ShiftDir.SHIFT_LEFT; regSHIFTCTRL_AUTOPULL = false; regSHIFTCTRL_OUT_SHIFTDIR = PIO.ShiftDir.SHIFT_LEFT; regSHIFTCTRL_AUTOPUSH = false; regPINCTRL_SIDESET_COUNT = 0; regPINCTRL_SET_COUNT = 0x5; regPINCTRL_OUT_COUNT = 0; regPINCTRL_IN_BASE = 0; regPINCTRL_SIDESET_BASE = 0; regPINCTRL_SET_BASE = 0; regPINCTRL_OUT_BASE = 0; // PIOEmuRegisters Status regBREAKPOINTS = 0; regTRACEPOINTS = 0; } public void restart() { /* * See RP2040 datasheet, Table 378: CTRL Register, SM_RESTART: * * Specifically, the following are cleared: input and output * shift counters; the contents of the input shift register; the * delay counter; the waiting-on-IRQ state; any stalled * instruction written to SMx_INSTR or run by OUT/MOV EXEC; any * pin write left asserted due to OUT_STICKY. */ isrShiftCount = 0; osrShiftCount = 32; isrValue = 0; isDelayCycle = false; outStickyPins = 0; outStickyBase = 0; outStickyCount = 0; havePendingOutOrSetPins = false; totalDelay = 0; pendingDelay = 0; pendingForcedInstruction = -1; isForcedInstruction = false; pendingExecdInstruction = -1; regEXECCTRL_OUT_STICKY = false; } public Bit jmpPin() { /* * RP2040 datasheet 3.4.2. "JMP": "JMP PIN branches on the GPIO * … independently of the state machine's other input mapping." * => Return global GPIO's input to peripherals rather than the * local GPIO pin state of this state machine's PIO. */ return pioGpio.getGPIO().getInToPeri(regEXECCTRL_JMP_PIN); } public void collatePins(final int pins, final int base, final int count, final boolean isSideSetOperation) { if (isSideSetOperation) { collateSideSetPins = pins; collateSideSetBase = base; collateSideSetCount = count; } else { outStickyPins = pins; outStickyBase = base; outStickyCount = count; havePendingOutOrSetPins = true; } } private void flushCollatePins() { if (havePendingOutOrSetPins || regEXECCTRL_OUT_STICKY) { final boolean outEn = !regEXECCTRL_INLINE_OUT_EN || (((outStickyPins >>> regEXECCTRL_OUT_EN_SEL) & 0x1) == 0x1); if (outEn) { pioGpio.collatePins(outStickyPins, outStickyBase, outStickyCount); } havePendingOutOrSetPins = false; } /* * RP2040 datasheet, Sect. 3.5.6. "GPIO Mapping": If side-set * overlaps with OUT/SET, side-set takes precedence. => Perform * collatePins() for side-set as last step. */ if (collateSideSetCount > 0) { pioGpio.collatePins(collateSideSetPins, collateSideSetBase, collateSideSetCount); collateSideSetCount = 0; } } public void collatePinDirs(final int pins, final int base, final int count) { pioGpio.collatePinDirs(pins, base, count); } public int getFIFOStatus() { final boolean fulfilled; if (regEXECCTRL_STATUS_SEL) { fulfilled = fifo.getRXLevel() < regEXECCTRL_STATUS_N; } else { fulfilled = fifo.getTXLevel() < regEXECCTRL_STATUS_N; } return fulfilled ? ~0 : 0; } public boolean isIsrCountBeyondThreshold() { return isrShiftCount >= (regSHIFTCTRL_PUSH_THRESH != 0 ? regSHIFTCTRL_PUSH_THRESH : 32); } public boolean isOsrCountBeyondThreshold() { return osrShiftCount >= (regSHIFTCTRL_PULL_THRESH != 0 ? regSHIFTCTRL_PULL_THRESH : 32); } private boolean consumePendingDelay() { if (pendingDelay == 0) return false; pendingDelay--; return true; } private void setPendingDelay(final int delay) { if (delay < 0) { throw new IllegalArgumentException("delay < 0: " + delay); } if (delay > 31) { throw new IllegalArgumentException("delay > 31: " + delay); } this.pendingDelay = delay; this.totalDelay = delay; } @Override public String toString() { return String.format("Status(SM%d)", SM.this.num); } } private SM() { throw new UnsupportedOperationException("unsupported empty constructor"); } public SM(final int num, final PrintStream console, final MasterClock masterClock, final PIOGPIO pioGpio, final Memory memory, final IRQ irq) { if (num < 0) { throw new IllegalArgumentException("SM num < 0: " + num); } if (num > 3) { throw new IllegalArgumentException("SM num > 3: " + num); } if (console == null) { throw new NullPointerException("console"); } if (masterClock == null) { throw new NullPointerException("masterClock"); } if (pioGpio == null) { throw new NullPointerException("pioGpio"); } if (memory == null) { throw new NullPointerException("memory"); } if (irq == null) { throw new NullPointerException("irq"); } this.num = num; this.console = console; this.masterClock = masterClock; this.pioGpio = pioGpio; this.memory = memory; this.irq = irq; status = new Status(); decoder = new Decoder(); fifo = new FIFO(num, irq); pll = new PLL(console); } public int getNum() { return num; } public PIOGPIO getPIOGPIO() { return pioGpio; } public Memory getMemory() { return memory; } public Status getStatus() { return status; } public FIFO getFIFO() { return fifo; } public PLL getPLL() { return pll; } public void reset() { status.reset(); decoder.reset(); fifo.reset(); pll.reset(); } public void setCLKDIV(final int clkdiv, final int mask, final boolean xor) { pll.setCLKDIV(Constants.hwSetBits(pll.getCLKDIV(), clkdiv, mask, xor)); } public int getCLKDIV() { return pll.getCLKDIV(); } public void resetCLKDIV() { pll.reset(); } public int outEnSel() { return status.regEXECCTRL_OUT_EN_SEL; } public boolean inlineOutEn() { return status.regEXECCTRL_INLINE_OUT_EN; } public boolean outSticky() { return status.regEXECCTRL_OUT_STICKY; } public void setEXECCTRL(final int execctrl, final int mask, final boolean xor) { setEXECCTRL(Constants.hwSetBits(getEXECCTRL(), execctrl, mask, xor)); } private void setEXECCTRL(final int execctrl) { status.regEXECCTRL_SIDE_EN = ((execctrl & SM0_EXECCTRL_SIDE_EN_BITS) >>> SM0_EXECCTRL_SIDE_EN_LSB) != 0x0; status.regEXECCTRL_SIDE_PINDIR = PIO.PinDir.fromValue((execctrl & SM0_EXECCTRL_SIDE_PINDIR_BITS) >>> SM0_EXECCTRL_SIDE_PINDIR_LSB); status.regEXECCTRL_JMP_PIN = (execctrl & SM0_EXECCTRL_JMP_PIN_BITS) >>> SM0_EXECCTRL_JMP_PIN_LSB; status.regEXECCTRL_OUT_EN_SEL = (execctrl & SM0_EXECCTRL_OUT_EN_SEL_BITS) >>> SM0_EXECCTRL_OUT_EN_SEL_LSB; status.regEXECCTRL_INLINE_OUT_EN = ((execctrl & SM0_EXECCTRL_INLINE_OUT_EN_BITS) >>> SM0_EXECCTRL_INLINE_OUT_EN_LSB) != 0x0; status.regEXECCTRL_OUT_STICKY = ((execctrl & SM0_EXECCTRL_OUT_STICKY_BITS) >>> SM0_EXECCTRL_OUT_STICKY_LSB) != 0x0; status.regEXECCTRL_WRAP_TOP = (execctrl & SM0_EXECCTRL_WRAP_TOP_BITS) >>> SM0_EXECCTRL_WRAP_TOP_LSB; status.regEXECCTRL_WRAP_BOTTOM = (execctrl & SM0_EXECCTRL_WRAP_BOTTOM_BITS) >>> SM0_EXECCTRL_WRAP_BOTTOM_LSB; status.regEXECCTRL_STATUS_SEL = ((execctrl & SM0_EXECCTRL_STATUS_SEL_BITS) >>> SM0_EXECCTRL_STATUS_SEL_LSB) != 0x0; status.regEXECCTRL_STATUS_N = (execctrl & SM0_EXECCTRL_STATUS_N_BITS) >>> SM0_EXECCTRL_STATUS_N_LSB; } public int getEXECCTRL() { return (isExecStalled() ? 1 : 0) << SM0_EXECCTRL_EXEC_STALLED_LSB | (status.regEXECCTRL_SIDE_EN ? 1 : 0) << SM0_EXECCTRL_SIDE_EN_LSB | status.regEXECCTRL_SIDE_PINDIR.getValue() << SM0_EXECCTRL_SIDE_PINDIR_LSB | status.regEXECCTRL_JMP_PIN << SM0_EXECCTRL_JMP_PIN_LSB | status.regEXECCTRL_OUT_EN_SEL << SM0_EXECCTRL_OUT_EN_SEL_LSB | (status.regEXECCTRL_INLINE_OUT_EN ? 1 : 0) << SM0_EXECCTRL_INLINE_OUT_EN_LSB | (status.regEXECCTRL_OUT_STICKY ? 1 : 0) << SM0_EXECCTRL_OUT_STICKY_LSB | status.regEXECCTRL_WRAP_TOP << SM0_EXECCTRL_WRAP_TOP_LSB | status.regEXECCTRL_WRAP_BOTTOM << SM0_EXECCTRL_WRAP_BOTTOM_LSB | (status.regEXECCTRL_STATUS_SEL ? 1 : 0) << SM0_EXECCTRL_STATUS_SEL_LSB | status.regEXECCTRL_STATUS_N << SM0_EXECCTRL_STATUS_N_LSB; } public void setSHIFTCTRL(final int shiftctrl, final int mask, final boolean xor) { setSHIFTCTRL(Constants.hwSetBits(getSHIFTCTRL(), shiftctrl, mask, xor)); } private void setSHIFTCTRL(final int shiftctrl) { fifo.setJoinRX(((shiftctrl & SM0_SHIFTCTRL_FJOIN_RX_BITS) >>> SM0_SHIFTCTRL_FJOIN_RX_LSB) != 0x0); fifo.setJoinTX(((shiftctrl & SM0_SHIFTCTRL_FJOIN_TX_BITS) >>> SM0_SHIFTCTRL_FJOIN_TX_LSB) != 0x0); status.regSHIFTCTRL_PULL_THRESH = (shiftctrl & SM0_SHIFTCTRL_PULL_THRESH_BITS) >>> SM0_SHIFTCTRL_PULL_THRESH_LSB; status.regSHIFTCTRL_PUSH_THRESH = (shiftctrl & SM0_SHIFTCTRL_PUSH_THRESH_BITS) >>> SM0_SHIFTCTRL_PUSH_THRESH_LSB; status.regSHIFTCTRL_OUT_SHIFTDIR = PIO.ShiftDir.fromValue((shiftctrl & SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS) >>> SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB); status.regSHIFTCTRL_IN_SHIFTDIR = PIO.ShiftDir.fromValue((shiftctrl & SM0_SHIFTCTRL_IN_SHIFTDIR_BITS) >>> SM0_SHIFTCTRL_IN_SHIFTDIR_LSB); status.regSHIFTCTRL_AUTOPULL = ((shiftctrl & SM0_SHIFTCTRL_AUTOPULL_BITS) >>> SM0_SHIFTCTRL_AUTOPULL_LSB) != 0x0; status.regSHIFTCTRL_AUTOPUSH = ((shiftctrl & SM0_SHIFTCTRL_AUTOPUSH_BITS) >>> SM0_SHIFTCTRL_AUTOPUSH_LSB) != 0x0; } public int getSHIFTCTRL() { return (fifo.getJoinRX() ? 1 : 0) << SM0_SHIFTCTRL_FJOIN_RX_LSB | (fifo.getJoinTX() ? 1 : 0) << SM0_SHIFTCTRL_FJOIN_TX_LSB | status.regSHIFTCTRL_PULL_THRESH << SM0_SHIFTCTRL_PULL_THRESH_LSB | status.regSHIFTCTRL_PUSH_THRESH << SM0_SHIFTCTRL_PUSH_THRESH_LSB | status.regSHIFTCTRL_OUT_SHIFTDIR.getValue() << SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB | status.regSHIFTCTRL_IN_SHIFTDIR.getValue() << SM0_SHIFTCTRL_IN_SHIFTDIR_LSB | (status.regSHIFTCTRL_AUTOPULL ? 1 : 0) << SM0_SHIFTCTRL_AUTOPULL_LSB | (status.regSHIFTCTRL_AUTOPUSH ? 1 : 0) << SM0_SHIFTCTRL_AUTOPUSH_LSB; } public void setPINCTRL(final int pinctrl, final int mask, final boolean xor) { setPINCTRL(Constants.hwSetBits(getPINCTRL(), pinctrl, mask, xor)); } private void setPINCTRL(final int pinctrl) { status.regPINCTRL_SIDESET_COUNT = (pinctrl & SM0_PINCTRL_SIDESET_COUNT_BITS) >>> SM0_PINCTRL_SIDESET_COUNT_LSB; status.regPINCTRL_SET_COUNT = (pinctrl & SM0_PINCTRL_SET_COUNT_BITS) >>> SM0_PINCTRL_SET_COUNT_LSB; status.regPINCTRL_OUT_COUNT = (pinctrl & SM0_PINCTRL_OUT_COUNT_BITS) >>> SM0_PINCTRL_OUT_COUNT_LSB; status.regPINCTRL_IN_BASE = (pinctrl & SM0_PINCTRL_IN_BASE_BITS) >>> SM0_PINCTRL_IN_BASE_LSB; status.regPINCTRL_SIDESET_BASE = (pinctrl & SM0_PINCTRL_SIDESET_BASE_BITS) >>> SM0_PINCTRL_SIDESET_BASE_LSB; status.regPINCTRL_SET_BASE = (pinctrl & SM0_PINCTRL_SET_BASE_BITS) >>> SM0_PINCTRL_SET_BASE_LSB; status.regPINCTRL_OUT_BASE = (pinctrl & SM0_PINCTRL_OUT_BASE_BITS) >>> SM0_PINCTRL_OUT_BASE_LSB; } public int getPINCTRL() { return status.regPINCTRL_SIDESET_COUNT << SM0_PINCTRL_SIDESET_COUNT_LSB | status.regPINCTRL_SET_COUNT << SM0_PINCTRL_SET_COUNT_LSB | status.regPINCTRL_OUT_COUNT << SM0_PINCTRL_OUT_COUNT_LSB | status.regPINCTRL_IN_BASE << SM0_PINCTRL_IN_BASE_LSB | status.regPINCTRL_SIDESET_BASE << SM0_PINCTRL_SIDESET_BASE_LSB | status.regPINCTRL_SET_BASE << SM0_PINCTRL_SET_BASE_LSB | status.regPINCTRL_OUT_BASE << SM0_PINCTRL_OUT_BASE_LSB; } public void clockRisingEdge(final boolean smEnabled, final long wallClock) { status.smEnabled = smEnabled; if (smEnabled) { pll.risingEdge(wallClock); status.clockEnabled = pll.getClockEnable(); } else { status.clockEnabled = false; } /* * Sect. 3.5.7.: ... instructions written to the INSTR register * ... execute immediately, ignoring the state machine clock * divider. */ status.processing = status.clockEnabled || (status.pendingForcedInstruction >= 0); if (status.processing) { try { if ((status.pendingForcedInstruction >= 0) || (status.pendingExecdInstruction >= 0) || !status.consumePendingDelay()) { status.isDelayCycle = false; fetchAndDecode(); } else { status.isDelayCycle = true; } } catch (final Decoder.DecodeException e) { console.println(e.getMessage()); } } else { status.origin = INSTR_ORIGIN_UNKNOWN; } } public void clockFallingEdge(final long wallClock) { if (status.smEnabled) { pll.fallingEdge(wallClock); } if (status.processing) { try { execute(); } catch (final RuntimeException e) { e.printStackTrace(console); console.printf("internal error: %s%n", e.getMessage()); } } } public void restart() { status.restart(); } public Bit getIRQ(final int index) { return irq.get(index); } public void clearIRQ(final int index) { irq.clear(index); } public void setIRQ(final int index) { irq.set(index); } /** * @return <code>true</code> if operation stall due to * full FIFO. */ public boolean rxPush(final boolean ifFull, final boolean block) { final boolean isrCountBeyondThreshold = status.isIsrCountBeyondThreshold(); if (!ifFull || isrCountBeyondThreshold) { final boolean succeeded = fifo.rxPush(status.isrValue, block); if (succeeded) { status.isrValue = 0; status.isrShiftCount = 0; return false; } else { return block; } } return false; } /** * @return True if operation stall due to empty FIFO. */ public boolean txPull(final boolean ifEmpty, final boolean block) { final boolean osrCountBeyondThreshold = status.isOsrCountBeyondThreshold(); if (!ifEmpty || osrCountBeyondThreshold) { synchronized(fifo) { final boolean fifoEmpty = fifo.fstatTxEmpty(); if (fifoEmpty) { if (!block) { status.osrValue = status.regX; status.osrShiftCount = 0; } return block; // stall on block } else { status.osrValue = fifo.txPull(block); status.osrShiftCount = 0; return false; } } } else { return false; } } public static int saturate(final int base, final int increment, final int limit) { final int sum = base + increment; return sum < limit ? sum : limit; } public int getISRValue() { return status.isrValue; } public void setISRValue(final int value) { status.isrValue = value; } public void setISRValue(final int value, final int mask, final boolean xor) { status.isrValue = Constants.hwSetBits(status.isrValue, value, mask, xor); } public int getISRShiftCount() { return status.isrShiftCount; } public void setISRShiftCount(final int value, final int mask, final boolean xor) { status.isrShiftCount = Constants.hwSetBits(status.isrShiftCount, value, mask, xor); } public int getOSRValue() { return status.osrValue; } public void setOSRValue(final int value) { status.osrValue = value; } public void setOSRValue(final int value, final int mask, final boolean xor) { status.osrValue = Constants.hwSetBits(status.osrValue, value, mask, xor); } public int getOSRShiftCount() { return status.osrShiftCount; } public void setOSRShiftCount(final int value, final int mask, final boolean xor) { status.osrShiftCount = Constants.hwSetBits(status.osrShiftCount, value, mask, xor); } public void setSideSetCount(final int count) { if (count < 0) { throw new IllegalArgumentException("side set count < 0: " + count); } if (count > 5) { throw new IllegalArgumentException("side set count > 5: " + count); } status.regPINCTRL_SIDESET_COUNT = count; } public PIO.ShiftDir getInShiftDir() { return status.regSHIFTCTRL_IN_SHIFTDIR; } public PIO.ShiftDir getOutShiftDir() { return status.regSHIFTCTRL_OUT_SHIFTDIR; } public int getX() { return status.regX; } public void setX(final int value) { status.regX = value; } public void setX(final int value, final int mask, final boolean xor) { status.regX = Constants.hwSetBits(status.regX, value, mask, xor); } private void decX() { status.regX--; } public int getY() { return status.regY; } public void setY(final int value) { status.regY = value; } public void setY(final int value, final int mask, final boolean xor) { status.regY = Constants.hwSetBits(status.regY, value, mask, xor); } private void decY() { status.regY--; } public void put(final int data) { synchronized(fifo) { fifo.txDMAWrite(data); } } public void putRXF(final int data) { synchronized(fifo) { fifo.rxPush(data, false); } } public int get() { synchronized(fifo) { final int value = fifo.rxDMARead(); return value; } } public int getTXF() { synchronized(fifo) { final int value = fifo.txPull(false); return value; } } public boolean isRXFIFOFull() { return fifo.fstatRxFull(); } public boolean isRXFIFOEmpty() { return fifo.fstatRxEmpty(); } public int getRXFIFOLevel() { return fifo.getRXLevel(); } public boolean isTXFIFOFull() { return fifo.fstatTxFull(); } public boolean isTXFIFOEmpty() { return fifo.fstatTxEmpty(); } public int getTXFIFOLevel() { return fifo.getTXLevel(); } public void setBreakPoints(final int breakPoints, final int mask, final boolean xor) { status.regBREAKPOINTS = Constants.hwSetBits(status.regBREAKPOINTS, breakPoints, mask, xor); } public int getBreakPoints() { return status.regBREAKPOINTS; } public void setTracePoints(final int tracePoints, final int mask, final boolean xor) { status.regTRACEPOINTS = Constants.hwSetBits(status.regTRACEPOINTS, tracePoints, mask, xor); } public int getTracePoints() { return status.regTRACEPOINTS; } private int encodeJmp(final Instruction.Jmp.Condition condition, final int address) { if (condition == null) { throw new NullPointerException("condition"); } if (address < 0) { throw new IllegalArgumentException("address < 0: " + address); } if (address > MEMORY_SIZE - 1) { throw new IllegalArgumentException("address > " + (MEMORY_SIZE - 1) + ": " + address); } final Instruction.Jmp instruction = new Instruction.Jmp(); instruction.setCondition(condition); instruction.setAddress(address); return instruction.encode(status.regPINCTRL_SIDESET_COUNT, status.regEXECCTRL_SIDE_EN); } public int getPC() { return status.regADDR; } public void setPC(final int value) { if (value < 0) { throw new IllegalArgumentException("pc value < 0: " + value); } if (value > MEMORY_SIZE - 1) { throw new IllegalArgumentException("pc value > " + (MEMORY_SIZE - 1) + ": " + value); } // sync: don't change regADDR while PC is updated synchronized(memory.FETCH_LOCK) { status.regADDR = value; } } public void setPC(final int value, final int mask, final boolean xor) { final int pc = Constants.hwSetBits(status.regADDR, value, mask, xor); setPC(pc & (MEMORY_SIZE - 1)); } private void updatePC() { synchronized(memory.FETCH_LOCK) { if (status.regADDR == status.regEXECCTRL_WRAP_TOP) { status.regADDR = status.regEXECCTRL_WRAP_BOTTOM; } else { status.regADDR = (status.regADDR + 1) & (MEMORY_SIZE - 1); } if (((status.regBREAKPOINTS >>> status.regADDR) & 0x1) != 0x0) { masterClock.setMode(MasterClock.Mode.SINGLE_STEP); } } } private short fetch() { final int pendingForcedInstruction = status.pendingForcedInstruction; if (pendingForcedInstruction >= 0) { status.pendingForcedInstruction = -1; status.isForcedInstruction = true; status.origin = INSTR_ORIGIN_FORCED; return (short)pendingForcedInstruction; } final int pendingExecdInstruction = status.pendingExecdInstruction; if (pendingExecdInstruction >= 0) { status.pendingExecdInstruction = -1; status.origin = INSTR_ORIGIN_EXECD; return (short)pendingExecdInstruction; } // notify blocking methods that condition may have changed memory.FETCH_LOCK.notifyAll(); status.origin = status.regADDR & (MEMORY_SIZE - 1); return memory.get(status.regADDR); } public int getOpCode() { /* * TODO / FIXME: getOpCode() works only for instructions created * from a call to decode(), but will return 0 for synthesized * ones. */ final Instruction instruction = status.instruction; if (instruction == null) { /* * Trying to access instruction before any decode has been * executed. * * TODO: The RP2040 datasheet is unclear for this situation: * Table 378 "CTRL Register" states for CTRL_SM_ENABLE: * * "When disabled, a state machine will cease executing * instructions, except those written directly to SMx_INSTR by * the system" * * And Table 395 "SMx_INSTR Registers" does *not* provide a * reset value for SMx_INSTR. * * This means, at startup, as long as a state machine has not * yet been enabled and therefore no instruction has been * fetched and decoded so far, the value of register SMx_INSTR * upon read access is undefined. * * For this specific case, we assume that SMx_INSTR will return * a value of 0. */ return 0; } return instruction.getOpCode(); } public int getPendingForcedInstruction() { return status.pendingForcedInstruction; } public int getFORCED_INSTR() { if (status.pendingForcedInstruction >= 0) { return 0x00010000 | (status.pendingForcedInstruction & 0x0000ffff); } return 0x0; } public void clearPendingForcedInstruction() { status.pendingForcedInstruction = -1; } public int getPendingExecdInstruction() { return status.pendingExecdInstruction; } public int getEXECD_INSTR() { if (status.pendingExecdInstruction >= 0) { return 0x00010000 | (status.pendingExecdInstruction & 0x0000ffff); } return 0x0; } public void clearPendingExecdInstruction() { status.pendingExecdInstruction = -1; } public void forceInstruction(final int instruction) { if (instruction < 0) { throw new IllegalArgumentException("instruction < 0: " + instruction); } if (instruction > 65535) { throw new IllegalArgumentException("instruction > 65535: " + instruction); } final boolean discarded; synchronized(memory.FETCH_LOCK) { discarded = status.pendingForcedInstruction >= 0; status.pendingForcedInstruction = instruction; } if (discarded) { console.println("WARNING: " + "discarding already pending forced instruction"); } } public void execInstruction(final int instruction) { synchronized(memory.FETCH_LOCK) { if (status.pendingExecdInstruction >= 0) { throw new InternalError("already have pending EXEC instruction"); } if (instruction < 0) { throw new IllegalArgumentException("instruction < 0: " + instruction); } if (instruction > 65535) { throw new IllegalArgumentException("instruction > 65535: " + instruction); } status.pendingExecdInstruction = instruction; } } public boolean isExecStalled() { synchronized(memory.FETCH_LOCK) { return (status.pendingForcedInstruction >= 0) && isStalled(); } } public int getINSTR_ORIGIN() { final int origin = status.origin; final int mode = origin < 0 ? origin & 0x3 : INSTR_ORIGIN_MEMORY; final int address = origin < 0 ? 0 : origin & (MEMORY_SIZE - 1); return (mode << 5) | address; } /** * Tracking the total delay of the latest instruction with delay in * effect is, strictly speaking, not necessary for the emulation, * but highly useful as additional information for client * applications, e.g. when displaying the percentage of already * passed delay. */ public int getTotalDelay() { return status.totalDelay; } public int getPendingDelay() { return status.pendingDelay; } private void fetchAndDecode() throws Decoder.DecodeException { synchronized(memory.FETCH_LOCK) { final short word = fetch(); final Instruction instruction = decoder.decode(word, status.regPINCTRL_SIDESET_COUNT, status.regEXECCTRL_SIDE_EN); if (((status.regTRACEPOINTS >>> status.regADDR) & 0x1) != 0x0) { console.println("SM" + num + ": " + instruction); } status.instruction = instruction; } } private void executeInstruction() { if (status.isDelayCycle && !status.isForcedInstruction) { return; } final Instruction instruction = status.instruction; if (instruction == null) { throw new InternalError("seems emulator started with falling " + "clock edge: can not execute instruction " + "before decode"); } status.resultState = instruction.execute(this); if (status.resultState == Instruction.ResultState.COMPLETE) { /* * Sect. 3.4.2.2.: "Delay cycles … take place after … the program * counter is updated" (though this specifically refers to JMP * instruction). => Update PC immediately, before executing * delay. */ updatePC(); } /* * Sect. 3.5.7.: "Delay cycles are ignored on instructions written * to the INSTR register." */ if (!status.isForcedInstruction) { if (status.resultState != Instruction.ResultState.STALL) { status.setPendingDelay(instruction.getDelay()); } } else { status.isForcedInstruction = false; } } private void executeAsyncAutoPull() { /* * Cp. pseudocode sequence for non-"OUT" cycles in RP2040 * datasheet, Sect. 3.5.4.2. "Autopull Details". */ final boolean osrCountBeyondThreshold = status.isOsrCountBeyondThreshold(); if (osrCountBeyondThreshold) { final boolean txFifoEmpty = fifo.fstatTxEmpty(); /* * TODO: Check: Possible race condition between above * fifo.fstatTxEmpty() and below fifo.txPull()? */ if (!txFifoEmpty) { status.osrValue = fifo.txPull(false); status.osrShiftCount = 0; } } } private void execute() { executeInstruction(); /* * TODO: Clarify when the asynchronous fill mechanism is enabled. * On each master clock cycle? Or only when the state machine is * enabled? Or even only if clock enabled is true * (i.e. considering clock divider)? Currently, we check for * clock enable. */ if (status.clockEnabled && status.regSHIFTCTRL_AUTOPULL) { if (!(status.instruction instanceof Instruction.Out)) { executeAsyncAutoPull(); } } status.flushCollatePins(); } public boolean isStalled() { return status.resultState == Instruction.ResultState.STALL; } public boolean isDelayCycle() { return !status.isForcedInstruction && status.isDelayCycle; } @Override public String toString() { return "SM" + num + "{PC=" + getPC() + "}"; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/SwingUtils.java ================================================ /* * @(#)SwingUtils.java 1.00 21/04/07 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio; import java.awt.Component; import java.awt.Dimension; import java.io.IOException; import java.net.URL; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JMenuItem; public class SwingUtils { public static ImageIcon createImageIcon(final String iconFileName, final String label) throws IOException { final String iconPath = "/media/" + iconFileName; final URL iconURL = SwingUtils.class.getResource(iconPath); if (iconURL != null) { return new ImageIcon(iconURL, label); } throw new IOException("icon resource not found: " + iconPath); } public static JMenuItem createIconMenuItem(final String iconFileName, final String label) { final JMenuItem menuItem = new JMenuItem(label); try { final ImageIcon icon = createImageIcon(iconFileName, label); menuItem.setIcon(icon); } catch (final IOException e) { System.err.println("failed creating JMenuItem icon: " + e.getMessage()); } return menuItem; } public static JButton createIconButton(final String iconFileName, final String label) { final JButton button = new JButton(label); try { final ImageIcon icon = createImageIcon(iconFileName, label); return new JButton(icon); } catch (final IOException e) { System.err.println("failed creating JButton icon: " + e.getMessage()); return null; } } public static void setPreferredWidthAsMaximum(final Component component) { final Dimension preferredSize = component.getPreferredSize(); final Dimension maximumSize = new Dimension(preferredSize.width, Integer.MAX_VALUE); component.setMaximumSize(maximumSize); } public static void setPreferredHeightAsMaximum(final Component component) { final Dimension preferredSize = component.getPreferredSize(); final Dimension maximumSize = new Dimension(Integer.MAX_VALUE, preferredSize.height); component.setMaximumSize(maximumSize); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/doctool/DocsBuilder.java ================================================ /* * @(#)DocsBuilder.java 1.00 21/04/18 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.doctool; import java.io.FileWriter; import java.io.IOException; /** * Automatically create Sphinx documentation for all Monitor commands, * using its integrated help functionality. */ public class DocsBuilder { public static String fill(final char ch, final int length) { final StringBuilder s = new StringBuilder(); s.setLength(length); for (int pos = 0; pos < length; pos++) { s.setCharAt(pos, ch); } return s.toString(); } public static String csvEncode(final String raw) { final StringBuilder s = new StringBuilder(); s.append("\""); boolean escaped = false; for (final char ch : raw.toCharArray()) { if (escaped) { s.append(ch); escaped = false; } else if (ch == '\\') { escaped = true; } else if (ch == '"') { s.append("\\\""); } else { s.append(ch); } } s.append("\""); return s.toString(); } /** * Replaces all characters that could have special meaning for * Sphinx. */ public static String createIdFromLabel(final String registersSetLabel) { return registersSetLabel .trim() .toLowerCase() .replace(" ", "_") .replace("\"", "") .replace("'", "") .replace("`", "") .replace(":", ""); } public static final String leadinComment = ".. # WARNING: This sphinx documentation file was automatically%n" + ".. # created directly from documentation info in the source code.%n" + ".. # DO NOT CHANGE THIS FILE, since changes will be lost upon%n" + ".. # its next update. Instead, change the info in the source code.%n" + ".. # This file was automatically created on:%n" + ".. # %s%n" + "%n"; public static void writeToFile(final String rstFilePath, final String docs) throws IOException { try { final FileWriter writer = new FileWriter(rstFilePath); writer.write(docs); writer.close(); } catch (final IOException e) { final String message = String.format("failed creating documentation file %s: %s%n", rstFilePath, e.getMessage()); throw new IOException(message, e); } } public static void main(final String argv[]) { System.out.println("building monitor commands documentation..."); MonitorCommandsDocsBuilder.main(argv); System.out.println("building registers documentation..."); RegistersDocsBuilder.main(argv); System.out.println("building example scripts documentation..."); ExampleScriptsDocsBuilder.main(argv); System.out.println("documentation successfully built"); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/doctool/ExampleScriptsDocsBuilder.java ================================================ /* * @(#)ExampleScriptsDocsBuilder.java 1.00 21/06/10 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.doctool; import java.io.IOException; import java.time.Instant; import java.util.Map; import org.soundpaint.rp2040pio.monitor.ScriptInfo; /** * Automatically create Sphinx documentation for all example scripts, * using the script file name and extracting information from the * comments in the script file header. */ public class ExampleScriptsDocsBuilder { private ExampleScriptsDocsBuilder() { throw new UnsupportedOperationException("unsupported empty constructor"); } private void listExampleScript(final StringBuilder s, final ScriptInfo scriptInfo) { final String scriptId = scriptInfo.getScriptId(); final String scriptName = scriptInfo.getScriptName(); final String description = scriptInfo.getDescription(); s.append(String.format(".. index::%n")); s.append(String.format(" single: script; %s%n", scriptId)); s.append(String.format(" single: %s script%n", scriptId)); s.append(String.format("%n")); s.append(String.format(".. _%s-example-script:%n", scriptId)); s.append(String.format("%n")); final String scriptTitle = String.format("%s (``%s``)", scriptName, scriptId); s.append(String.format(scriptTitle + "%n")); s.append(String.format("%s%n", "^".repeat(scriptTitle.length()))); s.append(String.format("%n")); s.append(description); s.append(String.format("%n")); } private void listExampleScriptGroup(final StringBuilder s, final Map scriptsGroupInfo, final String groupName) { s.append(String.format(".. index::%n")); s.append(String.format(" single: script group; %s%n", groupName)); s.append(String.format(" single: %s script group%n", groupName)); s.append(String.format("%n")); s.append(String.format(".. _%s-example-script-group:%n", groupName)); s.append(String.format("%n")); final String groupTitle = String.format("%s", groupName); s.append(String.format(groupTitle + "%n")); s.append(String.format("%s%n", "-".repeat(groupTitle.length()))); s.append(String.format("%n")); for (final String scriptId : scriptsGroupInfo.keySet()) { final ScriptInfo scriptInfo = scriptsGroupInfo.get(scriptId); listExampleScript(s, scriptInfo); } } private void listExampleScripts(final Map> scriptsInfo, final StringBuilder s) throws IOException { Map defaultScriptsGroupInfo = null; for (final String groupName : scriptsInfo.keySet()) { final Map scriptsGroupInfo = scriptsInfo.get(groupName); if (groupName != ScriptInfo.DEFAULT_GROUP_NAME) { listExampleScriptGroup(s, scriptsGroupInfo, groupName); } else { // defer default group to end defaultScriptsGroupInfo = scriptsGroupInfo; } } if (defaultScriptsGroupInfo != null) { listExampleScriptGroup(s, defaultScriptsGroupInfo, ScriptInfo.DEFAULT_GROUP_NAME); } else { throw new IOException("default script group not found"); } } private String createDocs() throws IOException { final StringBuilder s = new StringBuilder(); s.append(String.format(DocsBuilder.leadinComment, Instant.now())); s.append(String.format(".. index::%n")); s.append(String.format(" single: scripts; examples%n")); s.append(String.format(" single: example scripts%n")); s.append(String.format(" single: reference; example scripts%n")); s.append(String.format("%n")); s.append(String.format(".. _example-scripts-reference:%n")); s.append(String.format("%n")); s.append(String.format("Example Scripts Reference%n")); s.append(String.format("=========================%n")); s.append(String.format("%n")); s.append(String.format("The RP2040 PIO Emulator and client%n")); s.append(String.format("applications are bundled with a set of%n")); s.append(String.format("built-in example scripts. These scripts%n")); s.append(String.format("loosely follow some of the PIO code examples%n")); s.append(String.format("in the RP2040 datasheet, but are adapted to%n")); s.append(String.format("run as Monitor scripts, using the Monitor%n")); s.append(String.format("specific syntax of commands.%n")); s.append(String.format("%n")); s.append(String.format("In the Monitor application, the set of these")); s.append(String.format("built-in example scripts can be listed with%n")); s.append(String.format("the Monitor command ``script --list``.%n")); s.append(String.format("%n")); final Map> scriptsInfo = ScriptInfo.createScriptsInfo(); listExampleScripts(scriptsInfo, s); return s.toString(); } public ExampleScriptsDocsBuilder(final String rstFilePath) throws IOException { final String docs = createDocs(); DocsBuilder.writeToFile(rstFilePath, docs); } public static void main(final String argv[]) { try { new ExampleScriptsDocsBuilder("example-scripts.rst"); } catch (final IOException e) { final String message = String.format("failed creating example scripts documentation: %s%n", e.getMessage()); System.err.printf(message); System.exit(-1); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/doctool/MonitorCommandsDocsBuilder.java ================================================ /* * @(#)MonitorCommandsDocsBuilder.java 1.00 21/04/18 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.doctool; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.io.PrintStream; import java.time.Instant; import java.util.ArrayList; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.soundpaint.rp2040pio.AddressSpace; import org.soundpaint.rp2040pio.Emulator; import org.soundpaint.rp2040pio.LocalAddressSpace; import org.soundpaint.rp2040pio.PicoEmuRegisters; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.monitor.CommandRegistry; import org.soundpaint.rp2040pio.sdk.SDK; /** * Automatically create Sphinx documentation for all Monitor commands, * using its integrated help functionality. */ public class MonitorCommandsDocsBuilder { private MonitorCommandsDocsBuilder() { throw new UnsupportedOperationException("unsupported empty constructor"); } private String createCommandDocs(final Command command) { final StringBuilder s = new StringBuilder(); final String commandName = command.getFullName(); s.append(String.format(".. index::%n")); s.append(String.format(" single: monitor command; %s%n", commandName)); s.append(String.format(" single: %s%n", commandName)); s.append(String.format("%n")); s.append(String.format(".. _%s-command-label:%n", commandName)); s.append(String.format("%n")); s.append(String.format(commandName + "%n")); s.append(String.format(DocsBuilder.fill('-', commandName.length()) + "%n")); s.append(String.format("%n")); s.append(String.format("**Usage**%n")); s.append(String.format("^^^^^^^^^%n")); s.append(String.format("%n%s%n%n", command.getUsage())); s.append(String.format("**Description**%n")); s.append(String.format("^^^^^^^^^^^^^^^%n")); s.append(String.format("%n%s%n%n", command.getSingleLineDescription())); final String optionsHelp = command.getOptionsHelp(); if (!optionsHelp.isEmpty()) { s.append(String.format("**Options**%n")); s.append(String.format("^^^^^^^^^^^%n")); s.append(String.format("%n%s%n", optionsHelp)); } final String notes = command.getNotes(); if ((notes != null) && !notes.isEmpty()) { s.append(String.format("**Notes**%n")); s.append(String.format("^^^^^^^^^%n")); s.append(String.format("%n" + notes + "%n%n")); } s.append(String.format(":ref:`Back to Overview `%n")); s.append(String.format("%n")); return s.toString(); } private String createCommandsOverview(final CommandRegistry commandRegistry) { final StringBuilder s = new StringBuilder(); s.append(String.format(".. index::%n")); s.append(String.format(" single: monitor; commands overview%n")); s.append(String.format("%n")); s.append(String.format(".. _commands-overview:%n")); s.append(String.format("%n")); s.append(String.format("Overview%n")); s.append(String.format("--------%n")); s.append(String.format("%n")); s.append(String.format("The monitor supports all of the commands%n")); s.append(String.format("listed below.%n")); s.append(String.format("%n")); s.append(String.format(".. csv-table::%n")); s.append(String.format(" :header: Command, Short Description%n")); s.append(String.format(" :widths: 20, 80%n")); s.append(String.format("%n")); for (final Command command : commandRegistry) { final String commandName = command.getFullName(); final String commandRef = String.format(":ref:`%s <%s-command-label>`", commandName, commandName); final String commandDescription = command.getSingleLineDescription(); s.append(String.format(" %s,%s%n", DocsBuilder.csvEncode(commandRef), DocsBuilder.csvEncode(commandDescription))); } s.append(String.format("%n")); return s.toString(); } private String createDocs() throws IOException { final StringBuilder s = new StringBuilder(); s.append(String.format(DocsBuilder.leadinComment, Instant.now())); s.append(String.format("Monitor & Control Program Commands Reference%n")); s.append(String.format("============================================%n")); s.append(String.format("%n")); s.append(String.format("The *Monitor & Control Program*, or in short,%n")); s.append(String.format("just *monitor*, features a set of built-in%n")); s.append(String.format("commands with integrated, self-documenting%n")); s.append(String.format("help. The following reference documentation%n")); s.append(String.format("has been compiled from these sources.%n")); s.append(String.format("%n")); final PrintStream console = System.out; final Emulator emulator = new Emulator(console); final AddressSpace memory = new LocalAddressSpace(emulator); final BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); final SDK sdk = new SDK(console, memory); final CommandRegistry commandRegistry = new CommandRegistry(console, in, sdk, null); s.append(createCommandsOverview(commandRegistry)); for (final Command command : commandRegistry) { s.append(createCommandDocs(command)); } emulator.terminate(); return s.toString(); } public MonitorCommandsDocsBuilder(final String rstFilePath) throws IOException { final String docs = createDocs(); DocsBuilder.writeToFile(rstFilePath, docs); } public static void main(final String argv[]) { try { new MonitorCommandsDocsBuilder("monitor-commands.rst"); } catch (final IOException e) { final String message = String.format("failed creating monitor commands documentation: %s%n", e.getMessage()); System.err.printf(message); System.exit(-1); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/doctool/RegistersDocs.java ================================================ /* * @(#)RegistersDocs.java 1.00 21/04/15 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.doctool; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import org.soundpaint.rp2040pio.Constants; /** * Documentation interface for automatic creation of registers * documentation from annotations in the code. */ public interface RegistersDocs { public enum BitsType { RESERVED("―", "n/a"), UNUSED("―", "unused"), SC("SC", "???"), WC("WC", "write 1 to clear"), RW("RW", "read/write"), RO("RO", "read-only"), WO("WO", "write-only"), RF("RF", "read to trigger function"), WF("WF", "wrtite to trigger function"); private final String id; private final String description; private BitsType(final String id, final String description) { this.id = id; this.description = description; } public boolean isRelevant() { return (this != RESERVED) && (this != UNUSED); } public String getId() { return id; } public String getDescription() { return description; } } public static class BitsRange { private final int msb; private final int lsb; private BitsRange() { throw new UnsupportedOperationException("unsupported empty constructor"); } public BitsRange(final int msb, final int lsb) { if (msb < 0) { throw new IllegalArgumentException("msb < 0: " + msb); } if (msb > 31) { throw new IllegalArgumentException("msb > 31: " + msb); } if (lsb < 0) { throw new IllegalArgumentException("lsb < 0: " + lsb); } if (lsb > 31) { throw new IllegalArgumentException("lsb > 31: " + lsb); } if (lsb > msb) { throw new IllegalArgumentException("lsb > msb: " + lsb + " > " + msb); } this.msb = msb; this.lsb = lsb; } public int getMsb() { return msb; } public int getLsb() { return lsb; } public String toShortString() { return msb == lsb ? String.format("%d", msb) : String.format("%d:%d", msb, lsb); } @Override public String toString() { return msb == lsb ? String.format("bit %d", msb) : String.format("bits [%d:%d]", msb, lsb); } } public static class BitsInfo { private final String name; private final BitsRange bitsRange; private final String description; private final BitsType type; private final Integer resetValue; private BitsInfo() { throw new UnsupportedOperationException("unsupported empty constructor"); } public BitsInfo(final String name, final int msb, final int lsb, final String description, final BitsType type, final Integer resetValue) { this(name, new BitsRange(msb, lsb), description, type, resetValue); } public BitsInfo(final String name, final BitsRange bitsRange, final String description, final BitsType type, final Integer resetValue) { if (bitsRange == null) { throw new NullPointerException("bitsRange"); } if (type == null) { throw new NullPointerException("type"); } if (resetValue != null) { final int msb = bitsRange.msb; final int lsb = bitsRange.lsb; final long maxResetValue = ((long)0x1 << (msb - lsb + 1)) - 1; final long resetValueAsLong = 0x00000000FFFFFFFFL & (long)resetValue; if (resetValueAsLong > maxResetValue) { final String message = String.format("%s [%d:%d]: " + "resetValueAsLong > maxResetValue: %d > %d", name, msb, lsb, resetValueAsLong, maxResetValue); throw new IllegalArgumentException(message); } } this.name = name; this.bitsRange = bitsRange; this.description = description; this.type = type; this.resetValue = resetValue; } public int getMsb() { return bitsRange.msb; } public int getLsb() { return bitsRange.lsb; } public String getName() { return name; } public BitsRange getBitsRange() { return bitsRange; } public String getDescription() { return description; } public BitsType getType() { return type; } public Integer getResetValue() { return resetValue; } private static String renderName(final String name) { return name != null ? name + ": " : ""; } private static String renderName(final String name, final String defaultName) { return renderName(name != null ? name : defaultName); } private String renderBitsRange() { return bitsRange.toString(); } private String renderDescription() { return description != null ? " " + description : ""; } private String renderType() { return String.format("Type: %s", type); } private String renderResetValue() { if ((type == BitsType.UNUSED) || (type == BitsType.RESERVED)) return ""; return String.format(", Reset Value: %s", resetValue != null ? resetValue : "―"); } public String toString(final String defaultName) { return String.format("%s %s%s, %s %s", renderName(name, defaultName), renderBitsRange(), renderDescription(), renderType(), renderResetValue()); } @Override public String toString() { return toString(null); } } public static class RegisterDetails { public static final int SM_UNDEFINED = -1; private String info; private int smNum; private List bitsInfos; private static void checkSmNum(final int smNum) { if (smNum != SM_UNDEFINED) { Constants.checkSmNum(smNum); } } private RegisterDetails() { throw new UnsupportedOperationException("unsupported empty constructor"); } public RegisterDetails(final String info, final BitsInfo[] bitsInfos) { this(info, Arrays.asList(bitsInfos)); } public RegisterDetails(final String info, final int smNum, final BitsInfo[] bitsInfos) { this(info, smNum, Arrays.asList(bitsInfos)); } public RegisterDetails(final String info, final List bitsInfos) { this(info, SM_UNDEFINED, bitsInfos); } public RegisterDetails(final String info, final int smNum, final List bitsInfos) { Objects.requireNonNull(info); Objects.requireNonNull(bitsInfos); checkSmNum(smNum); this.info = info; this.smNum = smNum; this.bitsInfos = new ArrayList(); this.bitsInfos.addAll(bitsInfos); } public String getInfo() { return info; } public int getSmNum() { return smNum; } public Iterable getBitsInfos() { final List bitsInfosCopy = new ArrayList(); bitsInfosCopy.addAll(bitsInfos); return bitsInfosCopy; } public RegisterDetails createCopyForDifferentSm(final int smNum) { return new RegisterDetails(info, smNum, bitsInfos); } } String getInfo(); RegisterDetails getRegisterDetails(); } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/doctool/RegistersDocsBuilder.java ================================================ /* * @(#)RegistersDocsBuilder.java 1.00 21/04/15 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.doctool; import java.io.IOException; import java.time.Instant; import java.util.ArrayList; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.soundpaint.rp2040pio.PicoEmuRegisters; import org.soundpaint.rp2040pio.PIOEmuRegisters; /** * Automatically create registers documentation from annotations in * the code. */ public class RegistersDocsBuilder & RegistersDocs> { private static String formatBitsRange(final RegistersDocs.BitsInfo bitsInfo) { final int msb = bitsInfo.getMsb(); final int lsb = bitsInfo.getLsb(); if (msb == lsb) return String.format("%d", msb); return String.format("%d:%d", msb, lsb); } private static String formatName(final RegistersDocs.BitsInfo bitsInfo) { final String name = bitsInfo.getName(); if (bitsInfo.getType() == RegistersDocs.BitsType.RESERVED) return "Reserved."; if (bitsInfo.getType() == RegistersDocs.BitsType.UNUSED) return "Unused."; if (name == null) return "―"; return String.format(name); } private static String formatTableDescription(final String defaultDescription, final Iterable bitsInfos) { boolean hasBitsDescription = false; for (final RegistersDocs.BitsInfo bitsInfo : bitsInfos) { if (bitsInfo.getDescription() != null) { hasBitsDescription = true; break; } } if (hasBitsDescription) { if (defaultDescription != null) { return String.format(defaultDescription); } else { return null; } } else { return null; } } private static String formatDescription(final RegistersDocs.BitsInfo bitsInfo, final String defaultDescription) { final RegistersDocs.BitsType type = bitsInfo.getType(); final String bitsInfoDescription = bitsInfo.getDescription(); final String description; if (bitsInfoDescription != null) { description = bitsInfoDescription; } else if (type == RegistersDocs.BitsType.RESERVED) { description = "―"; } else if (type == RegistersDocs.BitsType.UNUSED) { description = "―"; } else if (defaultDescription != null) { description = defaultDescription; } else { description = "―"; } return DocsBuilder.csvEncode(description.replace("%n", " ")); } private static String formatType(final RegistersDocs.BitsInfo bitsInfo) { final RegistersDocs.BitsType type = bitsInfo.getType(); return String.format("%s", (type == RegistersDocs.BitsType.RESERVED) || (type == RegistersDocs.BitsType.UNUSED) ? "―" : type); } private static String formatResetValue(final RegistersDocs.BitsInfo bitsInfo) { final Integer resetValue = bitsInfo.getResetValue(); if (resetValue == null) return "―"; return String.format("%d", resetValue); } private String createDetailTableLabels(final List regsList) { final StringBuilder labels = new StringBuilder(); for (final T reg : regsList) { labels.append(String.format(".. _%s-details-label:%n", reg.toString())); } labels.append(String.format("%n")); return labels.toString(); } private String createDetailTableIndices(final List regsList) { final StringBuilder labels = new StringBuilder(); for (final T reg : regsList) { labels.append(String.format(".. index::%n")); labels.append(String.format(" single: register details; %s%n", reg.toString())); labels.append(String.format(" single: %s%n%n", reg.toString())); } return labels.toString(); } private String formatRegNames(final List regsList) { final StringBuilder regNames = new StringBuilder(); boolean multiple = false; for (final T reg : regsList) { if (regNames.length() > 0) { regNames.append(", "); multiple = true; } regNames.append(reg.toString()); } regNames.append(multiple ? " Registers" : " Register"); return String.format("%s", regNames); } private String formatOffsets(final List regsList) { final StringBuilder offsets = new StringBuilder(); boolean haveMultipleRegs = false; for (final T reg : regsList) { if (offsets.length() > 0) { offsets.append(", "); haveMultipleRegs = true; } offsets.append(String.format("0x%03x", reg.ordinal() << 2)); } return String.format("**%s:** %s", haveMultipleRegs ? "Offsets" : "Offset", offsets); } private String createDetailTable(final String registersSetLabel, final RegistersDocs.RegisterDetails registerDetails, final List regsList) { final StringBuilder s = new StringBuilder(); s.append(createDetailTableLabels(regsList)); s.append(createDetailTableIndices(regsList)); final String regNames = formatRegNames(regsList); final String registersSetId = DocsBuilder.createIdFromLabel(registersSetLabel); final String headLine = String.format(":ref:`%s `: %s", registersSetLabel, registersSetId, regNames); s.append(String.format("%s%n", headLine)); s.append(String.format("%s%n", DocsBuilder.fill('-', headLine.length()))); s.append(String.format("%n")); final String offsets = formatOffsets(regsList); s.append(String.format("%s%n", offsets)); s.append(String.format("%n")); final String defaultDescription = registerDetails.getInfo(); final String tableDescription = formatTableDescription(defaultDescription, registerDetails.getBitsInfos()); if (tableDescription != null) { s.append(String.format("**Description**%n%n%s%n", tableDescription)); s.append(String.format("%n")); } s.append(String.format(".. csv-table::%n")); s.append(String.format(" :header: Bits, Name, Description, Type, Reset%n")); s.append(String.format(" :widths: 8, 20, 40, 8, 20%n")); s.append(String.format("%n")); for (final T.BitsInfo bitsInfo : registerDetails.getBitsInfos()) { final String bitsRange = formatBitsRange(bitsInfo); final String name = formatName(bitsInfo); final String description = formatDescription(bitsInfo, defaultDescription); final String type = formatType(bitsInfo); final String reset = formatResetValue(bitsInfo); s.append(String.format(" %s, %s, %s, %s, %s%n", bitsRange, name, description, type, reset)); } s.append(String.format("%n")); return s.toString(); } private String createDetailTableRef(final T reg) { final String regName = reg.toString(); return String.format(":ref:`%s <%s-details-label>`", regName, regName); } private String createOverviewTable(final String registersSetLabel, final String registersSetDescription, final EnumSet regs, final Map> registerDetails2regs) { final StringBuilder s = new StringBuilder(); final String registersSetId = DocsBuilder.createIdFromLabel(registersSetLabel); s.append(String.format(".. _section-top_%s:%n", registersSetId)); s.append(String.format("%n")); s.append(String.format(".. index::%n")); s.append(String.format(" single: %s%n", registersSetLabel)); s.append(String.format(" single: registers set; %s%n", registersSetLabel)); s.append(String.format("%n")); final String sectionHeader = String.format("%s", registersSetLabel); s.append(String.format("%s%n", sectionHeader)); s.append(String.format("%s%n", DocsBuilder.fill('=', sectionHeader.length()))); s.append(String.format("%n")); final String overviewTableHeader = "List of Registers"; s.append(String.format("%s%n", overviewTableHeader)); s.append(String.format("%s%n", DocsBuilder.fill('-', overviewTableHeader.length()))); s.append(String.format("%n")); if (registersSetDescription != null) { s.append(String.format(registersSetDescription)); s.append(String.format("%n")); s.append(String.format("%n")); } s.append(String.format(".. csv-table::%n")); s.append(String.format(" :header: Offset, Name, Info%n")); s.append(String.format(" :widths: 8, 20, 40%n")); s.append(String.format("%n")); int address = 0x000; for (final T reg : regs) { final T.RegisterDetails registerDetails = reg.getRegisterDetails(); final List regsList; if (registerDetails2regs.containsKey(registerDetails)) { regsList = registerDetails2regs.get(registerDetails); } else { regsList = new ArrayList(); registerDetails2regs.put(registerDetails, regsList); } regsList.add(reg); s.append(String.format(" 0x%03x, %s, %s%n", address, createDetailTableRef(reg), DocsBuilder.csvEncode(reg.getInfo(). replace("%n", " ")))); address += 0x004; } s.append(String.format("%n")); return s.toString(); } private String createDocs(final String registersSetLabel, final String registersSetDescription, final EnumSet regs) { final Map> registerDetails2regs = new LinkedHashMap>(); final StringBuilder s = new StringBuilder(); s.append(String.format(DocsBuilder.leadinComment, Instant.now())); s.append(createOverviewTable(registersSetLabel, registersSetDescription, regs, registerDetails2regs)); for (final T.RegisterDetails registerDetails : registerDetails2regs.keySet()) { final List regsList = registerDetails2regs.get(registerDetails); s.append(createDetailTable(registersSetLabel, registerDetails, regsList)); } return s.toString(); } private RegistersDocsBuilder() { throw new UnsupportedOperationException("unsupported empty constructor"); } public RegistersDocsBuilder(final Class regsClass, final String registerSetLabel, final String registerSetDescription, final String rstFilePath) throws IOException { final String docs = createDocs(registerSetLabel, registerSetDescription, EnumSet.allOf(regsClass)); DocsBuilder.writeToFile(rstFilePath, docs); } public static void main(final String argv[]) { try { new RegistersDocsBuilder (PicoEmuRegisters.Regs.class, PicoEmuRegisters.Regs.getRegisterSetLabel(), PicoEmuRegisters.Regs.getRegisterSetDescription(), "pico-emu-registers.rst"); new RegistersDocsBuilder (PIOEmuRegisters.Regs.class, PIOEmuRegisters.Regs.getRegisterSetLabel(), PIOEmuRegisters.Regs.getRegisterSetDescription(), "pio-emu-registers.rst"); } catch (final IOException e) { final String message = String.format("failed creating registers documentation: %s%n", e.getMessage()); System.err.printf(message); System.exit(-1); } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/Command.java ================================================ /* * @(#)Command.java 1.00 21/03/28 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.soundpaint.rp2040pio.CmdOptions; /** * Common abstract super class for all monitor commands. */ public abstract class Command { private static final List> EMPTY_OPTION_DECLARATIONS = new ArrayList>(); protected static final CmdOptions.FlagOptionDeclaration optHelp = CmdOptions.createFlagOption(false, 'h', "help", CmdOptions.Flag.OFF, "display this help text and exit"); protected static final String commandHint = "For a list of available commands, enter 'help'.%n" + "When invoked with the \"-s\" script execution option, the specified%n" + "script is executed, and the monitor exits immediately after execution."; protected static final String helpNotes = "For detail help of a command, enter: -h.%n" + "Commands may be abbreviated as long as unambiguity is preserved."; protected static final String panicNotes = "The system may be now in a corrupted state.%n" + "You may consider to fully reset the emulator with%n" + "the \"reset\" commmand, if unexpected behavior shows up."; protected final PrintStream console; private final String fullName; private final String singleLineDescription; private final String notes; private final List> optionDeclarations; private final CmdOptions options; private Command() { throw new UnsupportedOperationException("unsupported empty constructor"); } public Command(final PrintStream console, final String fullName, final String singleLineDescription) { this(console, fullName, singleLineDescription, null, EMPTY_OPTION_DECLARATIONS); } public Command(final PrintStream console, final String fullName, final String singleLineDescription, final String notes) { this(console, fullName, singleLineDescription, notes, EMPTY_OPTION_DECLARATIONS); } public Command(final PrintStream console, final String fullName, final String singleLineDescription, final CmdOptions.OptionDeclaration[] optionDeclarations) { this(console, fullName, singleLineDescription, Arrays.asList(optionDeclarations)); } public Command(final PrintStream console, final String fullName, final String singleLineDescription, final String notes, final CmdOptions.OptionDeclaration[] optionDeclarations) { this(console, fullName, singleLineDescription, notes, Arrays.asList(optionDeclarations)); } public Command(final PrintStream console, final String fullName, final String singleLineDescription, final List> optionDeclarations) { this(console, fullName, singleLineDescription, null, optionDeclarations); } public Command(final PrintStream console, final String fullName, final String singleLineDescription, final String notes, final List> optionDeclarations) { if (console == null) { throw new NullPointerException("console"); } if (fullName == null) { throw new NullPointerException("fullName"); } if (fullName.length() == 0) { throw new IllegalArgumentException("empty fullName"); } if (singleLineDescription == null) { throw new NullPointerException("singleLineDescription"); } if (optionDeclarations == null) { throw new NullPointerException("optionDeclarations"); } for (final CmdOptions.OptionDeclaration decl : optionDeclarations) { if (decl == null) { throw new NullPointerException("null value in optionDeclarations"); } } this.console = console; this.fullName = fullName; this.singleLineDescription = singleLineDescription; this.notes = notes; this.optionDeclarations = new ArrayList>(); this.optionDeclarations.addAll(optionDeclarations); this.optionDeclarations.add(optHelp); try { options = new CmdOptions(fullName, singleLineDescription, notes, this.optionDeclarations); } catch (final CmdOptions.ParseException e) { throw new InternalError("unexpected command configuration error"); } } public String getFullName() { return fullName; } public String getSingleLineDescription() { return singleLineDescription; } public String getNotes() { return notes; } public String getUsage() { return options.getUsage(); } public String getOptionsHelp() { return options.getOptionsHelp(); } public Iterator> getOptionDeclarationsIterator() { return optionDeclarations.iterator(); } /** * Returns true if no error occurred. */ public void parse(final String[] argv) throws CmdOptions.ParseException { options.parse(argv, true); checkValidity(options); } /** * Returns true if no error occurred and the command has been * executed. */ public boolean execute() throws IOException { if (options.getValue(optHelp) == CmdOptions.Flag.ON) { console.println(options.getFullInfo()); return false; } return execute(options); } /** * Returns true if no error occurred and the command has been * executed. */ protected abstract boolean execute(final CmdOptions options) throws IOException; /** * Subclasses that implement this abstract class should override * this method and throw a CmdOptions.ParseException, if they * require additional constraints that the supplied options do not * fulfill. An example for a possible constraint is a combination * of two options that both are valid if specified alone, but that * can not be specified together. Another example is a value * constraint, e.g. if a string parameter must be from a fixed set * of strings or may not contain specific special characters. */ protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { /** * Example code for illegal combination of silent flag and verbose * flag: * * if ((options.getValue(optSilent) == CmdOptions.Flag.ON) && * (options.getValue(optVerbose) == CmdOptions.Flag.ON)) { * throw new CmdOptions. * ParseException("at most one of 'silent', 'verbose' allowed"); * } */ } public String getHelp() { return options.getFullInfo(); } @Override public String toString() { return "command \"" + fullName + "\""; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/CommandRegistry.java ================================================ /* * @(#)CommandRegistry.java 1.00 21/03/28 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.ParseException; import org.soundpaint.rp2040pio.monitor.commands.BreakPoints; import org.soundpaint.rp2040pio.monitor.commands.Clear; import org.soundpaint.rp2040pio.monitor.commands.Clock; import org.soundpaint.rp2040pio.monitor.commands.Enter; import org.soundpaint.rp2040pio.monitor.commands.Execute; import org.soundpaint.rp2040pio.monitor.commands.Fifo; import org.soundpaint.rp2040pio.monitor.commands.Gpio; import org.soundpaint.rp2040pio.monitor.commands.Help; import org.soundpaint.rp2040pio.monitor.commands.Interrupt; import org.soundpaint.rp2040pio.monitor.commands.Label; import org.soundpaint.rp2040pio.monitor.commands.Load; import org.soundpaint.rp2040pio.monitor.commands.Quit; import org.soundpaint.rp2040pio.monitor.commands.PinCtrl; import org.soundpaint.rp2040pio.monitor.commands.Read; import org.soundpaint.rp2040pio.monitor.commands.Registers; import org.soundpaint.rp2040pio.monitor.commands.Reset; import org.soundpaint.rp2040pio.monitor.commands.Save; import org.soundpaint.rp2040pio.monitor.commands.Script; import org.soundpaint.rp2040pio.monitor.commands.SideSet; import org.soundpaint.rp2040pio.monitor.commands.Sm; import org.soundpaint.rp2040pio.monitor.commands.Trace; import org.soundpaint.rp2040pio.monitor.commands.Unassemble; import org.soundpaint.rp2040pio.monitor.commands.Unload; import org.soundpaint.rp2040pio.monitor.commands.Version; import org.soundpaint.rp2040pio.monitor.commands.Wait; import org.soundpaint.rp2040pio.monitor.commands.Wrap; import org.soundpaint.rp2040pio.monitor.commands.Write; import org.soundpaint.rp2040pio.sdk.SDK; /** * Used for command dispatching. */ public class CommandRegistry implements Iterable { private final PrintStream console; private final Set commands; private final HashMap> token2commands; private final Command quit; private CommandRegistry() { throw new UnsupportedOperationException("unsupported empty constructor"); } public CommandRegistry(final PrintStream console, final BufferedReader in, final SDK sdk, final String appFullName) { if (console == null) { throw new NullPointerException("console"); } this.console = console; commands = new TreeSet((cmd1, cmd2) -> cmd1.getFullName().compareTo(cmd2.getFullName())); token2commands = new HashMap>(); quit = installCommands(in, sdk, appFullName); } private Quit installCommands(final BufferedReader in, final SDK sdk, final String appFullName) { final Quit quit; add(new BreakPoints(console, sdk)); add(new Clear(console)); add(new Clock(console, sdk)); add(new Enter(console, sdk, in)); add(new Execute(console, sdk)); add(new Fifo(console, sdk)); add(new Gpio(console, sdk)); add(new Help(console, this)); add(new Interrupt(console, sdk)); add(new Label(console, sdk)); add(new Load(console, sdk)); add(quit = new Quit(console)); add(new PinCtrl(console, sdk)); add(new Read(console, sdk)); add(new Registers(console, sdk)); add(new Reset(console, sdk)); add(new Save(console, sdk)); add(new Script(console, this)); add(new SideSet(console, sdk)); add(new Sm(console, sdk)); add(new Trace(console, sdk)); add(new Unassemble(console, sdk)); add(new Unload(console, sdk)); add(new Version(console, sdk, appFullName)); add(new Wait(console, sdk)); add(new Wrap(console, sdk)); add(new Write(console, sdk)); return quit; } private void add(final Command command) { if (commands.contains(command)) { throw new IllegalArgumentException("command already registered"); } final String fullName = command.getFullName(); for (final Command otherCommand : commands) { final String otherFullName = otherCommand.getFullName(); if (fullName.startsWith(otherFullName)) { throw new IllegalArgumentException("name clash with other command: " + otherCommand); } if (otherFullName.startsWith(fullName)) { throw new IllegalArgumentException("name clash with other command: " + otherCommand); } } commands.add(command); updateTokenHashes(command); } private void updateTokenHashes(final Command command) { final String fullName = command.getFullName(); for (int i = 1; i <= fullName.length(); i++) { final String partialName = fullName.substring(0, i); final List commands; if (token2commands.containsKey(partialName)) { commands = token2commands.get(partialName); } else { commands = new ArrayList(); token2commands.put(partialName, commands); } commands.add(command); } } public void remove(final Command command) { if (!commands.contains(command)) { throw new IllegalArgumentException("no such command registered"); } commands.remove(command); final String fullName = command.getFullName(); for (int i = 1; i < fullName.length(); i++) { final String partialName = fullName.substring(0, i); final List commands = token2commands.get(partialName); commands.remove(command); } } public List lookup(final String partialName) { if (token2commands.containsKey(partialName)) { return token2commands.get(partialName); } return null; } @Override public Iterator iterator() { return commands.iterator(); } private ParseException createCommandParseException(final Command command, final Exception e) { final String helpNotes = String.format(Command.helpNotes); final String message = String.format("%s:%n%s%n%s", command, e.getMessage(), helpNotes); return new ParseException(message); } /** * @return <code>true</code>, if and only if command * "quit" is to be successfully be executed (the command itself, not * showing help with "-h" option). * @throws <code>ParseException</code>, if the command * can not be executed due to a parse exception. In dry-run mode, * checking for this exception can be used to check the syntax of a * command without actually executing it. */ public boolean parseAndExecute(final String commandLine, boolean dryRun) throws ParseException { final String[] argv; try { argv = CmdOptions.splitArgs(commandLine); } catch (final CmdOptions.ParseException e) { final String message = String.format("failed tokenizing command line: %s%n", e.getMessage()); throw new ParseException(message, e); } if (argv.length == 0) { return false; } final String commandToken = argv[0]; final List matchingCommands = lookup(commandToken); if ((matchingCommands == null) || (matchingCommands.size() == 0)) { final String message = String.format("unknown command: %s%n%s%n", commandToken, String.format(Command.commandHint)); throw new ParseException(message); } if (matchingCommands.size() > 1) { final String message = String.format("ambiguous command: %s%npossible resolutions: %s%n", commandToken, matchingCommands); throw new ParseException(message); } final Command command = matchingCommands.get(0); try { command.parse(argv); } catch (final CmdOptions.ParseException e) { throw createCommandParseException(command, e); } if (dryRun) { return false; } final boolean executed; try { executed = command.execute(); } catch (final IOException e) { throw createCommandParseException(command, e); } return (command == quit) && executed; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/Monitor.java ================================================ /* * @(#)Monitor.java 1.00 21/02/02 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.PrintStream; import java.util.Arrays; import java.util.List; import org.soundpaint.rp2040pio.AddressSpace; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.IOUtils; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.RemoteAddressSpaceClient; import org.soundpaint.rp2040pio.sdk.GPIOSDK; import org.soundpaint.rp2040pio.sdk.Panic; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; import org.soundpaint.rp2040pio.sdk.Program; import org.soundpaint.rp2040pio.sdk.ProgramParser; /** * Program Execution Monitor And Control */ public class Monitor { private static final String APP_TITLE = "Monitor"; private static final String APP_FULL_NAME = "Emulation Monitor Control Program Version 0.1"; private static final CmdOptions.FlagOptionDeclaration optVersion = CmdOptions.createFlagOption(false, 'V', "version", CmdOptions.Flag.OFF, "display version information and exit"); private static final CmdOptions.FlagOptionDeclaration optHelp = CmdOptions.createFlagOption(false, 'h', "help", CmdOptions.Flag.OFF, "display this help text and exit"); private static final CmdOptions.IntegerOptionDeclaration optPort = CmdOptions.createIntegerOption("PORT", false, 'p', "port", Constants. REGISTER_SERVER_DEFAULT_PORT_NUMBER, "use PORT as server port number"); private static final CmdOptions.StringOptionDeclaration optExample = CmdOptions.createStringOption("NAME", false, 'e', "example", null, "name of built-in example script to execute"); private static final CmdOptions.StringOptionDeclaration optFile = CmdOptions.createStringOption("PATH", false, 'f', "file", null, "path of monitor script file to execute"); private static final List> optionDeclarations = Arrays.asList(new CmdOptions.OptionDeclaration[] { optVersion, optHelp, optPort, optExample, optFile }); private final BufferedReader in; private final PrintStream console; private final SDK sdk; private final PIOSDK pioSdk; private final GPIOSDK gpioSdk; private final CmdOptions options; private final CommandRegistry commands; private Monitor() { throw new UnsupportedOperationException("unsupported empty constructor"); } public Monitor(final BufferedReader in, final PrintStream console, final String[] argv) throws IOException { if (in == null) { throw new NullPointerException("in"); } if (console == null) { throw new NullPointerException("console"); } this.in = in; this.console = console; if ((options = parseArgs(argv)) != null) { printAbout(); sdk = new SDK(console, connect()); pioSdk = sdk.getPIO0SDK(); gpioSdk = sdk.getGPIOSDK(); commands = new CommandRegistry(console, in, sdk, APP_FULL_NAME); } else { sdk = null; pioSdk = null; gpioSdk = null; commands = null; } } private CmdOptions parseArgs(final String argv[]) throws IOException { final CmdOptions options; try { options = new CmdOptions(APP_TITLE, APP_FULL_NAME, null, optionDeclarations); options.parse(argv); checkValidity(options); } catch (final CmdOptions.ParseException e) { final String message = String.format("parsing command line failed: %s", e.getMessage()); throw new IOException(message); } if (options.getValue(optVersion) == CmdOptions.Flag.ON) { printAbout(); return null; } if (options.getValue(optHelp) == CmdOptions.Flag.ON) { console.println(options.getFullInfo()); return null; } return options; } private void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { final int port = options.getValue(optPort); if ((port < 0) || (port > 65535)) { throw new CmdOptions. ParseException("PORT must be in the range 0…65535"); } if (options.isDefined(optExample) && options.isDefined(optFile)) { throw new CmdOptions. ParseException("at most one of options \"-e\" and \"-f\" may be " + "specified at the same time"); } } private void printAbout() { console.printf("%s for%n%s%n%s%n%s%n", APP_FULL_NAME, Constants.getEmulatorIdAndVersionWithOs(), Constants.getMonitorCopyrightNotice(), String.format(Command.commandHint)); } private AddressSpace connect() throws IOException { final int port = options.getValue(optPort); try { console.printf("connecting to emulation server at port %d…%n", port); return new RemoteAddressSpaceClient(console, null, port); } catch (final IOException e) { final String message = String.format("failed to connect to emulation server: %s%n" + "check that emulation server runs at port address %d%n", e.getMessage(), port); throw new IOException(message); } } private int run(final boolean localEcho) { if (sdk == null) return 0; final BufferedReader scriptIn; try { if (options.isDefined(optExample)) { final String optExampleValue = options.getValue(optExample); final String resourcePath = String.format("/examples/%s.mon", optExampleValue); scriptIn = IOUtils.getReaderForResourcePath(resourcePath); } else if (options.isDefined(optFile)) { final String optFileValue = options.getValue(optFile); scriptIn = IOUtils.getReaderForResourcePath(optFileValue); } else { scriptIn = null; } } catch (final IOException e) { console.println(e.getMessage()); return -1; } return (scriptIn != null) ? session(scriptIn, false, true, "script> ") : session(in, false, localEcho, "> "); } private int session(final BufferedReader in, final boolean dryRun, final boolean localEcho, final String prompt) { try { while (true) { console.print(prompt); try { final String line = in.readLine(); if (line == null) break; if (localEcho) console.println(line); if (commands.parseAndExecute(line, dryRun)) break; } catch (final Panic | IOException e) { console.println(e.getMessage()); if (e instanceof Panic) { console.printf(Command.panicNotes); console.println(); } } } console.println("bye"); return 0; } catch (final RuntimeException e) { console.printf("fatal error: %s%n", e.getMessage()); console.println(); console.println("detailed debug information:"); e.printStackTrace(console); return -1; } } public static int main(final String argv[], final InputStream in, final PrintStream out, final boolean localEcho) { final BufferedReader reader = new BufferedReader(new InputStreamReader(in)); try { final int exitCode = new Monitor(reader, out, argv).run(localEcho); return exitCode; } catch (final IOException e) { out.println(e.getMessage()); return -1; } } public static void main(final String argv[]) { final int exitCode = main(argv, System.in, System.out, false); System.exit(exitCode); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/MonitorUtils.java ================================================ /* * @(#)MonitorUtils.java 1.00 21/04/14 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor; import java.io.IOException; import java.io.LineNumberReader; import java.io.PrintStream; import java.util.List; import java.util.stream.Collectors; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.IOUtils; import org.soundpaint.rp2040pio.PinState; import org.soundpaint.rp2040pio.sdk.GPIOSDK; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Utility methods that are used by multiple monitor commands. */ public class MonitorUtils { public static boolean listExampleHexDumps(final PrintStream console) throws IOException { final String suffix = ".hex"; final List examples = IOUtils.list("examples").stream(). filter(s -> s.endsWith(suffix)). map(s -> { return s.substring(0, s.length() - suffix.length()); }). collect(Collectors.toList()); for (final String example : examples) { console.printf("(pio*:sm*) %s%n", example); } return true; } public static boolean showExampleHexDump(final PrintStream console, final String hexDumpId) throws IOException { final String resourcePath = String.format("/examples/%s.hex", hexDumpId); final LineNumberReader reader = IOUtils.getReaderForResourcePath(resourcePath); console.printf("(pio*:sm*) [hex dump %s]%n", hexDumpId); while (true) { final String line = reader.readLine(); if (line == null) break; console.printf("(pio*:sm*) %3d: %s%n", reader.getLineNumber(), line); } console.printf("(pio*:sm*) [end of hex dump %s]%n", hexDumpId); return true; } private static String asBitArrayDisplay(final PinState[] pinStates) { final StringBuffer display = new StringBuffer(); for (int gpioNum = 0; gpioNum < Constants.GPIO_NUM; gpioNum++) { if (((gpioNum & 0x7) == 0) && (gpioNum > 0)) { display.append(' '); } final PinState pinState = pinStates[gpioNum]; final String bitDisplay = pinState.getLevel().toChar(pinState.getDirection()); display.append(bitDisplay); } return display.toString(); } /** * Global GPIO view of pins. */ public static String gpioDisplay(final SDK sdk, final GPIOSDK.Override override) throws IOException { final PinState[] pinStates = sdk.getGPIOSDK().getPinStates(override); final String gpioPinBits = asBitArrayDisplay(pinStates); return String.format("(pio%s:sm*) %s%n", "*", gpioPinBits); } /** * @param pioNum Either 0 or 1 for GPIO pins of PIO0 or PIO1. */ public static String gpioDisplay(final SDK sdk, final int pioNum) throws IOException { Constants.checkPioNum(pioNum, "PIO index number"); final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); final PinState[] pinStates = pioSdk.getPinStates(); final String pioNumId = String.format("%d", pioNum); final String gpioPinBits = asBitArrayDisplay(pinStates); return String.format("(pio%s:sm*) %s%n", pioNumId, gpioPinBits); } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/ScriptInfo.java ================================================ /* * @(#)ScriptInfo.java 1.00 21/06/20 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor; import java.io.IOException; import java.io.LineNumberReader; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; import org.soundpaint.rp2040pio.IOUtils; public class ScriptInfo { public static class ParseException extends IOException { private static final long serialVersionUID = 8116265717249238138L; private static String createMessage(final String innerMessage, final String scriptId) { return String.format("script info parse exception: script %s: %s", scriptId, innerMessage); } private ParseException() { throw new UnsupportedOperationException("unsupported empty constructor"); } public ParseException(final String innerMessage, final String scriptId) { super(createMessage(innerMessage, scriptId)); } } public static final String DEFAULT_GROUP_NAME = "Other"; private final String scriptId; private final String scriptName; private final String groupName; private final String description; public ScriptInfo(final String scriptId, final String scriptName, final String groupName, final String description) { if (scriptId == null) { throw new NullPointerException("scriptId"); } if (scriptName == null) { throw new NullPointerException("scriptName"); } this.scriptId = scriptId; this.scriptName = scriptName; this.groupName = groupName; this.description = description; } public String getScriptId() { return scriptId; } public String getScriptName() { return scriptName; } public String getGroupName() { return groupName; } public String getDescription() { return description; } private static void checkHeaderAlreadyDefined(final String header, final String headerId, final String scriptId) throws IOException { if (header != null) { final String message = String.format("duplicate '%s' header", headerId); throw new ScriptInfo.ParseException(message, scriptId); } } private static void checkHeaderDefined(final String header, final String headerId, final String scriptId) throws IOException { if (header == null) { final String message = String.format("missing '%s' header", headerId); throw new ScriptInfo.ParseException(message, scriptId); } } private static final String SCRIPT_HEADER_ID = "Script:"; private static final String GROUP_HEADER_ID = "Group:"; private static ScriptInfo createScriptInfo(final String scriptId) throws IOException { final String resourcePath = String.format("/examples/%s.mon", scriptId); final LineNumberReader reader = IOUtils.getReaderForResourcePath(resourcePath); // parse headers String scriptName = null; String groupName = null; while (true) { final String line = reader.readLine(); if (line == null) break; if (!(line.startsWith("#"))) break; final String comment = line.substring(1).trim(); if (comment.isEmpty()) break; if (comment.startsWith(SCRIPT_HEADER_ID)) { checkHeaderAlreadyDefined(scriptName, SCRIPT_HEADER_ID, scriptId); scriptName = comment.substring(SCRIPT_HEADER_ID.length()).trim(); } else if (comment.startsWith(GROUP_HEADER_ID)) { checkHeaderAlreadyDefined(groupName, GROUP_HEADER_ID, scriptId); groupName = comment.substring(GROUP_HEADER_ID.length()).trim(); } else { final String message = String.format("invalid start of header line: %s", comment); throw new ScriptInfo.ParseException(message, scriptId); } } checkHeaderDefined(scriptName, SCRIPT_HEADER_ID, scriptId); if (groupName == null) groupName = ScriptInfo.DEFAULT_GROUP_NAME; // parse description final StringBuffer s = new StringBuffer(); while (true) { final String line = reader.readLine(); if (line == null) break; if (!(line.startsWith("#"))) break; s.append(String.format("%s%n", line.substring(1).trim())); } final String description = s.toString(); return new ScriptInfo(scriptId, scriptName, groupName, description); } public static Map> createScriptsInfo() throws IOException { final String suffix = ".mon"; final List scriptIds = IOUtils.list("examples").stream(). filter(t -> t.endsWith(suffix)). map(t -> { return t.substring(0, t.length() - suffix.length()); }). collect(Collectors.toList()); final Map> scriptsInfo = new TreeMap>(); for (final String scriptId : scriptIds) { final ScriptInfo scriptInfo = createScriptInfo(scriptId); final String groupName = scriptInfo.getGroupName(); final Map scriptsGroupInfo; if (scriptsInfo.containsKey(groupName)) { scriptsGroupInfo = scriptsInfo.get(groupName); } else { scriptsGroupInfo = new TreeMap(); scriptsInfo.put(groupName, scriptsGroupInfo); } scriptsGroupInfo.put(scriptId, scriptInfo); } return scriptsInfo; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/BreakPoints.java ================================================ /* * @(#)BreakPoints.java 1.00 21/04/04 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "breakpoints" manages breakpoints. Breakpoints are * not a feature of the RP2040 itself, but have been added to the PIO * emulator for advanced debugging. */ public class BreakPoints extends Command { private static final String fullName = "breakpoints"; private static final String singleLineDescription = "change breakpoints"; private static final String notes = "For displaying breakpoints, use the \"unassemble\" command."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", 0, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.IntegerOptionDeclaration optAdd = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "add", null, "add breakpoint at specified address " + "(0x00…0x1f)"); private static final CmdOptions.IntegerOptionDeclaration optDelete = CmdOptions.createIntegerOption("ADDRESS", false, 'd', "delete", null, "remove breakpoint from specified " + "address (0x00…0x1f)"); private final SDK sdk; public BreakPoints(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optAdd, optDelete }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final int smNum = options.getValue(optSm); if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } if (!options.isDefined(optAdd) && !options.isDefined(optDelete)) { throw new CmdOptions. ParseException("at least one of options -a and -d must be specified"); } } } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int smNum = options.getValue(optSm); final Integer optAddValue = options.getValue(optAdd); final Integer optDeleteValue = options.getValue(optDelete); final int address = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_BREAKPOINTS); if (optAddValue != null) { sdk.hwSetBits(address, 0x1 << optAddValue); } if (optDeleteValue != null) { sdk.hwClearBits(address, 0x1 << optDeleteValue); } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Clear.java ================================================ /* * @(#)Clear.java 1.00 21/04/22 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "clear" clears the screen and, optionally, * scrollback buffer. */ public class Clear extends Command { private static final String fullName = "clear"; private static final String singleLineDescription = "clear screen and optionally scrollback buffer"; private static final CmdOptions.FlagOptionDeclaration optBuffer = CmdOptions.createFlagOption(false, 'b', "buffer", CmdOptions.Flag.OFF, "also clear scrollback buffer"); public Clear(final PrintStream console) { super(console, fullName, singleLineDescription, new CmdOptions.OptionDeclaration[] { optBuffer }); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { if (options.getValue(optBuffer).isOn()) { console.printf("\u001b[3J"); } console.printf("\u001b[2J"); console.printf("\u001b[H"); return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Clock.java ================================================ /* * @(#)Clock.java 1.00 21/05/29 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.PIORegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "clock" displays or modifies a state machine's * clock configuration. */ public class Clock extends Command { private static final float FRAC_MUL = 1.0f / 256; private static final float MIN_DIVIDER = 1.0f; private static final float MAX_DIVIDER = 65536.0f; private static final String fullName = "clock"; private static final String singleLineDescription = "display or change internal state machine's clock configuration"; private static final String notes = "If none of the modification options is specified, the status%n"+ "of the clock of the selected is displayed.%n" + "Otherwise, for all specified options \"-i\", \"-f\" and%n" + "\"-r\", the corresponding modification will be performed for%n" + "the selected state machine. Option \"-d\" can be used%n" + "alternatively to options \"-i\" and \"-f\"."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", 0, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.IntegerOptionDeclaration optIntDivider = CmdOptions.createIntegerOption("NUMBER", false, 'i', "int-divider", null, "set clock divider integer part for " + "selected PIO and SM"); private static final CmdOptions.IntegerOptionDeclaration optFracDivider = CmdOptions.createIntegerOption("NUMBER", false, 'f', "frac-divider", null, "set clock divider fractional part for " + "selected PIO and SM"); private static final CmdOptions.FloatOptionDeclaration optDivider = CmdOptions.createFloatOption("NUMBER", false, 'd', "divider", null, "set nearby clock divider from float " + "value for selected PIO and SM"); private static final CmdOptions.FlagOptionDeclaration optRestart = CmdOptions.createFlagOption(false, 'r', "restart", CmdOptions.Flag.OFF, "restart clock for selected PIO and SM"); private final SDK sdk; public Clock(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optIntDivider, optFracDivider, optDivider, optRestart }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final int smNum = options.getValue(optSm); if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } final Integer optIntDividerValue = options.getValue(optIntDivider); final Integer optFracDividerValue = options.getValue(optFracDivider); final Float optDividerValue = options.getValue(optDivider); if (optDividerValue != null) { if ((optIntDividerValue != null) || (optFracDividerValue != null)) { final String message = "when option \"-d\" is defined, none of competing options " + "\"-i\" or \"-f\" may be defined"; throw new CmdOptions.ParseException(message); } final float divider = optDividerValue; if (divider < MIN_DIVIDER) { final String message = String.format("divider < %f: %f", MIN_DIVIDER, divider); throw new CmdOptions.ParseException(message); } if (divider > MAX_DIVIDER) { final String message = String.format("divider > %f: %f", MAX_DIVIDER, divider); throw new CmdOptions.ParseException(message); } } if (optIntDividerValue != null) { final int intDivider = optIntDividerValue; if ((intDivider < 0) || (intDivider > 0x10000)) { final String message = String.format("expected integer divider value in the range " + "0…0x%5x, but got: 0x%x", 0x10000, intDivider); throw new CmdOptions.ParseException(message); } if (optFracDividerValue != null) { if ((intDivider & 0xffff) == 0) { if (optFracDividerValue != 0) { final String message = String.format("if int-divider is 0, frac-divider must " + "also be 0, but got: %x", optFracDividerValue); throw new CmdOptions.ParseException(message); } } } } if (optFracDividerValue != null) { final int fracDivider = optFracDividerValue; if ((fracDivider < 0) || (fracDivider > 0xff)) { final String message = String.format("expected fractional divider value in the range " + "0…0x%02x, but got: 0x%x", 0xff, fracDivider); throw new CmdOptions.ParseException(message); } } } } private int getClkDivValue(final int pioNum, final int smNum) throws IOException { final int addressClkDiv = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_CLKDIV); return sdk.readAddress(addressClkDiv); } private int getIntDivider(final int clkDivValue) { return (clkDivValue & Constants.SM0_CLKDIV_INT_BITS) >>> Constants.SM0_CLKDIV_INT_LSB; } private int getFracDivider(final int clkDivValue) { return (clkDivValue & Constants.SM0_CLKDIV_FRAC_BITS) >>> Constants.SM0_CLKDIV_FRAC_LSB; } private boolean getEnabled(final int pioNum, final int smNum) throws IOException { final int addressClkEnable = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_CLK_ENABLE); return sdk.readAddress(addressClkEnable) != 0x0; } private void displayStatus(final int pioNum, final int smNum) throws IOException { final int clkDivValue = getClkDivValue(pioNum, smNum); final int intDivider = getIntDivider(clkDivValue); final int displayIntDivider = intDivider == 0 ? 0x10000 : intDivider; final int fracDivider = getFracDivider(clkDivValue); final float divider = displayIntDivider + FRAC_MUL * fracDivider; final boolean enabled = getEnabled(pioNum, smNum); console.printf("(pio%d:sm%d) int-divider=0x%05x, frac-divider=0x%02x " + "(divider=%f)%n", pioNum, smNum, displayIntDivider, fracDivider, divider); console.printf(" enabled=%s%n", enabled); } private void setIntDivider(final int pioNum, final int smNum, final int intDivider) throws IOException { final int addressClkDiv = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_CLKDIV); final int clkDiv = intDivider << Constants.SM0_CLKDIV_INT_LSB; final int mask = Constants.SM0_CLKDIV_INT_BITS; sdk.hwWriteMasked(addressClkDiv, clkDiv, mask); final int displayIntDivider = intDivider == 0 ? 0x10000 : intDivider; console.printf("(pio%d:sm%d) set int-divider=0x%05x%n", pioNum, smNum, displayIntDivider); } private void setFracDivider(final int pioNum, final int smNum, final int fracDivider) throws IOException { final int addressClkDiv = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_CLKDIV); final int clkDiv = fracDivider << Constants.SM0_CLKDIV_FRAC_LSB; final int mask = Constants.SM0_CLKDIV_FRAC_BITS; sdk.hwWriteMasked(addressClkDiv, clkDiv, mask); console.printf("(pio%d:sm%d) set frac-divider=0x%02x%n", pioNum, smNum, fracDivider); } private void setDivider(final int pioNum, final int smNum, final float divider) throws IOException { final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); pioSdk.smSetClkDiv(smNum, divider); final int clkDivValue = getClkDivValue(pioNum, smNum); final int intDivider = getIntDivider(clkDivValue); final int fracDivider = getFracDivider(clkDivValue); final float setDivider = (intDivider == 0 ? 0x10000 : intDivider) + FRAC_MUL * fracDivider; console.printf("(pio%d:sm%d) set divider=%f%n", pioNum, smNum, setDivider); } private void restart(final int pioNum, final int smNum) throws IOException { final int addressCtrl = PIORegisters.getAddress(pioNum, PIORegisters.Regs.CTRL); final int mask = (0x1 << (Constants.CTRL_SM_RESTART_LSB + smNum)) & Constants.CTRL_SM_RESTART_BITS; sdk.hwSetBits(addressCtrl, mask); console.printf("(pio%d:sm%d) restarted clock%n", pioNum, smNum); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int smNum = options.getValue(optSm); final Integer optIntDividerValue = options.getValue(optIntDivider); final Integer optFracDividerValue = options.getValue(optFracDivider); final Float optDividerValue = options.getValue(optDivider); final boolean optRestartValue = options.getValue(optRestart).isOn(); final boolean haveModOp = (optIntDividerValue != null) || (optFracDividerValue != null) || (optDividerValue != null) || optRestartValue; if (!haveModOp) { displayStatus(pioNum, smNum); } if (optIntDividerValue != null) { setIntDivider(pioNum, smNum, optIntDividerValue); } if (optFracDividerValue != null) { setFracDivider(pioNum, smNum, optFracDividerValue); } if (optDividerValue != null) { setDivider(pioNum, smNum, optDividerValue); } if (optRestartValue) { restart(pioNum, smNum); } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Enter.java ================================================ /* * @(#)Enter.java 1.00 21/03/31 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.PIORegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "enter" lets the user enter instruction opcodes to * be stored in a PIO's program memory. */ public class Enter extends Command { private static final String fullName = "enter"; private static final String singleLineDescription = "enter instruction opcodes; exit by entering an empty line"; private static final String enterInstructions = "Per input line, enter 16 bit hex intruction word without '0x' prefix.%n" + "Enter \".\" to keep current instruction word.%n" + "Enter empty line to quit enter mode.%n"; private final SDK sdk; private final BufferedReader in; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optAddress = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "address", null, "start address (0x00…0x1f)"); private static final CmdOptions.IntegerOptionDeclaration optValue = CmdOptions.createIntegerOption("NUMBER", false, 'v', "value", null, "instruction op-code"); public Enter(final PrintStream console, final SDK sdk, final BufferedReader in) { super(console, fullName, singleLineDescription, new CmdOptions.OptionDeclaration[] { optPio, optAddress, optValue }); if (sdk == null) { throw new NullPointerException("sdk"); } if (in == null) { throw new NullPointerException("in"); } this.sdk = sdk; this.in = in; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } } } private void unassemble(final int pioNum, final PIOSDK pioSdk, final int address) throws IOException { final PIOSDK.InstructionInfo instructionInfo = pioSdk.getMemoryInstruction(pioNum, address, false, true); console.printf("(pio%d:sm*) %02x: %s%n", pioNum, address, instructionInfo.getToolTipText()); } private boolean enterWord(final int pioNum, final PIOSDK pioSdk, final int address, final String line) throws IOException { try { final int value = Integer.parseInt(line, 16); sdk.writeAddress(PIORegisters.getMemoryAddress(pioNum, address), value); unassemble(pioNum, pioSdk, address); return true; } catch (final NumberFormatException e) { console.printf("error: expected 16 bit hex value or '.' or empty line, " + "but got: %s%n", line); return false; } } private static String stripOffComment(final String line) { if ((line == null) || line.isEmpty()) return line; final int hashPos = line.indexOf('#'); if (hashPos < 0) return line; return line.substring(0, hashPos); } private void enterWords(final int pioNum, final PIOSDK pioSdk, final int startAddress) throws IOException { console.printf(enterInstructions); int address = startAddress; int count = 0; while (true) { unassemble(pioNum, pioSdk, address); final int currentValue = sdk.readAddress(PIOEmuRegisters.getMemoryAddress(pioNum, address)); console.printf("(pio%d:sm*) %02x: (%04x) ", pioNum, address, currentValue); final String line = stripOffComment(in.readLine()).trim(); if ((line == null) || line.isEmpty()) break; if (!line.equals(".")) { if (!enterWord(pioNum, pioSdk, address, line)) continue; } address = (address + 1) & Constants.MEMORY_SIZE - 1; count++; } console.printf("entered %d words%n", count); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final Integer optAddressValue = options.getValue(optAddress); final int address = optAddressValue != null ? optAddressValue & Constants.MEMORY_SIZE - 1 : 0; final Integer optValueValue = options.getValue(optValue); final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); if (optValueValue != null) { sdk.writeAddress(PIORegisters.getMemoryAddress(pioNum, address), optValueValue); unassemble(pioNum, pioSdk, address); } else { enterWords(pioNum, pioSdk, address); } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Execute.java ================================================ /* * @(#)Execute.java 1.00 21/04/04 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.PIORegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "execute" writes an instruction for immediate * execution (including jumps) and then resuming execution. * * Note: The idea of this command is to provide explicit access to the * PIO SMx_INSTR registers. Downside of this approach is, that * writing to the registers schedules an instruction for execution * during the _next_ clock cycle, while reading from the register will * return the instruction that is executed during the _current_ clock * cycle, that is, in general, a different instruction than the * inserted one. Also, once inserted, there is no way to revert * insertion. An alternative approach is to separate both functions: * Provide a command "insert" for inserting an instruction to be * executed in the upcoming clock cycle, for showing the inserted * instruction, if any, and for deleting the inserted instruction, if * any; and also, have a separate command for showing the currently * executed instruction (maybe show it together with the registers via * the "registers" command). */ public class Execute extends Command { private static final String fullName = "execute"; private static final String singleLineDescription = "set instruction for immediate execution or display " + "instructions currently executed or pending for execution"; private static final String notes = "Writes an instruction for immediate execution (including jumps)%n" + "and then resuming execution or displays the currently excuted%n" + "instruction. Immediate execution means execution during the next%n" + "clock cycle.%n" + "%n" + "Options -p and -s select the state machine that this command%n" + "applies to. Default is PIO0 and SM0.%n" + "%n" + "If neither of options -c, -d, -e, -f is specified, the instruction%n" + "currently being executed by the selected state machine and any%n" + "pending forced or EXEC'd instruction will be displayed.%n" + "%n" + "If option -f is specified, the specified instruction is written%n" + "for immediate execution (forced instruction).%n" + "If option -c is specified, any pending forced instruction%n" + "will be cancelled.%n" + "If option -e is specified, the specified instruction is written%n" + "for execution on the next enabled clock cycle (EXEC'd instruction),%n" + "provided that there is no pending forced instruction that would have%n" + "higher priority of execution.%n" + "If option -d is specified, any pending EXEC'd instruction%n" + "will be deleted."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", 0, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.IntegerOptionDeclaration optForce = CmdOptions.createIntegerOption("CODE", false, 'f', "force", null, "set or overwrite opcode of forced " + "instruction to be executed"); private static final CmdOptions.IntegerOptionDeclaration optExec = CmdOptions.createIntegerOption("CODE", false, 'e', "exec", null, "set or overwrite opcode of EXEC'd " + "instruction to be executed"); private static final CmdOptions.FlagOptionDeclaration optCancel = CmdOptions.createFlagOption(false, 'c', "cancel", CmdOptions.Flag.OFF, "cancel pending forced instruction, if any"); private static final CmdOptions.FlagOptionDeclaration optDelete = CmdOptions.createFlagOption(false, 'd', "delete", CmdOptions.Flag.OFF, "delete pending EXEC'd instruction, if any"); private final SDK sdk; public Execute(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optForce, optExec, optCancel, optDelete }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final int smNum = options.getValue(optSm); if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } if (options.isDefined(optForce) && options.getValue(optCancel).isOn()) { throw new CmdOptions. ParseException("at most one of options \"-c\" and \"-f\" may be " + "specified at the same time"); } if (options.isDefined(optExec) && options.getValue(optDelete).isOn()) { throw new CmdOptions. ParseException("at most one of options \"-e\" and \"-d\" may be " + "specified at the same time"); } } } private int getPendingDelay(final int pioNum, final int smNum) throws IOException { final int addressPendingDelay = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_PENDING_DELAY); final int pendingDelay = sdk.readAddress(addressPendingDelay); return pendingDelay & 0x1f; } private void displayInstruction(final int pioNum, final int smNum, final PIOSDK pioSdk, final int origin, final int opCode) throws IOException { final PIOSDK.InstructionInfo instructionInfo = pioSdk.getInstructionFromOpCode(smNum, origin, "", opCode, false, false, 0); console.printf("(pio%d:sm%d) last executed: %s%n", pioNum, smNum, instructionInfo.getToolTipText()); } private void displayForcedInstruction(final int pioNum, final int smNum, final SDK sdk, final PIOSDK pioSdk) throws IOException { final int forcedInstrAddress = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_FORCED_INSTR); final int forcedInstr = sdk.readAddress(forcedInstrAddress); if ((forcedInstr & 0x00010000) != 0x0) { final PIOSDK.InstructionInfo forcedInstrInfo = pioSdk.getInstructionFromOpCode(smNum, Constants.INSTR_ORIGIN_FORCED, "", forcedInstr & 0xffff, true, false, 0); console.printf("(pio%d:sm%d) forced instr : %s%n", pioNum, smNum, forcedInstrInfo.getFullStatement()); } } private void displayExecdInstruction(final int pioNum, final int smNum, final SDK sdk, final PIOSDK pioSdk) throws IOException { final int execdInstrAddress = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_EXECD_INSTR); final int execdInstr = sdk.readAddress(execdInstrAddress); if ((execdInstr & 0x00010000) != 0x0) { final PIOSDK.InstructionInfo execdInstrInfo = pioSdk.getInstructionFromOpCode(smNum, Constants.INSTR_ORIGIN_EXECD, "", execdInstr & 0xffff, true, false, 0); console.printf("(pio%d:sm%d) execd instr : %s%n", pioNum, smNum, execdInstrInfo.getFullStatement()); } } private void displayPendingDelay(final int pioNum, final int smNum, final PIOSDK.InstructionInfo currentInstrInfo) throws IOException { final int pendingDelay = getPendingDelay(pioNum, smNum); final int totalDelay = currentInstrInfo.getDelay(); if (totalDelay > 0) { console.printf("(pio%d:sm%d) pending delay: %d of %d cycles done%n", pioNum, smNum, totalDelay - pendingDelay, totalDelay); } } private void displayInstructions(final int pioNum, final int smNum, final SDK sdk, final PIOSDK pioSdk) throws IOException { final PIOSDK.InstructionInfo currentInstrInfo = pioSdk.getCurrentInstruction(smNum, true, true); console.printf("(pio%d:sm%d) last executed: %s%n", pioNum, smNum, currentInstrInfo.getFullStatement()); displayForcedInstruction(pioNum, smNum, sdk, pioSdk); displayExecdInstruction(pioNum, smNum, sdk, pioSdk); displayPendingDelay(pioNum, smNum, currentInstrInfo); } private void deleteExecdInstruction(final int pioNum, final int smNum, final SDK sdk, final PIOSDK pioSdk) throws IOException { final int clearExecdAddress = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_CLEAR_EXECD); sdk.writeAddress(clearExecdAddress, 0); console.printf("(pio%d:sm%d) deleted any pending EXEC'd instruction%n", pioNum, smNum); } private void setExecdInstruction(final int pioNum, final int smNum, final SDK sdk, final PIOSDK pioSdk, final int instr) throws IOException { final int execdInstrAddress = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_EXECD_INSTR); sdk.writeAddress(execdInstrAddress, instr); console.println("EXEC'd instruction written for pending execution:"); displayInstruction(pioNum, smNum, pioSdk, Constants.INSTR_ORIGIN_EXECD, instr); } private void cancelForcedInstruction(final int pioNum, final int smNum, final SDK sdk, final PIOSDK pioSdk) throws IOException { final int clearForcedAddress = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_CLEAR_FORCED); sdk.writeAddress(clearForcedAddress, 0); console.printf("(pio%d:sm%d) cancelled any pending forced instruction%n", pioNum, smNum); } private void setForcedInstruction(final int pioNum, final int smNum, final SDK sdk, final PIOSDK pioSdk, final int instr) throws IOException { final int instrAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_INSTR); sdk.writeAddress(instrAddress, instr); console.println("forced instruction written for pending execution:"); displayInstruction(pioNum, smNum, pioSdk, Constants.INSTR_ORIGIN_FORCED, instr); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int smNum = options.getValue(optSm); final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); final Integer optForceValue = options.getValue(optForce); final Integer optExecValue = options.getValue(optExec); if (optForceValue != null) { setForcedInstruction(pioNum, smNum, sdk, pioSdk, optForceValue); } if (options.getValue(optCancel).isOn()) { cancelForcedInstruction(pioNum, smNum, sdk, pioSdk); } if (optExecValue != null) { setExecdInstruction(pioNum, smNum, sdk, pioSdk, optExecValue); } if (options.getValue(optDelete).isOn()) { deleteExecdInstruction(pioNum, smNum, sdk, pioSdk); } if ((optForceValue == null) && (optExecValue == null) && !options.getValue(optCancel).isOn() && !options.getValue(optDelete).isOn()) { displayInstructions(pioNum, smNum, sdk, pioSdk); } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Fifo.java ================================================ /* * @(#)Fifo.java 1.00 21/04/05 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.PIORegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "fifo" displays or modifies a state machine's * FIFO status. * * TODO: It is unclear whether the FIFO is internally organized as a * cyclic buffer, or a shift register. Therefore, the numbering of * the FIFO memory registers may appear wrong, if the emulator's * behavior is compared directly with a real RP2040's behavior. */ public class Fifo extends Command { private static final String fullName = "fifo"; private static final String singleLineDescription = "display or change internal state machine's FIFO status"; private static final String notes = "Use options \"-p\" and \"-s\" to select a state machine.%n" + "If none of the FIFO modification options is specified, the status%n"+ "of the FIFO of the selected state machine is displayed.%n" + "Option '-a' together with option '-v' can be used for directly%n" + "low-level write a value into one of the 8 FIFO's data registers.%n" + "Otherwise, for all specified modification options \"-d\", \"-e\",%n" + "\"-j\", \"-u\", \"--threshold\", \"--shift-left\", \"--shift-right\"%n" + "and \"--auto\", the corresponding modification will be performed for%n" + "the selected state machine and the selected FIFO (either RX or TX).%n" + "Modification option \"-c\" will clear both FIFOs and, if specified%n" + "together with one of the other modification options, will be%n" + "executed first. Similarly, options \"--clear-tx-stall\",%n" + "\"--clear-tx-over\", \"clear-rx-under\" and \"clear-rx-stall\"%n" + "will clear the corresponding FDEBUG flag of the specified%n" + "state machine."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", 0, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.IntegerOptionDeclaration optAddress = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "address", null, "FIFO memory address (0x0…0x7) to write " + "value into"); private static final CmdOptions.IntegerOptionDeclaration optValue = CmdOptions.createIntegerOption("VALUE", false, 'v', "value", null, "value to enqueue or directly write " + "into FIFO memory"); private static final CmdOptions.FlagOptionDeclaration optClear = CmdOptions.createFlagOption(false, 'c', "clear", CmdOptions.Flag.OFF, "clear both FIFOs, RX and TX"); private static final CmdOptions.FlagOptionDeclaration optClearTxStall = CmdOptions.createFlagOption(false, null, "clear-tx-stall", CmdOptions.Flag.OFF, "clear FDEBUG flag 'TX Stall'"); private static final CmdOptions.FlagOptionDeclaration optClearTxOver = CmdOptions.createFlagOption(false, null, "clear-tx-over", CmdOptions.Flag.OFF, "clear FDEBUG flag 'TX Over'"); private static final CmdOptions.FlagOptionDeclaration optClearRxUnder = CmdOptions.createFlagOption(false, null, "clear-rx-under", CmdOptions.Flag.OFF, "clear FDEBUG flag 'RX Under'"); private static final CmdOptions.FlagOptionDeclaration optClearRxStall = CmdOptions.createFlagOption(false, null, "clear-rx-stall", CmdOptions.Flag.OFF, "clear FDEBUG flag 'RX Stall'"); private static final CmdOptions.FlagOptionDeclaration optDequeue = CmdOptions.createFlagOption(false, 'd', "dequeue", CmdOptions.Flag.OFF, "dequeue value from either RX or TX FIFO"); private static final CmdOptions.FlagOptionDeclaration optEnqueue = CmdOptions.createFlagOption(false, 'e', "enqueue", CmdOptions.Flag.OFF, "enqueue value provided with option -v " + "into either RX or TX FIFO"); private static final CmdOptions.FlagOptionDeclaration optJoin = CmdOptions.createFlagOption(false, 'j', "join", CmdOptions.Flag.OFF, "let either RX or TX FIFO steal the other " + "FIFO's storage"); private static final CmdOptions.FlagOptionDeclaration optUnjoin = CmdOptions.createFlagOption(false, 'u', "unjoin", CmdOptions.Flag.OFF, "revoke join operation of either RX or TX FIFO"); private static final CmdOptions.IntegerOptionDeclaration optThreshold = CmdOptions.createIntegerOption("NUMBER", false, null, "threshold", null, "set pull threshold (when TX selected) " + "or push threshold (when RX selected)"); private static final CmdOptions.FlagOptionDeclaration optShiftLeft = CmdOptions.createFlagOption(false, null, "shift-left", CmdOptions.Flag.OFF, "set shift direction left for OSR (when TX " + "selected or for ISR (when RX selected)"); private static final CmdOptions.FlagOptionDeclaration optShiftRight = CmdOptions.createFlagOption(false, null, "shift-right", CmdOptions.Flag.OFF, "set shift direction left for OSR (when TX " + "selected or for ISR (when RX selected)"); private static final CmdOptions.BooleanOptionDeclaration optAuto = CmdOptions.createBooleanOption(false, null, "auto", null, "turn on or off auto-pull (when TX " + "selected) or auto-push (when RX selected)"); private static final CmdOptions.FlagOptionDeclaration optTX = CmdOptions.createFlagOption(false, 't', "tx", CmdOptions.Flag.OFF, "apply modification on TX FIFO"); private static final CmdOptions.FlagOptionDeclaration optRX = CmdOptions.createFlagOption(false, 'r', "rx", CmdOptions.Flag.OFF, "apply modification on RX FIFO"); private enum Type { RX, TX; }; private final SDK sdk; public Fifo(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optAddress, optValue, optClear, optClearTxStall, optClearTxOver, optClearRxUnder, optClearRxStall, optDequeue, optEnqueue, optJoin, optUnjoin, optThreshold, optShiftLeft, optShiftRight, optAuto, optTX, optRX }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final int smNum = options.getValue(optSm); if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } final Integer optAddressValue = options.getValue(optAddress); final Integer optValueValue = options.getValue(optValue); if (optAddressValue != null) { final int address = optAddressValue; if ((address < 0) || (address > (Constants.FIFO_DEPTH << 1) - 1)) { final String message = String.format("expected address value in the range 0…%d, " + "but got: %d", (Constants.FIFO_DEPTH << 1) - 1, address); throw new CmdOptions.ParseException(message); } } if (options.isDefined(optAddress) || options.getValue(optEnqueue).isOn()) { if (optValueValue == null) { throw new CmdOptions.ParseException("missing option: -v"); } } if (optValueValue != null) { if (!options.isDefined(optAddress) && !options.getValue(optEnqueue).isOn()) { throw new CmdOptions.ParseException("missing option: -a or -e"); } } final Integer optThresholdValue = options.getValue(optThreshold); if (optThresholdValue != null) { final int threshold = optThresholdValue; if ((threshold < 0) || (threshold > 32 /* assume (32 == 0) */)) { final String message = String.format("expected threshold value in the range 0…%d, " + "but got: %d", 32, threshold); throw new CmdOptions.ParseException(message); } } final Boolean optAutoValue = options.getValue(optAuto); if (options.getValue(optRX).isOn() && options.getValue(optTX).isOn()) { final String message = "either option -r and -t can be specified, but not both"; throw new CmdOptions.ParseException(message); } int opCount = 0; if (optAddressValue != null) opCount++; if (options.getValue(optDequeue).isOn()) opCount++; if (options.getValue(optEnqueue).isOn()) opCount++; if (options.getValue(optJoin).isOn()) opCount++; if (options.getValue(optUnjoin).isOn()) opCount++; if (optThresholdValue != null) opCount++; if (options.getValue(optShiftLeft).isOn()) opCount++; if (options.getValue(optShiftRight).isOn()) opCount++; if (optAutoValue != null) opCount++; if (opCount > 1) { final String message = "only one of options -a, -d, -q, -j, -u, --threshold, " + "--shift-left, --shift-right and --auto may be specified"; throw new CmdOptions.ParseException(message); } if (opCount > 0) { if (!options.getValue(optRX).isOn() && !options.getValue(optTX).isOn()) { final String message = "if one of options -d, -q, -j, -u, --threshold, " + "--shift-left, --shift-right or --auto is specified, either " + "option -r or -t must be specified to select a FIFO"; throw new CmdOptions.ParseException(message); } } else { if (options.getValue(optRX).isOn() && options.getValue(optTX).isOn()) { final String message = "options -r or -t may be specified only if one of " + "options -d, -q, -j, -u, --threshold, " + "--shift-left, --shift-right and --auto is specified"; throw new CmdOptions.ParseException(message); } } } } private int getSMFReadPtr(final int pioNum, final int smNum) throws IOException { final int addressFReadPtr = PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.FREAD_PTR); final int fReadPtr = sdk.readAddress(addressFReadPtr); return (fReadPtr >>> (smNum << 3)) & 0xff; } private int getSMFLevel(final int pioNum, final int smNum) throws IOException { final int addressFLevel = PIORegisters.getAddress(pioNum, PIORegisters.Regs.FLEVEL); final int fLevel = sdk.readAddress(addressFLevel); return (fLevel >>> (smNum << 3)) & 0xff; } private static String shiftDirectionAsString(final boolean isRight) { return isRight ? "right" : "left"; } private boolean getFDebug(final int smNum, final int fDebugValue, final int lsb, final int bits) { return (((fDebugValue & bits) >>> (lsb + smNum)) & 0x01) != 0x0; } private boolean getFTxStall(final int smNum, final int fDebugValue) { return getFDebug(smNum, fDebugValue, Constants.FDEBUG_TXSTALL_LSB, Constants.FDEBUG_TXSTALL_BITS); } private boolean getFTxOver(final int smNum, final int fDebugValue) { return getFDebug(smNum, fDebugValue, Constants.FDEBUG_TXOVER_LSB, Constants.FDEBUG_TXOVER_BITS); } private boolean getFRxUnder(final int smNum, final int fDebugValue) { return getFDebug(smNum, fDebugValue, Constants.FDEBUG_RXUNDER_LSB, Constants.FDEBUG_RXUNDER_BITS); } private boolean getFRxStall(final int smNum, final int fDebugValue) { return getFDebug(smNum, fDebugValue, Constants.FDEBUG_RXSTALL_LSB, Constants.FDEBUG_RXSTALL_BITS); } private void displayFifo(final int pioNum, final int smNum) throws IOException { final int smfReadPtr = getSMFReadPtr(pioNum, smNum); final int txReadPtr = smfReadPtr & 0xf; final int rxReadPtr = (smfReadPtr & 0xf0) >> 4; final int addressFLevel = PIORegisters.getAddress(pioNum, PIORegisters.Regs.FLEVEL); final int smfLevel = getSMFLevel(pioNum, smNum); final int txLevel = smfLevel & 0xf; final int rxLevel = (smfLevel >>> 4) & 0xf; final int addressFDebug = PIORegisters.getAddress(pioNum, PIORegisters.Regs.FDEBUG); final int fDebugValue = sdk.readAddress(addressFDebug); final boolean fTxStall = getFTxStall(smNum, fDebugValue); final boolean fTxOver = getFTxOver(smNum, fDebugValue); final boolean fRxUnder = getFRxUnder(smNum, fDebugValue); final boolean fRxStall = getFRxStall(smNum, fDebugValue); final int addressShiftCtrl = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL); final int shiftCtrlValue = sdk.readAddress(addressShiftCtrl); final boolean fJoinRxValue = (shiftCtrlValue & Constants.SM0_SHIFTCTRL_FJOIN_RX_BITS) != 0x0; final boolean fJoinTxValue = (shiftCtrlValue & Constants.SM0_SHIFTCTRL_FJOIN_TX_BITS) != 0x0; final boolean autoPullValue = (shiftCtrlValue & Constants.SM0_SHIFTCTRL_AUTOPULL_BITS) != 0x0; final boolean autoPushValue = (shiftCtrlValue & Constants.SM0_SHIFTCTRL_AUTOPUSH_BITS) != 0x0; final boolean outShiftRight = (shiftCtrlValue & Constants.SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS) != 0x0; final boolean inShiftRight = (shiftCtrlValue & Constants.SM0_SHIFTCTRL_IN_SHIFTDIR_BITS) != 0x0; final int pullThresholdBits = (shiftCtrlValue & Constants.SM0_SHIFTCTRL_PULL_THRESH_BITS) >>> Constants.SM0_SHIFTCTRL_PULL_THRESH_LSB; final int pushThresholdBits = (shiftCtrlValue & Constants.SM0_SHIFTCTRL_PUSH_THRESH_BITS) >>> Constants.SM0_SHIFTCTRL_PUSH_THRESH_LSB; final StringBuffer fifoHeader = new StringBuffer(); Type type = fJoinRxValue ? (fJoinTxValue ? null : Type.RX) : Type.TX; int regCount = 0; for (int index = 0; index < (Constants.FIFO_DEPTH << 1); index++) { if (!fJoinRxValue && !fJoinTxValue && (index == Constants.FIFO_DEPTH)) { type = Type.RX; regCount = 0; } fifoHeader.append(type != null ? type : "__"); fifoHeader.append(String.format("%01x ", regCount++)); } final StringBuffer fifoContents = new StringBuffer(); for (int fifoMemAddress = 0; fifoMemAddress < (Constants.FIFO_DEPTH << 1); fifoMemAddress++) { final int addressFifoMem = PIOEmuRegisters.getFIFOMemAddress(pioNum, smNum, fifoMemAddress); final int fifoMemValue = sdk.readAddress(addressFifoMem); final String readPtr = (!fJoinRxValue && (fifoMemAddress == txReadPtr)) || (!fJoinTxValue && (fifoMemAddress == rxReadPtr)) ? "→" : " "; fifoContents.append(String.format("%08x%s ", fifoMemValue, readPtr)); } final StringBuffer fifoLevels = new StringBuffer(); if (!fJoinRxValue) { fifoLevels.append(String.format("TX_LEVEL=%01x", txLevel)); } if (!fJoinTxValue) { if (fifoLevels.length() > 0) fifoLevels.append(", "); fifoLevels.append(String.format("RX_LEVEL=%01x", rxLevel)); } final int pullThreshold = Constants.checkBitCount(pullThresholdBits, "TX: OSR threshold"); final int pushThreshold = Constants.checkBitCount(pushThresholdBits, "RX: ISR threshold"); console.printf("(pio%d:sm%d) %s%n", pioNum, smNum, fifoHeader); console.printf(" %s%n", fifoContents); if (fifoLevels.length() > 0) { console.printf(" (%s)%n", fifoLevels); } console.printf("(pio%d:sm%d) TX: threshold=%d, shift direction=%s, " + "auto-pull=%s%n", pioNum, smNum, pullThreshold, shiftDirectionAsString(outShiftRight), autoPullValue); console.printf("(pio%d:sm%d) RX: threshold=%d, shift direction=%s, " + "auto-push=%s%n", pioNum, smNum, pushThreshold, shiftDirectionAsString(inShiftRight), autoPushValue); console.printf("(pio%d:sm%d) FDEBUG: TX Stall: %s, TX Over: %s, " + "RX Under: %s, RX Stall: %s%n", pioNum, smNum, fTxStall, fTxOver, fRxUnder, fRxStall); } private void setThreshold(final int pioNum, final int smNum, final Type type, final int threshold) throws IOException { final int addressShiftCtrl = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL); final int mask; final int lsb; if (type == Type.TX) { mask = Constants.SM0_SHIFTCTRL_PULL_THRESH_BITS; lsb = Constants.SM0_SHIFTCTRL_PULL_THRESH_LSB; } else { mask = Constants.SM0_SHIFTCTRL_PUSH_THRESH_BITS; lsb = Constants.SM0_SHIFTCTRL_PUSH_THRESH_LSB; } sdk.hwWriteMasked(addressShiftCtrl, threshold << lsb, mask); console.printf("(pio%d:sm%d) set %s threshold to %d%n", pioNum, smNum, type == Type.TX ? "pull" : "push", threshold); } private void setShiftDir(final int pioNum, final int smNum, final Type type, final boolean right) throws IOException { final int addressShiftCtrl = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL); final int mask; final int lsb; if (type == Type.TX) { mask = Constants.SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS; lsb = Constants.SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB; } else { mask = Constants.SM0_SHIFTCTRL_IN_SHIFTDIR_BITS; lsb = Constants.SM0_SHIFTCTRL_IN_SHIFTDIR_LSB; } sdk.hwWriteMasked(addressShiftCtrl, (right ? 0x1 : 0x0) << lsb, mask); console.printf("(pio%d:sm%d) set shift direction for %s to %s%n", pioNum, smNum, type == Type.TX ? "OSR" : "ISR", shiftDirectionAsString(right)); } private void setAuto(final int pioNum, final int smNum, final Type type, final boolean auto) throws IOException { final int addressShiftCtrl = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL); final int mask; final int lsb; if (type == Type.TX) { mask = Constants.SM0_SHIFTCTRL_AUTOPULL_BITS; lsb = Constants.SM0_SHIFTCTRL_AUTOPULL_LSB; } else { mask = Constants.SM0_SHIFTCTRL_AUTOPUSH_BITS; lsb = Constants.SM0_SHIFTCTRL_AUTOPUSH_LSB; } sdk.hwWriteMasked(addressShiftCtrl, (auto ? 0x1 : 0x0) << lsb, mask); console.printf("(pio%d:sm%d) set auto-%s=%s%n", pioNum, smNum, type == Type.TX ? "pull" : "push", auto); } private void writeFifoAddress(final int pioNum, final int smNum, final int fifoMemAddress, final int value) throws IOException { final int address = PIOEmuRegisters.getFIFOMemAddress(pioNum, smNum, fifoMemAddress); sdk.writeAddress(address, value); console.printf("(pio%d:sm%d) wrote value %08x into FIFO register %1x%n", pioNum, smNum, value, fifoMemAddress); } private void clear(final int pioNum, final int smNum) throws IOException { final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); pioSdk.smClearFIFOs(smNum); console.printf("(pio%d:sm%d) cleared FIFOs%n", pioNum, smNum); } private void clearFDebug(final int pioNum, final int smNum, final int lsb, final String flagName) throws IOException { final int addressFDebug = PIORegisters.getAddress(pioNum, PIORegisters.Regs.FDEBUG); sdk.writeAddress(addressFDebug, 0x1 << (lsb + smNum)); console.printf("(pio%d:sm%d) cleared FDEBUG flag %s%n", pioNum, smNum, flagName); } private void dequeue(final int pioNum, final int smNum, final Type type) throws IOException { final int address = type == Type.TX ? PIOEmuRegisters.getTXFAddress(pioNum, smNum) : PIORegisters.getRXFAddress(pioNum, smNum); final int value = sdk.readAddress(address); console.printf("(pio%d:sm%d) dequeued 0x%08x from %s%n", pioNum, smNum, value, type); } private void enqueue(final int pioNum, final int smNum, final Type type, final int value) throws IOException { final int address = type == Type.RX ? PIOEmuRegisters.getRXFAddress(pioNum, smNum) : PIORegisters.getTXFAddress(pioNum, smNum); sdk.writeAddress(address, value); console.printf("(pio%d:sm%d) enqueued 0x%08x to %s%n", pioNum, smNum, value, type); } private void setFJoin(final int pioNum, final int smNum, final Type type, final boolean join) throws IOException { final int addressShiftCtrl = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL); final int mask = type == Type.RX ? Constants.SM0_SHIFTCTRL_FJOIN_RX_BITS : Constants.SM0_SHIFTCTRL_FJOIN_TX_BITS; if (join) { sdk.hwSetBits(addressShiftCtrl, mask); console.printf("(pio%d:sm%d) set join %s%n", pioNum, smNum, type); } else { sdk.hwClearBits(addressShiftCtrl, mask); console.printf("(pio%d:sm%d) unset join %s%n", pioNum, smNum, type); } } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int smNum = options.getValue(optSm); final Integer optAddressValue = options.getValue(optAddress); final boolean optClearValue = options.getValue(optClear).isOn(); final boolean optClearTxStallValue = options.getValue(optClearTxStall).isOn(); final boolean optClearTxOverValue = options.getValue(optClearTxOver).isOn(); final boolean optClearRxUnderValue = options.getValue(optClearRxUnder).isOn(); final boolean optClearRxStallValue = options.getValue(optClearRxStall).isOn(); final Integer optValueValue = options.getValue(optValue); final boolean optDequeueValue = options.getValue(optDequeue).isOn(); final boolean optEnqueueValue = options.getValue(optEnqueue).isOn(); final boolean optJoinValue = options.getValue(optJoin).isOn(); final boolean optUnjoinValue = options.getValue(optUnjoin).isOn(); final Integer optThresholdValue = options.getValue(optThreshold); final boolean optShiftLeftValue = options.getValue(optShiftLeft).isOn(); final boolean optShiftRightValue = options.getValue(optShiftRight).isOn(); final Boolean optAutoValue = options.getValue(optAuto); final boolean haveModOp = optClearValue || optClearTxStallValue || optClearTxOverValue || optClearRxUnderValue || optClearRxStallValue || optDequeueValue || optEnqueueValue || optJoinValue || optUnjoinValue || (optThresholdValue != null) || optShiftLeftValue || optShiftRightValue || (optAutoValue != null) || (optAddressValue != null) || (optValueValue != null); if (!haveModOp) { displayFifo(pioNum, smNum); } if (optClearValue) { clear(pioNum, smNum); } if (optClearTxStallValue) { clearFDebug(pioNum, smNum, Constants.FDEBUG_TXSTALL_LSB, "TX Stall"); } if (optClearTxOverValue) { clearFDebug(pioNum, smNum, Constants.FDEBUG_TXOVER_LSB, "TX Over"); } if (optClearRxUnderValue) { clearFDebug(pioNum, smNum, Constants.FDEBUG_RXUNDER_LSB, "RX Under"); } if (optClearRxStallValue) { clearFDebug(pioNum, smNum, Constants.FDEBUG_RXSTALL_LSB, "RX Stall"); } final Type type = options.getValue(optRX).isOn() ? Type.RX : Type.TX; if (optDequeueValue) { dequeue(pioNum, smNum, type); } if (optEnqueueValue) { enqueue(pioNum, smNum, type, optValueValue); } if (optJoinValue) { setFJoin(pioNum, smNum, type, true); } if (optUnjoinValue) { setFJoin(pioNum, smNum, type, false); } if (optThresholdValue != null) { setThreshold(pioNum, smNum, type, optThresholdValue); } if (optShiftLeftValue) { setShiftDir(pioNum, smNum, type, false); } if (optShiftRightValue) { setShiftDir(pioNum, smNum, type, true); } if (optAutoValue != null) { setAuto(pioNum, smNum, type, optAutoValue); } if ((optAddressValue != null) && (optValueValue != null)) { writeFifoAddress(pioNum, smNum, optAddressValue, optValueValue); } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Gpio.java ================================================ /* * @(#)Gpio.java 1.00 21/04/06 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.GPIOIOBank0Registers; import org.soundpaint.rp2040pio.PicoEmuRegisters; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.monitor.MonitorUtils; import org.soundpaint.rp2040pio.sdk.GPIOSDK; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "gpio" the status of the RP2040's GPIO pins. * Support for modifying GPIO status is not yet implemented. * * TODO: Also show GPIO status individual for each PIO / SM, as well * as if PIO GPIO pins are routed to RP2040's GPIO pins or not. Also * consider adding support for changing status of GPIO pins. */ public class Gpio extends Command { private enum Policy { PASS("pass"), INVERT("invert"), LOW("low"), HIGH("high"); private final String displayValue; private Policy(final String displayValue) { this.displayValue = displayValue; } public String getDisplayValue() { return displayValue; } public static Policy fromValue(final int value) { if ((value < 0) || (value >= POLICIES.length)) { throw new IllegalArgumentException("value: " + value); } return POLICIES[value]; } } private static final Policy[] POLICIES = Policy.values(); private static final String fullName = "gpio"; private static final String singleLineDescription = "display or change status of GPIO pins"; private static final String notes = "Each PIO has a set of local GPIO pins that, depending on the GPIO's%n" + "function selection settings, are propagated to the RP2040's GPIO%n" + "pins or not. Use this command for displaying the RP2040's GPIO pins%n" + "after function selection, or as directly output by a specific PIO's%n" + "local GPIO pins.%n" + "%n" + "Use one of options \"-i\", \"-s\", \"-c\", \"-e\", \"-d\", together%n" + "with option \"-g\", for either initializing a GPIO pin for a PIO, or%n" + "for clearing or setting its status or for specifying its pin%n" + "direction by enabling or disabling its output, respectively.%n" + "Use options \"-p\" and \"-g\" option to specify which PIO and GPIO%n" + "pin to apply the operation. Option \"-p\" can be ommitted when%n" + "clearing or setting GPIO pin status; in that case, the operation%n" + "will apply the new pin status as external input for the specified%n" + "pin.%n" + "%n" + "If none of options \"-i\", \"-s\", \"-c\", \"-e\", \"-d\", nor any of%n" + "the override options is specified, the current status of all GPIO pins%n" + "will be displayed, depending on option \"-p\" for either of the PIOs or,%n" + "if \"-p\" is not specified, for the GPIO after function selection." + "%n" + "One of options \"--override-irq\", \"--override-in\", \"--override-oe\", and%n" + "\"--override-out\" may be specified together with one of policy options%n" + "\"--pass\", \"--invert\", \"--low\", \"--high\" to change override policy%n" + "of the specified GPIO pin. If no policy option is specified, the current%n" + "policy is displayed for the specified override target."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", null, "PIO number, either 0 or 1 or undefined"); private static final CmdOptions.IntegerOptionDeclaration optGpio = CmdOptions.createIntegerOption("NUMBER", false, 'g', "gpio", null, "number of GPIO pin (0…31)"); private static final CmdOptions.FlagOptionDeclaration optInit = CmdOptions.createFlagOption(false, 'i', "init", CmdOptions.Flag.OFF, "initialize GPIO pin for use with the " + "specified PIO"); private static final CmdOptions.FlagOptionDeclaration optSet = CmdOptions.createFlagOption(false, 's', "set", CmdOptions.Flag.OFF, "set GPIO pin of the specified PIO or input"); private static final CmdOptions.FlagOptionDeclaration optClear = CmdOptions.createFlagOption(false, 'c', "clear", CmdOptions.Flag.OFF, "clear GPIO pin of the specified PIO or input"); private static final CmdOptions.FlagOptionDeclaration optEnable = CmdOptions.createFlagOption(false, 'e', "enable", CmdOptions.Flag.OFF, "enable GPIO output of the specified PIO, " + "setting direction to \"out\""); private static final CmdOptions.FlagOptionDeclaration optDisable = CmdOptions.createFlagOption(false, 'd', "disable", CmdOptions.Flag.OFF, "disable GPIO output of the specified PIO, " + "setting direction to \"in\""); private static final CmdOptions.FlagOptionDeclaration optBefore = CmdOptions.createFlagOption(false, null, "before", CmdOptions.Flag.OFF, "when displaying global GPIO status, show " + "status before rather than after override"); private static final CmdOptions.FlagOptionDeclaration optOverrideIrq = CmdOptions.createFlagOption(false, null, "override-irq", CmdOptions.Flag.OFF, "specify override policy for a GPIO pin " + "interrupt input"); private static final CmdOptions.FlagOptionDeclaration optOverrideIn = CmdOptions.createFlagOption(false, null, "override-in", CmdOptions.Flag.OFF, "specify override policy for a GPIO pin " + "peripheral input"); private static final CmdOptions.FlagOptionDeclaration optOverrideOe = CmdOptions.createFlagOption(false, null, "override-oe", CmdOptions.Flag.OFF, "specify override policy for a GPIO pin " + "output enable"); private static final CmdOptions.FlagOptionDeclaration optOverrideOut = CmdOptions.createFlagOption(false, null, "override-out", CmdOptions.Flag.OFF, "specify override policy for a GPIO pin " + "output level"); private static final CmdOptions.FlagOptionDeclaration optPass = CmdOptions.createFlagOption(false, null, "pass", CmdOptions.Flag.OFF, "select 'pass' override policy"); private static final CmdOptions.FlagOptionDeclaration optInvert = CmdOptions.createFlagOption(false, null, "invert", CmdOptions.Flag.OFF, "select 'invert' override policy"); private static final CmdOptions.FlagOptionDeclaration optLow = CmdOptions.createFlagOption(false, null, "low", CmdOptions.Flag.OFF, "select 'low' override policy"); private static final CmdOptions.FlagOptionDeclaration optHigh = CmdOptions.createFlagOption(false, null, "high", CmdOptions.Flag.OFF, "select 'high' override policy"); private final SDK sdk; public Gpio(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optGpio, optInit, optSet, optClear, optEnable, optDisable, optBefore, optOverrideIrq, optOverrideIn, optOverrideOe, optOverrideOut, optPass, optInvert, optLow, optHigh }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { if (options.isDefined(optPio)) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } } if (options.isDefined(optGpio)) { final int gpioNum = options.getValue(optGpio); if ((gpioNum < 0) || (gpioNum > Constants.GPIO_NUM - 1)) { final String message = String.format("GPIO number must be in the range 0x00…0x%02x", Constants.GPIO_NUM - 1); throw new CmdOptions.ParseException(message); } } int editOpCount = 0; if (options.getValue(optSet) == CmdOptions.Flag.ON) editOpCount++; if (options.getValue(optClear) == CmdOptions.Flag.ON) editOpCount++; int manageOpCount = 0; if (options.getValue(optInit) == CmdOptions.Flag.ON) manageOpCount++; if (options.getValue(optEnable) == CmdOptions.Flag.ON) manageOpCount++; if (options.getValue(optDisable) == CmdOptions.Flag.ON) manageOpCount++; int overrideOpCount = 0; if (options.getValue(optOverrideIrq) == CmdOptions.Flag.ON) overrideOpCount++; if (options.getValue(optOverrideIn) == CmdOptions.Flag.ON) overrideOpCount++; if (options.getValue(optOverrideOe) == CmdOptions.Flag.ON) overrideOpCount++; if (options.getValue(optOverrideOut) == CmdOptions.Flag.ON) overrideOpCount++; int overridePolicyCount = 0; if (options.getValue(optPass) == CmdOptions.Flag.ON) overridePolicyCount++; if (options.getValue(optInvert) == CmdOptions.Flag.ON) overridePolicyCount++; if (options.getValue(optLow) == CmdOptions.Flag.ON) overridePolicyCount++; if (options.getValue(optHigh) == CmdOptions.Flag.ON) overridePolicyCount++; final int count = editOpCount + manageOpCount + overrideOpCount; if (count > 1) { throw new CmdOptions. ParseException("at most one of options \"-i\", \"-s\", \"-c\", " + "\"-e\", \"-d\" and the override options may be specified " + "at the same time"); } if (count > 0) { if (!options.isDefined(optGpio)) { throw new CmdOptions. ParseException("option not specified: " + optGpio); } if (options.getValue(optBefore) == CmdOptions.Flag.ON) { throw new CmdOptions. ParseException("option 'before' only valid when no operation is specified"); } } if (count == 0) { if (options.isDefined(optGpio)) { throw new CmdOptions. ParseException("option may be specified only together with an operation: " + optGpio); } } if (overridePolicyCount > 1) { throw new CmdOptions. ParseException("at most one of options \"--pass\", \"--invert\", " + "\"--low\", and \"--high\" may be specified " + "at the same time"); } if (overrideOpCount < overridePolicyCount) { throw new CmdOptions. ParseException("override policy may not be specified without override option"); } if (manageOpCount > 0) { if (!options.isDefined(optPio)) { throw new CmdOptions. ParseException("option not specified: " + optPio); } } if (overrideOpCount > 0) { if (options.isDefined(optPio)) { throw new CmdOptions. ParseException("option must not be specified for overrides: " + optPio); } } } } private void displayGpio(final Integer optPioValue, final boolean before) throws IOException { final GPIOSDK.Override override = before ? GPIOSDK.Override.BEFORE : GPIOSDK.Override.AFTER; final String gpioDisplay = optPioValue != null ? MonitorUtils.gpioDisplay(sdk, optPioValue) : MonitorUtils.gpioDisplay(sdk, override); console.printf(gpioDisplay); } private void initGpio(final int pioNum, final int gpioNum) throws IOException { final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); pioSdk.gpioInit(gpioNum); console.printf("(pio%d:sm*) initialized GPIO pin %02x for use with PIO%d%n", pioNum, gpioNum, pioNum); } private void setGpio(final Integer pioNum, final int gpioNum) throws IOException { if (pioNum != null) { final int address = PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.GPIO_PINS); final int mask = 0x1 << gpioNum; sdk.hwSetBits(address, mask); console.printf("(pio%d:sm*) set GPIO output pin %02x of PIO%d to 1%n", pioNum, gpioNum, pioNum); } else { final int address = PicoEmuRegisters.getAddress(PicoEmuRegisters.Regs.GPIO_PADIN); final int mask = 0x1 << gpioNum; sdk.hwSetBits(address, mask); console.printf("(pio*:sm*) set GPIO external input %02x to 1%n", gpioNum, pioNum); } } private void clearGpio(final Integer pioNum, final int gpioNum) throws IOException { if (pioNum != null) { final int address = PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.GPIO_PINS); final int mask = 0x1 << gpioNum; sdk.hwClearBits(address, mask); console.printf("(pio%d:sm*) set GPIO output pin %02x of PIO%d to 0%n", pioNum, gpioNum, pioNum); } else { final int address = PicoEmuRegisters.getAddress(PicoEmuRegisters.Regs.GPIO_PADIN); final int mask = 0x1 << gpioNum; sdk.hwClearBits(address, mask); console.printf("(pio*:sm*) set GPIO external input %02x to 0%n", gpioNum, pioNum); } } private void enableGpio(final int pioNum, final int gpioNum) throws IOException { final int address = PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.GPIO_PINDIRS); final int mask = 0x1 << gpioNum; sdk.hwSetBits(address, mask); console.printf("(pio%d:sm*) set direction of GPIO pin %02x of PIO%d to " + "\"out\"%n", pioNum, gpioNum, pioNum); } private void disableGpio(final int pioNum, final int gpioNum) throws IOException { final int address = PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.GPIO_PINDIRS); final int mask = 0x1 << gpioNum; sdk.hwClearBits(address, mask); console.printf("(pio%d:sm*) set direction of GPIO pin %02x of PIO%d to " + "\"in\"%n", pioNum, gpioNum, pioNum); } private void setOverride(final String target, final String policy, final int gpioNum, final int overridePolicy, final int lsb, final int policyBits) throws IOException { final int address = GPIOIOBank0Registers.getGPIOAddress(gpioNum, GPIOIOBank0Registers.Regs.GPIO0_CTRL); sdk.hwWriteMasked(address, overridePolicy << lsb, policyBits); console.printf("(pio*:sm*) set %s override of GPIO pin %02x to policy '%s'%n", target, gpioNum, policy); } private void displayOverride(final String target, final int gpioNum, final int lsb, final int policyBits) throws IOException { final int address = GPIOIOBank0Registers.getGPIOAddress(gpioNum, GPIOIOBank0Registers.Regs.GPIO0_CTRL); final int ctrl = sdk.readAddress(address); final Policy policy = Policy.fromValue((ctrl & policyBits) >>> lsb); console.printf("(pio*:sm*) %s override of GPIO pin %02x policy is '%s'%n", target, gpioNum, policy.getDisplayValue()); } private void displayOrSetOverride(final String target, final String policy, final int gpioNum, final Integer overridePolicy, final int lsb, final int policyBits) throws IOException { if (policy != null) { setOverride(target, policy, gpioNum, overridePolicy, lsb, policyBits); } else { displayOverride(target, gpioNum, lsb, policyBits); } } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final boolean init = options.getValue(optInit) == CmdOptions.Flag.ON; final boolean clear = options.getValue(optClear) == CmdOptions.Flag.ON; final boolean set = options.getValue(optSet) == CmdOptions.Flag.ON; final boolean enable = options.getValue(optEnable) == CmdOptions.Flag.ON; final boolean disable = options.getValue(optDisable) == CmdOptions.Flag.ON; final boolean overrideIrq = options.getValue(optOverrideIrq) == CmdOptions.Flag.ON; final boolean overrideIn = options.getValue(optOverrideIn) == CmdOptions.Flag.ON; final boolean overrideOe = options.getValue(optOverrideOe) == CmdOptions.Flag.ON; final boolean overrideOut = options.getValue(optOverrideOut) == CmdOptions.Flag.ON; final String policy; final Integer policyBits; if (options.getValue(optHigh) == CmdOptions.Flag.ON) { policy = "high"; policyBits = 0x3; } else if (options.getValue(optLow) == CmdOptions.Flag.ON) { policy = "low"; policyBits = 0x2; } else if (options.getValue(optInvert) == CmdOptions.Flag.ON) { policy = "invert"; policyBits = 0x1; } else if (options.getValue(optPass) == CmdOptions.Flag.ON) { policy = "pass"; policyBits = 0x0; } else { policy = null; policyBits = null; } if (init || clear || set || enable || disable || overrideIrq || overrideIn || overrideOe || overrideOut) { final Integer pioNum = options.getValue(optPio); final Integer gpioNum = options.getValue(optGpio); if (init) { initGpio(pioNum, gpioNum); } else if (set) { setGpio(pioNum, gpioNum); } else if (clear) { clearGpio(pioNum, gpioNum); } else if (enable) { enableGpio(pioNum, gpioNum); } else if (disable) { disableGpio(pioNum, gpioNum); } else if (overrideIrq) { displayOrSetOverride("IRQ ", policy, gpioNum, policyBits, Constants.IO_BANK0_GPIO0_CTRL_IRQOVER_LSB, Constants.IO_BANK0_GPIO0_CTRL_IRQOVER_BITS); } else if (overrideIn) { displayOrSetOverride("input", policy, gpioNum, policyBits, Constants.IO_BANK0_GPIO0_CTRL_INOVER_LSB, Constants.IO_BANK0_GPIO0_CTRL_INOVER_BITS); } else if (overrideOe) { displayOrSetOverride("output enable", policy, gpioNum, policyBits, Constants.IO_BANK0_GPIO0_CTRL_OEOVER_LSB, Constants.IO_BANK0_GPIO0_CTRL_OEOVER_BITS); } else if (overrideOut) { displayOrSetOverride("output", policy, gpioNum, policyBits, Constants.IO_BANK0_GPIO0_CTRL_OUTOVER_LSB, Constants.IO_BANK0_GPIO0_CTRL_OUTOVER_BITS); } else { throw new InternalError("unexpected case fall-through"); } } else { final boolean before = options.getValue(optBefore) == CmdOptions.Flag.ON; displayGpio(options.getValue(optPio), before); } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Help.java ================================================ /* * @(#)Help.java 1.00 21/03/28 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.monitor.CommandRegistry; /** * Monitor command "help" prints a list of all available monitor * commands. */ public class Help extends Command { private static final String fullName = "help"; private static final String singleLineDescription = "list all available monitor commands"; private final CommandRegistry commands; public Help(final PrintStream console, final CommandRegistry commands) { super(console, fullName, singleLineDescription); if (commands == null) { throw new NullPointerException("commands"); } this.commands = commands; } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) { console.println("Available commands:"); for (final Command command : commands) { console.printf(" %-12s %s%n", command.getFullName(), command.getSingleLineDescription()); } console.println(); console.printf(helpNotes); console.println(); return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Interrupt.java ================================================ /* * @(#)Interrupt.java 1.00 21/06/08 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.PIORegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "interrupt" displays or modifies a PIO's interrupt * flags configuration and status. */ public class Interrupt extends Command { private static final String fullName = "interrupt"; private static final String singleLineDescription = "display or change a PIO's IRQs configuration and status"; private static final String notes = "Use option \"-p\"to select a PIO.%n" + "If none of the IRQ modification options is specified, the status%n"+ "of the IRQ bits of the selected state machine is displayed.%n" + "For modification operation, additionally specify either option%n" + "\"-s\" for selecting a state machine within the selected PIO when%n" + "going to modify an SM specific IRQ flag. Or specify option \"-i\"%n" + "for selecting one of those IRQ flags that are visible to all SMs.%n" + "Use option \"-t\" to apply modification on the TXNFULL group of%n" + "IRQ flags, or option \"-r\" to apply on the RXNEMPTY group of%n" + "IRQ flags; if none of those two options is specified, modification%n" + "will affect the SM group of IRQ flags.%n" + "For disabling the selected IRQ flag, use option \"-d\".%n" + "For enabling the selected IRQ flag, use option \"-e\".%n" + "For enforcing the selected IRQ flag to be set, use option \"-f\".%n" + "For undoing enforcement, use option \"-u\".%n" + "For selecting to which IRQ (IRQ0, IRQ1) option \"-e\", \"-d\",%n" + "\"-f\" or \"-u\" will apply, use option \"-0\" or \"-1\".%n" + "For setting or clearing one of the IRQs visible to all SMs, use%n" + "option \"-v\"."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", null, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.IntegerOptionDeclaration optIrq = CmdOptions.createIntegerOption("NUMBER", false, 'i', "irq", null, "PIO IRQ number (0…7)"); private static final CmdOptions.FlagOptionDeclaration optTxNFull = CmdOptions.createFlagOption(false, 't', "txnfull", CmdOptions.Flag.OFF, "select TXNFULL interrupt flag of the " + "selected state machine for modification"); private static final CmdOptions.FlagOptionDeclaration optRxNEmpty = CmdOptions.createFlagOption(false, 'r', "rxnempty", CmdOptions.Flag.OFF, "select RXNEMPTY interrupt flag of the " + "selected state machine for modification"); private static final CmdOptions.FlagOptionDeclaration optDisable = CmdOptions.createFlagOption(false, 'd', "disable", CmdOptions.Flag.OFF, "override selected interrupt flag to " + "be always cleared"); private static final CmdOptions.FlagOptionDeclaration optEnable = CmdOptions.createFlagOption(false, 'e', "enable", CmdOptions.Flag.OFF, "revert \"-d\" option for the specified" + "interrupt flag"); private static final CmdOptions.FlagOptionDeclaration optForce = CmdOptions.createFlagOption(false, 'f', "force", CmdOptions.Flag.OFF, "override selected interrupt flag to " + "be always set"); private static final CmdOptions.FlagOptionDeclaration optUnforce = CmdOptions.createFlagOption(false, 'u', "unforce", CmdOptions.Flag.OFF, "revert \"-f\" option for the specified" + "interrupt flag"); private static final CmdOptions.FlagOptionDeclaration optZero = CmdOptions.createFlagOption(false, '0', "irq0", CmdOptions.Flag.OFF, "select PIO IRQ0 as override target"); private static final CmdOptions.FlagOptionDeclaration optOne = CmdOptions.createFlagOption(false, '1', "irq1", CmdOptions.Flag.OFF, "select PIO IRQ1 as override target"); private static final CmdOptions.BooleanOptionDeclaration optValue = CmdOptions.createBooleanOption(false, 'v', "value", null, "set value for the selected IRQ flag " + "of those visible to all SMs"); private enum FlagsGroup { IRQ_SM(8, "IRQ / SM"), TXNFULL(4, "TxNFull"), RXNEMPTY(0, "RxNEmpty"); private final int lsb; private final String label; private FlagsGroup(final int lsb, final String label) { this.lsb = lsb; this.label = label; } public int getLSB() { return lsb; } public String getLabel() { return label; } public static FlagsGroup fromOptions(final boolean txNFull, final boolean rxNEmpty) { if (txNFull && rxNEmpty) { throw new IllegalArgumentException("txNFull and rxNEmpty both true"); } return txNFull ? TXNFULL : (rxNEmpty ? RXNEMPTY : IRQ_SM); } } private final SDK sdk; public Interrupt(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optIrq, optTxNFull, optRxNEmpty, optDisable, optEnable, optForce, optUnforce, optZero, optOne, optValue }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final boolean txNFull = options.getValue(optTxNFull).isOn(); final boolean rxNEmpty = options.getValue(optRxNEmpty).isOn(); final boolean enable = options.getValue(optEnable).isOn(); final boolean disable = options.getValue(optDisable).isOn(); final boolean force = options.getValue(optForce).isOn(); final boolean unforce = options.getValue(optUnforce).isOn(); final boolean zero = options.getValue(optZero).isOn(); final boolean one = options.getValue(optOne).isOn(); final Boolean optValueValue = options.getValue(optValue); final boolean haveMaskingOrForcingOp = enable || disable || force || unforce; final boolean haveModOp = haveMaskingOrForcingOp || (optValueValue != null); final Integer optSmValue = options.getValue(optSm); if (optSmValue != null) { final int smNum = optSmValue; if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } if (!haveModOp) { final String message = "missing option: -e, -d, -f, -u, -l or -h"; throw new CmdOptions.ParseException(message); } } final Integer optIrqValue = options.getValue(optIrq); if (optIrqValue != null) { final int irqNum = optIrqValue; if ((irqNum < 0) || (irqNum > 7)) { throw new CmdOptions. ParseException("IRQ number must be in the range 0…7"); } if (optValueValue == null) { throw new CmdOptions.ParseException("missing option: -v"); } if (optSmValue != null) { throw new CmdOptions.ParseException("conflicting options: -s and -i"); } if (txNFull) { throw new CmdOptions.ParseException("conflicting options: -i and -t"); } if (rxNEmpty) { throw new CmdOptions.ParseException("conflicting options: -i and -t"); } } if (enable && disable) { throw new CmdOptions.ParseException("conflicting options: -e and -d"); } if (force && unforce) { throw new CmdOptions.ParseException("conflicting options: -f and -u"); } if (txNFull | rxNEmpty) { if ((optSmValue == null) && (optIrqValue == null)) { throw new CmdOptions.ParseException("missing option: -i or -s"); } } final boolean haveIrqSelection = zero || one; if (haveIrqSelection) { if (!haveMaskingOrForcingOp) { final String message = "missing option: -e, -d, -f or -u"; throw new CmdOptions.ParseException(message); } } if (haveMaskingOrForcingOp) { if (!haveIrqSelection) { throw new CmdOptions.ParseException("missing option: -0 or -1"); } } if (haveModOp) { if ((optSmValue == null) && (optIrqValue == null)) { throw new CmdOptions.ParseException("missing option: -s or -i"); } } } } private static String int2bin(final int d) { return String.format("%4s", Integer.toBinaryString(d)).replace(' ', '0'); } private void displayInterrupts(final int pioNum) throws IOException { console.printf("(pio%d:sm*) IRQ / SM TxNFull RxNEmpty%n", pioNum); console.printf(" 76543210 3210 3210%n"); final int addressIrq = PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.IRQ); final int irq = sdk.readAddress(addressIrq); final int addressIntR = PIORegisters.getAddress(pioNum, PIORegisters.Regs.INTR); final int intR = sdk.readAddress(addressIntR); console.printf(" %s%s %s %s (INTR)%n", int2bin(irq >> 4), int2bin(irq & 0xf), int2bin((intR >> 4) & 0xf), int2bin(intR & 0xf)); final int addressIRQ0_IntE = PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ0_INTE); final int i0IntE = sdk.readAddress(addressIRQ0_IntE); console.printf(" %s %s %s (IRQ0_INTE)%n", int2bin((i0IntE >> 8) & 0xf), int2bin((i0IntE >> 4) & 0xf), int2bin(i0IntE & 0xf)); final int addressIRQ0_IntF = PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ0_INTF); final int i0IntF = sdk.readAddress(addressIRQ0_IntF); console.printf(" %s %s %s (IRQ0_INTF)%n", int2bin((i0IntF >> 8) & 0xf), int2bin((i0IntF >> 4) & 0xf), int2bin(i0IntF & 0xf)); final int addressIRQ0_IntS = PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ0_INTS); final int i0IntS = sdk.readAddress(addressIRQ0_IntS); console.printf(" %s %s %s (IRQ0_INTS)%n", int2bin((i0IntS >> 8) & 0xf), int2bin((i0IntS >> 4) & 0xf), int2bin(i0IntS & 0xf)); final int addressIRQ1_IntE = PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ1_INTE); final int i1IntE = sdk.readAddress(addressIRQ1_IntE); console.printf(" %s %s %s (IRQ1_INTE)%n", int2bin((i1IntE >> 8) & 0xf), int2bin((i1IntE >> 4) & 0xf), int2bin(i1IntE & 0xf)); final int addressIRQ1_IntF = PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ1_INTF); final int i1IntF = sdk.readAddress(addressIRQ1_IntF); console.printf(" %s %s %s (IRQ1_INTF)%n", int2bin((i1IntF >> 8) & 0xf), int2bin((i1IntF >> 4) & 0xf), int2bin(i1IntF & 0xf)); final int addressIRQ1_IntS = PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ1_INTS); final int i1IntS = sdk.readAddress(addressIRQ1_IntS); console.printf(" %s %s %s (IRQ1_INTS)%n", int2bin((i1IntS >> 8) & 0xf), int2bin((i1IntS >> 4) & 0xf), int2bin(i1IntS & 0xf)); } private void disable(final int pioNum, final int smNum, final FlagsGroup flagsGroup, final int irqNum) throws IOException { final int addressINTE = PIORegisters.getAddress(pioNum, irqNum == 0 ? PIORegisters.Regs.IRQ0_INTE : PIORegisters.Regs.IRQ1_INTE); final int lsb = flagsGroup.getLSB(); sdk.hwClearBits(addressINTE, 1 << (lsb + smNum)); console.printf("(pio%d:sm%d) disabled %s for IRQ%d (IRQ%d_INTE)%n", pioNum, smNum, flagsGroup.getLabel(), irqNum, irqNum); } private void enable(final int pioNum, final int smNum, final FlagsGroup flagsGroup, final int irqNum) throws IOException { final int addressINTE = PIORegisters.getAddress(pioNum, irqNum == 0 ? PIORegisters.Regs.IRQ0_INTE : PIORegisters.Regs.IRQ1_INTE); final int lsb = flagsGroup.getLSB(); sdk.hwSetBits(addressINTE, 1 << (lsb + smNum)); console.printf("(pio%d:sm%d) enabled %s for IRQ%d (IRQ%d_INTE)%n", pioNum, smNum, flagsGroup.getLabel(), irqNum, irqNum); } private void force(final int pioNum, final int smNum, final FlagsGroup flagsGroup, final int irqNum) throws IOException { final int addressINTF = PIORegisters.getAddress(pioNum, irqNum == 0 ? PIORegisters.Regs.IRQ0_INTF : PIORegisters.Regs.IRQ1_INTF); final int lsb = flagsGroup.getLSB(); sdk.hwSetBits(addressINTF, 1 << (lsb + smNum)); console.printf("(pio%d:sm%d) set force %s for IRQ%d (IRQ%d_INTF)%n", pioNum, smNum, flagsGroup.getLabel(), irqNum, irqNum); } private void unforce(final int pioNum, final int smNum, final FlagsGroup flagsGroup, final int irqNum) throws IOException { final int addressINTF = PIORegisters.getAddress(pioNum, irqNum == 0 ? PIORegisters.Regs.IRQ0_INTF : PIORegisters.Regs.IRQ1_INTF); final int lsb = flagsGroup.getLSB(); sdk.hwClearBits(addressINTF, 1 << (lsb + smNum)); console.printf("(pio%d:sm%d) unset force %s for IRQ%d (IRQ%d_INTF)%n", pioNum, smNum, flagsGroup.getLabel(), irqNum, irqNum); } private void setValue(final int pioNum, final int irqNum, final Boolean value) throws IOException { final int bitValue = value ? 1 : 0; final int addressIrq = PIORegisters.getAddress(pioNum, value ? PIORegisters.Regs.IRQ_FORCE : PIORegisters.Regs.IRQ); sdk.hwWriteMasked(addressIrq, 1 << irqNum, 1 << irqNum); console.printf("(pio%d:sm*) set IRQ bit %d to %d%n", pioNum, irqNum, bitValue); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final Integer optSmValue = options.getValue(optSm); final Integer optIrqValue = options.getValue(optIrq); final boolean disable = options.getValue(optDisable).isOn(); final boolean enable = options.getValue(optEnable).isOn(); final boolean force = options.getValue(optForce).isOn(); final boolean unforce = options.getValue(optUnforce).isOn(); final boolean zero = options.getValue(optZero).isOn(); final boolean one = options.getValue(optOne).isOn(); final Boolean optValueValue = options.getValue(optValue); final boolean txNFull = options.getValue(optTxNFull).isOn(); final boolean rxNEmpty = options.getValue(optRxNEmpty).isOn(); final boolean haveModOp = enable || disable || force || unforce || zero || one || (optValueValue != null); final FlagsGroup flagsGroup = FlagsGroup.fromOptions(txNFull, rxNEmpty); final int irqNum = one ? 1 : (zero ? 0 : -1); if (!haveModOp) { displayInterrupts(pioNum); } if (disable) { disable(pioNum, optSmValue, flagsGroup, irqNum); } if (enable) { enable(pioNum, optSmValue, flagsGroup, irqNum); } if (force) { force(pioNum, optSmValue, flagsGroup, irqNum); } if (unforce) { unforce(pioNum, optSmValue, flagsGroup, irqNum); } if (optValueValue != null) { setValue(pioNum, optSmValue != null ? optSmValue : optIrqValue, optValueValue); } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Label.java ================================================ /* * @(#)Label.java 1.00 21/03/29 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "label" displays a register's name, if available. */ public class Label extends Command { private static final String fullName = "label"; private static final String singleLineDescription = "display a register's label"; private static final CmdOptions.IntegerOptionDeclaration optAddress = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "address", null, "address (0x00000000…0xffffffff) of the " + "register to display"); private final SDK sdk; public Label(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, new CmdOptions.OptionDeclaration[] { optAddress }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if ((options.getValue(optHelp) != CmdOptions.Flag.ON) && (!options.isDefined(optAddress))) { throw new CmdOptions. ParseException("option not specified: " + optAddress); } } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int address = options.getValue(optAddress); final boolean validAddress = sdk.providesAddress(address); if (!validAddress) { final String message = String.format("unsupported address: 0x%08x", address); throw new IOException(message); } final String label = sdk.getLabelForAddress(address); console.printf("%s (0x%08x)%n" , label, address); return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Load.java ================================================ /* * @(#)Load.java 1.00 21/03/31 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.BufferedReader; import java.io.IOException; import java.io.LineNumberReader; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.IOUtils; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.monitor.MonitorUtils; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "load" loads a program from a file and stores it in * a PIO's memory. */ public class Load extends Command { private static final String fullName = "load"; private static final String singleLineDescription = "load program from file and mark affected PIO memory area as allocated"; private static final String notes = "The \"load\" command reads in the specified hex dump and stores it%n" + "as a PIO program in one of the two PIOs' instruction memory.%n" + "By convention, hex dump files have \".hex\" file name suffix.%n" + "%n" + "Built-in example hex dumps are available that can be listed with%n" + "the \"-l\" option. To select any of the example hex dumps, use the%n" + "\"-e\" option and pass to this option the hex dump's name as shown%n" + "in the list of available built-in hex dumps. To view a built-in hex%n" + "dump prior to loading it, use the \"-s\" option.%n" + "For user-provided hex dumps, use the \"-f\" option to specify the%n" + "file path of the hex dump, including the \".hex\" file name suffix." + "%n" + "Note that tracking memory allocation is not a feature of the%n" + "RP2040, but local to this monitor instance, just to avoid%n" + "accidentally overwriting your own PIO programs. Other applications%n" + "that concurrently access the RP2040 will therefore ignore%n" + "this instance's allocation tracking and may arbitrarily%n" + "overwrite allocated PIO memory, using their own allocation scheme.%n" + "%n" + "Expected file format:%n" + "The program file to be loaded must be a regular text file with%n" + "either \"\\n\" or \"\\r\\n\" line endings and UTF-8 encoding.%n" + "Other encodings may also work for the core instruction opcodes, but%n" + "may give unexpected results if meta information is relevant.%n" + "Empty lines are ignored. Each non-empty line of the text file%n" + "must contain either meta information or an opcode. A meta%n" + "information line starts with a leading '#' character and may%n" + "contain either a special directive, or, if the '#' is followed by%n" + "a ';' character, an arbitrary user comment. Each non-empty line that%n" + "is not a meta information line must contain a single opcode. Each%n" + "opcode is a 32 bits integer and represented as a plain four-digit%n" + "hexadecimal value without leading \"0x\". The maximum allowed number%n" + "of opcodes is 32."; private final SDK sdk; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.FlagOptionDeclaration optList = CmdOptions.createFlagOption(false, 'l', "list", CmdOptions.Flag.OFF, "list names of available example hex dumps"); private static final CmdOptions.StringOptionDeclaration optShow = CmdOptions.createStringOption("NAME", false, 's', "show", null, "name of built-in example hex dump to show"); private static final CmdOptions.StringOptionDeclaration optExample = CmdOptions.createStringOption("NAME", false, 'e', "example", null, "name of built-in example hex dump to load"); private static final CmdOptions.StringOptionDeclaration optFile = CmdOptions.createStringOption("PATH", false, 'f', "file", null, "path of hex dump file to load"); private static final CmdOptions.IntegerOptionDeclaration optAddress = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "address", null, "preferred program start address " + "(0x00…0x1f)"); public Load(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optList, optShow, optExample, optFile, optAddress }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final boolean optListValue = options.getValue(optList) == CmdOptions.Flag.ON; final String optShowValue = options.getValue(optShow); final String optExampleValue = options.getValue(optExample); final String optFileValue = options.getValue(optFile); int count = 0; if (optListValue) count++; if (optShowValue != null) count++; if (optExampleValue != null) count++; if (optFileValue != null) count++; if (options.getValue(optHelp) != CmdOptions.Flag.ON) { if (count == 0) { throw new CmdOptions. ParseException("at least one of options \"-l\", \"-s\", \"-e\" " + "and \"-f\" must be specified"); } } if (count > 1) { throw new CmdOptions. ParseException("at most one of options \"-l\", \"-s\", \"-e\" " + "and \"-f\" may be specified at the same time"); } } private boolean loadHexDump(final int pioNum, final BufferedReader reader, final String hexDumpId, final Integer address) throws IOException { final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); final int assignedAddress = address != null ? pioSdk.addProgramAtOffset(hexDumpId, reader, address) : pioSdk.addProgram(hexDumpId, reader); console.printf("(pio%d:sm*) loaded program %s at address 0x%02x%n", pioNum, hexDumpId, assignedAddress); return true; } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final boolean optListValue = options.getValue(optList) == CmdOptions.Flag.ON; final String optShowValue = options.getValue(optShow); final String optExampleValue = options.getValue(optExample); final String optFileValue = options.getValue(optFile); final Integer optAddressValue = options.getValue(optAddress); if (optListValue) { return MonitorUtils.listExampleHexDumps(console); } else if (optShowValue != null) { return MonitorUtils.showExampleHexDump(console, optShowValue); } else if (optExampleValue != null) { final String resourcePath = String.format("/examples/%s.hex", optExampleValue); final LineNumberReader reader = IOUtils.getReaderForResourcePath(resourcePath); return loadHexDump(pioNum, reader, optExampleValue, optAddressValue); } else if (optFileValue != null) { final LineNumberReader reader = IOUtils.getReaderForResourcePath(optFileValue); return loadHexDump(pioNum, reader, optFileValue, optAddressValue); } return false; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/PinCtrl.java ================================================ /* * @(#)PinCtrl.java 1.00 21/06/01 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIORegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "pinctrl" displays or modifies a state machine's * pin control. */ public class PinCtrl extends Command { private static final String fullName = "pinctrl"; private static final String singleLineDescription = "display or change state machine's pin control"; private static final String notes = "Use options \"-p\" and \"-s\" to select a state machine.%n" + "If none of the pin control modification options is specified, the%n" + "status of the pin control of the selected state machine is displayed.%n" + "For setting pin count or pin base for SET / OUT / IN instructions or%n" + "the GPIO pin number to check when executing the JMP PIN instruction,%n" + "use the corresponding \"--set-count\", \"--set-base\",%n" + "\"--out-count\", \"--out-base\", \"--in-base\" or \"--jmp-pin\"%n" + "option.%n" + "This command does not support setting the side-set count or%n" + "side-set base. For modifying side-set configuration, use the%n" + "monitor command \"side-set\" instead."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", 0, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.IntegerOptionDeclaration optSetCount = CmdOptions.createIntegerOption("COUNT", false, null, "set-count", null, "number of GPIO pins asserted by SET "+ "instruction (0…5)"); private static final CmdOptions.IntegerOptionDeclaration optSetBase = CmdOptions.createIntegerOption("NUMBER", false, null, "set-base", null, "lowest-numbered GPIO pin affected by "+ "SET PINS / PINDIRS instruction (0…31)"); private static final CmdOptions.IntegerOptionDeclaration optOutCount = CmdOptions.createIntegerOption("COUNT", false, null, "out-count", null, "number of GPIO pins asserted by OUT PINS "+ "/ PINDIRS or MOV PINS instruction (0…32)"); private static final CmdOptions.IntegerOptionDeclaration optOutBase = CmdOptions.createIntegerOption("NUMBER", false, null, "out-base", null, "lowest-numbered GPIO pin affected by OUT "+ "/ MOV PINS / PINDIRS instruction (0…31)"); private static final CmdOptions.IntegerOptionDeclaration optInBase = CmdOptions.createIntegerOption("NUMBER", false, null, "in-base", null, "GPIO pin mapped to LSB for IN " + "instruction (0…31)"); private static final CmdOptions.IntegerOptionDeclaration optJmpPin = CmdOptions.createIntegerOption("NUMBER", false, null, "jmp-pin", null, "GPIO pin to check when executing " + "JMP PIN instruction (0…31)"); private final SDK sdk; public PinCtrl(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optSetCount, optSetBase, optOutCount, optOutBase, optInBase, optJmpPin }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final int smNum = options.getValue(optSm); if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } final Integer optSetCountValue = options.getValue(optSetCount); if (optSetCountValue != null) { final int setCount = optSetCountValue; if ((setCount < 0) || (setCount > 5)) { throw new CmdOptions. ParseException("set-count must be in the range 0…5"); } } final Integer optSetBaseValue = options.getValue(optSetBase); if (optSetBaseValue != null) { final int setBase = optSetBaseValue; if ((setBase < 0) || (setBase > 31)) { throw new CmdOptions. ParseException("set-base must be in the range 0…31"); } } final Integer optOutCountValue = options.getValue(optOutCount); if (optOutCountValue != null) { final int outCount = optOutCountValue; if ((outCount < 0) || (outCount > 32)) { throw new CmdOptions. ParseException("out-count must be in the range 0…32"); } } final Integer optOutBaseValue = options.getValue(optOutBase); if (optOutBaseValue != null) { final int outBase = optOutBaseValue; if ((outBase < 0) || (outBase > 31)) { throw new CmdOptions. ParseException("out-base must be in the range 0…31"); } } final Integer optInBaseValue = options.getValue(optInBase); if (optInBaseValue != null) { final int inBase = optInBaseValue; if ((inBase < 0) || (inBase > 31)) { throw new CmdOptions. ParseException("in-base must be in the range 0…31"); } } final Integer optJmpPinValue = options.getValue(optJmpPin); if (optJmpPinValue != null) { final int jmpPin = optJmpPinValue; if ((jmpPin < 0) || (jmpPin > 31)) { throw new CmdOptions. ParseException("jmp-pin must be in the range 0…31"); } } } } private void displayPinCtrl(final int pioNum, final int smNum, final SDK sdk) throws IOException { final int pinCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL); final int pinCtrl = sdk.readAddress(pinCtrlAddress); final int execCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL); final int execCtrl = sdk.readAddress(execCtrlAddress); final int setCount = (pinCtrl & Constants.SM0_PINCTRL_SET_COUNT_BITS) >>> Constants.SM0_PINCTRL_SET_COUNT_LSB; final int setBase = (pinCtrl & Constants.SM0_PINCTRL_SET_BASE_BITS) >>> Constants.SM0_PINCTRL_SET_BASE_LSB; final int outCount = (pinCtrl & Constants.SM0_PINCTRL_OUT_COUNT_BITS) >>> Constants.SM0_PINCTRL_OUT_COUNT_LSB; final int outBase = (pinCtrl & Constants.SM0_PINCTRL_OUT_BASE_BITS) >>> Constants.SM0_PINCTRL_OUT_BASE_LSB; final int inBase = (pinCtrl & Constants.SM0_PINCTRL_IN_BASE_BITS) >>> Constants.SM0_PINCTRL_IN_BASE_LSB; final int jmpPin = (execCtrl & Constants.SM0_EXECCTRL_JMP_PIN_BITS) >>> Constants.SM0_EXECCTRL_JMP_PIN_LSB; console.printf("(pio%d:sm%d) set-count=%d, set-base=%d%n", pioNum, smNum, setCount, setBase); console.printf("(pio%d:sm%d) out-count=%d, out-base=%d%n", pioNum, smNum, outCount, outBase); console.printf("(pio%d:sm%d) jmp-pin=%d, in-base=%d%n", pioNum, smNum, jmpPin, inBase); } private void setAny(final int pioNum, final int smNum, final SDK sdk, final String name, final int value, final int mask, final int lsb, final boolean execCtrl) throws IOException { final int address = execCtrl ? PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL) : PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL); final int bits = value << lsb; sdk.hwWriteMasked(address, bits, mask); console.printf("(pio%d:sm%d) set %s to %d%n", pioNum, smNum, name, value); } private void setSetCount(final int pioNum, final int smNum, final SDK sdk, final int count) throws IOException { setAny(pioNum, smNum, sdk, "SET count", count, Constants.SM0_PINCTRL_SET_COUNT_BITS, Constants.SM0_PINCTRL_SET_COUNT_LSB, false); } private void setSetBase(final int pioNum, final int smNum, final SDK sdk, final int base) throws IOException { setAny(pioNum, smNum, sdk, "SET base", base, Constants.SM0_PINCTRL_SET_BASE_BITS, Constants.SM0_PINCTRL_SET_BASE_LSB, false); } private void setOutCount(final int pioNum, final int smNum, final SDK sdk, final int count) throws IOException { setAny(pioNum, smNum, sdk, "OUT count", count, Constants.SM0_PINCTRL_OUT_COUNT_BITS, Constants.SM0_PINCTRL_OUT_COUNT_LSB, false); } private void setOutBase(final int pioNum, final int smNum, final SDK sdk, final int base) throws IOException { setAny(pioNum, smNum, sdk, "OUT base", base, Constants.SM0_PINCTRL_OUT_BASE_BITS, Constants.SM0_PINCTRL_OUT_BASE_LSB, false); } private void setInBase(final int pioNum, final int smNum, final SDK sdk, final int base) throws IOException { setAny(pioNum, smNum, sdk, "IN base", base, Constants.SM0_PINCTRL_IN_BASE_BITS, Constants.SM0_PINCTRL_IN_BASE_LSB, false); } private void setJmpPin(final int pioNum, final int smNum, final SDK sdk, final int gpioNum) throws IOException { setAny(pioNum, smNum, sdk, "JMP PIN", gpioNum, Constants.SM0_EXECCTRL_JMP_PIN_BITS, Constants.SM0_EXECCTRL_JMP_PIN_LSB, true); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int smNum = options.getValue(optSm); final Integer optSetCountValue = options.getValue(optSetCount); final Integer optSetBaseValue = options.getValue(optSetBase); final Integer optOutCountValue = options.getValue(optOutCount); final Integer optOutBaseValue = options.getValue(optOutBase); final Integer optInBaseValue = options.getValue(optInBase); final Integer optJmpPinValue = options.getValue(optJmpPin); if ((optSetCountValue == null) && (optSetBaseValue == null) && (optOutCountValue == null) && (optOutBaseValue == null) && (optInBaseValue == null) && (optJmpPinValue == null)) { displayPinCtrl(pioNum, smNum, sdk); } else { if (optSetCountValue != null) { setSetCount(pioNum, smNum, sdk, optSetCountValue); } if (optSetBaseValue != null) { setSetBase(pioNum, smNum, sdk, optSetBaseValue); } if (optOutCountValue != null) { setOutCount(pioNum, smNum, sdk, optOutCountValue); } if (optOutBaseValue != null) { setOutBase(pioNum, smNum, sdk, optOutBaseValue); } if (optInBaseValue != null) { setInBase(pioNum, smNum, sdk, optInBaseValue); } if (optJmpPinValue != null) { setJmpPin(pioNum, smNum, sdk, optJmpPinValue); } } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Quit.java ================================================ /* * @(#)Quit.java 1.00 21/03/28 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.monitor.Command; /** * Monitor command "quit" quits the monitor application. */ public class Quit extends Command { private static final String fullName = "quit"; private static final String singleLineDescription = "quit monitor"; public Quit(final PrintStream console) { super(console, fullName, singleLineDescription); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) { return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Read.java ================================================ /* * @(#)Read.java 1.00 21/03/29 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "read" provides low-level read access to a * register. */ public class Read extends Command { private static final String fullName = "read"; private static final String singleLineDescription = "low-level read access to a register"; private static final CmdOptions.IntegerOptionDeclaration optAddress = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "address", null, "address (0x00000000…0xffffffff) of the " + "register to access"); private final SDK sdk; public Read(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, new CmdOptions.OptionDeclaration[] { optAddress }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { if (!options.isDefined(optAddress)) { throw new CmdOptions. ParseException("option not specified: " + optAddress); } } } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int address = options.getValue(optAddress); final boolean validAddress = sdk.providesAddress(address); if (!validAddress) { final String message = String.format("read from unsupported address: 0x%08x", address); throw new IOException(message); } final int value = sdk.readAddress(address); final String label = sdk.getLabelForAddress(address); console.printf("read %s (0x%08x): 0x%04x%n", label, address, value); return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Registers.java ================================================ /* * @(#)Registers.java 1.00 21/04/05 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "registers" displays or modifies a state machine's * internal registers. */ public class Registers extends Command { private static final String fullName = "registers"; private static final String singleLineDescription = "display or change internal registers of a state machine"; private static final String notes = "If none of the register options is specified, the status of%n"+ "all those registers is displayed.%n" + "Otherwise, for all specified register options, the corresponding%n" + "register is set to the specified value."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", 0, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.IntegerOptionDeclaration optRegX = CmdOptions.createIntegerOption("VALUE", false, 'x', "x", null, "set value of register X"); private static final CmdOptions.IntegerOptionDeclaration optRegY = CmdOptions.createIntegerOption("VALUE", false, 'y', "y", null, "set value of register Y"); private static final CmdOptions.IntegerOptionDeclaration optAddress = CmdOptions.createIntegerOption("VALUE", false, 'a', "address", null, "set value of PC to specified address"); private static final CmdOptions.IntegerOptionDeclaration optIsr = CmdOptions.createIntegerOption("VALUE", false, 'i', "isr", null, "set value of ISR register"); private static final CmdOptions.IntegerOptionDeclaration optIsrShiftCount = CmdOptions.createIntegerOption("VALUE", false, 'k', "isrshiftcount", null, "set value of ISR shift count register"); private static final CmdOptions.IntegerOptionDeclaration optOsr = CmdOptions.createIntegerOption("VALUE", false, 'o', "osr", null, "set value of OSR register"); private static final CmdOptions.IntegerOptionDeclaration optOsrShiftCount = CmdOptions.createIntegerOption("VALUE", false, 'q', "osrshiftcount", null, "set value of OSR shift count register"); private final SDK sdk; public Registers(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optRegX, optRegY, optAddress, optIsr, optIsrShiftCount, optOsr, optOsrShiftCount }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final int smNum = options.getValue(optSm); if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } } } private void displayRegisters(final int pioNum, final int smNum) throws IOException { final int addressRegX = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_REGX); final int regXValue = sdk.readAddress(addressRegX); final int addressRegY = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_REGY); final int regYValue = sdk.readAddress(addressRegY); final int addressPC = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_PC); final int pcValue = sdk.readAddress(addressPC); final int addressISR = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_ISR); final int isrValue = sdk.readAddress(addressISR); final int addressISRShiftCount = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_ISR_SHIFT_COUNT); final int isrShiftCountValue = sdk.readAddress(addressISRShiftCount); final int addressOSR = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_OSR); final int osrValue = sdk.readAddress(addressOSR); final int addressOSRShiftCount = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_OSR_SHIFT_COUNT); final int osrShiftCountValue = sdk.readAddress(addressOSRShiftCount); console.printf("(pio%d:sm%d) X=%08x, Y=%08x, PC=%02x%n", pioNum, smNum, regXValue, regYValue, pcValue); console.printf(" ISR=%08x, ISR_SHIFT_COUNT=%08x%n", isrValue, isrShiftCountValue); console.printf(" OSR=%08x, OSR_SHIFT_COUNT=%08x%n", osrValue, osrShiftCountValue); } private void setEmuRegister(final int pioNum, final int smNum, final int value, final int digits, final PIOEmuRegisters.Regs register) throws IOException { if (digits <= 0) { throw new IllegalArgumentException("digits <= 0"); } final int address = PIOEmuRegisters.getSMAddress(pioNum, smNum, register); sdk.writeAddress(address, value); console.printf("(pio%d:sm%d) set %s to value 0x%0" + digits + "x%n", pioNum, smNum, register, value); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int smNum = options.getValue(optSm); final Integer optRegXValue = options.getValue(optRegX); final Integer optRegYValue = options.getValue(optRegY); final Integer optAddressValue = options.getValue(optAddress); final Integer optIsrValue = options.getValue(optIsr); final Integer optIsrShiftCountValue = options.getValue(optIsrShiftCount); final Integer optOsrValue = options.getValue(optOsr); final Integer optOsrShiftCountValue = options.getValue(optOsrShiftCount); if ((optRegXValue == null) && (optRegYValue == null) && (optAddressValue == null) && (optIsrValue == null) && (optIsrShiftCountValue == null) && (optOsrValue == null) && (optOsrShiftCountValue == null)) { displayRegisters(pioNum, smNum); } if (optRegXValue != null) { setEmuRegister(pioNum, smNum, optRegXValue, 8, PIOEmuRegisters.Regs.SM0_REGX); } if (optRegYValue != null) { setEmuRegister(pioNum, smNum, optRegYValue, 8, PIOEmuRegisters.Regs.SM0_REGY); } if (optAddressValue != null) { setEmuRegister(pioNum, smNum, optAddressValue & (Constants.MEMORY_SIZE - 1), 2, PIOEmuRegisters.Regs.SM0_PC); } if (optIsrValue != null) { setEmuRegister(pioNum, smNum, optIsrValue, 8, PIOEmuRegisters.Regs.SM0_ISR); } if (optIsrShiftCountValue != null) { setEmuRegister(pioNum, smNum, optIsrShiftCountValue, 8, PIOEmuRegisters.Regs.SM0_ISR_SHIFT_COUNT); } if (optOsrValue != null) { setEmuRegister(pioNum, smNum, optOsrValue, 8, PIOEmuRegisters.Regs.SM0_OSR); } if (optOsrShiftCountValue != null) { setEmuRegister(pioNum, smNum, optOsrShiftCountValue, 8, PIOEmuRegisters.Regs.SM0_OSR_SHIFT_COUNT); } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Reset.java ================================================ /* * @(#)Reset.java 1.00 21/03/31 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "reset" initiates a full reset of the emulator's * complete internal state. */ public class Reset extends Command { private static final String fullName = "reset"; private static final String singleLineDescription = "emulator full reset"; private final SDK sdk; public Reset(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { sdk.reset(); console.println("(pio*:sm*) emulator successfully reset"); return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Save.java ================================================ /* * @(#)Save.java 1.00 21/04/05 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.time.Instant; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "save" saves a selected range of a PIO's * instruction memory to a file. */ public class Save extends Command { private static final String fullName = "save"; private static final String singleLineDescription = "save a selected range of a PIO's instruction memory to a file"; private static final String notes = "The file is written as a text file, with each instruction%n" + "added as a line consisting of its operation code represented%n" + "as hexadecimal 32 bit integer value (without \"0x\" prefix).%n" + "%n" + "If the specified stop address is lower than start address, then%n" + "the program is assumed to wrap from the highest memory address to%n" + "the first memory address. Any configuration of a SM specific wrap%n" + "or wrap target is ignored.%n" + "%n" + "If the file is specified to be not relocatable, a proper%n" + "\".origin\" directive will be added as a comment line.%n" + "%n" + "If a program name is provided, it will be added as a%n" + "\".program\" directive in a separate comment line.%n" + "%n" + "Comment lines start with the hash symbol \"#\"."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optStart = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "start", null, "first address (0x00…0x1f) of the program"); private static final CmdOptions.IntegerOptionDeclaration optStop = CmdOptions.createIntegerOption("ADDRESS", false, 's', "stop", null, "last address (0x00…0x1f, inclusive) of "+ "the program"); private static final CmdOptions.StringOptionDeclaration optFile = CmdOptions.createStringOption("PATH", false, 'f', "file", null, "path of file to write"); private static final CmdOptions.StringOptionDeclaration optName = CmdOptions.createStringOption("NAME", false, 'n', "name", null, "program name to be added as \".program\"" + "directive"); private static final CmdOptions.BooleanOptionDeclaration optOverWrite = CmdOptions.createBooleanOption(false, 'o', "overwrite", false, "overwrite if file already exists"); private static final CmdOptions.BooleanOptionDeclaration optRelocatable = CmdOptions.createBooleanOption(false, 'r', "relocatable", true, "true, if the PIO program may be loaded " + "anywhere into instruction memory"); private final SDK sdk; public Save(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optStart, optStop, optFile, optName, optOverWrite, optRelocatable }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final Integer optPioValue = options.getValue(optPio); if (optPioValue != null) { final int pioNum = optPioValue; if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1, if defined"); } } if (!options.isDefined(optStart)) { throw new CmdOptions. ParseException("option not specified: " + optStart); } if (!options.isDefined(optStop)) { throw new CmdOptions. ParseException("option not specified: " + optStop); } if (!options.isDefined(optFile)) { throw new CmdOptions. ParseException("option not specified: " + optFile); } } } private void writeProgram(final int pioNum, final int startAddress, final int stopAddress, final PrintWriter out, final String name, final boolean relocatable) throws IOException { out.printf("# ; PIO Program Hexdump%n"); out.printf("# ; automatically created on %s%n", Instant.now()); out.printf("# ; by Monitor Control Program%n"); out.printf("# ; %s%n", Constants.getEmulatorIdAndVersionWithOs()); if (name != null) { // TODO: Escape program name ("\r", "\n", "\"", …)? out.printf("# .program %s%n", name); } if (!relocatable) { out.printf("# .origin %d%n", startAddress); } int address = startAddress; do { final int instrAddress = PIOEmuRegisters.getMemoryAddress(pioNum, address); final int opCode = sdk.readAddress(instrAddress) & 0xffff; out.printf("%04x%n", opCode); address = (address + 1) & (Constants.MEMORY_SIZE - 1); } while (address != stopAddress); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int startAddress = options.getValue(optStart) & (Constants.MEMORY_SIZE - 1); final int stopAddress = (options.getValue(optStop) + 1) & (Constants.MEMORY_SIZE - 1); final String filePath = options.getValue(optFile); final String name = options.getValue(optName); final boolean overWrite = options.getValue(optOverWrite); final boolean relocatable = options.getValue(optRelocatable); final File file = new File(filePath); if (file.exists() & !overWrite) { console.println("file already exists: " + filePath); return false; } try { final PrintWriter out = new PrintWriter(filePath); writeProgram(pioNum, startAddress, stopAddress, out, name, relocatable); out.close(); console.printf("(pio%d:sm*) saved 0x%02x instruction words to file %s%n", pioNum, (stopAddress - startAddress) & 0x1f, filePath); return true; } catch (final IOException e) { console.println("failed saving to file: " + e.getMessage()); return false; } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Script.java ================================================ /* * @(#)Script.java 1.00 21/03/31 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.LineNumberReader; import java.io.PrintStream; import java.util.Map; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.IOUtils; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.monitor.CommandRegistry; import org.soundpaint.rp2040pio.monitor.ScriptInfo; import org.soundpaint.rp2040pio.sdk.Panic; /** * Monitor command "script" loads a monitor script from a file and * executes it. */ public class Script extends Command { private static final String fullName = "script"; private static final String singleLineDescription = "load monitor script from file and execute it"; private static final String notes = "By convention, monitor scripts files have \".mon\" file name suffix.%n" + "They contain commands to be executed verbatim as if they were%n" + "manually entered in exactly the same way." + "%n" + "For safety reasons as well as for providing for future extensions,%n" + "an additional flag \"+d\" is by default set to dry-run the script.%n" + "To actually run the script, you need to explicitly spcify \"-d\" to%n" + "override dry-run mode.%n" + "%n" + "Some built-in example scripts are available that can be listed with%n" + "the \"-l\" option. To execute a built-in script, use the \"-e\"%n" + "option and pass to this option the script's name as shown in the%n" + "list of available built-in scripts.%n" + "For user-provided script files, use the \"-f\" option to specify the%n" + "file path of the script, including the \".mon\" file name suffix."; private final CommandRegistry commands; private static final CmdOptions.FlagOptionDeclaration optList = CmdOptions.createFlagOption(false, 'l', "list", CmdOptions.Flag.OFF, "list names of available example scripts"); private static final CmdOptions.StringOptionDeclaration optShow = CmdOptions.createStringOption("NAME", false, 's', "show", null, "name of built-in example script to show"); private static final CmdOptions.StringOptionDeclaration optExample = CmdOptions.createStringOption("NAME", false, 'e', "example", null, "name of built-in example script to execute"); private static final CmdOptions.StringOptionDeclaration optFile = CmdOptions.createStringOption("PATH", false, 'f', "file", null, "path of monitor script file to execute"); private static final CmdOptions.BooleanOptionDeclaration optDryRun = CmdOptions.createBooleanOption(false, 'd', "dry-run", true, "dry-run the script commands rather than " + "actually executing them"); public Script(final PrintStream console, final CommandRegistry commands) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optList, optShow, optExample, optFile, optDryRun }); if (commands == null) { throw new NullPointerException("commands"); } this.commands = commands; } private LineNumberReader getReaderForResourcePath(final String resourcePath) throws IOException { final InputStream in = IOUtils.getStreamForResourcePath(resourcePath); return new LineNumberReader(new InputStreamReader(in)); } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { final boolean optListValue = options.getValue(optList) == CmdOptions.Flag.ON; final String optShowValue = options.getValue(optShow); final String optExampleValue = options.getValue(optExample); final String optFileValue = options.getValue(optFile); int count = 0; if (optListValue) count++; if (optShowValue != null) count++; if (optExampleValue != null) count++; if (optFileValue != null) count++; if (options.getValue(optHelp) != CmdOptions.Flag.ON) { if (count == 0) { throw new CmdOptions. ParseException("at least one of options \"-l\", \"-s\", \"-e\" " + "and \"-f\" must be specified"); } } if (count > 1) { throw new CmdOptions. ParseException("at most one of options \"-l\", \"-s\", \"-e\" " + "and \"-f\" may be specified at the same time"); } } private void listExampleScriptGroup(final Map scriptsGroupInfo, final String groupName) { final String groupTitle = String.format("%s:", groupName); console.printf("(pio*:sm*) %s%n", groupTitle); for (final String scriptId : scriptsGroupInfo.keySet()) { final ScriptInfo scriptInfo = scriptsGroupInfo.get(scriptId); console.printf("(pio*:sm*) %s%n", scriptInfo.getScriptId()); } } private void listExampleScripts() throws IOException { final Map> scriptsInfo = ScriptInfo.createScriptsInfo(); Map defaultScriptsGroupInfo = null; for (final String groupName : scriptsInfo.keySet()) { final Map scriptsGroupInfo = scriptsInfo.get(groupName); if (groupName != ScriptInfo.DEFAULT_GROUP_NAME) { listExampleScriptGroup(scriptsGroupInfo, groupName); } else { // defer default group to end defaultScriptsGroupInfo = scriptsGroupInfo; } } if (defaultScriptsGroupInfo != null) { listExampleScriptGroup(defaultScriptsGroupInfo, ScriptInfo.DEFAULT_GROUP_NAME); } else { throw new IOException("default script group not found"); } } private boolean showScript(final LineNumberReader in, final String scriptId) throws IOException { console.printf("(pio*:sm*) [script %s]%n", scriptId); while (true) { final String line = in.readLine(); if (line == null) break; console.printf("(pio*:sm*) %3d: %s%n", in.getLineNumber(), line); } console.printf("(pio*:sm*) [end of script %s]%n", scriptId); return true; } private int executeScript(final LineNumberReader in, final String scriptId, final boolean dryRun, final boolean localEcho, final String prompt) { final String action = dryRun ? "dry-running" : "running"; console.printf("(pio*:sm*) %s script %s%n", action, scriptId); while (true) { console.print(prompt); try { final String line = in.readLine(); if (line == null) break; if (localEcho) console.println(line); if (commands.parseAndExecute(line, dryRun)) break; } catch (final Panic | IOException e) { console.println(e.getMessage()); if (e instanceof Panic) { console.printf(Command.panicNotes); console.println(); } return -1; } } return 0; } private boolean executeScript(final LineNumberReader in, final String scriptId, final boolean dryRun) throws IOException { final int exitStatus = executeScript(in, scriptId, dryRun, true, "script> "); console.printf("(pio*:sm*) script %s exited with status %d%n", scriptId, exitStatus); return true; } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final boolean optListValue = options.getValue(optList) == CmdOptions.Flag.ON; final String optShowValue = options.getValue(optShow); final String optExampleValue = options.getValue(optExample); final String optFileValue = options.getValue(optFile); final boolean dryRun = options.getValue(optDryRun); if (optListValue) { listExampleScripts(); } else if (optShowValue != null) { final String resourcePath = String.format("/examples/%s.mon", optShowValue); final LineNumberReader reader = IOUtils.getReaderForResourcePath(resourcePath); return showScript(reader, optShowValue); } else if (optExampleValue != null) { final String resourcePath = String.format("/examples/%s.mon", optExampleValue); final LineNumberReader reader = IOUtils.getReaderForResourcePath(resourcePath); return executeScript(reader, optExampleValue, dryRun); } else if (optFileValue != null) { final LineNumberReader reader = IOUtils.getReaderForResourcePath(optFileValue); return executeScript(reader, optFileValue, dryRun); } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/SideSet.java ================================================ /* * @(#)SideSet.java 1.00 21/04/03 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIORegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "side-set" provides the same functionality like the * PIOASM directive ".side_set". */ public class SideSet extends Command { private static final String fullName = "side-set"; private static final String singleLineDescription = "display or control a state machine's side-set configuration"; private static final String notes = "Options -p and -s select the state machine that this command%n" + "applies to. Default is PIO0 and SM0.%n" + "%n" + "If none of the options -c, -b, ±o, ±d is specified, the currently%n" + "configured side-set of the selected state machine will be%n" + "displayed. If at least one of the options -c, -b, ±o, ±d is%n" + "specified, the corresponding settings will be adjusted, while for%n" + "those not specified the corresponding settings will keep unmodified."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", 0, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.IntegerOptionDeclaration optCount = CmdOptions.createIntegerOption("COUNT", false, 'c', "count", null, "number of side-set bits to be " + "used (0…5)"); private static final CmdOptions.IntegerOptionDeclaration optBase = CmdOptions.createIntegerOption("NUMBER", false, 'b', "base", null, "base GPIO pin (0…31) number "+ "of side-set"); protected static final CmdOptions.BooleanOptionDeclaration optOpt = CmdOptions.createBooleanOption(false, 'o', "opt", null, "make side-set values optional for " + "instructions"); protected static final CmdOptions.BooleanOptionDeclaration optPinDirs = CmdOptions.createBooleanOption(false, 'd', "pindirs", null, "apply side-set values to the PINDIRs and " + "not the PINs"); private final SDK sdk; public SideSet(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optCount, optBase, optOpt, optPinDirs }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final int smNum = options.getValue(optSm); if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } final Integer optCountValue = options.getValue(optCount); if (optCountValue != null) { final int count = optCountValue; if ((count < 0) || (count > 5)) { throw new CmdOptions. ParseException("count must be in the range 0…5"); } } final Integer optBaseValue = options.getValue(optBase); if (optBaseValue != null) { final int base = optBaseValue; if ((base < 0) || (base > 31)) { throw new CmdOptions. ParseException("base must be in the range 0…31"); } } } } private void displaySideSet(final int pioNum, final int smNum, final SDK sdk) throws IOException { final int pinCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL); final int pinCtrl = sdk.readAddress(pinCtrlAddress); final int count = (pinCtrl & Constants.SM0_PINCTRL_SIDESET_COUNT_BITS) >>> Constants.SM0_PINCTRL_SIDESET_COUNT_LSB; final int base = (pinCtrl & Constants.SM0_PINCTRL_SIDESET_BASE_BITS) >>> Constants.SM0_PINCTRL_SIDESET_BASE_LSB; final int execCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL); final int execCtrl = sdk.readAddress(execCtrlAddress); final boolean opt = ((execCtrl & Constants.SM0_EXECCTRL_SIDE_EN_BITS) >>> Constants.SM0_EXECCTRL_SIDE_EN_LSB) != 0x0; final boolean pinDirs = ((execCtrl & Constants.SM0_EXECCTRL_SIDE_PINDIR_BITS) >>> Constants.SM0_EXECCTRL_SIDE_PINDIR_LSB) != 0x0; final int nettoCount = opt ? count - 1 : count; console.printf("(pio%d:sm%d) count=%d, base=%d, opt=%s, pindirs=%s%n", pioNum, smNum, nettoCount, base, opt, pinDirs); } private void setSideSetCount(final int pioNum, final int smNum, final SDK sdk, final int nettoCount) throws IOException { final int execCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL); final int execCtrl = sdk.readAddress(execCtrlAddress); final boolean opt = ((execCtrl & Constants.SM0_EXECCTRL_SIDE_EN_BITS) >>> Constants.SM0_EXECCTRL_SIDE_EN_LSB) != 0x0; if (opt && (nettoCount == 5)) { console.printf("(pio%d:sm%d) ERROR: can not set count to 5, " + "since set side-set opt is set%n", pioNum, smNum); } else { final int count = nettoCount + (opt ? 1 : 0); final int pinCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL); final int writeMask = Constants.SM0_PINCTRL_SIDESET_COUNT_BITS; final int values = count << Constants.SM0_PINCTRL_SIDESET_COUNT_LSB; sdk.hwWriteMasked(pinCtrlAddress, values, writeMask); console.printf("(pio%d:sm%d) set side-set count to %d%n", pioNum, smNum, nettoCount); } } private void setSideSetBase(final int pioNum, final int smNum, final SDK sdk, final int base) throws IOException { final int pinCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL); final int writeMask = Constants.SM0_PINCTRL_SIDESET_BASE_BITS; final int values = base << Constants.SM0_PINCTRL_SIDESET_BASE_LSB; sdk.hwWriteMasked(pinCtrlAddress, values, writeMask); console.printf("(pio%d:sm%d) set side-set base GPIO pin to %d%n", pioNum, smNum, base); } private void setSideSetOpt(final int pioNum, final int smNum, final SDK sdk, final boolean opt) throws IOException { final int execCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL); final int execCtrl = sdk.readAddress(execCtrlAddress); if (opt != (((execCtrl & Constants.SM0_EXECCTRL_SIDE_EN_BITS) >>> Constants.SM0_EXECCTRL_SIDE_EN_LSB) != 0x0)) { /* * Note: From this command's perspective, it is a RP2040's * design flaw that the SMx_PINCTRL_SIDESET_COUNT is inclusive * of the enable bit. When the user changes side-set opt only, * they do not expect to change the available pin bits. For * example, pioasm will allocate *three* side-set bits when * declaring ".side_set 2 opt", but only two, when the "opt" is * dropped. Consequently, when changing side-set opt *only*, we * have also have to update the number of side-set bits. * * TODO: Need to lock this block (and any other writer to * side-set configuration) as critical section to avoid * corruption, if there are concurrent writers, such as another * Monitor instance. */ final int pinCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL); final int pinCtrl = sdk.readAddress(pinCtrlAddress); final int count = (pinCtrl & Constants.SM0_PINCTRL_SIDESET_COUNT_BITS) >>> Constants.SM0_PINCTRL_SIDESET_COUNT_LSB; if (opt && (count == 5)) { console.printf("(pio%d:sm%d) ERROR: can not set opt, since side-set " + "count is set to 5.%n", pioNum, smNum); } else { final int optBits = opt ? 0x1 << Constants.SM0_EXECCTRL_SIDE_EN_LSB : 0x0; sdk.hwWriteMasked(execCtrlAddress, optBits, Constants.SM0_EXECCTRL_SIDE_EN_BITS); final int newCount = count + (opt ? 1 : (count > 0 ? - 1 : 0)); final int countBits = newCount << Constants.SM0_PINCTRL_SIDESET_COUNT_LSB; sdk.hwWriteMasked(pinCtrlAddress, countBits, Constants.SM0_PINCTRL_SIDESET_COUNT_BITS); console.printf("(pio%d:sm%d) set side-set opt=%s%n", pioNum, smNum, opt); } } else { console.printf("(pio%d:sm%d) set side-set opt=%s%n", pioNum, smNum, opt); } } private void setSideSetPinDirs(final int pioNum, final int smNum, final SDK sdk, final boolean pinDirs) throws IOException { final int execCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL); final int writeMask = Constants.SM0_EXECCTRL_SIDE_PINDIR_BITS; final int values = pinDirs ? 0x1 << Constants.SM0_EXECCTRL_SIDE_PINDIR_LSB : 0x0; sdk.hwWriteMasked(execCtrlAddress, values, writeMask); console.printf("(pio%d:sm%d) set side-set pindirs=%s%n", pioNum, smNum, pinDirs); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int smNum = options.getValue(optSm); final Integer optCountValue = options.getValue(optCount); final Integer optBaseValue = options.getValue(optBase); final Boolean optOptValue = options.getValue(optOpt); final Boolean optPinDirsValue = options.getValue(optPinDirs); if ((optCountValue == null) && (optBaseValue == null) && (optOptValue == null) && (optPinDirsValue == null)) { displaySideSet(pioNum, smNum, sdk); } else { if (optCountValue != null) { setSideSetCount(pioNum, smNum, sdk, optCountValue); } if (optBaseValue != null) { setSideSetBase(pioNum, smNum, sdk, optBaseValue); } if (optOptValue != null) { setSideSetOpt(pioNum, smNum, sdk, optOptValue); } if (optPinDirsValue != null) { setSideSetPinDirs(pioNum, smNum, sdk, optPinDirsValue); } } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Sm.java ================================================ /* * @(#)Sm.java 1.00 21/04/01 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.Decoder; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "sm" enables or disables a PIO's state machine * or shows if it is enabled. */ public class Sm extends Command { private static final String fullName = "sm"; private static final String singleLineDescription = "enable or disable or restart state machine(s) or show if enabled"; private static final String notes = "Use options \"-p\" and \"-s\" to select a state machine.%n" + "Enable or disable the selected state machine with option%n" + "\"+e\" or \"-e\", respectively. Restart the selected%n" + "state machine with option \"+r\".%n" + "If none of options \"+e\", \"-e\", \"-r\" is specified,%n" + "show if the state machine is currently enabled."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", 0, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.BooleanOptionDeclaration optEnable = CmdOptions.createBooleanOption(false, 'e', "enable", null, "enable or disable the selected " + "state machine"); private static final CmdOptions.FlagOptionDeclaration optRestart = CmdOptions.createFlagOption(false, 'r', "restart", CmdOptions.Flag.OFF, "restart the selected state machine"); private final SDK sdk; public Sm(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optEnable, optRestart }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final int smNum = options.getValue(optSm); if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } } } private void displaySmStatus(final int pioNum, final int smNum) throws IOException { final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); final boolean enabled = pioSdk.smGetEnabled(smNum); console.printf("(pio%d:sm%d) %s%n", pioNum, smNum, enabled ? "enabled" : "disabled"); } private void setEnableStatus(final int pioNum, final int smNum, final boolean enable) throws IOException { final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); pioSdk.smSetEnabled(smNum, enable); console.printf("(pio%d:sm%d) set %s%n", pioNum, smNum, enable ? "enabled" : "disabled"); } private void restart(final int pioNum, final int smNum) throws IOException { final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); pioSdk.smRestart(smNum); console.printf("(pio%d:sm%d) restarted%n", pioNum, smNum); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int smNum = options.getValue(optSm); final Boolean optEnableValue = options.getValue(optEnable); final boolean optRestartValue = options.getValue(optRestart).isOn(); final boolean haveModOp = (optEnableValue != null) || optRestartValue; if (!haveModOp) { displaySmStatus(pioNum, smNum); } if (optEnableValue != null) { setEnableStatus(pioNum, smNum, optEnableValue); } if (optRestartValue) { restart(pioNum, smNum); } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Trace.java ================================================ /* * @(#)Trace.java 1.00 21/03/28 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.Bit; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.Direction; import org.soundpaint.rp2040pio.GPIOIOBank0Registers; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.PinState; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.monitor.MonitorUtils; import org.soundpaint.rp2040pio.sdk.GPIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "trace" lets the emulator execute clock cycles step * by step. */ public class Trace extends Command { private static final String fullName = "trace"; private static final String singleLineDescription = "trace program by performing a number of clock cycles"; private static final CmdOptions.IntegerOptionDeclaration optCycles = CmdOptions.createIntegerOption("COUNT", false, 'c', "cycles", 1, "number of cycles to apply"); private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", null, "limit options -l and -i to PIO number, " + "either 0 or 1 or both, if undefined"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", null, "limit option -i to SM number, one of " + "0, 1, 2 or 3, or all, if undefined"); private static final CmdOptions.FlagOptionDeclaration optPc = CmdOptions.createFlagOption(false, 'i', "show-instr", CmdOptions.Flag.OFF, "show address of instruction pointer (aka " + "PC reg) for selected SMs of selected PIOs"); private static final CmdOptions.FlagOptionDeclaration optGpio = CmdOptions.createFlagOption(false, 'g', "show-gpio", CmdOptions.Flag.OFF, "show status of (global) GPIO pins"); private static final CmdOptions.FlagOptionDeclaration optPioGpio = CmdOptions.createFlagOption(false, 'l', "show-local-gpio", CmdOptions.Flag.OFF, "show status of (local PIO's) GPIO pins"); private static final CmdOptions.FlagOptionDeclaration optBefore = CmdOptions.createFlagOption(false, null, "before", CmdOptions.Flag.OFF, "when displaying global GPIO status, show " + "status before rather than after override"); private static final CmdOptions.IntegerOptionDeclaration optWait = CmdOptions.createIntegerOption("NUMBER", false, 'w', "wait", 0, "before each cycle, sleep for the " + "specified time [ms] or until interrupted"); private static final int[][] addressPioSmPc = {{ PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM0_PC), PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM1_PC), PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM2_PC), PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM3_PC) }, { PIOEmuRegisters.getAddress(1, PIOEmuRegisters.Regs.SM0_PC), PIOEmuRegisters.getAddress(1, PIOEmuRegisters.Regs.SM1_PC), PIOEmuRegisters.getAddress(1, PIOEmuRegisters.Regs.SM2_PC), PIOEmuRegisters.getAddress(1, PIOEmuRegisters.Regs.SM3_PC) }}; private final SDK sdk; public Trace(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, new CmdOptions.OptionDeclaration[] { optPio, optSm, optCycles, optPc, optPioGpio, optGpio, optBefore, optWait }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { final Integer optPioValue = options.getValue(optPio); if (optPioValue != null) { final int pioNum = optPioValue; if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1, if defined"); } } final Integer optSmValue = options.getValue(optSm); if (optSmValue != null) { final int smNum = optSmValue; if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3, if defined"); } } final int cycles = options.getValue(optCycles); if (cycles < 0) { throw new CmdOptions. ParseException("COUNT must be a non-negative value", optCycles); } final int wait = options.getValue(optWait); if (wait < 0) { throw new CmdOptions. ParseException("NUMBER must be a non-negative value", optWait); } if (options.getValue(optBefore) == CmdOptions.Flag.ON) { if (!options.isDefined(optGpio)) { throw new CmdOptions. ParseException("option \"before\" may be specified when option "+ "option \"-g\" is specified"); } } } private void displayPcValues(final int pioNumFirst, final int pioNumLast, final int smNumFirst, final int smNumLast) throws IOException { for (int pioNum = pioNumFirst; pioNum <= pioNumLast; pioNum++) { for (int smNum = smNumFirst; smNum <= smNumLast; smNum++) { console.printf("(pio%d:sm%d) PC=%02x%n", pioNum, smNum, sdk.readAddress(addressPioSmPc[pioNum][smNum])); } } } private void displayGpioValues(final boolean before) throws IOException { final GPIOSDK.Override override = before ? GPIOSDK.Override.BEFORE : GPIOSDK.Override.AFTER; final String gpioDisplay = MonitorUtils.gpioDisplay(sdk, override); console.printf(gpioDisplay); } private void displayGpioValues(final int pioNumFirst, final int pioNumLast) throws IOException { for (int pioNum = pioNumFirst; pioNum <= pioNumLast; pioNum++) { console.printf(MonitorUtils.gpioDisplay(sdk, pioNum)); } } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final Integer optPioValue = options.getValue(optPio); final Integer optSmValue = options.getValue(optSm); final int pioNumFirst = optPioValue != null ? optPioValue : 0; final int pioNumLast = optPioValue != null ? optPioValue : Constants.PIO_NUM - 1; final int smNumFirst = optSmValue != null ? optSmValue : 0; final int smNumLast = optSmValue != null ? optSmValue : Constants.SM_COUNT - 1; final int cycles = options.getValue(optCycles); final int wait = options.getValue(optWait); final int wait0 = wait / 2; final int wait1 = wait - wait0; for (int i = 0; i < cycles; i++) { if (wait0 > 0) { try { Thread.sleep(wait0); } catch (final InterruptedException e) { console.printf("(pio*:sm*) Interrupted: %s%n", e.getMessage()); } } sdk.triggerCyclePhase0(true); if (wait1 > 0) { try { Thread.sleep(wait1); } catch (final InterruptedException e) { console.printf("(pio*:sm*) Interrupted: %s%n", e.getMessage()); } } sdk.triggerCyclePhase1(true); if (options.getValue(optPc).isOn()) { displayPcValues(pioNumFirst, pioNumLast, smNumFirst, smNumLast); } if (options.getValue(optPioGpio).isOn()) { displayGpioValues(pioNumFirst, pioNumLast); } if (options.getValue(optGpio).isOn()) { final boolean before = options.getValue(optBefore) == CmdOptions.Flag.ON; displayGpioValues(before); } } console.println(cycles + " clock cycle" + (cycles != 1 ? "s" : "") + " executed."); return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Unassemble.java ================================================ /* * @(#)Unassemble.java 1.00 21/03/28 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.Decoder; import org.soundpaint.rp2040pio.PIOEmuRegisters; import org.soundpaint.rp2040pio.PIORegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "unassemble" displays instructions of a PIO's * instruction memory in a human-readable form. */ public class Unassemble extends Command { private static final String fullName = "unassemble"; private static final String singleLineDescription = "unassemble program memory"; private static final String notes = "Memory locations marked as allocated are prefixed with leading 'X'.%n" + "%n" + "Note that tracking memory allocation is not a feature of the%n" + "RP2040, but local to this monitor instance, just to avoid%n" + "accidentally overwriting your own PIO programs. Other applications%n" + "that concurrently access the RP2040 will therefore ignore%n" + "this instance's allocation tracking and may arbitrarily%n" + "overwrite allocated PIO memory, using their own allocation scheme.%n" + "%n" + "Note that the same PIO program may unassemble to differently%n" + "displayed instructions for different state machines, since%n" + "some settings specific to a particular state machine, such as%n" + "side-set count, will affect interpretation of op-codes.%n" + "Therefore, the unassemble command supports the \"sm\" argument%n" + "for displaying the instructions as interpreted by the selected%n" + "state machine, according to its current settings."; private static final String lockedSymbol = "🔒"; private static final String unlockedSymbol = " "; private static final String wrapSymbol = "← "; private static final String wrapTargetSymbol = "→ "; private static final String selfWrapSymbol = "↔ "; private static final String noWrapSymbol = " "; private static final String breakPointSymbol = "🛑"; private static final String noBreakPointSymbol = " "; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", 0, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.IntegerOptionDeclaration optStart = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "address", 0, "start address (0x00…0x1f)"); private static final CmdOptions.IntegerOptionDeclaration optCount = CmdOptions.createIntegerOption("COUNT", false, 'c', "count", Constants.MEMORY_SIZE, "number of instructions to unassemble"); private final SDK sdk; public Unassemble(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optStart, optCount }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final int smNum = options.getValue(optSm); if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } } } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int smNum = options.getValue(optSm); final int count = options.getValue(optCount); if (count == 0) return true; final int startAddress = options.getValue(optStart) & (Constants.MEMORY_SIZE - 1); final int stopAddress = (startAddress + count) & (Constants.MEMORY_SIZE - 1); final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); final int addressAddr = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_ADDR); final int addrValue = sdk.readAddress(addressAddr) & (Constants.MEMORY_SIZE - 1); final int memoryAllocation = pioSdk.getMemoryAllocation(); final int addressExecCtrl = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL); final int execCtrl = sdk.readAddress(addressExecCtrl); final int wrap = (execCtrl & Constants.SM0_EXECCTRL_WRAP_TOP_BITS) >>> Constants.SM0_EXECCTRL_WRAP_TOP_LSB; final int wrapTarget = (execCtrl & Constants.SM0_EXECCTRL_WRAP_BOTTOM_BITS) >>> Constants.SM0_EXECCTRL_WRAP_BOTTOM_LSB; final int addressBreakPoints = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_BREAKPOINTS); final int breakPoints = sdk.readAddress(addressBreakPoints); int address = startAddress; do { final boolean isCurrentAddr = address == addrValue; final PIOSDK.InstructionInfo instructionInfo = pioSdk.getMemoryInstruction(smNum, address, true, true); final boolean isAllocated = ((memoryAllocation >>> address) & 0x1) != 0x0; final boolean isWrap = address == wrap; final boolean isWrapTarget = address == wrapTarget; final String displayWrap = isWrap ? (isWrapTarget ? selfWrapSymbol : wrapSymbol) : (isWrapTarget ? wrapTargetSymbol : noWrapSymbol); final boolean isBreakPoint = ((breakPoints >>> address) & 0x1) != 0x0; if (isCurrentAddr) { console.printf("\u001b[38;5;196m"); } console.printf("(pio%d:sm%d) %s %s %s%s%n", pioNum, smNum, (isBreakPoint ? breakPointSymbol : noBreakPointSymbol), (isAllocated ? lockedSymbol : unlockedSymbol), displayWrap, instructionInfo.getToolTipText()); if (isCurrentAddr) { console.printf("\u001b[0m"); } address = (address + 1) & (Constants.MEMORY_SIZE - 1); } while (address != stopAddress); return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Unload.java ================================================ /* * @(#)Unload.java 1.00 21/04/03 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.BufferedReader; import java.io.IOException; import java.io.LineNumberReader; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.IOUtils; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.monitor.MonitorUtils; import org.soundpaint.rp2040pio.sdk.PIOSDK; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "unload" removes a program from a PIO's memory. */ public class Unload extends Command { private static final String fullName = "unload"; private static final String singleLineDescription = "zero PIO memory area for the specified program and unmark it as allocated"; private static final String notes = "The \"unload\" command first reads in the specified hex dump in order%n" + "to determine the program length of the corresponding PIO program.%n" + "Then, the identified instruction memory area that is associated with%n" + "the PIO program in the specified PIO will be zeroed, and any memory%n" + "allocation marks found in this memory area will be removed.%n" + "%n" + "Built-in example hex dumps are available that can be listed with%n" + "the \"-l\" option. To select any of the example hex dumps, use the%n" + "\"-e\" option and pass to this option the hex dump's name as shown%n" + "in the list of available built-in hex dumps. To view a built-in hex%n" + "dump prior to unloading it, use the \"-s\" option.%n" + "For user-provided hex dumps, use the \"-f\" option to specify the%n" + "file path of the hex dump, including the \".hex\" file name suffix." + "%n" + "Note that tracking memory allocation is not a feature of the%n" + "RP2040, but local to this monitor instance, just to avoid%n" + "accidentally overwriting your own PIO programs. Other applications%n" + "that concurrently access the RP2040 will therefore ignore%n" + "this instance's allocation tracking and may arbitrarily%n" + "overwrite allocated PIO memory, using their own allocation scheme.%n" + "%n" + "For information about the expected file format, enter the command%n" + "\"load -h\" to view the help information of the \"load\" command."; private final SDK sdk; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.FlagOptionDeclaration optList = CmdOptions.createFlagOption(false, 'l', "list", CmdOptions.Flag.OFF, "list names of available example hex dumps"); private static final CmdOptions.StringOptionDeclaration optShow = CmdOptions.createStringOption("NAME", false, 's', "show", null, "name of example hex dump to show"); private static final CmdOptions.StringOptionDeclaration optExample = CmdOptions.createStringOption("NAME", false, 'e', "example", null, "name of example hex dump to unload"); private static final CmdOptions.StringOptionDeclaration optFile = CmdOptions.createStringOption("STRING", false, 'f', "file", null, "path of hex dump file to unload"); private static final CmdOptions.IntegerOptionDeclaration optAddress = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "address", null, "start address (0x00…0x1f) of the area " + "to free"); public Unload(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optList, optShow, optExample, optFile, optAddress }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final boolean optListValue = options.getValue(optList) == CmdOptions.Flag.ON; final String optShowValue = options.getValue(optShow); final String optExampleValue = options.getValue(optExample); final String optFileValue = options.getValue(optFile); int count = 0; if (optListValue) count++; if (optShowValue != null) count++; if (optExampleValue != null) count++; if (optFileValue != null) count++; if (options.getValue(optHelp) != CmdOptions.Flag.ON) { if (count == 0) { throw new CmdOptions. ParseException("at least one of options \"-l\", \"-s\", \"-e\" " + "and \"-f\" must be specified"); } } if (count > 1) { throw new CmdOptions. ParseException("at most one of options \"-l\", \"-s\", \"-e\" " + "and \"-f\" may be specified at the same time"); } if (options.getValue(optHelp) != CmdOptions.Flag.ON) { if (options.isDefined(optExample) || options.isDefined(optFile)) { if (!options.isDefined(optAddress)) { throw new CmdOptions. ParseException("option not specified: " + optAddress); } } } } private boolean unloadHexDump(final int pioNum, final BufferedReader reader, final String hexDumpId, final int loadedOffset) throws IOException { final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK(); pioSdk.removeProgram(hexDumpId, reader, loadedOffset); console.printf("removed program %s from PIO %d, address 0x%02x%n", hexDumpId, pioNum, loadedOffset); return true; } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final boolean optListValue = options.getValue(optList) == CmdOptions.Flag.ON; final String optShowValue = options.getValue(optShow); final String optExampleValue = options.getValue(optExample); final String optFileValue = options.getValue(optFile); final Integer optAddressValue = options.getValue(optAddress); if (optListValue) { return MonitorUtils.listExampleHexDumps(console); } else if (optShowValue != null) { return MonitorUtils.showExampleHexDump(console, optShowValue); } else if (optExampleValue != null) { final String resourcePath = String.format("/examples/%s.hex", optExampleValue); final LineNumberReader reader = IOUtils.getReaderForResourcePath(resourcePath); return unloadHexDump(pioNum, reader, optExampleValue, optAddressValue); } else if (optFileValue != null) { final LineNumberReader reader = IOUtils.getReaderForResourcePath(optFileValue); return unloadHexDump(pioNum, reader, optFileValue, optAddressValue); } return false; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Version.java ================================================ /* * @(#)Version.java 1.00 21/03/29 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "version" prints the emulator's version identifier. */ public class Version extends Command { private static final String fullName = "version"; private static final String singleLineDescription = "print emulator version"; private final SDK sdk; private final String appFullName; public Version(final PrintStream console, final SDK sdk, final String appFullName) { super(console, fullName, singleLineDescription); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; this.appFullName = appFullName; } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { console.printf("%s%n%s%n%s%n", appFullName, sdk.getEmulatorInfo(), Constants.getCmdLineCopyrightNotice()); return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Wait.java ================================================ /* * @(#)Wait.java 1.00 21/03/30 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "wait" observes a register's bits and will not * return until the register's value matches an expected bit pattern, * or when a timeout has occurred. * * TODO: There is currently no way to cancel an ongoing wait command * other than killing the monitor application. */ public class Wait extends Command { private static final String fullName = "wait"; private static final String singleLineDescription = "wait for a register's bits to match an expected value"; private static final CmdOptions.IntegerOptionDeclaration optAddress = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "address", null, "address (0x00000000…0xffffffff) of the " + "register to observe"); private static final CmdOptions.IntegerOptionDeclaration optExpectedValue = CmdOptions.createIntegerOption("VALUE", false, 'v', "value", null, "expected value to match"); private static final CmdOptions.IntegerOptionDeclaration optMask = CmdOptions.createIntegerOption("MASK", false, 'm', "mask", 0xffffffff, "bit mask to select bits to match"); private static final CmdOptions.IntegerOptionDeclaration optCycles = CmdOptions.createIntegerOption("COUNT", false, 'c', "cycles", 0, "timeout after cycles or no timeout, if 0"); private static final CmdOptions.IntegerOptionDeclaration optTime = CmdOptions.createIntegerOption("COUNT", false, 't', "time", 100000, "timeout after millis or no timeout, if 0"); private final SDK sdk; public Wait(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, new CmdOptions.OptionDeclaration[] { optAddress, optExpectedValue, optMask, optCycles, optTime }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { if (!options.isDefined(optAddress)) { throw new CmdOptions. ParseException("option not specified", optAddress); } if (!options.isDefined(optExpectedValue)) { throw new CmdOptions. ParseException("option not specified", optExpectedValue); } } final int cycles = options.getValue(optCycles); if (cycles < 0) { throw new CmdOptions. ParseException("COUNT must be a non-negative value", optCycles); } final int time = options.getValue(optTime); if (time < 0) { throw new CmdOptions. ParseException("COUNT must be a non-negative value", optTime); } } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int address = options.getValue(optAddress); final int expectedValue = options.getValue(optExpectedValue); final boolean validAddress = sdk.providesAddress(address); if (!validAddress) { final String message = String.format("wait on unsupported address: 0x%08x", address); throw new IOException(message); } final int mask = options.getValue(optMask); final int cycles = options.getValue(optCycles); final int time = options.getValue(optTime); final int result = sdk.wait(address, expectedValue, mask, cycles, time); console.printf("wait on 0x%08x for 0x%08x returned 0x%08x%n", address, expectedValue, result); return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Wrap.java ================================================ /* * @(#)Wrap.java 1.00 21/04/04 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.PIORegisters; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "wrap" provides functionality comparable to the * PIOASM directives ".wrap_target" and ".wrap". */ public class Wrap extends Command { private static final String fullName = "wrap"; private static final String singleLineDescription = "display or control a state machine's wrap and wrap target configuration"; private static final String notes = "Options -p and -s select the state machine that this command%n" + "applies to. Default is PIO0 and SM0.%n" + "%n" + "If none of the options -w, -t is specified, the currently%n" + "configured wrap and wrap target of the selected state machine will be%n" + "displayed. If at least one of the options -w, -t is%n" + "specified, the corresponding settings will be adjusted, while for%n" + "those not specified the corresponding settings will keep unmodified."; private static final CmdOptions.IntegerOptionDeclaration optPio = CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", 0, "PIO number, either 0 or 1"); private static final CmdOptions.IntegerOptionDeclaration optSm = CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", 0, "SM number, one of 0, 1, 2 or 3"); private static final CmdOptions.IntegerOptionDeclaration optWrap = CmdOptions.createIntegerOption("ADDRESS", false, 'w', "wrap", null, "wrap (WRAP_TOP) address (0x00…0x1f)"); private static final CmdOptions.IntegerOptionDeclaration optWrapTarget = CmdOptions.createIntegerOption("ADDRESS", false, 't', "target", null, "wrap target (WRAP_BOTTOM) address " + "(0x00…0x1f)"); private final SDK sdk; public Wrap(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] { optPio, optSm, optWrap, optWrapTarget }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { final int pioNum = options.getValue(optPio); if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { throw new CmdOptions. ParseException("PIO number must be either 0 or 1"); } final int smNum = options.getValue(optSm); if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { throw new CmdOptions. ParseException("SM number must be one of 0, 1, 2 or 3"); } final Integer optWrapValue = options.getValue(optWrap); if (optWrapValue != null) { final int wrap = optWrapValue; if ((wrap < 0) || (wrap > Constants.MEMORY_SIZE - 1)) { final String message = String.format("wrap address must be in the range 0x00…0x%02x", Constants.MEMORY_SIZE - 1); throw new CmdOptions.ParseException(message); } } final Integer optWrapTargetValue = options.getValue(optWrapTarget); if (optWrapTargetValue != null) { final int wrapTarget = optWrapTargetValue; if ((wrapTarget < 0) || (wrapTarget > Constants.MEMORY_SIZE - 1)) { final String message = String.format("wrap target address must be in the range " + "0x00…0x%02x", Constants.MEMORY_SIZE - 1); throw new CmdOptions.ParseException(message); } } } } private void displayWrap(final int pioNum, final int smNum, final SDK sdk) throws IOException { final int execCtrlAddress = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL); final int execCtrl = sdk.readAddress(execCtrlAddress); final int wrap = (execCtrl & Constants.SM0_EXECCTRL_WRAP_TOP_BITS) >>> Constants.SM0_EXECCTRL_WRAP_TOP_LSB; final int wrapTarget = (execCtrl & Constants.SM0_EXECCTRL_WRAP_BOTTOM_BITS) >>> Constants.SM0_EXECCTRL_WRAP_BOTTOM_LSB; console.printf("(pio%d:sm%d) wrap=%d, wrap_target=%d%n", pioNum, smNum, wrap, wrapTarget); } private void setWrap(final int pioNum, final int smNum, final SDK sdk, final int wrap) throws IOException { final int address = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL); final int writeMask = Constants.SM0_EXECCTRL_WRAP_TOP_BITS; final int values = wrap << Constants.SM0_EXECCTRL_WRAP_TOP_LSB; sdk.hwWriteMasked(address, values, writeMask); console.printf("(pio%d:sm%d) set wrap=%d%n", pioNum, smNum, wrap); } private void setWrapTarget(final int pioNum, final int smNum, final SDK sdk, final int wrapTarget) throws IOException { final int address = PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL); final int writeMask = Constants.SM0_EXECCTRL_WRAP_BOTTOM_BITS; final int values = wrapTarget << Constants.SM0_EXECCTRL_WRAP_BOTTOM_LSB; sdk.hwWriteMasked(address, values, writeMask); console.printf("(pio%d:sm%d) set wrap_target=%d%n", pioNum, smNum, wrapTarget); } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int pioNum = options.getValue(optPio); final int smNum = options.getValue(optSm); final Integer optWrapValue = options.getValue(optWrap); final Integer optWrapTargetValue = options.getValue(optWrapTarget); if ((optWrapValue == null) && (optWrapTargetValue == null)) { displayWrap(pioNum, smNum, sdk); } else { if (optWrapValue != null) { setWrap(pioNum, smNum, sdk, optWrapValue); } if (optWrapTargetValue != null) { setWrapTarget(pioNum, smNum, sdk, optWrapTargetValue); } } return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/monitor/commands/Write.java ================================================ /* * @(#)Write.java 1.00 21/03/30 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.monitor.commands; import java.io.IOException; import java.io.PrintStream; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.monitor.Command; import org.soundpaint.rp2040pio.sdk.SDK; /** * Monitor command "write" provides low-level write access to a * register. */ public class Write extends Command { private static final String fullName = "write"; private static final String singleLineDescription = "low-level write access to a register"; private static final CmdOptions.IntegerOptionDeclaration optAddress = CmdOptions.createIntegerOption("ADDRESS", false, 'a', "address", null, "address (0x00000000…0xffffffff) of the " + "register to access"); private static final CmdOptions.IntegerOptionDeclaration optValue = CmdOptions.createIntegerOption("VALUE", false, 'v', "value", null, "value to write"); private final SDK sdk; public Write(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, new CmdOptions.OptionDeclaration[] { optAddress, optValue }); if (sdk == null) { throw new NullPointerException("sdk"); } this.sdk = sdk; } @Override protected void checkValidity(final CmdOptions options) throws CmdOptions.ParseException { if (options.getValue(optHelp) != CmdOptions.Flag.ON) { if (!options.isDefined(optAddress)) { throw new CmdOptions. ParseException("option not specified: " + optAddress); } if (!options.isDefined(optValue)) { throw new CmdOptions. ParseException("option not specified: " + optValue); } } } /** * Returns true if no error occurred and the command has been * executed. */ @Override protected boolean execute(final CmdOptions options) throws IOException { final int address = options.getValue(optAddress); final int value = options.getValue(optValue); final boolean validAddress = sdk.providesAddress(address); if (!validAddress) { final String message = String.format("write to unsupported address: 0x%08x", address); throw new IOException(message); } sdk.writeAddress(address, value); final String label = sdk.getLabelForAddress(address); console.printf("wrote 0x%04x to %s (0x%08x)%n", value, label, address); return true; } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/observer/ActionPanel.java ================================================ /* * @(#)ActionPanel.java 1.00 21/04/10 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.observer; import java.awt.event.KeyEvent; import java.io.IOException; import java.util.Objects; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JToolBar; import org.soundpaint.rp2040pio.SwingUtils; public class ActionPanel extends JToolBar { private static final long serialVersionUID = -3674776627922144842L; private static final ImageIcon iconClose; static { try { iconClose = SwingUtils.createImageIcon("quit16x16.png", "Quit"); } catch (final IOException e) { final String message = String.format("failed loading icon: %s", e.getMessage()); System.out.println(message); throw new InternalError(message, e); } } public ActionPanel(final T observer) { Objects.requireNonNull(observer); addAdditionalButtons(observer); add(Box.createHorizontalGlue()); final JButton btClose = new JButton(iconClose); btClose.setToolTipText("Quit Application"); btClose.addActionListener((event) -> { observer.close(); }); add(btClose); } /** * Override this method to add additional buttons to appear to the * left side of the close button. The default implementation of * this method is empty. */ protected void addAdditionalButtons(final T observer) { } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/observer/ConnectDialog.java ================================================ /* * @(#)ConnectDialog.java 1.00 21/05/30 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.observer; import java.awt.BorderLayout; import java.awt.event.KeyEvent; import java.io.IOException; import java.util.Objects; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JTextField; import javax.swing.border.Border; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; public class ConnectDialog extends JDialog { private static final long serialVersionUID = -8971812083742002912L; private class ActionPanel extends Box { private static final long serialVersionUID = 2187278488162670119L; private final JButton btOk; private final JButton btCancel; public ActionPanel() { super(BoxLayout.LINE_AXIS); setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); btOk = new JButton("Ok"); btOk.setMnemonic(KeyEvent.VK_O); btOk.addActionListener((event) -> { if (apply()) { ConnectDialog.this.setVisible(false); } }); add(btOk); add(Box.createHorizontalGlue()); btCancel = new JButton("Cancel"); btCancel.setMnemonic(KeyEvent.VK_C); btCancel.addActionListener((event) -> ConnectDialog.this.setVisible(false)); add(btCancel); } } private final GUIObserver observer; private final JTextField tfPort; private int savedPort; private ConnectDialog() { throw new UnsupportedOperationException("unsupported default constructor"); } public ConnectDialog(final GUIObserver observer, final int defaultPort) { super(observer, "Connect to Emulation Server"); Objects.requireNonNull(observer); this.observer = observer; savedPort = defaultPort; tfPort = new JTextField(String.valueOf(savedPort)); getContentPane().add(createConnectionDetails()); getContentPane().add(new ActionPanel(), BorderLayout.SOUTH); pack(); } public void makeVisible() { tfPort.setText(String.valueOf(savedPort)); setVisible(true); } private Box createConnectionDetails() { final Box vBox = new Box(BoxLayout.PAGE_AXIS); final Border loweredEtched = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); final TitledBorder titled = BorderFactory.createTitledBorder(loweredEtched, "Connection Details"); titled.setTitleJustification(TitledBorder.CENTER); vBox.setBorder(titled); vBox.add(createServerPortLine()); vBox.add(Box.createVerticalGlue()); return vBox; } private Box createServerPortLine() { final Box hBox = new Box(BoxLayout.LINE_AXIS); hBox.add(new JLabel("Server")); final JTextField tfServer = new JTextField("localhost"); tfServer.setEnabled(false); hBox.add(tfServer); hBox.add(Box.createHorizontalStrut(20)); final JLabel lbPort = new JLabel("Port"); hBox.add(lbPort); hBox.add(tfPort); hBox.add(Box.createHorizontalGlue()); return hBox; } private boolean apply() { try { final int port = Integer.parseInt(tfPort.getText().trim()); if ((port <= 0) || (port > 65535)) { JOptionPane. showMessageDialog(this, "Port number must be in the range 1…65535.", "Invalid Port Number", JOptionPane.ERROR_MESSAGE); return false; } observer.connect(port); savedPort = port; return true; } catch (final NumberFormatException e) { JOptionPane. showMessageDialog(this, "Port is not a valid integer number.", "Invalid Port", JOptionPane.ERROR_MESSAGE); return false; } catch (final IOException e) { JOptionPane. showMessageDialog(this, e.getMessage(), "Failed Connecting", JOptionPane.ERROR_MESSAGE); return false; } } } /* * Local Variables: * coding:utf-8 * mode:Java * End: */ ================================================ FILE: java/org/soundpaint/rp2040pio/observer/GUIObserver.java ================================================ /* * @(#)GUIObserver.java 1.00 21/04/01 * * Copyright (C) 2021 Jürgen Reuter * * 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. * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * For updates and more info or contacting the author, visit: * * * Author's web site: www.juergen-reuter.de */ package org.soundpaint.rp2040pio.observer; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.WindowEvent; import java.io.IOException; import java.io.PrintStream; import java.util.Arrays; import java.util.List; import java.util.Objects; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenuBar; import javax.swing.SwingUtilities; import javax.swing.UIManager; import org.soundpaint.rp2040pio.Constants; import org.soundpaint.rp2040pio.CmdOptions; import org.soundpaint.rp2040pio.PicoEmuRegisters; import org.soundpaint.rp2040pio.RemoteAddressSpaceClient; import org.soundpaint.rp2040pio.sdk.SDK; /** * Common abstract super class for all Swing-GUI based observer * implementations. */ public abstract class GUIObserver extends JFrame { private static final long serialVersionUID = 3771005056052179959L; private static final String DEFAULT_APP_FULL_NAME = "Emulation Observer Version 0.1"; private static final CmdOptions.FlagOptionDeclaration optVersion = CmdOptions.createFlagOption(false, 'V', "version", CmdOptions.Flag.OFF, "display version information and exit"); private static final CmdOptions.FlagOptionDeclaration optHelp = CmdOptions.createFlagOption(false, 'h', "help", CmdOptions.Flag.OFF, "display this help text and exit"); private static final CmdOptions.IntegerOptionDeclaration optPort = CmdOptions.createIntegerOption("PORT", false, 'p', "port", Constants. REGISTER_SERVER_DEFAULT_PORT_NUMBER, "use PORT as server port number"); private static final CmdOptions.IntegerOptionDeclaration optRefresh = CmdOptions.createIntegerOption("TIME", false, 'r', "refresh", 1000, "autorefresh after