diff options
author | Josh Blum <josh@joshknows.com> | 2010-02-10 11:57:58 -0800 |
---|---|---|
committer | Josh Blum <josh@joshknows.com> | 2010-02-10 11:57:58 -0800 |
commit | d088a11bf7c257ba45bf0c37a41e5038b302bc44 (patch) | |
tree | 8bbb4d90285a181c603c117de4504e73066afe48 /firmware | |
parent | e2044e13ec4ad94e9739402257134abd92cf1521 (diff) | |
download | uhd-d088a11bf7c257ba45bf0c37a41e5038b302bc44.tar.gz uhd-d088a11bf7c257ba45bf0c37a41e5038b302bc44.tar.bz2 uhd-d088a11bf7c257ba45bf0c37a41e5038b302bc44.zip |
Copied a snapshot of the usrp2 firmware into the microblaze firmware directory in the uhd repo.
Added erllc copyrights to the files created and modified at erllc.
Diffstat (limited to 'firmware')
146 files changed, 20097 insertions, 0 deletions
diff --git a/firmware/microblaze/.gitignore b/firmware/microblaze/.gitignore new file mode 100644 index 000000000..2381f1328 --- /dev/null +++ b/firmware/microblaze/.gitignore @@ -0,0 +1,40 @@ +/*-stamp +/*.a +/*.bin +/*.dump +/*.log +/*.rom +/.deps +/Makefile +/Makefile.in +/aclocal.m4 +/autom4te.cache +/blink_leds +/blink_leds2 +/build +/compile +/config.h +/config.h.in +/config.log +/config.status +/configure +/depcomp +/eth_test +/gen_eth_packets +/ibs_rx_test +/ibs_tx_test +/install-sh +/libtool +/ltmain.sh +/missing +/py-compile +/rcv_eth_packets +/run_tests.sh +/stamp-h1 +/test1 +/test_phy_comm +/timer_test +/buf_ram_test +/buf_ram_zero +/hello +/configure.lineno diff --git a/firmware/microblaze/AUTHORS b/firmware/microblaze/AUTHORS new file mode 100644 index 000000000..c365261f8 --- /dev/null +++ b/firmware/microblaze/AUTHORS @@ -0,0 +1,3 @@ +Eric Blossom <eb@comsec.com> +Matt Ettus <matt@ettus.com> +Josh Blum <josh@ettus.com> diff --git a/firmware/microblaze/COPYING b/firmware/microblaze/COPYING new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/firmware/microblaze/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 a brief idea of what it does.> + Copyright (C) <year> <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 3 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, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU 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. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/firmware/microblaze/ChangeLog b/firmware/microblaze/ChangeLog new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/firmware/microblaze/ChangeLog @@ -0,0 +1 @@ + diff --git a/firmware/microblaze/INSTALL b/firmware/microblaze/INSTALL new file mode 100644 index 000000000..5458714e1 --- /dev/null +++ b/firmware/microblaze/INSTALL @@ -0,0 +1,234 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/firmware/microblaze/Makefile.am b/firmware/microblaze/Makefile.am new file mode 100644 index 000000000..fbb0abe57 --- /dev/null +++ b/firmware/microblaze/Makefile.am @@ -0,0 +1,26 @@ +# Copyright 2010 Ettus Research LLC +# +# Copyright 2007,2008 Free Software Foundation, Inc. +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +EXTRA_DIST = \ + u2_flash_tool + +SUBDIRS = include lib apps + + diff --git a/firmware/microblaze/Makefile.common b/firmware/microblaze/Makefile.common new file mode 100644 index 000000000..b291df967 --- /dev/null +++ b/firmware/microblaze/Makefile.common @@ -0,0 +1,62 @@ +# +# Copyright 2010 Ettus Research LLC +# +# Copyright 2007 Free Software Foundation, Inc. +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +STD_INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/lib + +#HAL_IO = -DHAL_IO_USES_DBOARD_PINS +HAL_IO = -DHAL_IO_USES_UART + +AM_CPPFLAGS = $(HAL_IO) $(STD_INCLUDES) + +STD_CFLAGS = --std=gnu99 -Wall -Werror-implicit-function-declaration -mxl-soft-div -msoft-float + +MB_CFLAGS = -mxl-soft-mul -mxl-barrel-shift + +AM_CFLAGS = $(STD_CFLAGS) $(MB_CFLAGS) +#AM_CFLAGS = $(STD_CFLAGS) -mxl-soft-mul -mxl-barrel-shift -mxl-gp-opt -G 16384 + + +#LINKER_SCRIPT = $(top_srcdir)/lib/microblaze.ld +#AM_LDFLAGS = -Wl,-T,$(LINKER_SCRIPT) -Wl,-defsym -Wl,_STACK_SIZE=1024 +#AM_LDFLAGS = -Wl,-T,$(LINKER_SCRIPT) -Wl,-Map -Wl,$@.map +#AM_LDFLAGS = -Wl,-T,$(LINKER_SCRIPT) +AM_LDFLAGS = -Wl,-Map -Wl,$@.map -Wl,-defsym -Wl,_STACK_SIZE=3072 +#AM_LDFLAGS = -Wl,-defsym -Wl,_STACK_SIZE=2048 + + +%.bin : % + mb-objcopy -O binary $< $@ + +%.dump : % + mb-objdump -DSC $< > $@ + +%.rom : %.bin + hexdump -v -e'1/1 "%.2X\n"' $< > $@ + + +#.PRECIOUS : %.bin + + +BINS = $(noinst_PROGRAMS:=.bin) +ROMS = $(noinst_PROGRAMS:=.rom) +DUMPS = $(noinst_PROGRAMS:=.dump) + +noinst_DATA = $(BINS) $(ROMS) $(DUMPS) + +CLEANFILES = $(ROMS) $(DUMPS) $(BINS) diff --git a/firmware/microblaze/NEWS b/firmware/microblaze/NEWS new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/firmware/microblaze/NEWS diff --git a/firmware/microblaze/README b/firmware/microblaze/README new file mode 100644 index 000000000..1479ffe1f --- /dev/null +++ b/firmware/microblaze/README @@ -0,0 +1,3 @@ +./bootstrap +./configure --host=mb +make diff --git a/firmware/microblaze/apps/.gitignore b/firmware/microblaze/apps/.gitignore new file mode 100644 index 000000000..b8ab0dc8d --- /dev/null +++ b/firmware/microblaze/apps/.gitignore @@ -0,0 +1,77 @@ +/*-stamp +/*.a +/*.bin +/*.dump +/*.log +/*.rom +/*.map +/.deps +/Makefile +/Makefile.in +/aclocal.m4 +/autom4te.cache +/blink_leds +/blink_leds2 +/build +/compile +/config.h +/config.h.in +/config.log +/config.status +/configure +/depcomp +/echo +/eth_test +/gen_eth_packets +/ibs_rx_test +/ibs_tx_test +/install-sh +/libtool +/ltmain.sh +/missing +/py-compile +/rcv_eth_packets +/run_tests.sh +/stamp-h1 +/test1 +/test_phy_comm +/timer_test +/buf_ram_test +/buf_ram_zero +/hello +/test_printf +/test_spi +/test_i2c +/gen_pause_frames +/test_serdes +/rx_only +/tx_only +/tx_standalone +/tx_drop +/tx_drop2 +/tx_drop_rate_limited +/test_lsdac +/test_lsadc +/read_dbids +/test_db_spi +/ramp_lsdac +/eth_to_serdes +/serdes_to_dsp +/sd_gentest +/sd_bounce +/can_i_sub +/tx_only_v2 +/rx_only_v2 +/txrx +/eth_serdes +/serdes_txrx +/set_hw_rev +/test_sd +/factory_test +/test_ram +/mimo_tx +/mimo_tx_slave +/burn_dbsrx_eeprom +/burnrev30 +/burnrev31 +/burnrev40 diff --git a/firmware/microblaze/apps/Makefile.am b/firmware/microblaze/apps/Makefile.am new file mode 100644 index 000000000..91e83b90a --- /dev/null +++ b/firmware/microblaze/apps/Makefile.am @@ -0,0 +1,76 @@ +# +# Copyright 2007,2008 Free Software Foundation, Inc. +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +LDADD = ../lib/libu2fw.a + +noinst_PROGRAMS = \ + blink_leds \ + blink_leds2 \ + buf_ram_test \ + burn_dbsrx_eeprom \ + can_i_sub \ + echo \ + hello \ + read_dbids \ + set_hw_rev \ + test1 \ + test_db_spi \ + test_i2c \ + test_sd \ + test_ram \ + test_phy_comm \ + test_lsadc \ + test_lsdac \ + timer_test \ + txrx \ + burnrev30 \ + burnrev31 \ + burnrev40 \ + sd_gentest \ + sd_bounce + +#nononono = \ +# eth_serdes \ +# gen_eth_packets \ +# rcv_eth_packets \ +# tx_standalone \ +# factory_test \ +# serdes_txrx \ +# mimo_tx \ +# mimo_tx_slave \ +# ibs_rx_test \ +# ibs_tx_test + +# tx_drop_SOURCES = tx_drop.c app_common.c +# tx_drop_rate_limited_SOURCES = tx_drop_rate_limited.c app_common.c +# tx_drop2_SOURCES = tx_drop2.c app_common.c +txrx_SOURCES = txrx.c app_common_v2.c +factory_test_SOURCES = factory_test.c app_common_v2.c +eth_serdes_SOURCES = eth_serdes.c app_passthru_v2.c +serdes_txrx_SOURCES = serdes_txrx.c app_common_v2.c +mimo_tx_SOURCES = mimo_tx.c mimo_app_common_v2.c +mimo_tx_slave_SOURCES = mimo_tx_slave.c app_common_v2.c + +noinst_HEADERS = \ + app_common_v2.h \ + app_passthru_v2.h \ + mimo_app_common_v2.h + + +MOSTLYCLEANFILES = *.map diff --git a/firmware/microblaze/apps/app_common_v2.c b/firmware/microblaze/apps/app_common_v2.c new file mode 100644 index 000000000..e409c77db --- /dev/null +++ b/firmware/microblaze/apps/app_common_v2.c @@ -0,0 +1,716 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* -*- c++ -*- */ +/* + * Copyright 2007,2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app_common_v2.h" +#include "buffer_pool.h" +#include "memcpy_wa.h" +#include "ethernet.h" +#include "nonstdio.h" +#include "print_rmon_regs.h" +#include "db.h" +#include "db_base.h" +#include "clocks.h" +#include "u2_init.h" +#include <string.h> +#include <stddef.h> + +volatile bool link_is_up = false; // eth handler sets this +int cpu_tx_buf_dest_port = PORT_ETH; + +// If this is non-zero, this dbsm could be writing to the ethernet +dbsm_t *ac_could_be_sending_to_eth; + +static unsigned char exp_seqno __attribute__((unused)) = 0; + +static bool +set_time(const op_set_time_t *p) +{ + //printf("Setting time: secs %u, ticks %u\n", p->time_secs, p->time_ticks); + //sr_time64->secs = p->time_secs; //set below... + sr_time64->ticks = p->time_ticks; + switch (p->type){ + case OP_SET_TIME_TYPE_NOW: + sr_time64->imm = 1; + break; + case OP_SET_TIME_TYPE_PPS: + sr_time64->imm = 0; + break; + } + sr_time64->secs = p->time_secs; //set this last to latch the regs + return true; +} + +static inline bool +config_clock_cmd(const op_config_clock_t *p) +{ + //handle the 10 mhz ref source + clocks_mimo_config(p->flags & MC_REF_CLK_MASK); + + //handle the pps config + uint32_t pps_flags = 0; + if (p->flags & MC_PPS_POLARITY_NEG) pps_flags |= 0x00 << 0; + if (p->flags & MC_PPS_POLARITY_POS) pps_flags |= 0x01 << 0; + if (p->flags & MC_PPS_SOURCE_SMA) pps_flags |= 0x00 << 1; + if (p->flags & MC_PPS_SOURCE_MIMO) pps_flags |= 0x01 << 1; + sr_time64->flags = pps_flags; + + return true; +} + +static size_t +op_id_cmd(const op_generic_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_id_reply_t *r = (op_id_reply_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) // no room + return 0; + + // Build reply subpacket + + r->opcode = OP_ID_REPLY; + r->len = sizeof(op_id_reply_t); + r->rid = p->rid; + r->addr = *ethernet_mac_addr(); + r->hw_rev = (u2_hw_rev_major << 8) | u2_hw_rev_minor; + // r->fpga_md5sum = ; // FIXME + // r->sw_md5sum = ; // FIXME + + return r->len; +} + + +static size_t +config_tx_v2_cmd(const op_config_tx_v2_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_config_tx_reply_v2_t *r = (op_config_tx_reply_v2_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + struct tune_result tune_result; + memset(&tune_result, 0, sizeof(tune_result)); + + bool ok = true; + + if (p->valid & CFGV_GAIN){ + ok &= db_set_gain(tx_dboard, p->gain); + } + + if (p->valid & CFGV_FREQ){ + bool was_streaming = is_streaming(); + if (was_streaming) + stop_rx_cmd(); + + u2_fxpt_freq_t f = u2_fxpt_freq_from_hilo(p->freq_hi, p->freq_lo); + bool tune_ok = db_tune(tx_dboard, f, &tune_result); + ok &= tune_ok; + print_tune_result("Tx", tune_ok, f, &tune_result); + + if (was_streaming) + restart_streaming(); + } + + if (p->valid & CFGV_INTERP_DECIM){ + int interp = p->interp; + int hb1 = 0; + int hb2 = 0; + + if (!(interp & 1)){ + hb2 = 1; + interp = interp >> 1; + } + + if (!(interp & 1)){ + hb1 = 1; + interp = interp >> 1; + } + + if (interp < MIN_CIC_INTERP || interp > MAX_CIC_INTERP) + ok = false; + else { + dsp_tx_regs->interp_rate = (hb1<<9) | (hb2<<8) | interp; + // printf("Interp: %d, register %d\n", p->interp, (hb1<<9) | (hb2<<8) | interp); + } + } + + if (p->valid & CFGV_SCALE_IQ){ + dsp_tx_regs->scale_iq = p->scale_iq; + } + + // Build reply subpacket + + r->opcode = OP_CONFIG_TX_REPLY_V2; + r->len = sizeof(*r); + r->rid = p->rid; + r->ok = ok; + r->inverted = tune_result.inverted; + r->baseband_freq_hi = u2_fxpt_freq_hi(tune_result.baseband_freq); + r->baseband_freq_lo = u2_fxpt_freq_lo(tune_result.baseband_freq); + r->duc_freq_hi = u2_fxpt_freq_hi(tune_result.dxc_freq); + r->duc_freq_lo = u2_fxpt_freq_lo(tune_result.dxc_freq); + r->residual_freq_hi = u2_fxpt_freq_hi(tune_result.residual_freq); + r->residual_freq_lo = u2_fxpt_freq_lo(tune_result.residual_freq); + return r->len; +} + +static size_t +config_rx_v2_cmd(const op_config_rx_v2_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_config_rx_reply_v2_t *r = (op_config_rx_reply_v2_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + struct tune_result tune_result; + memset(&tune_result, 0, sizeof(tune_result)); + + bool ok = true; + + if (p->valid & CFGV_GAIN){ + ok &= db_set_gain(rx_dboard, p->gain); + } + + if (p->valid & CFGV_FREQ){ + bool was_streaming = is_streaming(); + if (was_streaming) + stop_rx_cmd(); + + u2_fxpt_freq_t f = u2_fxpt_freq_from_hilo(p->freq_hi, p->freq_lo); + bool tune_ok = db_tune(rx_dboard, f, &tune_result); + ok &= tune_ok; + print_tune_result("Rx", tune_ok, f, &tune_result); + + if (was_streaming) + restart_streaming(); + } + + if (p->valid & CFGV_INTERP_DECIM){ + int decim = p->decim; + int hb1 = 0; + int hb2 = 0; + + if(!(decim & 1)) { + hb2 = 1; + decim = decim >> 1; + } + + if(!(decim & 1)) { + hb1 = 1; + decim = decim >> 1; + } + + if (decim < MIN_CIC_DECIM || decim > MAX_CIC_DECIM) + ok = false; + else { + dsp_rx_regs->decim_rate = (hb1<<9) | (hb2<<8) | decim; + // printf("Decim: %d, register %d\n", p->decim, (hb1<<9) | (hb2<<8) | decim); + } + } + + if (p->valid & CFGV_SCALE_IQ){ + dsp_rx_regs->scale_iq = p->scale_iq; + } + + // Build reply subpacket + + r->opcode = OP_CONFIG_RX_REPLY_V2; + r->len = sizeof(*r); + r->rid = p->rid; + r->ok = ok; + r->inverted = tune_result.inverted; + r->baseband_freq_hi = u2_fxpt_freq_hi(tune_result.baseband_freq); + r->baseband_freq_lo = u2_fxpt_freq_lo(tune_result.baseband_freq); + r->ddc_freq_hi = u2_fxpt_freq_hi(tune_result.dxc_freq); + r->ddc_freq_lo = u2_fxpt_freq_lo(tune_result.dxc_freq); + r->residual_freq_hi = u2_fxpt_freq_hi(tune_result.residual_freq); + r->residual_freq_lo = u2_fxpt_freq_lo(tune_result.residual_freq); + + return r->len; +} + +static void +fill_db_info(u2_db_info_t *p, const struct db_base *db) +{ + p->dbid = db->dbid; + p->freq_min_hi = u2_fxpt_freq_hi(db->freq_min); + p->freq_min_lo = u2_fxpt_freq_lo(db->freq_min); + p->freq_max_hi = u2_fxpt_freq_hi(db->freq_max); + p->freq_max_lo = u2_fxpt_freq_lo(db->freq_max); + p->gain_min = db->gain_min; + p->gain_max = db->gain_max; + p->gain_step_size = db->gain_step_size; +} + +static size_t +dboard_info_cmd(const op_generic_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_dboard_info_reply_t *r = (op_dboard_info_reply_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + r->opcode = OP_DBOARD_INFO_REPLY; + r->len = sizeof(*r); + r->rid = p->rid; + r->ok = true; + + fill_db_info(&r->tx_db_info, tx_dboard); + fill_db_info(&r->rx_db_info, rx_dboard); + + return r->len; +} + +static size_t +peek_cmd(const op_peek_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_generic_t *r = (op_generic_t *) reply_payload; + + //putstr("peek: addr="); puthex32(p->addr); + //printf(" bytes=%u\n", p->bytes); + + if ((reply_payload_space < (sizeof(*r) + p->bytes)) || + p->bytes > MAX_SUBPKT_LEN - sizeof(op_generic_t)) { + putstr("peek: insufficient reply packet space\n"); + return 0; // FIXME do partial read? + } + + r->opcode = OP_PEEK_REPLY; + r->len = sizeof(*r)+p->bytes; + r->rid = p->rid; + r->ok = true; + + memcpy_wa(reply_payload+sizeof(*r), (void *)p->addr, p->bytes); + + return r->len; +} + +static bool +poke_cmd(const op_poke_t *p) +{ + int bytes = p->len - sizeof(*p); + //putstr("poke: addr="); puthex32(p->addr); + //printf(" bytes=%u\n", bytes); + + uint8_t *src = (uint8_t *)p + sizeof(*p); + memcpy_wa((void *)p->addr, src, bytes); + + return true; +} + +static bool +set_lo_offset_cmd(const op_freq_t *p) +{ + u2_fxpt_freq_t f = u2_fxpt_freq_from_hilo(p->freq_hi, p->freq_lo); + if (p->opcode == OP_SET_TX_LO_OFFSET) + return db_set_lo_offset(tx_dboard, f); + else + return db_set_lo_offset(rx_dboard, f); +} + +static size_t +gpio_read_cmd(const op_gpio_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_gpio_read_reply_t *r = (op_gpio_read_reply_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) // no room + return 0; + + // Build reply subpacket + + r->opcode = OP_GPIO_READ_REPLY; + r->len = sizeof(op_gpio_read_reply_t); + r->rid = p->rid; + r->ok = true; + r->mbz = 0; + r->value = hal_gpio_read(p->bank); + + return r->len; +} + +static size_t +generic_reply(const op_generic_t *p, + void *reply_payload, size_t reply_payload_space, + bool ok) +{ + op_generic_t *r = (op_generic_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + r->opcode = p->opcode | OP_REPLY_BIT; + r->len = sizeof(*r); + r->rid = p->rid; + r->ok = ok; + + return r->len; +} + +static size_t +add_eop(void *reply_payload, size_t reply_payload_space) +{ + op_generic_t *r = (op_generic_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + r->opcode = OP_EOP; + r->len = sizeof(*r); + r->rid = 0; + r->ok = 0; + + return r->len; +} + +/*********************************************************************** + * Global vars to make the control replies + **********************************************************************/ +#define REPLY_PAYLOAD_MAX_LEN (4 * sizeof(u2_subpkt_t)) +uint16_t host_dst_udp_port; +uint16_t host_src_udp_port; +struct in_addr host_dst_ip_addr; +struct in_addr host_src_ip_addr; +eth_mac_addr_t host_dst_mac_addr; +eth_mac_addr_t host_src_mac_addr; + +/*********************************************************************** + * Handle input control data and produce output control data + **********************************************************************/ +static size_t handle_control_packets( + const void *data_in, size_t len_in, void *data_out +){ + // point to the begining of outgoing payload (subpackets) + uint8_t *reply_payload = data_out; + size_t reply_payload_space = REPLY_PAYLOAD_MAX_LEN; + + // point to beginning of incoming payload (subpackets) + uint8_t *payload = (uint8_t *)data_in; + size_t payload_len = len_in; + + size_t subpktlen = 0; + bool ok = false; + + while (payload_len >= sizeof(op_generic_t)){ + const op_generic_t *gp = (const op_generic_t *) payload; + subpktlen = 0; + + // printf("\nopcode = %d\n", gp->opcode); + + switch(gp->opcode){ + case OP_EOP: // end of subpackets + goto end_of_subpackets; + + case OP_ID: + subpktlen = op_id_cmd(gp, reply_payload, reply_payload_space); + break; + + case OP_CONFIG_TX_V2: + subpktlen = config_tx_v2_cmd((op_config_tx_v2_t *) payload, reply_payload, reply_payload_space); + break; + + case OP_CONFIG_RX_V2: + subpktlen = config_rx_v2_cmd((op_config_rx_v2_t *) payload, reply_payload, reply_payload_space); + break; + + case OP_START_RX_STREAMING: + start_rx_streaming_cmd((op_start_rx_streaming_t *) payload); + ok = true; + goto generic_reply; + + case OP_STOP_RX: + stop_rx_cmd(); + ok = true; + goto generic_reply; + + case OP_BURN_MAC_ADDR: + ok = ethernet_set_mac_addr(&((op_burn_mac_addr_t *)payload)->addr); + goto generic_reply; + + case OP_CONFIG_CLOCK: + ok = config_clock_cmd((op_config_clock_t *) payload); + goto generic_reply; + + case OP_DBOARD_INFO: + subpktlen = dboard_info_cmd(gp, reply_payload, reply_payload_space); + break; + + case OP_SET_TIME: + ok = set_time((op_set_time_t *) payload); + goto generic_reply; + + case OP_PEEK: + subpktlen = peek_cmd((op_peek_t *)payload, reply_payload, reply_payload_space); + break; + + case OP_POKE: + ok = poke_cmd((op_poke_t *)payload); + goto generic_reply; + + case OP_SET_TX_LO_OFFSET: + case OP_SET_RX_LO_OFFSET: + ok = set_lo_offset_cmd((op_freq_t *)payload); + goto generic_reply; + + case OP_RESET_DB: + db_init(); + ok = true; + goto generic_reply; + + case OP_GPIO_SET_DDR: + ok = true; + hal_gpio_set_ddr(((op_gpio_t *)payload)->bank, + ((op_gpio_t *)payload)->value, + ((op_gpio_t *)payload)->mask); + goto generic_reply; + + case OP_GPIO_SET_SELS: + ok = true; + hal_gpio_set_sels(((op_gpio_set_sels_t *)payload)->bank, + (char *)(&((op_gpio_set_sels_t *)payload)->sels)); + goto generic_reply; + + case OP_GPIO_READ: + subpktlen = gpio_read_cmd((op_gpio_t *) payload, reply_payload, reply_payload_space); + break; + + case OP_GPIO_WRITE: + ok = true; + hal_gpio_write(((op_gpio_t *)payload)->bank, + ((op_gpio_t *)payload)->value, + ((op_gpio_t *)payload)->mask); + goto generic_reply; + + case OP_GPIO_STREAM: + ok = true; + dsp_rx_regs->gpio_stream_enable = (uint32_t)((op_gpio_t *)payload)->value; + goto generic_reply; + + // Add new opcode handlers here + + generic_reply: + subpktlen = generic_reply(gp, reply_payload, reply_payload_space, ok); + break; + + default: + printf("app_common_v2: unhandled opcode = %d\n", gp->opcode); + break; + } + + int t = (gp->len + 3) & ~3; // bump to a multiple of 4 + payload += t; + payload_len -= t; + + subpktlen = (subpktlen + 3) & ~3; // bump to a multiple of 4 + reply_payload += subpktlen; + reply_payload_space -= subpktlen; + } + + end_of_subpackets: + + // add the EOP marker + subpktlen = add_eop(reply_payload, reply_payload_space); + subpktlen = (subpktlen + 3) & ~3; // bump to a multiple of 4 + reply_payload += subpktlen; + reply_payload_space -= subpktlen; + + // how big the payload? + return REPLY_PAYLOAD_MAX_LEN - reply_payload_space; +} + +static void +send_reply(void *reply, size_t reply_len) +{ + if (reply_len < 64) reply_len = 64; + + // wait for buffer to become idle + hal_set_leds(0x4, 0x4); + while((buffer_pool_status->status & BPS_IDLE(CPU_TX_BUF)) == 0) + ; + hal_set_leds(0x0, 0x4); + + // copy reply into CPU_TX_BUF + memcpy_wa(buffer_ram(CPU_TX_BUF), reply, reply_len); + + // wait until nobody else is sending to the ethernet + if (ac_could_be_sending_to_eth){ + hal_set_leds(0x8, 0x8); + dbsm_wait_for_opening(ac_could_be_sending_to_eth); + hal_set_leds(0x0, 0x8); + } + + if (0){ + printf("sending_reply to port %d, len = %d\n", cpu_tx_buf_dest_port, (int)reply_len); + print_buffer(buffer_ram(CPU_TX_BUF), reply_len/4); + } + + // fire it off + bp_send_from_buf(CPU_TX_BUF, cpu_tx_buf_dest_port, 1, 0, reply_len/4); + + // wait for it to complete (not long, it's a small pkt) + while((buffer_pool_status->status & (BPS_DONE(CPU_TX_BUF) | BPS_ERROR(CPU_TX_BUF))) == 0) + ; + + bp_clear_buf(CPU_TX_BUF); +} + +static uint16_t +chksum_buffer(void *buff_, size_t len) +{ + uint16_t *buff = (uint16_t *) buff_; + + // sum the individual 16 bit words + uint32_t sum = 0; + for (size_t i = 0; i < len/sizeof(uint16_t); i++){ + sum += buff[i]; + } + + // take only 16 bits out of the 32 bit sum and add up the carries + while (sum >> 16){ + sum = (sum & 0xffff) + (sum >> 16); + } + + // one's complement the result + return ~sum; +} + +static struct in_addr +create_ip_from_host(struct in_addr addr){ + //get an address that looks like the hosts + uint8_t low_byte = addr.s_addr & 0xff; + low_byte += 1; + if (low_byte == 0xff) low_byte = 0; + if (low_byte == 0x00) low_byte = 1; + addr.s_addr = (addr.s_addr & ~0xff) | low_byte; + return addr; +} + +static void +handle_control_chan_frame(u2_eth_ip_udp_t *pkt, size_t len) +{ + // setup reply + struct { + uint32_t ctrl_word; + u2_eth_ip_udp_t hdr; + uint8_t payload[REPLY_PAYLOAD_MAX_LEN]; + } reply _AL4; + memset(&reply, 0, sizeof(reply)); + + // process the control data + size_t len_out = handle_control_packets( + (uint8_t*)pkt + sizeof(u2_eth_ip_udp_t), + len - sizeof(u2_eth_ip_udp_t), reply.payload + ); + size_t total_len = sizeof(reply) - REPLY_PAYLOAD_MAX_LEN + len_out; + reply.ctrl_word = total_len; + + // load the ethernet header + reply.hdr.eth.dst = host_dst_mac_addr; + reply.hdr.eth.src = host_src_mac_addr; + reply.hdr.eth.ethertype = ETHERTYPE_IPV4; + + // load the ip header + reply.hdr.ip.ip_hl = sizeof(u2_ipv4_hdr_t)/sizeof(uint32_t); + reply.hdr.ip.ip_v = 4; + reply.hdr.ip.ip_tos = 0; + reply.hdr.ip.ip_len = sizeof(u2_ipv4_hdr_t) + sizeof(u2_udp_hdr_t) + len_out; + reply.hdr.ip.ip_id = 0; + reply.hdr.ip.ip_off = IP_DF; + reply.hdr.ip.ip_ttl = 255; + reply.hdr.ip.ip_p = IP_PROTO_UDP; + reply.hdr.ip.ip_sum = 0; + reply.hdr.ip.ip_src = host_src_ip_addr; + reply.hdr.ip.ip_dst = host_dst_ip_addr; + reply.hdr.ip.ip_sum = chksum_buffer(&reply.hdr.ip, sizeof(u2_ipv4_hdr_t)); + + // load the udp header + reply.hdr.udp.src_port = pkt->udp.dst_port; + reply.hdr.udp.dst_port = pkt->udp.src_port; + reply.hdr.udp.length = sizeof(u2_udp_hdr_t) + len_out; + reply.hdr.udp.checksum = 0; + + //send the reply + send_reply(&reply, total_len); +} + + +/* + * Called when an ethernet packet is received. + * Return true if we handled it here, otherwise + * it'll be passed on to the DSP Tx pipe + */ +bool +eth_pkt_inspector(dbsm_t *sm, int bufno) +{ + u2_eth_ip_udp_t *pkt = (u2_eth_ip_udp_t *) buffer_ram(bufno); + size_t byte_len = (buffer_pool_status->last_line[bufno] - 3) * 4; + + if (pkt->eth.ethertype != ETHERTYPE_IPV4) + return true; // ignore, probably bogus PAUSE frame from MAC + + // inspect rcvd frame and figure out what do do. + switch (pkt->udp.dst_port){ + + case 32768: + //record the ip and mac addrs (used when setting up data init) + host_dst_ip_addr = pkt->ip.ip_src; + host_src_ip_addr = create_ip_from_host(pkt->ip.ip_src); + host_dst_mac_addr = pkt->eth.src; + host_src_mac_addr = *ethernet_mac_addr(); + handle_control_chan_frame(pkt, byte_len); + return true; + + case 32769: + //record the udp data ports (used when setting up data init) + host_dst_udp_port = pkt->udp.src_port; + host_src_udp_port = pkt->udp.dst_port; + return false; // pass it on to Tx DSP + + } + return true; // ignore, whatever +} + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ + link_is_up = speed != 0; + hal_set_leds(link_is_up ? LED_RJ45 : 0x0, LED_RJ45); + printf("\neth link changed: speed = %d\n", speed); +} + + +void +print_tune_result(char *msg, bool tune_ok, + u2_fxpt_freq_t target_freq, struct tune_result *r) +{ +#if 0 + printf("db_tune %s %s\n", msg, tune_ok ? "true" : "false"); + putstr(" target_freq "); print_fxpt_freq(target_freq); newline(); + putstr(" baseband_freq "); print_fxpt_freq(r->baseband_freq); newline(); + putstr(" dxc_freq "); print_fxpt_freq(r->dxc_freq); newline(); + putstr(" residual_freq "); print_fxpt_freq(r->residual_freq); newline(); + printf(" inverted %s\n", r->inverted ? "true" : "false"); +#endif +} diff --git a/firmware/microblaze/apps/app_common_v2.h b/firmware/microblaze/apps/app_common_v2.h new file mode 100644 index 000000000..fa4c06d9b --- /dev/null +++ b/firmware/microblaze/apps/app_common_v2.h @@ -0,0 +1,83 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_APP_COMMON_H +#define INCLUDED_APP_COMMON_H + +#include <stdbool.h> +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "memory_map.h" +#include "hal_io.h" +#include <stddef.h> +#include <db.h> + +#define CPU_TX_BUF 7 // cpu -> eth + +#define _AL4 __attribute__((aligned (4))) + +extern volatile bool link_is_up; // eth handler sets this + +// If there's a dbsm that sends to the ethernet, put it's address here +extern dbsm_t *ac_could_be_sending_to_eth; + +extern int cpu_tx_buf_dest_port; + +/* + * Called when an ethernet packet is received. + * Return true if we handled it here, otherwise + * it'll be passed on to the DSP Tx pipe + */ +bool eth_pkt_inspector(dbsm_t *sm, int bufno); + +void link_changed_callback(int speed); + +void +print_tune_result(char *msg, bool tune_ok, + u2_fxpt_freq_t target_freq, struct tune_result *r); + + +void start_rx_streaming_cmd(op_start_rx_streaming_t *p); +void stop_rx_cmd(void); +void restart_streaming(void); +bool is_streaming(void); + +#include "usrp2_ipv4_packet.h" +#include "usrp2_udp_packet.h" + +/*! + * \brief consolidated packet: padding + ethernet header + ip header + udp header + */ +typedef struct { + uint16_t padding; + u2_eth_hdr_t eth; + u2_ipv4_hdr_t ip; + u2_udp_hdr_t udp; +} u2_eth_ip_udp_t; + +extern uint16_t host_dst_udp_port; +extern uint16_t host_src_udp_port; +extern struct in_addr host_dst_ip_addr; +extern struct in_addr host_src_ip_addr; +extern eth_mac_addr_t host_dst_mac_addr; +extern eth_mac_addr_t host_src_mac_addr; + +#endif /* INCLUDED_APP_COMMON_H */ diff --git a/firmware/microblaze/apps/app_passthru_v2.c b/firmware/microblaze/apps/app_passthru_v2.c new file mode 100644 index 000000000..406c56b3b --- /dev/null +++ b/firmware/microblaze/apps/app_passthru_v2.c @@ -0,0 +1,251 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app_passthru_v2.h" +#include "buffer_pool.h" +#include "memcpy_wa.h" +#include "ethernet.h" +#include "nonstdio.h" +#include "print_rmon_regs.h" +#include "db.h" +#include "clocks.h" +#include <string.h> + +volatile bool link_is_up = false; // eth handler sets this + + +// If this is non-zero, this dbsm could be writing to the ethernet +dbsm_t *ac_could_be_sending_to_eth; + +//static unsigned char exp_seqno = 0; + +void +set_reply_hdr(u2_eth_packet_t *reply_pkt, u2_eth_packet_t const *cmd_pkt) +{ + reply_pkt->ehdr.dst = cmd_pkt->ehdr.src; + reply_pkt->ehdr.src = *ethernet_mac_addr(); + reply_pkt->ehdr.ethertype = U2_ETHERTYPE; + reply_pkt->thdr.flags = 0; + reply_pkt->thdr.fifo_status = 0; // written by protocol engine + reply_pkt->thdr.seqno = 0; // written by protocol engine + reply_pkt->thdr.ack = 0; // written by protocol engine + u2p_set_word0(&reply_pkt->fixed, 0, CONTROL_CHAN); + reply_pkt->fixed.timestamp = timer_regs->time; +} + +static void +send_reply(unsigned char *reply, size_t reply_len) +{ + if (reply_len < 64) + reply_len = 64; + + // wait for buffer to become idle + hal_set_leds(0x4, 0x4); + while((buffer_pool_status->status & BPS_IDLE(CPU_TX_BUF)) == 0) + ; + hal_set_leds(0x0, 0x4); + + // copy reply into CPU_TX_BUF + memcpy_wa(buffer_ram(CPU_TX_BUF), reply, reply_len); + + // wait until nobody else is sending to the ethernet + if (ac_could_be_sending_to_eth){ + hal_set_leds(0x8, 0x8); + dbsm_wait_for_opening(ac_could_be_sending_to_eth); + hal_set_leds(0x0, 0x8); + } + + // fire it off + bp_send_from_buf(CPU_TX_BUF, PORT_ETH, 1, 0, reply_len/4); + + // wait for it to complete (not long, it's a small pkt) + while((buffer_pool_status->status & (BPS_DONE(CPU_TX_BUF) | BPS_ERROR(CPU_TX_BUF))) == 0) + ; + + bp_clear_buf(CPU_TX_BUF); +} + + +static size_t +op_id_cmd(const op_generic_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_id_reply_t *r = (op_id_reply_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) // no room + return 0; + + // Build reply subpacket + + r->opcode = OP_ID_REPLY; + r->len = sizeof(op_id_reply_t); + r->rid = p->rid; + r->addr = *ethernet_mac_addr(); + r->hw_rev = 0x0000; // FIXME + // r->fpga_md5sum = ; // FIXME + // r->sw_md5sum = ; // FIXME + + // FIXME Add d'board info, including dbid, min/max gain, min/max freq + + return r->len; +} + +static size_t +add_eop(void *reply_payload, size_t reply_payload_space) +{ + op_generic_t *r = (op_generic_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + r->opcode = OP_EOP; + r->len = sizeof(*r); + r->rid = 0; + r->ok = 0; + + return r->len; +} + +bool +handle_control_chan_frame(u2_eth_packet_t *pkt, size_t len) +{ + unsigned char reply[sizeof(u2_eth_packet_t) + 4 * sizeof(u2_subpkt_t)] _AL4; + unsigned char *reply_payload = &reply[sizeof(u2_eth_packet_t)]; + int reply_payload_space = sizeof(reply) - sizeof(u2_eth_packet_t); + + bool handled_it = false; + + // initialize reply + memset(reply, 0, sizeof(reply)); + set_reply_hdr((u2_eth_packet_t *) reply, pkt); + + // point to beginning of payload (subpackets) + unsigned char *payload = ((unsigned char *) pkt) + sizeof(u2_eth_packet_t); + int payload_len = len - sizeof(u2_eth_packet_t); + + size_t subpktlen = 0; + + while (payload_len >= sizeof(op_generic_t)){ + const op_generic_t *gp = (const op_generic_t *) payload; + subpktlen = 0; + + switch(gp->opcode){ + case OP_EOP: // end of subpackets + goto end_of_subpackets; + + case OP_ID: + subpktlen = op_id_cmd(gp, reply_payload, reply_payload_space); + handled_it = true; + break; + + default: + if (0){ + printf("\npassing on %d\n", gp->opcode); + } + break; + } + + int t = (gp->len + 3) & ~3; // bump to a multiple of 4 + payload += t; + payload_len -= t; + + subpktlen = (subpktlen + 3) & ~3; // bump to a multiple of 4 + reply_payload += subpktlen; + reply_payload_space -= subpktlen; + } + + end_of_subpackets: + + if (handled_it){ + // add the EOP marker + subpktlen = add_eop(reply_payload, reply_payload_space); + subpktlen = (subpktlen + 3) & ~3; // bump to a multiple of 4 + reply_payload += subpktlen; + reply_payload_space -= subpktlen; + + send_reply(reply, reply_payload - reply); + } + + return handled_it; +} + + +/* + * Called when an ethernet packet is received. + * Return true if we handled it here, otherwise + * it'll be passed on to the DSP Tx pipe + */ +bool +eth_pkt_inspector(dbsm_t *sm, int bufno) +{ + u2_eth_packet_t *pkt = (u2_eth_packet_t *) buffer_ram(bufno); + size_t byte_len = (buffer_pool_status->last_line[bufno] - 3) * 4; + + //static size_t last_len = 0; + + // hal_toggle_leds(0x1); + + // inspect rcvd frame and figure out what do do. + + if (pkt->ehdr.ethertype != U2_ETHERTYPE) + return true; // ignore, probably bogus PAUSE frame from MAC + + int chan = u2p_chan(&pkt->fixed); + + switch (chan){ + case CONTROL_CHAN: + return handle_control_chan_frame(pkt, byte_len); + break; + + case 0: + default: +#if 0 + if (last_len != 0){ + if (byte_len != last_len){ + printf("Len: %d last: %d\n", byte_len, last_len); + } + } + last_len = byte_len; + + if((pkt->thdr.seqno) == exp_seqno){ + exp_seqno++; + //putchar('.'); + } + else { + // putchar('S'); + //printf("S%d %d ",exp_seqno,pkt->thdr.seqno); + exp_seqno = pkt->thdr.seqno + 1; + } +#endif + return false; // pass it on to Tx DSP + break; + } +} + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ + link_is_up = speed != 0; + hal_set_leds(link_is_up ? LED_RJ45 : 0x0, LED_RJ45); + printf("\neth link changed: speed = %d\n", speed); +} diff --git a/firmware/microblaze/apps/app_passthru_v2.h b/firmware/microblaze/apps/app_passthru_v2.h new file mode 100644 index 000000000..3904c670f --- /dev/null +++ b/firmware/microblaze/apps/app_passthru_v2.h @@ -0,0 +1,54 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_APP_COMMON_H +#define INCLUDED_APP_COMMON_H + +#include <stdbool.h> +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "memory_map.h" +#include "hal_io.h" +#include <stddef.h> +#include <db.h> + +#define CPU_TX_BUF 7 // cpu -> eth + +#define _AL4 __attribute__((aligned (4))) + +extern volatile bool link_is_up; // eth handler sets this + + +// If there's a dbsm that sends to the ethernet, put it's address here +extern dbsm_t *ac_could_be_sending_to_eth; + + +void set_reply_hdr(u2_eth_packet_t *reply_pkt, u2_eth_packet_t const *cmd_pkt); + +/* + * Called when an ethernet packet is received. + * Return true if we handled it here, otherwise + * it'll be passed on to the DSP Tx pipe + */ +bool eth_pkt_inspector(dbsm_t *sm, int bufno); + +void link_changed_callback(int speed); + +bool handle_control_chan_frame(u2_eth_packet_t *pkt, size_t len); + +#endif /* INCLUDED_APP_COMMON_H */ diff --git a/firmware/microblaze/apps/bitrot/tx_drop.c b/firmware/microblaze/apps/bitrot/tx_drop.c new file mode 100644 index 000000000..3a336e938 --- /dev/null +++ b/firmware/microblaze/apps/bitrot/tx_drop.c @@ -0,0 +1,261 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common.h" +#include "print_rmon_regs.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +/* + * Like tx_only.c, but we discard data packets instead of sending them to the + * DSP TX pipeline. + */ + +int total_rx_pkts = 0; +int total_rx_bytes = 0; + + +static int timer_delta = MASTER_CLK_RATE/1000; // tick at 1kHz + +/* + * This program can respond to queries from the host + * and stream rx samples. + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx eth flow + */ +//#define CPU_RX_BUF 0 // eth -> cpu +//#define CPU_TX_BUF 1 // cpu -> eth + +#define DSP_RX_BUF_0 2 // dsp rx -> eth (double buffer) +#define DSP_RX_BUF_1 3 // dsp rx -> eth +#define DSP_TX_BUF_0 4 // eth -> dsp tx (double buffer) +#define DSP_TX_BUF_1 5 // eth -> dsp tx + + +/* + * ================================================================ + * configure DSP TX double buffering state machine + * ================================================================ + */ + +// 4 lines of ethernet hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE 4 +#define DSP_TX_SAMPLES_PER_FRAME 250 // not used except w/ debugging +#define DSP_TX_EXTRA_LINES 2 // reads word0 + timestamp + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { + PORT_ETH, + 0, + BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { + PORT_DSP, + DSP_TX_FIRST_LINE, // starts just past ethernet header + 0 // filled in from last_line register +}; + +dbsm_t dsp_tx_sm; // the state machine + + +// ---------------------------------------------------------------- + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +void +timer_irq_handler(unsigned irq) +{ + hal_set_timeout(timer_delta); // schedule next timeout +} + +// Tx DSP underrun +void +underrun_irq_handler(unsigned irq) +{ + putchar('U'); + + dbsm_stop(&dsp_tx_sm); + dsp_tx_regs->clear_state = 1; + dbsm_start(&dsp_tx_sm); // restart sm so we're listening to ethernet again + + // putstr("\nirq: underrun\n"); +} + + +void +start_rx_cmd(const eth_mac_addr_t *host, op_start_rx_t *p) +{ +} + +void +stop_rx_cmd(void) +{ +} + +static void +setup_tx() +{ + dsp_tx_regs->clear_state = 1; + bp_clear_buf(DSP_TX_BUF_0); + bp_clear_buf(DSP_TX_BUF_1); + + int tx_scale = 256; + int interp = 32; + + op_config_tx_t def_config; + memset(&def_config, 0, sizeof(def_config)); + def_config.phase_inc = 408021893; // 9.5 MHz [2**32 * fc/fsample] + def_config.scale_iq = (tx_scale << 16) | tx_scale; + def_config.interp = interp; + + // setup Tx DSP regs + config_tx_cmd(&def_config); +} + + +inline static void +buffer_irq_handler(unsigned irq) +{ + uint32_t status = buffer_pool_status->status; + + if (status & BPS_ERROR_ALL){ + // FIXME rare path, handle error conditions + putstr("Errors! status = "); + puthex32_nl(status); + + printf("total_rx_pkts = %d\n", total_rx_pkts); + printf("total_rx_bytes = %d\n", total_rx_bytes); + + print_rmon_regs(); + + if (status & (BPS_ERROR(DSP_TX_BUF_0) | BPS_ERROR(DSP_TX_BUF_1))){ + dbsm_stop(&dsp_tx_sm); + dsp_tx_regs->clear_state = 1; // try to restart + dbsm_start(&dsp_tx_sm); + return; + } + } + + dbsm_process_status(&dsp_tx_sm, status); + + if (status & BPS_DONE(CPU_TX_BUF)){ + bp_clear_buf(CPU_TX_BUF); + } +} + + +/* + * Called when an ethernet packet is received. + * + * Claim that we handled all the packets, + * dropping those destined for the TX DSP chain + * on the ground. + */ +bool +nop_eth_pkt_inspector(dbsm_t *sm, int bufno) +{ + hal_toggle_leds(0x1); + + u2_eth_packet_t *pkt = (u2_eth_packet_t *) buffer_ram(bufno); + size_t byte_len = (buffer_pool_status->last_line[bufno] - 1) * 4; + + total_rx_pkts++; + total_rx_bytes += byte_len; + + // inspect rcvd frame and figure out what do do. + + if (pkt->ehdr.ethertype != U2_ETHERTYPE) + return true; // ignore, probably bogus PAUSE frame from MAC + + int chan = u2p_chan(&pkt->fixed); + + switch (chan){ + case CONTROL_CHAN: + handle_control_chan_frame(pkt, byte_len); + return true; // we handled the packet + break; + + case 0: + default: + return true; // We handled the data by dropping it :) + break; + } +} + + +int +main(void) +{ + u2_init(); + + // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output + hal_gpio_set_tx_mode(15, 0, GPIOM_FPGA_1); + hal_gpio_set_rx_mode(15, 0, GPIOM_FPGA_1); + + putstr("\ntx_drop\n"); + + // Control LEDs + hal_set_leds(0x0, 0x3); + + pic_register_handler(IRQ_UNDERRUN, underrun_irq_handler); + + ethernet_register_link_changed_callback(link_changed_callback); + ethernet_init(); + + // initialize double buffering state machine for ethernet -> DSP Tx + + dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, + &dsp_tx_recv_args, &dsp_tx_send_args, + nop_eth_pkt_inspector); + + // program tx registers + setup_tx(); + + // kick off the state machine + dbsm_start(&dsp_tx_sm); + + while(1){ + buffer_irq_handler(0); + } +} + diff --git a/firmware/microblaze/apps/bitrot/tx_drop2.c b/firmware/microblaze/apps/bitrot/tx_drop2.c new file mode 100644 index 000000000..3afd722e6 --- /dev/null +++ b/firmware/microblaze/apps/bitrot/tx_drop2.c @@ -0,0 +1,292 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common.h" +#include "print_rmon_regs.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +/* + * Like tx_only.c, but we discard data packets instead of sending them to the + * DSP TX pipeline. + */ + +int total_rx_pkts = 0; +int total_rx_bytes = 0; + + +static int timer_delta = MASTER_CLK_RATE/1000; // tick at 1kHz + +/* + * This program can respond to queries from the host + * and stream rx samples. + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx eth flow + */ +//#define CPU_RX_BUF 0 // eth -> cpu +//#define CPU_TX_BUF 1 // cpu -> eth + +#define DSP_RX_BUF_0 2 // dsp rx -> eth (double buffer) +#define DSP_RX_BUF_1 3 // dsp rx -> eth +#define DSP_TX_BUF_0 4 // eth -> dsp tx (double buffer) +#define DSP_TX_BUF_1 5 // eth -> dsp tx + + +/* + * ================================================================ + * configure DSP RX double buffering state machine + * ================================================================ + */ + +// 4 lines of ethernet hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE 5 +#define DSP_RX_SAMPLES_PER_FRAME 128 +#define DSP_RX_EXTRA_LINES 1 // writes timestamp + +// Receive from DSP Rx +buf_cmd_args_t dsp_rx_recv_args = { + PORT_DSP, + DSP_RX_FIRST_LINE, + BP_LAST_LINE +}; + +// send to ethernet +buf_cmd_args_t dsp_rx_send_args = { + PORT_ETH, + 0, // starts with ethernet header in line 0 + 0, // filled in from last_line register +}; + +dbsm_t dsp_rx_sm; // the state machine + +/* + * ================================================================ + * configure DSP TX double buffering state machine + * ================================================================ + */ + +// 4 lines of ethernet hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE 4 +#define DSP_TX_SAMPLES_PER_FRAME 250 // not used except w/ debugging +#define DSP_TX_EXTRA_LINES 2 // reads word0 + timestamp + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { + PORT_ETH, + 0, + BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { + PORT_DSP, + DSP_TX_FIRST_LINE, // starts just past ethernet header + 0 // filled in from last_line register +}; + +dbsm_t dsp_tx_sm; // the state machine + + +// ---------------------------------------------------------------- + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +void +timer_irq_handler(unsigned irq) +{ + hal_set_timeout(timer_delta); // schedule next timeout +} + +// Tx DSP underrun +void +underrun_irq_handler(unsigned irq) +{ + putchar('U'); + + dbsm_stop(&dsp_tx_sm); + dsp_tx_regs->clear_state = 1; + dbsm_start(&dsp_tx_sm); // restart sm so we're listening to ethernet again + + // putstr("\nirq: underrun\n"); +} + + +void +start_rx_cmd(const eth_mac_addr_t *host, op_start_rx_t *p) +{ +} + +void +stop_rx_cmd(void) +{ +} + +static void +setup_tx() +{ + dsp_tx_regs->clear_state = 1; + bp_clear_buf(DSP_TX_BUF_0); + bp_clear_buf(DSP_TX_BUF_1); + +#if 1 + int tx_scale = 256; + int interp = 32; + + op_config_tx_t def_config; + memset(&def_config, 0, sizeof(def_config)); + def_config.phase_inc = 408021893; // 9.5 MHz [2**32 * fc/fsample] + def_config.scale_iq = (tx_scale << 16) | tx_scale; + def_config.interp = interp; + + // setup Tx DSP regs + config_tx_cmd(&def_config); +#endif +} + + +inline static void +buffer_irq_handler(unsigned irq) +{ + uint32_t status = buffer_pool_status->status; + + if (status & BPS_ERROR_ALL){ + // FIXME rare path, handle error conditions + putstr("Errors! status = "); + puthex32_nl(status); + + printf("total_rx_pkts = %d\n", total_rx_pkts); + printf("total_rx_bytes = %d\n", total_rx_bytes); + + print_rmon_regs(); + + if (status & (BPS_ERROR(DSP_TX_BUF_0) | BPS_ERROR(DSP_TX_BUF_1))){ + dbsm_stop(&dsp_tx_sm); + dsp_tx_regs->clear_state = 1; // try to restart + dbsm_start(&dsp_tx_sm); + return; + } + } + + dbsm_process_status(&dsp_tx_sm, status); + + if (status & BPS_DONE(CPU_TX_BUF)){ + bp_clear_buf(CPU_TX_BUF); + } +} + +/* + * Called when an ethernet packet is received. + * Return true if we handled it here (always!) + */ +bool +nop_eth_pkt_inspector(dbsm_t *sm, int bufno) +{ + hal_toggle_leds(0x1); + + u2_eth_packet_t *pkt = (u2_eth_packet_t *) buffer_ram(bufno); + size_t byte_len = (buffer_pool_status->last_line[bufno] - 1) * 4; + + total_rx_pkts++; + total_rx_bytes += byte_len; + + // inspect rcvd frame and figure out what do do. + + if (pkt->ehdr.ethertype != U2_ETHERTYPE) + return true; // ignore, probably bogus PAUSE frame from MAC + + int chan = u2p_chan(&pkt->fixed); + + switch (chan){ + case CONTROL_CHAN: + handle_control_chan_frame(pkt, byte_len); + return true; // we handled the packet + break; + + case 0: + default: + return true; // say we handled it + break; + } +} + + +int +main(void) +{ + u2_init(); + + // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output + hal_gpio_set_tx_mode(15, 0, GPIOM_FPGA_1); + hal_gpio_set_rx_mode(15, 0, GPIOM_FPGA_1); // no printing... + + putstr("\ntx_drop2\n"); + + // Control LEDs + hal_set_leds(0x0, 0x3); + + // pic_register_handler(IRQ_OVERRUN, overrun_irq_handler); + pic_register_handler(IRQ_UNDERRUN, underrun_irq_handler); + + //pic_register_handler(IRQ_TIMER, timer_irq_handler); + //hal_set_timeout(timer_delta); + + ethernet_register_link_changed_callback(link_changed_callback); + + ethernet_init(); + + // initialize double buffering state machine for ethernet -> DSP Tx + + dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, + &dsp_tx_recv_args, &dsp_tx_send_args, + nop_eth_pkt_inspector); + + // program tx registers + setup_tx(); + + // kick off the state machine + dbsm_start(&dsp_tx_sm); + + while(1){ + buffer_irq_handler(0); + } +} + diff --git a/firmware/microblaze/apps/bitrot/tx_drop_rate_limited.c b/firmware/microblaze/apps/bitrot/tx_drop_rate_limited.c new file mode 100644 index 000000000..2b8db7f7d --- /dev/null +++ b/firmware/microblaze/apps/bitrot/tx_drop_rate_limited.c @@ -0,0 +1,233 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common.h" +#include "print_rmon_regs.h" +#include "eth_mac.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +/* + * receive packets from ethernet at a fixed rate and discard them + */ + +int total_rx_pkts = 0; +int total_rx_bytes = 0; + + +static int timer_delta = (int)(MASTER_CLK_RATE * 10e-6); // 10us / tick + + +/* + * This program can respond to queries from the host + * and stream rx samples. + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx eth flow + */ +//#define CPU_RX_BUF 0 // eth -> cpu +//#define CPU_TX_BUF 1 // cpu -> eth + +#define DSP_RX_BUF_0 2 // dsp rx -> eth (double buffer) +#define DSP_RX_BUF_1 3 // dsp rx -> eth +#define DSP_TX_BUF_0 4 // eth -> dsp tx (double buffer) +#define DSP_TX_BUF_1 5 // eth -> dsp tx + + + +// ---------------------------------------------------------------- + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +static volatile bool receive_packet_now = false; + +void +timer_irq_handler(unsigned irq) +{ + hal_set_timeout(timer_delta); // schedule next timeout + receive_packet_now = true; +} + + +// Tx DSP underrun +void +underrun_irq_handler(unsigned irq) +{ + putchar('U'); +} + + +void +start_rx_cmd(const eth_mac_addr_t *host, op_start_rx_t *p) +{ +} + +void +stop_rx_cmd(void) +{ +} + +static void +setup_tx() +{ + dsp_tx_regs->clear_state = 1; + bp_clear_buf(DSP_TX_BUF_0); + bp_clear_buf(DSP_TX_BUF_1); + + int tx_scale = 256; + int interp = 32; + + op_config_tx_t def_config; + memset(&def_config, 0, sizeof(def_config)); + def_config.phase_inc = 408021893; // 9.5 MHz [2**32 * fc/fsample] + def_config.scale_iq = (tx_scale << 16) | tx_scale; + def_config.interp = interp; + + // setup Tx DSP regs + config_tx_cmd(&def_config); +} + + +/* + * Called when an ethernet packet is received. + * + * Claim that we handled all the packets, + * dropping those destined for the TX DSP chain + * on the ground. + */ +bool +nop_eth_pkt_inspector(dbsm_t *sm, int bufno) +{ + hal_toggle_leds(0x1); + + u2_eth_packet_t *pkt = (u2_eth_packet_t *) buffer_ram(bufno); + size_t byte_len = (buffer_pool_status->last_line[bufno] - 1) * 4; + + total_rx_pkts++; + total_rx_bytes += byte_len; + + // inspect rcvd frame and figure out what do do. + + if (pkt->ehdr.ethertype != U2_ETHERTYPE) + return true; // ignore, probably bogus PAUSE frame from MAC + + int chan = u2p_chan(&pkt->fixed); + + switch (chan){ + case CONTROL_CHAN: + handle_control_chan_frame(pkt, byte_len); + return true; // we handled the packet + break; + + case 0: + default: + return true; // We handled the data by dropping it :) + break; + } +} + + +inline static void +buffer_irq_handler(unsigned irq) +{ + uint32_t status = buffer_pool_status->status; + + if (status & (BPS_DONE(CPU_TX_BUF) | BPS_ERROR(CPU_TX_BUF))) + bp_clear_buf(CPU_TX_BUF); + + if (status & (BPS_DONE(DSP_TX_BUF_0) | BPS_ERROR(DSP_TX_BUF_0))){ + bp_clear_buf(DSP_TX_BUF_0); + + if (status & BPS_ERROR(DSP_TX_BUF_0)){ + int crc = eth_mac_read_rmon(0x05); + int fifo_full = eth_mac_read_rmon(0x06); + int too_short_too_long = eth_mac_read_rmon(0x07); + putstr("Errors! status = "); + puthex32_nl(status); + + printf("crc_err\t\t= %d\n", crc); + printf("fifo_full\t\t= %d\n", fifo_full); + printf("too_short_too_long\t= %d\n", too_short_too_long); + + printf("total_rx_pkts = %d\n", total_rx_pkts); + printf("total_rx_bytes = %d\n", total_rx_bytes); + } + else + nop_eth_pkt_inspector(0, DSP_TX_BUF_0); + } + + if (receive_packet_now && (status & BPS_IDLE(DSP_TX_BUF_0))){ + receive_packet_now = false; + bp_receive_to_buf(DSP_TX_BUF_0, PORT_ETH, 1, 0, BP_LAST_LINE); + } +} + + +int +main(void) +{ + u2_init(); + + // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output + hal_gpio_set_tx_mode(15, 0, GPIOM_FPGA_1); + hal_gpio_set_rx_mode(15, 0, GPIOM_FPGA_1); + + putstr("\ntx_drop_rate_limited\n"); + + // Control LEDs + hal_set_leds(0x0, 0x3); + + pic_register_handler(IRQ_UNDERRUN, underrun_irq_handler); + + pic_register_handler(IRQ_TIMER, timer_irq_handler); + hal_set_timeout(timer_delta); + + ethernet_register_link_changed_callback(link_changed_callback); + ethernet_init(); + + // program tx registers + setup_tx(); + + // start a receive from ethernet + bp_receive_to_buf(DSP_TX_BUF_0, PORT_ETH, 1, 0, BP_LAST_LINE); + + while(1){ + buffer_irq_handler(0); + } +} + diff --git a/firmware/microblaze/apps/blink_leds.c b/firmware/microblaze/apps/blink_leds.c new file mode 100644 index 000000000..682ca8db2 --- /dev/null +++ b/firmware/microblaze/apps/blink_leds.c @@ -0,0 +1,40 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "hal_io.h" +#include "nonstdio.h" + +int +main(void) +{ + int counter = 0; + + u2_init(); + + putstr("blink_leds\n"); + while(1){ + output_regs->leds = (counter++ & 0x3); + } + + return 0; +} diff --git a/firmware/microblaze/apps/blink_leds2.c b/firmware/microblaze/apps/blink_leds2.c new file mode 100644 index 000000000..13e78afb3 --- /dev/null +++ b/firmware/microblaze/apps/blink_leds2.c @@ -0,0 +1,53 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "hal_io.h" +#include "pic.h" +#include "nonstdio.h" + +//#define DELTA_T (MASTER_CLK_RATE/2) // 0.5s (10ns per tick) +#define DELTA_T 5000 // 5 us (10ns per tick) + + +void +timer_handler(unsigned irq) +{ + hal_set_timeout(DELTA_T); // schedule next timeout + hal_toggle_leds(0x2); +} + +int +main(void) +{ + u2_init(); + + putstr("blink_leds2\n"); + pic_register_handler(IRQ_ONETIME, timer_handler); + hal_set_timeout(DELTA_T); // schedule next timeout + + while(1){ + hal_toggle_leds(0x1); + } + + return 0; +} diff --git a/firmware/microblaze/apps/buf_ram_test.c b/firmware/microblaze/apps/buf_ram_test.c new file mode 100644 index 000000000..1aca2aec5 --- /dev/null +++ b/firmware/microblaze/apps/buf_ram_test.c @@ -0,0 +1,89 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "u2_init.h" +#include "memory_map.h" +#include <stdbool.h> +#include "nonstdio.h" +#include "hal_io.h" +#include "mdelay.h" + + +static void +write_bufs(void) +{ + int i, n; + int counter = 0; + + for (n = 0; n < NBUFFERS; n++){ + volatile int *p = buffer_ram(n); + for (i = 0; i < BP_NLINES; i++) + p[i] = counter++; + } +} + +// return number of errors detected +static int +check_bufs(void) +{ + int i, n; + int counter = 0; + int nerrors = 0; + + for (n = 0; n < NBUFFERS; n++){ + volatile int *p = buffer_ram(n); + for (i = 0; i < BP_NLINES; i++, counter++){ + int rd = p[i]; + if (rd != counter){ + putchar('b'); + putchar(n + '0'); + putchar('['); + puthex16(i); + putstr("] exp: "); + puthex32(counter); + putstr(" got: "); + puthex32_nl(rd); + nerrors++; + } + } + } + return nerrors; +} + + +int +main(void) +{ + u2_init(); + + output_regs->leds = 0; + + write_bufs(); + int nerrors = check_bufs(); + + if (nerrors == 0){ + output_regs->leds = 0x3; // leds on -> PASS + putstr("PASS\n"); + } + else { + output_regs->leds = 0x0; // leds off -> FAIL + putstr("FAIL\n"); + } + + hal_finish(); + return 0; +} diff --git a/firmware/microblaze/apps/burn_dbsrx_eeprom.c b/firmware/microblaze/apps/burn_dbsrx_eeprom.c new file mode 100644 index 000000000..116d4d8d0 --- /dev/null +++ b/firmware/microblaze/apps/burn_dbsrx_eeprom.c @@ -0,0 +1,106 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "i2c.h" +#include "usrp2_i2c_addr.h" +#include "mdelay.h" +#include "hal_io.h" +#include "nonstdio.h" +#include <stdbool.h> + + + +int read_dboard_eeprom(int i2c_addr); + + +#define USRP_DBID_DBS_RX 0x0002 +#define USRP_DBID_DBS_RX_WITH_CLOCK_MOD 0x000d + +const char dbs_rx_rev2_eeprom[] = { + 0xdb, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18 +}; + +#define LED_VALS (LED_A | LED_B | LED_C | LED_D) +#define LED_MASK (LED_A | LED_B | LED_C | LED_D) + +int +main(void) +{ + u2_init(); + + puts("\nburn_dbsrx_eeprom\n"); + + hal_set_leds(0, ~0); // all off + + int i2c_addr = I2C_ADDR_RX_A; + int dbid = read_dboard_eeprom(i2c_addr); + bool ok; + const char *msg = 0; + + switch (dbid){ + case -1: + msg = "No RX daughterboard found"; + goto bad; + + case -2: + msg = "Invalid RX EEPROM contents"; + goto bad; + + case USRP_DBID_DBS_RX_WITH_CLOCK_MOD: + msg = "RX Daughterboard already reports being a DBS RX w/ CLOCK_MOD"; + goto good; + + case USRP_DBID_DBS_RX: + // Says it's a DBS_RX, attempt to burn the EEPROM + ok = eeprom_write(i2c_addr, 0, + dbs_rx_rev2_eeprom, sizeof(dbs_rx_rev2_eeprom)); + if (ok){ + msg = "Successfully programmed db as DBS RX Rev 2.1"; + goto good; + } + else { + msg = "Failed to write daugherboard eeprom"; + goto bad; + } + + default: + msg = "Daughterboard is not a DBS RX; ignored"; + goto bad; + } + + good: + puts(msg); + hal_set_leds(LED_VALS, LED_MASK); + while (1) + ; + + bad: + puts(msg); + while(1){ + hal_toggle_leds(LED_VALS); + mdelay(50); + } +} diff --git a/firmware/microblaze/apps/burnrev30.c b/firmware/microblaze/apps/burnrev30.c new file mode 100644 index 000000000..40fa53e34 --- /dev/null +++ b/firmware/microblaze/apps/burnrev30.c @@ -0,0 +1,162 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <clocks.h> +#include "sd.h" +#include "mdelay.h" + +#define HW_REV_MAJOR 3 +#define HW_REV_MINOR 0 + +int test_ram() +{ + int i,j,k; + output_regs->ram_page = 1<<10; + + extram[0] = 0xDEADBEEF; + extram[1] = 0xF00D1234; + extram[7] = 0x76543210; + + output_regs->ram_page = 2<<10; + extram[7] = 0x55555555; + extram[1] = 0xaaaaaaaa; + extram[0] = 0xeeeeeeee; + + output_regs->ram_page = 1<<10; + + i = extram[0]; + k = extram[1]; + j = extram[7]; + + if((i != 0xDEADBEEF)||(j!=0x76543210)||(k!=0xF00D1234)) { + puts("RAM FAIL1!\n"); + puthex32_nl(i); + puthex32_nl(j); + puthex32_nl(k); + return 0; + } + + output_regs->ram_page = 2<<10; + + j = extram[7]; + k = extram[1]; + i = extram[0]; + + if((i != 0xeeeeeeee)||(j!=0x55555555)||(k!=0xaaaaaaaa)) { + puts("RAM FAIL2!\n"); + puthex32_nl(i); + puthex32_nl(j); + puthex32_nl(k); + return 0; + } + return 1; +} + +int test_sd() +{ + int i = sd_init(); + if(i==0) { + puts("FAILED INIT of Card\n"); + return 0; + } + + unsigned char buf[512]; + i = sd_read_block(2048,buf); + if(i == 0) { + puts("READ Command Rejected\n"); + return 0; + } + if((buf[0]==0xb8)&&(buf[1]==0x08)&&(buf[2]==0x00)&&(buf[3]==0x50)) + ; + else { + puts("Read bad data from SD Card\n"); + return 0; + } + return 1; +} + +int +main(void) +{ + u2_init(); + + putstr("\nFactory Test, Board Rev 3.0\n"); + + bool ok = true; + unsigned char maj = HW_REV_MAJOR; + unsigned char min = HW_REV_MINOR; + ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_MSB, &maj, 1); + ok &= eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_LSB, &min, 1); + + putstr("\nset_hw_rev\n"); + if (ok) + printf("OK: set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); + else { + printf("FAILED to set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); + hal_finish(); + return 0; + } + + if(test_sd()) + puts("SD OK\n"); + else { + puts("SD FAIL\n"); + //hal_finish(); + //return 0; + } + if(test_ram()) + puts("RAM OK\n"); + else { + puts("RAM FAIL\n"); + hal_finish(); + return 0; + } + + print_mac_addr(ethernet_mac_addr()->addr); + newline(); + + clocks_mimo_config(MC_WE_LOCK_TO_SMA); + + while (!clocks_lock_detect()) { + puts("No Lock"); + mdelay(1000); + } + puts("Clock Locked\n"); + +} diff --git a/firmware/microblaze/apps/burnrev31.c b/firmware/microblaze/apps/burnrev31.c new file mode 100644 index 000000000..f6b08d187 --- /dev/null +++ b/firmware/microblaze/apps/burnrev31.c @@ -0,0 +1,162 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <clocks.h> +#include "sd.h" +#include "mdelay.h" + +#define HW_REV_MAJOR 3 +#define HW_REV_MINOR 1 + +int test_ram() +{ + int i,j,k; + output_regs->ram_page = 1<<10; + + extram[0] = 0xDEADBEEF; + extram[1] = 0xF00D1234; + extram[7] = 0x76543210; + + output_regs->ram_page = 2<<10; + extram[7] = 0x55555555; + extram[1] = 0xaaaaaaaa; + extram[0] = 0xeeeeeeee; + + output_regs->ram_page = 1<<10; + + i = extram[0]; + k = extram[1]; + j = extram[7]; + + if((i != 0xDEADBEEF)||(j!=0x76543210)||(k!=0xF00D1234)) { + puts("RAM FAIL1!\n"); + puthex32_nl(i); + puthex32_nl(j); + puthex32_nl(k); + return 0; + } + + output_regs->ram_page = 2<<10; + + j = extram[7]; + k = extram[1]; + i = extram[0]; + + if((i != 0xeeeeeeee)||(j!=0x55555555)||(k!=0xaaaaaaaa)) { + puts("RAM FAIL2!\n"); + puthex32_nl(i); + puthex32_nl(j); + puthex32_nl(k); + return 0; + } + return 1; +} + +int test_sd() +{ + int i = sd_init(); + if(i==0) { + puts("FAILED INIT of Card\n"); + return 0; + } + + unsigned char buf[512]; + i = sd_read_block(2048,buf); + if(i == 0) { + puts("READ Command Rejected\n"); + return 0; + } + if((buf[0]==0xb8)&&(buf[1]==0x08)&&(buf[2]==0x00)&&(buf[3]==0x50)) + ; + else { + puts("Read bad data from SD Card\n"); + return 0; + } + return 1; +} + +int +main(void) +{ + u2_init(); + + putstr("\nFactory Test, Board Rev 3.1\n"); + + bool ok = true; + unsigned char maj = HW_REV_MAJOR; + unsigned char min = HW_REV_MINOR; + ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_MSB, &maj, 1); + ok &= eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_LSB, &min, 1); + + putstr("\nset_hw_rev\n"); + if (ok) + printf("OK: set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); + else { + printf("FAILED to set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); + hal_finish(); + return 0; + } + + if(test_sd()) + puts("SD OK\n"); + else { + puts("SD FAIL\n"); + //hal_finish(); + //return 0; + } + if(test_ram()) + puts("RAM OK\n"); + else { + puts("RAM FAIL\n"); + hal_finish(); + return 0; + } + + print_mac_addr(ethernet_mac_addr()->addr); + newline(); + + clocks_mimo_config(MC_WE_LOCK_TO_SMA); + + while (!clocks_lock_detect()) { + puts("No Lock"); + mdelay(1000); + } + puts("Clock Locked\n"); + +} diff --git a/firmware/microblaze/apps/burnrev40.c b/firmware/microblaze/apps/burnrev40.c new file mode 100644 index 000000000..362270961 --- /dev/null +++ b/firmware/microblaze/apps/burnrev40.c @@ -0,0 +1,162 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <clocks.h> +#include "sd.h" +#include "mdelay.h" + +#define HW_REV_MAJOR 4 +#define HW_REV_MINOR 0 + +int test_ram() +{ + int i,j,k; + output_regs->ram_page = 1<<10; + + extram[0] = 0xDEADBEEF; + extram[1] = 0xF00D1234; + extram[7] = 0x76543210; + + output_regs->ram_page = 2<<10; + extram[7] = 0x55555555; + extram[1] = 0xaaaaaaaa; + extram[0] = 0xeeeeeeee; + + output_regs->ram_page = 1<<10; + + i = extram[0]; + k = extram[1]; + j = extram[7]; + + if((i != 0xDEADBEEF)||(j!=0x76543210)||(k!=0xF00D1234)) { + puts("RAM FAIL1!\n"); + puthex32_nl(i); + puthex32_nl(j); + puthex32_nl(k); + return 0; + } + + output_regs->ram_page = 2<<10; + + j = extram[7]; + k = extram[1]; + i = extram[0]; + + if((i != 0xeeeeeeee)||(j!=0x55555555)||(k!=0xaaaaaaaa)) { + puts("RAM FAIL2!\n"); + puthex32_nl(i); + puthex32_nl(j); + puthex32_nl(k); + return 0; + } + return 1; +} + +int test_sd() +{ + int i = sd_init(); + if(i==0) { + puts("FAILED INIT of Card\n"); + return 0; + } + + unsigned char buf[512]; + i = sd_read_block(2048,buf); + if(i == 0) { + puts("READ Command Rejected\n"); + return 0; + } + if((buf[0]==0xb8)&&(buf[1]==0x08)&&(buf[2]==0x00)&&(buf[3]==0x50)) + ; + else { + puts("Read bad data from SD Card\n"); + return 0; + } + return 1; +} + +int +main(void) +{ + u2_init(); + + putstr("\nFactory Test, Board Rev 4.0\n"); + + bool ok = true; + unsigned char maj = HW_REV_MAJOR; + unsigned char min = HW_REV_MINOR; + ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_MSB, &maj, 1); + ok &= eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_LSB, &min, 1); + + putstr("\nset_hw_rev\n"); + if (ok) + printf("OK: set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); + else { + printf("FAILED to set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); + hal_finish(); + return 0; + } + + if(test_sd()) + puts("SD OK\n"); + else { + puts("SD FAIL\n"); + //hal_finish(); + //return 0; + } + if(test_ram()) + puts("RAM OK\n"); + else { + puts("RAM FAIL\n"); + hal_finish(); + return 0; + } + + print_mac_addr(ethernet_mac_addr()->addr); + newline(); + + clocks_mimo_config(MC_WE_LOCK_TO_SMA); + + while (!clocks_lock_detect()) { + puts("No Lock"); + mdelay(1000); + } + puts("Clock Locked\n"); + +} diff --git a/firmware/microblaze/apps/can_i_sub.c b/firmware/microblaze/apps/can_i_sub.c new file mode 100644 index 000000000..ed49791f0 --- /dev/null +++ b/firmware/microblaze/apps/can_i_sub.c @@ -0,0 +1,25 @@ +#include <u2_init.h> +#include <nonstdio.h> + +//typedef long long int64_t; + + +int64_t sub(int64_t a, int64_t b); +void print(int64_t d); + +int main(void) +{ + u2_init(); + + int64_t d = sub(462550990848000LL, 462028800000000LL); + print_uint64(d); + newline(); + return 0; +} + +int64_t sub(int64_t a, int64_t b) +{ + return a - b; +} + + diff --git a/firmware/microblaze/apps/double_buffer_fragment.c b/firmware/microblaze/apps/double_buffer_fragment.c new file mode 100644 index 000000000..cfc061247 --- /dev/null +++ b/firmware/microblaze/apps/double_buffer_fragment.c @@ -0,0 +1,138 @@ +#if 0 +void +double_buffering(int port) { + unsigned int localstatus = buffer_pool_status->status; + + if(localstatus & BPS_DONE_0) { + bp_clear_buf(0); + if(buffer_state[0] == FILLING) { + buffer_state[0] = FULL; + if(buffer_state[1] == EMPTY) { + bp_receive_to_buf(1, 1, 1, 10, 509); // DSP_RX to buffer 1, use 500 lines + buffer_state[1] = FILLING; + } + else + dsp_rx_idle = 1; + if(serdes_tx_idle) { + serdes_tx_idle = 0; + bp_send_from_buf(0, port, 1, 10, 509); // SERDES_TX from buffer 0 + buffer_state[0] = EMPTYING; + } + } + else { // buffer was emptying + buffer_state[0] = EMPTY; + if(dsp_rx_idle) { + dsp_rx_idle = 0; + bp_receive_to_buf(0, 1, 1, 10, 509); // DSP_RX to buffer 0, use 500 lines + buffer_state[0] = FILLING; + } + if(buffer_state[1] == FULL) { + bp_send_from_buf(1, port, 1, 10, 509); // SERDES_TX from buffer 1 + buffer_state[1] = EMPTYING; + } + else + serdes_tx_idle = 1; + } + putstr("Int Proc'ed 0\n"); + } + + if(localstatus & BPS_DONE_1) { + bp_clear_buf(1); + if(buffer_state[1] == FILLING) { + buffer_state[1] = FULL; + if(buffer_state[0] == EMPTY) { + bp_receive_to_buf(0, 1, 1, 10, 509); // DSP_RX to buffer 1, use 500 lines + buffer_state[0] = FILLING; + } + else + dsp_rx_idle = 1; + if(serdes_tx_idle) { + serdes_tx_idle = 0; + bp_send_from_buf(1, port, 1, 10, 509); // SERDES_TX from buffer 1 + buffer_state[1] = EMPTYING; + } + } + else { // buffer was emptying + buffer_state[1] = EMPTY; + if(dsp_rx_idle) { + dsp_rx_idle = 0; + bp_receive_to_buf(1, 1, 1, 10, 509); // DSP_RX to buffer 1, use 500 lines + buffer_state[1] = FILLING; + } + if(buffer_state[0] == FULL) { + bp_send_from_buf(0, port, 1, 10, 509); // SERDES_TX from buffer 0 + buffer_state[0] = EMPTYING; + } + else + serdes_tx_idle = 1; + } + putstr("Int Proc'ed 1\n"); + } + + if(localstatus & BPS_DONE_2) { + bp_clear_buf(2); + if(buffer_state[2] == FILLING) { + buffer_state[2] = FULL; + if(buffer_state[3] == EMPTY) { + bp_receive_to_buf(3, port, 1, 5, 504); // SERDES_RX to buffer 3, use 500 lines + buffer_state[3] = FILLING; + } + else + serdes_rx_idle = 1; + if(dsp_tx_idle) { + dsp_tx_idle = 0; + bp_send_from_buf(2, 1, 1, 5, 504); // DSP_TX from buffer 2 + buffer_state[2] = EMPTYING; + } + } + else { // buffer was emptying + buffer_state[2] = EMPTY; + if(serdes_rx_idle) { + serdes_rx_idle = 0; + bp_receive_to_buf(2, port, 1, 5, 504); // SERDES_RX to buffer 2 + buffer_state[2] = FILLING; + } + if(buffer_state[3] == FULL) { + bp_send_from_buf(3, 1, 1, 5, 504); // DSP_TX from buffer 3 + buffer_state[3] = EMPTYING; + } + else + dsp_tx_idle = 1; + } + putstr("Int Proc'ed 2\n"); + } + + if(localstatus & BPS_DONE_3) { + bp_clear_buf(3); + if(buffer_state[3] == FILLING) { + buffer_state[3] = FULL; + if(buffer_state[2] == EMPTY) { + bp_receive_to_buf(2, port, 1, 5, 504); // SERDES_RX to buffer 2, use 500 lines + buffer_state[2] = FILLING; + } + else + serdes_rx_idle = 1; + if(dsp_tx_idle) { + dsp_tx_idle = 0; + bp_send_from_buf(3, 1, 1, 5, 504); // DSP_TX from buffer 3 + buffer_state[3] = EMPTYING; + } + } + else { // buffer was emptying + buffer_state[3] = EMPTY; + if(serdes_rx_idle) { + serdes_rx_idle = 0; + bp_receive_to_buf(3, port, 1, 5, 504); // SERDES_RX to buffer 3 + buffer_state[3] = FILLING; + } + if(buffer_state[2] == FULL) { + bp_send_from_buf(2, 1, 1, 5, 504); // DSP_TX from buffer 2 + buffer_state[2] = EMPTYING; + } + else + dsp_tx_idle = 1; + } + putstr("Int Proc'ed 3\n"); + } +} +#endif diff --git a/firmware/microblaze/apps/echo.c b/firmware/microblaze/apps/echo.c new file mode 100644 index 000000000..89108ee80 --- /dev/null +++ b/firmware/microblaze/apps/echo.c @@ -0,0 +1,34 @@ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "u2_init.h" +#include "stdio.h" + +int +main(void) +{ + u2_init(); + + puts("\n>>> echo <<<"); + + while (1){ + int ch = getchar(); + putchar(ch); + } + + return 0; +} diff --git a/firmware/microblaze/apps/eth_serdes.c b/firmware/microblaze/apps/eth_serdes.c new file mode 100644 index 000000000..2d2ddc1ca --- /dev/null +++ b/firmware/microblaze/apps/eth_serdes.c @@ -0,0 +1,233 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_passthru_v2.h" +#include "memcpy_wa.h" +#include "clocks.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +#define FW_SETS_SEQNO 1 // define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno __attribute__((unused)); // used when f/w is filling in sequence numbers +#endif + + +/* + * Full duplex Tx and Rx between ethernet and serdes + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx eth flow + */ +//#define CPU_RX_BUF 0 // eth -> cpu + +#define DSP_RX_BUF_0 2 // serdes -> eth (double buffer) +#define DSP_RX_BUF_1 3 // serdes -> eth +#define DSP_TX_BUF_0 4 // eth -> serdes (double buffer) +#define DSP_TX_BUF_1 5 // eth -> serdes + +/* + * ================================================================ + * configure serdes double buffering state machine (eth -> serdes) + * ================================================================ + */ + + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { + PORT_ETH, + 0, + BP_LAST_LINE +}; + +// send to serdes +buf_cmd_args_t dsp_tx_send_args = { + PORT_SERDES, + 0, + 0 // filled in from last_line register +}; + +dbsm_t dsp_tx_sm; // the state machine + +/* + * ==================================================================== + * configure serdes RX double buffering state machine (serdes -> eth) + * ==================================================================== + */ + +// receive from serdes +buf_cmd_args_t dsp_rx_recv_args = { + PORT_SERDES, + 0, + BP_LAST_LINE +}; + +// send to ETH +buf_cmd_args_t dsp_rx_send_args = { + PORT_ETH, + 0, // starts with ethernet header in line 0 + 0, // filled in from list_line register +}; + +dbsm_t dsp_rx_sm; // the state machine + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +// ---------------------------------------------------------------- + + +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY. This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet. A bit of a kludge + * but it should work. + */ + +bool +fw_sets_seqno_inspector(dbsm_t *sm, int buf_this) // returns false +{ +#if 0 + uint32_t *p = buffer_ram(buf_this); + uint32_t last_line = buffer_pool_status->last_line[buf_this] - sm->last_line_adj; + printf("fw_sets_seqno_inspector: buf_this = %d, last_line = %d\n", + buf_this, last_line); + + print_buffer(p, (last_line + 1)); +#endif + +#if 0 + uint32_t *p = buffer_ram(buf_this); + uint32_t seqno = fw_seqno++; + + // KLUDGE all kinds of nasty magic numbers and embedded knowledge + uint32_t t = p[4]; + t = (t & 0xffff00ff) | ((seqno & 0xff) << 8); + p[4] = t; +#endif + + return false; // we didn't handle the packet +} +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ + uint32_t status = buffer_pool_status->status; + + if (0 && (status & ~BPS_IDLE_ALL)){ + putstr("status = "); + puthex32_nl(status); + } + + dbsm_process_status(&dsp_tx_sm, status); + dbsm_process_status(&dsp_rx_sm, status); +} + +int +main(void) +{ + u2_init(); + + output_regs->led_src = 0x3; // h/w controls bottom two bits + clocks_enable_test_clk(true, 1); + + putstr("\neth <-> serdes\n"); + + ethernet_register_link_changed_callback(link_changed_callback); + ethernet_init(); + + // clocks_mimo_config(MC_WE_LOCK_TO_SMA | MC_PROVIDE_CLK_TO_MIMO); + clocks_mimo_config(MC_WE_DONT_LOCK | MC_PROVIDE_CLK_TO_MIMO); + +#if 0 + // make bit 15 of Tx gpio's be a s/w output + hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); + hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + +#if 1 + output_regs->debug_mux_ctrl = 1; + hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); + hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + + // initialize double buffering state machine for ethernet -> serdes + + dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, + &dsp_tx_recv_args, &dsp_tx_send_args, + eth_pkt_inspector); + + + // initialize double buffering state machine for serdes -> ethernet + + if (FW_SETS_SEQNO){ + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + fw_sets_seqno_inspector); + } + else { + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + dbsm_nop_inspector); + } + + // tell app_common that this dbsm could be sending to the ethernet + ac_could_be_sending_to_eth = &dsp_rx_sm; + + + // kick off the state machines + dbsm_start(&dsp_tx_sm); + dbsm_start(&dsp_rx_sm); + + //int which = 0; + + while(1){ + // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); + // which ^= 0x8000; + + buffer_irq_handler(0); + } +} diff --git a/firmware/microblaze/apps/factory_test.c b/firmware/microblaze/apps/factory_test.c new file mode 100644 index 000000000..e1fbb0e40 --- /dev/null +++ b/firmware/microblaze/apps/factory_test.c @@ -0,0 +1,438 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <clocks.h> +#include "sd.h" + +#define FW_SETS_SEQNO 1 // define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno; // used when f/w is filling in sequence numbers +#endif + + +/* + * Full duplex Tx and Rx between ethernet and DSP pipelines + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx eth flow + */ +//#define CPU_RX_BUF 0 // eth -> cpu + +#define DSP_RX_BUF_0 2 // dsp rx -> eth (double buffer) +#define DSP_RX_BUF_1 3 // dsp rx -> eth +#define DSP_TX_BUF_0 4 // eth -> dsp tx (double buffer) +#define DSP_TX_BUF_1 5 // eth -> dsp tx + +/* + * ================================================================ + * configure DSP TX double buffering state machine (eth -> dsp) + * ================================================================ + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4) + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { + PORT_ETH, + 0, + BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { + PORT_DSP, + DSP_TX_FIRST_LINE, // starts just past transport header + 0 // filled in from last_line register +}; + +dbsm_t dsp_tx_sm; // the state machine + +/* + * ================================================================ + * configure DSP RX double buffering state machine (dsp -> eth) + * ================================================================ + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4 + 1) + +// receive from DSP +buf_cmd_args_t dsp_rx_recv_args = { + PORT_DSP, + DSP_RX_FIRST_LINE, + BP_LAST_LINE +}; + +// send to ETH +buf_cmd_args_t dsp_rx_send_args = { + PORT_ETH, + 0, // starts with ethernet header in line 0 + 0, // filled in from list_line register +}; + +dbsm_t dsp_rx_sm; // the state machine + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +// variables for streaming mode + +static bool streaming_p = false; +static unsigned int streaming_items_per_frame = 0; +static int streaming_frame_count = 0; +#define FRAMES_PER_CMD 1000 + +bool is_streaming(void){ return streaming_p; } + +// ---------------------------------------------------------------- + + +void +restart_streaming(void) +{ + // setup RX DSP regs + dsp_rx_regs->clear_state = 1; // reset + + streaming_p = true; + streaming_frame_count = FRAMES_PER_CMD; + + dsp_rx_regs->rx_command = + MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, + streaming_items_per_frame, + 1, 1); // set "chain" bit + + // kick off the state machine + dbsm_start(&dsp_rx_sm); + + dsp_rx_regs->rx_time = 0; // enqueue first of two commands + + // make sure this one and the rest have the "now" and "chain" bits set. + dsp_rx_regs->rx_command = + MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, + streaming_items_per_frame, + 1, 1); + + dsp_rx_regs->rx_time = 0; // enqueue second command +} + +void +start_rx_streaming_cmd(const eth_mac_addr_t *host, op_start_rx_streaming_t *p) +{ + host_mac_addr = *host; // remember who we're sending to + + /* + * Construct ethernet header and word0 and preload into two buffers + */ + u2_eth_packet_t pkt; + memset(&pkt, 0, sizeof(pkt)); + pkt.ehdr.dst = *host; + pkt.ehdr.src = *ethernet_mac_addr(); + pkt.ehdr.ethertype = U2_ETHERTYPE; + u2p_set_word0(&pkt.fixed, 0, 0); + // DSP RX will fill in timestamp + + memcpy_wa(buffer_ram(DSP_RX_BUF_0), &pkt, sizeof(pkt)); + memcpy_wa(buffer_ram(DSP_RX_BUF_1), &pkt, sizeof(pkt)); + + + if (FW_SETS_SEQNO) + fw_seqno = 0; + + streaming_items_per_frame = p->items_per_frame; + restart_streaming(); +} + + +void +stop_rx_cmd(void) +{ + streaming_p = false; + dsp_rx_regs->clear_state = 1; // flush cmd queue + bp_clear_buf(DSP_RX_BUF_0); + bp_clear_buf(DSP_RX_BUF_1); +} + + +static void +setup_tx() +{ + dsp_tx_regs->clear_state = 1; + bp_clear_buf(DSP_TX_BUF_0); + bp_clear_buf(DSP_TX_BUF_1); + + int tx_scale = 256; + int interp = 32; + + // setup some defaults + + dsp_tx_regs->freq = 0; + dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; + dsp_tx_regs->interp_rate = interp; +} + + +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY. This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet. A bit of a kludge + * but it should work. + */ +bool +fw_sets_seqno_inspector(dbsm_t *sm, int buf_this) // returns false +{ + uint32_t *p = buffer_ram(buf_this); + uint32_t seqno = fw_seqno++; + + // KLUDGE all kinds of nasty magic numbers and embedded knowledge + uint32_t t = p[4]; + t = (t & 0xffff00ff) | ((seqno & 0xff) << 8); + p[4] = t; + + // queue up another rx command when required + if (streaming_p && --streaming_frame_count == 0){ + streaming_frame_count = FRAMES_PER_CMD; + dsp_rx_regs->rx_time = 0; + } + + return false; // we didn't handle the packet +} +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ + uint32_t status = buffer_pool_status->status; + + dbsm_process_status(&dsp_tx_sm, status); + dbsm_process_status(&dsp_rx_sm, status); +} + +int test_ram() +{ + int i,j,k; + output_regs->ram_page = 1<<10; + + extram[0] = 0xDEADBEEF; + extram[1] = 0xF00D1234; + extram[7] = 0x76543210; + + output_regs->ram_page = 2<<10; + extram[7] = 0x55555555; + extram[1] = 0xaaaaaaaa; + extram[0] = 0xeeeeeeee; + + output_regs->ram_page = 1<<10; + + i = extram[0]; + k = extram[1]; + j = extram[7]; + + if((i != 0xDEADBEEF)||(j!=0x76543210)||(k!=0xF00D1234)) { + puts("RAM FAIL1!\n"); + puthex32_nl(i); + puthex32_nl(j); + puthex32_nl(k); + return 0; + } + + output_regs->ram_page = 2<<10; + + j = extram[7]; + k = extram[1]; + i = extram[0]; + + if((i != 0xeeeeeeee)||(j!=0x55555555)||(k!=0xaaaaaaaa)) { + puts("RAM FAIL2!\n"); + puthex32_nl(i); + puthex32_nl(j); + puthex32_nl(k); + return 0; + } + return 1; +} + +int test_sd() +{ + int i = sd_init(); + if(i==0) { + puts("FAILED INIT of Card\n"); + return 0; + } + + unsigned char buf[512]; + i = sd_read_block(2048,buf); + if(i == 0) { + puts("READ Command Rejected\n"); + return 0; + } + if((buf[0]==0xb8)&&(buf[1]==0x08)&&(buf[2]==0x00)&&(buf[3]==0x50)) + ; + else { + puts("Read bad data from SD Card\n"); + return 0; + } + return 1; +} + +int +main(void) +{ + u2_init(); + + putstr("\nFactory Test\n"); + + print_mac_addr(ethernet_mac_addr()->addr); + newline(); + + if(test_sd()) + puts("SD OK\n"); + else { + puts("SD FAIL\n"); + // hal_finish(); + //return 0; + } + if(test_ram()) + puts("RAM OK\n"); + else { + puts("RAM FAIL\n"); + hal_finish(); + return 0; + } + + print_mac_addr(ethernet_mac_addr()->addr); + newline(); + + output_regs->led_src = 0x7; // make bottom 3 controlled by HW + + ethernet_register_link_changed_callback(link_changed_callback); + ethernet_init(); + + clocks_enable_tx_dboard(true,1); + clocks_mimo_config(MC_WE_LOCK_TO_SMA); +#if 0 + // make bit 15 of Tx gpio's be a s/w output + hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); + hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + + output_regs->debug_mux_ctrl = 1; +#if 0 + hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); + hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + + // initialize double buffering state machine for ethernet -> DSP Tx + + dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, + &dsp_tx_recv_args, &dsp_tx_send_args, + eth_pkt_inspector); + + + // initialize double buffering state machine for DSP RX -> Ethernet + + if (FW_SETS_SEQNO){ + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + fw_sets_seqno_inspector); + } + else { + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + dbsm_nop_inspector); + } + + // tell app_common that this dbsm could be sending to the ethernet + ac_could_be_sending_to_eth = &dsp_rx_sm; + + + // program tx registers + setup_tx(); + + // kick off the state machine + dbsm_start(&dsp_tx_sm); + + //int which = 0; + + while(1){ + // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); + // which ^= 0x8000; + + buffer_irq_handler(0); + + int pending = pic_regs->pending; // poll for under or overrun + + if (pending & PIC_UNDERRUN_INT){ + dbsm_handle_tx_underrun(&dsp_tx_sm); + pic_regs->pending = PIC_UNDERRUN_INT; // clear interrupt + putchar('U'); + } + + if (pending & PIC_OVERRUN_INT){ + dbsm_handle_rx_overrun(&dsp_rx_sm); + pic_regs->pending = PIC_OVERRUN_INT; // clear pending interrupt + + // FIXME Figure out how to handle this robustly. + // Any buffers that are emptying should be allowed to drain... + + if (streaming_p){ + // restart_streaming(); + // FIXME report error + } + else { + // FIXME report error + } + putchar('O'); + } + } +} diff --git a/firmware/microblaze/apps/gen_eth_packets.c b/firmware/microblaze/apps/gen_eth_packets.c new file mode 100644 index 000000000..4d521f6bf --- /dev/null +++ b/firmware/microblaze/apps/gen_eth_packets.c @@ -0,0 +1,187 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "memcpy_wa.h" +#include "print_rmon_regs.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +// ---------------------------------------------------------------- + +static eth_mac_addr_t dst_mac_addr = + {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}; + +// ---------------------------------------------------------------- + +// #define PACKET_SIZE 1500 // bytes +// #define ETH_DATA_RATE 1000000 // 1MB/s +// #define ETH_PACKET_RATE (ETH_DATA_RATE/PACKET_SIZE) // 13,3333 pkts/s + +// static int timer_delta = MASTER_CLK_RATE/ETH_PACKET_RATE; // ticks between interrupts + +static int timer_delta = (int)(MASTER_CLK_RATE * 1e-3); // tick at 1 kHz +static int sim_timer_delta = (int)(MASTER_CLK_RATE * 100e-6); // tick at 10 kHz + +static volatile bool send_packet_now = false; // timer handler sets this +static volatile bool link_is_up = false; // eth handler sets this + +int packet_number = 0; + + +#define CPU_TX_BUF 0 // cpu xmits ethernet frames from here +#define CPU_RX_BUF 1 // receive ethernet frames here + +// ---------------------------------------------------------------- + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ + link_is_up = speed == 0 ? false : true; + hal_set_leds(link_is_up ? 0x2 : 0x0, 0x2); + printf("\neth link changed: speed = %d\n", speed); +} + +void +timer_irq_handler(unsigned irq) +{ + hal_set_timeout(timer_delta); // schedule next timeout + send_packet_now = 1; +} + + +static void +init_packet(int *buf, const u2_eth_packet_t *pkt, int bufnum) +{ + int i = 0; + int mark = ((bufnum & 0xff) << 24) | 0x005A0000; + + for (i = 0; i < BP_NLINES; i++){ + buf[i] = mark | i; + mark ^= 0x00FF0000; + } + + // copy header into buffer + memcpy_wa(buf, pkt, sizeof(*pkt)); +} + +static void +init_packets(void) +{ + u2_eth_packet_t pkt __attribute__((aligned (4))); + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ehdr.dst = dst_mac_addr; + pkt.ehdr.src = *ethernet_mac_addr(); + pkt.ehdr.ethertype = U2_ETHERTYPE; + pkt.fixed.word0 = 0x01234567; + pkt.fixed.timestamp = 0xffffffff; + + // init just the one we're using + init_packet((void *)buffer_ram(CPU_TX_BUF), &pkt, CPU_TX_BUF); +} + +int +main(void) +{ + int npackets_sent = 0; + + u2_init(); + + // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output + //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + + putstr("\ngen_eth_packets\n"); + + hal_set_leds(0x0, 0x3); + + init_packets(); + + pic_register_handler(IRQ_TIMER, timer_irq_handler); + + if (hwconfig_simulation_p()) + timer_delta = sim_timer_delta; + + hal_set_timeout(timer_delta); + + ethernet_register_link_changed_callback(link_changed_callback); + ethernet_init(); + + /* + if (hwconfig_simulation_p()){ + eth_mac->speed = 4; // hardcode mac speed to 1000 + link_is_up = true; + } + */ + + // fire off a receive from the ethernet + bp_receive_to_buf(CPU_RX_BUF, PORT_ETH, 1, 0, BP_LAST_LINE); + + while(1){ + uint32_t status = buffer_pool_status->status; + + if (status & (BPS_DONE(CPU_RX_BUF) | BPS_ERROR(CPU_RX_BUF))){ + bp_clear_buf(CPU_RX_BUF); + // ignore incoming ethernet packets; they were looped back in sim + bp_receive_to_buf(CPU_RX_BUF, PORT_ETH, 1, 0, BP_LAST_LINE); + } + + if (status & (BPS_DONE(CPU_TX_BUF) | BPS_ERROR(CPU_TX_BUF))){ + if (status & BPS_ERROR(CPU_TX_BUF)){ + putchar('E'); + } + bp_clear_buf(CPU_TX_BUF); + npackets_sent++; + if ((npackets_sent & 0xF) == 0){ // print after every 16 packets + //print_rmon_regs(); + putchar('.'); + } + } + + if (link_is_up && send_packet_now && (status & BPS_IDLE(CPU_TX_BUF))){ + send_packet_now = false; + + // kick off the next packet + // FIXME set packet number in packet + + bp_send_from_buf(CPU_TX_BUF, PORT_ETH, 1, 0, 255); // 1KB total + hal_toggle_leds(0x1); + } + } + + hal_finish(); + return 1; +} diff --git a/firmware/microblaze/apps/gen_pause_frames.c b/firmware/microblaze/apps/gen_pause_frames.c new file mode 100644 index 000000000..0f81dafff --- /dev/null +++ b/firmware/microblaze/apps/gen_pause_frames.c @@ -0,0 +1,207 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "u2_eth_packet.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> + + +// ---------------------------------------------------------------- + +unsigned char dst_mac_addr[6] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +// ---------------------------------------------------------------- + +// #define PACKET_SIZE 1500 // bytes +// #define ETH_DATA_RATE 1000000 // 1MB/s +// #define ETH_PACKET_RATE (ETH_DATA_RATE/PACKET_SIZE) // 13,3333 pkts/s + +// static int timer_delta = MASTER_CLK_RATE/ETH_PACKET_RATE; // ticks between interrupts + +static int timer_delta = MASTER_CLK_RATE/1000; // tick at 1kHz + +static volatile bool send_packet_now = false; // timer handler sets this +static volatile bool link_is_up = false; // eth handler sets this + +int packet_number = 0; + +// ---------------------------------------------------------------- + +// debugging output on tx pins +#define LS_MASK 0xE0000 +#define LS_1000 0x80000 +#define LS_100 0x40000 +#define LS_10 0x20000 + + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ + int v = 0; + switch(speed){ + case 10: + v = LS_10; + link_is_up = true; + break; + + case 100: + v = LS_100; + link_is_up = true; + break; + + case 1000: + v = LS_100; + link_is_up = true; + break; + + default: + v = 0; + link_is_up = false; + break; + } + + hal_gpio_set_tx(v, LS_MASK); /* set debug bits on d'board */ + + putstr("\neth link changed: speed = "); + puthex16_nl(speed); +} + +void +timer_irq_handler(unsigned irq) +{ + hal_set_timeout(timer_delta); // schedule next timeout + send_packet_now = 1; +} + + +void +buffer_irq_handler(unsigned irq) +{ + // FIXME +} + +static void +init_packet(int *buf, const u2_eth_packet_t *pkt, int bufnum) +{ + int i = 0; + int mark = ((bufnum & 0xff) << 24) | 0x005A0000; + + for (i = 0; i < BP_NLINES; i++){ + buf[i] = mark | i; + mark ^= 0x00FF0000; + } + + // copy header into buffer + memcpy_wa(buf, pkt, sizeof(*pkt)); +} + +static void +init_packets(void) +{ + int i; + + u2_eth_packet_t pkt __attribute__((aligned (4))); + + for (i = 0; i < 6; i++){ + pkt.ehdr.dst.addr[i] = dst_mac_addr[i]; + } + pkt.ehdr.src = *ethernet_mac_addr(); + pkt.ehdr.ethertype = U2_ETHERTYPE; + + // fill ALL buffers for debugging + for (i = 0; i < 8; i++) + init_packet((void *)buffer_ram(i), &pkt, i); +} + +static int led_counter = 0; + +int +main(void) +{ + int send_pause = 1; + + u2_init(); + + // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output + //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + + putstr("\ngen_eth_packets\n"); + + // Control LEDs + output_regs->leds = 0x00; + + init_packets(); + + // pic_register_handler(IRQ_BUFFER, buffer_irq_handler); // poll for now + pic_register_handler(IRQ_TIMER, timer_irq_handler); + hal_set_timeout(timer_delta); + + ethernet_register_link_changed_callback(link_changed_callback); + + ethernet_init(); + + eth_mac->pause_frame_send_en = 1; + eth_mac->pause_quanta_set = 16384 / 512; + + // eth_mac->speed = 4; // FIXME hardcode mac speed to 1000 + + while(1){ + if (link_is_up && send_packet_now){ + send_packet_now = false; + + + if (send_pause) + eth_mac->xon_cpu = 1; + else + eth_mac->xon_cpu = 0; + + send_pause ^= 1; + + // kick off the next packet + // FIXME set packet number in packet + +#if 0 + bp_send_from_buf(0, PORT_ETH, 1, 0, 255); // 1KB total + + while ((buffer_pool_status->status & (BPS_DONE_0|BPS_ERROR_0)) == 0) + ; + bp_clear_buf(0); +#endif + + output_regs->leds = ((++led_counter) & 0x1) | (link_is_up ? 0x2 : 0x0); + } + } + + hal_finish(); + return 1; +} diff --git a/firmware/microblaze/apps/hello.c b/firmware/microblaze/apps/hello.c new file mode 100644 index 000000000..bce843093 --- /dev/null +++ b/firmware/microblaze/apps/hello.c @@ -0,0 +1,30 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "u2_init.h" +#include "stdio.h" + +int +main(void) +{ + u2_init(); + + puts("Hello World"); + puts("Goodbye World"); + + return 0; +} diff --git a/firmware/microblaze/apps/ibs_rx_test.c b/firmware/microblaze/apps/ibs_rx_test.c new file mode 100644 index 000000000..bdc04747e --- /dev/null +++ b/firmware/microblaze/apps/ibs_rx_test.c @@ -0,0 +1,82 @@ +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "nonstdio.h" + +#define PORT 2 // ethernet = 2, serdes = 0 +int dsp_rx_buf, dsp_tx_buf, serdes_rx_buf, serdes_tx_buf; +int dsp_rx_idle, dsp_tx_idle, serdes_rx_idle, serdes_tx_idle; + +int buffer_state[4]; + +static void __attribute__((unused)) +wait_until_status_nonzero(void) +{ + while (buffer_pool_status->status == 0) + ; +} + +int +main(void) +{ + int i; + + u2_init(); + + output_regs->adc_ctrl = 0x0A; + + dsp_rx_regs->freq = 0; + dsp_rx_regs->scale_iq = (1 << 16) | 1; + dsp_rx_regs->decim_rate = 8; + + volatile unsigned int *buffer0 = buffer_ram(0); + volatile unsigned int *buffer1 = buffer_ram(1); + volatile unsigned int *buffer2 = buffer_ram(2); + + putstr("Starting RX\n"); + bp_clear_buf(0); + bp_receive_to_buf(0, 1, 1, 0, 99); + + dsp_rx_regs->rx_command = (50 << 9) | 100; // Numlines, lines per frame + dsp_rx_regs->rx_time = 0x2000; + + dsp_rx_regs->rx_command = (137 << 9) | 50; // Numlines, lines per frame + dsp_rx_regs->rx_time = 0x2200; + + while (buffer_pool_status->status == 0) + ; + bp_clear_buf(0); + bp_clear_buf(1); + bp_receive_to_buf(1, 1, 1, 0, 99); + while (buffer_pool_status->status == 0) + ; + bp_clear_buf(2); + bp_receive_to_buf(2, 1, 1, 0, 99); + while (buffer_pool_status->status == 0) + ; + + for(i=0;i<100;i++) { + puthex(i); + putstr(" "); + puthex_nl(buffer0[i]); + } + for(i=0;i<60;i++) { + puthex(i); + putstr(" "); + puthex_nl(buffer1[i]); + } + for(i=0;i<60;i++) { + puthex(i); + putstr(" "); + puthex_nl(buffer2[i]); + } + //while(timer_regs -> time < 0x6000) + // {} + + putstr("Done\n"); + hal_finish(); + + return 1; +} diff --git a/firmware/microblaze/apps/ibs_tx_test.c b/firmware/microblaze/apps/ibs_tx_test.c new file mode 100644 index 000000000..ff9446d92 --- /dev/null +++ b/firmware/microblaze/apps/ibs_tx_test.c @@ -0,0 +1,160 @@ +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "nonstdio.h" + +// Globals +#define EMPTY 0 +#define FILLING 1 +#define FULL 2 +#define EMPTYING 3 + +#define PORT 2 // ethernet = 2, serdes = 0 +int dsp_rx_buf, dsp_tx_buf, serdes_rx_buf, serdes_tx_buf; +int dsp_rx_idle, dsp_tx_idle, serdes_rx_idle, serdes_tx_idle; + +int buffer_state[4]; + +static void +wait_until_status_nonzero(void) +{ + while (buffer_pool_status->status == 0) + ; +} + +int +main(void) +{ + int i; + + u2_init(); + + dsp_tx_regs->freq = 0; + dsp_tx_regs->scale_iq = (1 << 16) | 1; + dsp_tx_regs->interp_rate = 8; + + // Write data to be sent into the first buffer + volatile unsigned int *buffer0 = buffer_ram(0); + volatile unsigned int *buffer1 = buffer_ram(1); + + + putstr("Starting to fill in RAM\n"); + for(i=0;i<512;i++) + buffer0[i] = i; + putstr("Filled in RAM\n"); + + buffer0[0] = 7; // start and end of buffer, send immediately + buffer0[1] = 0x0000; // start time + bp_clear_buf(0); + bp_send_from_buf(0, 1, 1, 0, 9); + while (buffer_pool_status->status == 0) + ; + while(timer_regs -> time < 0x6000) + {} + + buffer0[0] = 3; // start and end of buffer + buffer0[1] = 0x8000; // start time + bp_clear_buf(0); + bp_send_from_buf(0, 1, 1, 0, 9); + while (buffer_pool_status->status == 0) + ; + while(timer_regs -> time < 0x8400) + {} + + buffer0[0] = 3; // start and end of buffer + buffer0[1] = 0x8800; // start time + bp_clear_buf(0); + bp_send_from_buf(0, 1, 1, 0, 9); + while (buffer_pool_status->status == 0) + ; + while(timer_regs -> time < 0x9000) + {} + + buffer0[0] = 0x2; // not last + buffer0[1] = 0x9100; // start time + bp_clear_buf(0); + bp_send_from_buf(0, 1, 1, 0, 9); + while (buffer_pool_status->status == 0) + ; + buffer0[0] = 0x1; // last + buffer0[1] = 0x0000; // start time + bp_clear_buf(0); + bp_send_from_buf(0, 1, 1, 0, 9); + while (buffer_pool_status->status == 0) + ; + + + buffer0[0] = 0x3; // first and last + buffer0[1] = 0x8000; // Time in the past + bp_clear_buf(0); + bp_send_from_buf(0, 1, 1, 0, 9); + while (buffer_pool_status->status == 0) + ; + + /* + buffer0[0] = 0x2; // not last + buffer0[1] = 0x9600; // start time + bp_clear_buf(0); + bp_send_from_buf(0, 1, 1, 0, 9); + while (buffer_pool_status->status == 0) + ; + */ + + while(timer_regs -> time < 0xa000) + {} + + putstr("Done\n"); + + while(1) + {} + hal_finish(); + + // Send a bunch, let them pile up in FIFO + bp_send_from_buf(0, 2, 1, 21, 80); wait_until_status_nonzero(); + bp_clear_buf(0); + putstr("First add'l TX done\n"); + bp_send_from_buf(0, 2, 1, 81, 288); wait_until_status_nonzero(); + bp_clear_buf(0); + bp_send_from_buf(0, 2, 1, 289, 292); wait_until_status_nonzero(); + bp_clear_buf(0); + bp_send_from_buf(0, 2, 1, 293, 326); wait_until_status_nonzero(); + bp_clear_buf(0); + bp_send_from_buf(0, 2, 1, 327, 399); wait_until_status_nonzero(); + bp_clear_buf(0); + bp_send_from_buf(0, 2, 1, 400, 511); wait_until_status_nonzero(); + bp_clear_buf(0); + putstr("All add'l TX done\n"); + + bp_receive_to_buf(1, 2, 1, 21, 80); wait_until_status_nonzero(); + bp_clear_buf(1); + putstr("First add'l RX done\n"); + bp_receive_to_buf(1, 2, 1, 81, 288); wait_until_status_nonzero(); + bp_clear_buf(1); + bp_receive_to_buf(1, 2, 1, 289, 292); wait_until_status_nonzero(); + bp_clear_buf(1); + bp_receive_to_buf(1, 2, 1, 293, 326); wait_until_status_nonzero(); + bp_clear_buf(1); + bp_receive_to_buf(1, 2, 1, 327, 399); wait_until_status_nonzero(); + bp_clear_buf(1); + bp_receive_to_buf(1, 2, 1, 400, 511); wait_until_status_nonzero(); + bp_clear_buf(1); + putstr("All add'l RX done\n"); + + for(i=0;i<512;i++) + if(buffer0[i] != buffer1[i]) { + putstr("ERROR at location: "); + puthex_nl(i); + putstr("Value sent: "); + puthex_nl(buffer0[i]); + putstr("Value rcvd: "); + puthex_nl(buffer1[i]); + //break; + } + + putstr("Done Testing\n"); + + hal_finish(); + return 1; +} diff --git a/firmware/microblaze/apps/mimo_app_common_v2.c b/firmware/microblaze/apps/mimo_app_common_v2.c new file mode 100644 index 000000000..5dbecb0d0 --- /dev/null +++ b/firmware/microblaze/apps/mimo_app_common_v2.c @@ -0,0 +1,582 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mimo_app_common_v2.h" +#include "buffer_pool.h" +#include "memcpy_wa.h" +#include "ethernet.h" +#include "nonstdio.h" +#include "print_rmon_regs.h" +#include "db.h" +#include "db_base.h" +#include "clocks.h" +#include "u2_init.h" +#include <string.h> + +volatile bool link_is_up = false; // eth handler sets this +int cpu_tx_buf_dest_port = PORT_ETH; + +// If this is non-zero, this dbsm could be writing to the ethernet +dbsm_t *ac_could_be_sending_to_eth; + +static unsigned char exp_seqno __attribute__((unused)) = 0; + +void abort(void); + +static bool +burn_mac_addr(const op_burn_mac_addr_t *p) +{ + return ethernet_set_mac_addr(&p->addr); +} + +static bool +sync_to_pps(const op_generic_t *p) +{ + timesync_regs->sync_on_next_pps = 1; + putstr("SYNC to PPS\n"); + return true; +} + +static bool +config_mimo_cmd(const op_config_mimo_t *p) +{ + clocks_mimo_config(p->flags); + return true; +} + +void +set_reply_hdr(u2_eth_packet_t *reply_pkt, u2_eth_packet_t const *cmd_pkt) +{ + reply_pkt->ehdr.dst = cmd_pkt->ehdr.src; + reply_pkt->ehdr.src = *ethernet_mac_addr(); + reply_pkt->ehdr.ethertype = U2_ETHERTYPE; + reply_pkt->thdr.flags = 0; + reply_pkt->thdr.fifo_status = 0; // written by protocol engine + reply_pkt->thdr.seqno = 0; // written by protocol engine + reply_pkt->thdr.ack = 0; // written by protocol engine + u2p_set_word0(&reply_pkt->fixed, 0, CONTROL_CHAN); + reply_pkt->fixed.timestamp = timer_regs->time; +} + +static void +send_reply(unsigned char *reply, size_t reply_len) +{ + if (reply_len < 64) + reply_len = 64; + + // wait for buffer to become idle + hal_set_leds(0x4, 0x4); + while((buffer_pool_status->status & BPS_IDLE(CPU_TX_BUF)) == 0) + ; + hal_set_leds(0x0, 0x4); + + // copy reply into CPU_TX_BUF + memcpy_wa(buffer_ram(CPU_TX_BUF), reply, reply_len); + + // wait until nobody else is sending to the ethernet + if (ac_could_be_sending_to_eth){ + hal_set_leds(0x8, 0x8); + dbsm_wait_for_opening(ac_could_be_sending_to_eth); + hal_set_leds(0x0, 0x8); + } + + if (0){ + printf("sending_reply to port %d, len = %d\n", cpu_tx_buf_dest_port, (int)reply_len); + print_buffer(buffer_ram(CPU_TX_BUF), reply_len/4); + } + + // fire it off + bp_send_from_buf(CPU_TX_BUF, cpu_tx_buf_dest_port, 1, 0, reply_len/4); + + // wait for it to complete (not long, it's a small pkt) + while((buffer_pool_status->status & (BPS_DONE(CPU_TX_BUF) | BPS_ERROR(CPU_TX_BUF))) == 0) + ; + + bp_clear_buf(CPU_TX_BUF); +} + + +static size_t +op_id_cmd(const op_generic_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_id_reply_t *r = (op_id_reply_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) // no room + return 0; + + // Build reply subpacket + + r->opcode = OP_ID_REPLY; + r->len = sizeof(op_id_reply_t); + r->rid = p->rid; + r->addr = *ethernet_mac_addr(); + r->hw_rev = (u2_hw_rev_major << 8) | u2_hw_rev_minor; + // r->fpga_md5sum = ; // FIXME + // r->sw_md5sum = ; // FIXME + + return r->len; +} + + +static size_t +config_tx_v2_cmd(const op_config_tx_v2_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_config_tx_reply_v2_t *r = (op_config_tx_reply_v2_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + struct tune_result tune_result; + memset(&tune_result, 0, sizeof(tune_result)); + + bool ok = true; + +#if 0 + if (p->valid & CFGV_GAIN){ + ok &= db_set_gain(tx_dboard, p->gain); + } + + if (p->valid & CFGV_FREQ){ + bool was_streaming = is_streaming(); + if (was_streaming) + stop_rx_cmd(); + + u2_fxpt_freq_t f = u2_fxpt_freq_from_hilo(p->freq_hi, p->freq_lo); + bool tune_ok = db_tune(tx_dboard, f, &tune_result); + ok &= tune_ok; + print_tune_result("Tx", tune_ok, f, &tune_result); + + if (was_streaming) + restart_streaming(); + } + + if (p->valid & CFGV_INTERP_DECIM){ + int interp = p->interp; + int hb1 = 0; + int hb2 = 0; + + if (!(interp & 1)){ + hb2 = 1; + interp = interp >> 1; + } + + if (!(interp & 1)){ + hb1 = 1; + interp = interp >> 1; + } + + if (interp < MIN_CIC_INTERP || interp > MAX_CIC_INTERP) + ok = false; + else { + dsp_tx_regs->interp_rate = (hb1<<9) | (hb2<<8) | interp; + // printf("Interp: %d, register %d\n", p->interp, (hb1<<9) | (hb2<<8) | interp); + } + } + + if (p->valid & CFGV_SCALE_IQ){ + dsp_tx_regs->scale_iq = p->scale_iq; + } +#endif + + // Build reply subpacket + + r->opcode = OP_CONFIG_TX_REPLY_V2; + r->len = sizeof(*r); + r->rid = p->rid; + r->ok = ok; + r->inverted = tune_result.inverted; + r->baseband_freq_hi = u2_fxpt_freq_hi(tune_result.baseband_freq); + r->baseband_freq_lo = u2_fxpt_freq_lo(tune_result.baseband_freq); + r->duc_freq_hi = u2_fxpt_freq_hi(tune_result.dxc_freq); + r->duc_freq_lo = u2_fxpt_freq_lo(tune_result.dxc_freq); + r->residual_freq_hi = u2_fxpt_freq_hi(tune_result.residual_freq); + r->residual_freq_lo = u2_fxpt_freq_lo(tune_result.residual_freq); + return r->len; +} + +static size_t +config_rx_v2_cmd(const op_config_rx_v2_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_config_rx_reply_v2_t *r = (op_config_rx_reply_v2_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + struct tune_result tune_result; + memset(&tune_result, 0, sizeof(tune_result)); + + bool ok = true; + + if (p->valid & CFGV_GAIN){ + ok &= db_set_gain(rx_dboard, p->gain); + } + + if (p->valid & CFGV_FREQ){ + bool was_streaming = is_streaming(); + if (was_streaming) + stop_rx_cmd(); + + u2_fxpt_freq_t f = u2_fxpt_freq_from_hilo(p->freq_hi, p->freq_lo); + bool tune_ok = db_tune(rx_dboard, f, &tune_result); + ok &= tune_ok; + print_tune_result("Rx", tune_ok, f, &tune_result); + + if (was_streaming) + restart_streaming(); + } + + if (p->valid & CFGV_INTERP_DECIM){ + int decim = p->decim; + int hb1 = 0; + int hb2 = 0; + + if(!(decim & 1)) { + hb2 = 1; + decim = decim >> 1; + } + + if(!(decim & 1)) { + hb1 = 1; + decim = decim >> 1; + } + + if (decim < MIN_CIC_DECIM || decim > MAX_CIC_DECIM) + ok = false; + else { + dsp_rx_regs->decim_rate = (hb1<<9) | (hb2<<8) | decim; + // printf("Decim: %d, register %d\n", p->decim, (hb1<<9) | (hb2<<8) | decim); + } + } + + if (p->valid & CFGV_SCALE_IQ){ + dsp_rx_regs->scale_iq = p->scale_iq; + } + + // Build reply subpacket + + r->opcode = OP_CONFIG_RX_REPLY_V2; + r->len = sizeof(*r); + r->rid = p->rid; + r->ok = ok; + r->inverted = tune_result.inverted; + r->baseband_freq_hi = u2_fxpt_freq_hi(tune_result.baseband_freq); + r->baseband_freq_lo = u2_fxpt_freq_lo(tune_result.baseband_freq); + r->ddc_freq_hi = u2_fxpt_freq_hi(tune_result.dxc_freq); + r->ddc_freq_lo = u2_fxpt_freq_lo(tune_result.dxc_freq); + r->residual_freq_hi = u2_fxpt_freq_hi(tune_result.residual_freq); + r->residual_freq_lo = u2_fxpt_freq_lo(tune_result.residual_freq); + + return r->len; +} + +static size_t +read_time_cmd(const op_generic_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_read_time_reply_t *r = (op_read_time_reply_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + r->opcode = OP_READ_TIME_REPLY; + r->len = sizeof(*r); + r->rid = p->rid; + r->time = timer_regs->time; + + return r->len; +} + +static void +fill_db_info(u2_db_info_t *p, const struct db_base *db) +{ + p->dbid = db->dbid; + p->freq_min_hi = u2_fxpt_freq_hi(db->freq_min); + p->freq_min_lo = u2_fxpt_freq_lo(db->freq_min); + p->freq_max_hi = u2_fxpt_freq_hi(db->freq_max); + p->freq_max_lo = u2_fxpt_freq_lo(db->freq_max); + p->gain_min = db->gain_min; + p->gain_max = db->gain_max; + p->gain_step_size = db->gain_step_size; +} + +static size_t +dboard_info_cmd(const op_generic_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_dboard_info_reply_t *r = (op_dboard_info_reply_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + r->opcode = OP_DBOARD_INFO_REPLY; + r->len = sizeof(*r); + r->rid = p->rid; + r->ok = true; + + fill_db_info(&r->tx_db_info, tx_dboard); + fill_db_info(&r->rx_db_info, rx_dboard); + + return r->len; +} + +static size_t +peek_cmd(const op_peek_t *p, + void *reply_payload, size_t reply_payload_space) +{ + op_generic_t *r = (op_generic_t *) reply_payload; + + putstr("peek: addr="); puthex32(p->addr); + printf(" bytes=%u\n", p->bytes); + + if ((reply_payload_space < (sizeof(*r) + p->bytes)) || + p->bytes > MAX_SUBPKT_LEN - sizeof(op_generic_t)) { + putstr("peek: insufficient reply packet space\n"); + return 0; // FIXME do partial read? + } + + r->opcode = OP_PEEK_REPLY; + r->len = sizeof(*r)+p->bytes; + r->rid = p->rid; + r->ok = true; + + memcpy_wa(reply_payload+sizeof(*r), (void *)p->addr, p->bytes); + + return r->len; +} + +static bool +poke_cmd(const op_poke_t *p) +{ + int bytes = p->len - sizeof(*p); + putstr("poke: addr="); puthex32(p->addr); + printf(" bytes=%u\n", bytes); + + uint8_t *src = (uint8_t *)p + sizeof(*p); + memcpy_wa((void *)p->addr, src, bytes); + + return true; +} + +static size_t +generic_reply(const op_generic_t *p, + void *reply_payload, size_t reply_payload_space, + bool ok) +{ + op_generic_t *r = (op_generic_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + r->opcode = p->opcode | OP_REPLY_BIT; + r->len = sizeof(*r); + r->rid = p->rid; + r->ok = ok; + + return r->len; +} + +static size_t +add_eop(void *reply_payload, size_t reply_payload_space) +{ + op_generic_t *r = (op_generic_t *) reply_payload; + if (reply_payload_space < sizeof(*r)) + return 0; // no room + + r->opcode = OP_EOP; + r->len = sizeof(*r); + r->rid = 0; + r->ok = 0; + + return r->len; +} + +void +handle_control_chan_frame(u2_eth_packet_t *pkt, size_t len) +{ + unsigned char reply[sizeof(u2_eth_packet_t) + 4 * sizeof(u2_subpkt_t)] _AL4; + unsigned char *reply_payload = &reply[sizeof(u2_eth_packet_t)]; + int reply_payload_space = sizeof(reply) - sizeof(u2_eth_packet_t); + + // initialize reply + memset(reply, 0, sizeof(reply)); + set_reply_hdr((u2_eth_packet_t *) reply, pkt); + + // point to beginning of payload (subpackets) + unsigned char *payload = ((unsigned char *) pkt) + sizeof(u2_eth_packet_t); + int payload_len = len - sizeof(u2_eth_packet_t); + + size_t subpktlen = 0; + + while (payload_len >= sizeof(op_generic_t)){ + const op_generic_t *gp = (const op_generic_t *) payload; + subpktlen = 0; + + // printf("\nopcode = %d\n", gp->opcode); + + switch(gp->opcode){ + case OP_EOP: // end of subpackets + goto end_of_subpackets; + + case OP_ID: + subpktlen = op_id_cmd(gp, reply_payload, reply_payload_space); + break; + + case OP_CONFIG_TX_V2: + subpktlen = config_tx_v2_cmd((op_config_tx_v2_t *) payload, + reply_payload, reply_payload_space); + break; + + case OP_CONFIG_RX_V2: + subpktlen = config_rx_v2_cmd((op_config_rx_v2_t *) payload, + reply_payload, reply_payload_space); + break; + + case OP_START_RX_STREAMING: + start_rx_streaming_cmd(&pkt->ehdr.src, (op_start_rx_streaming_t *) payload); + subpktlen = generic_reply(gp, reply_payload, reply_payload_space, true); + break; + + case OP_STOP_RX: + stop_rx_cmd(); + subpktlen = generic_reply(gp, reply_payload, reply_payload_space, true); + break; + + case OP_BURN_MAC_ADDR: + subpktlen = generic_reply(gp, reply_payload, reply_payload_space, + burn_mac_addr((op_burn_mac_addr_t *) payload)); + break; + + case OP_CONFIG_MIMO: + subpktlen = generic_reply(gp, reply_payload, reply_payload_space, + config_mimo_cmd((op_config_mimo_t *) payload)); + break; + + case OP_READ_TIME: + subpktlen = read_time_cmd(gp, reply_payload, reply_payload_space); + break; + + case OP_DBOARD_INFO: + subpktlen = dboard_info_cmd(gp, reply_payload, reply_payload_space); + break; + + case OP_SYNC_TO_PPS: + subpktlen = generic_reply(gp, reply_payload, reply_payload_space, + sync_to_pps((op_generic_t *) payload)); + break; + + case OP_PEEK: + subpktlen = peek_cmd((op_peek_t *)payload, reply_payload, reply_payload_space); + break; + + case OP_POKE: + subpktlen = generic_reply(gp, reply_payload, reply_payload_space, + poke_cmd((op_poke_t *)payload)); + break; + + default: + printf("app_common_v2: unhandled opcode = %d\n", gp->opcode); + break; + } + + int t = (gp->len + 3) & ~3; // bump to a multiple of 4 + payload += t; + payload_len -= t; + + subpktlen = (subpktlen + 3) & ~3; // bump to a multiple of 4 + reply_payload += subpktlen; + reply_payload_space -= subpktlen; + } + + end_of_subpackets: + + // add the EOP marker + subpktlen = add_eop(reply_payload, reply_payload_space); + subpktlen = (subpktlen + 3) & ~3; // bump to a multiple of 4 + reply_payload += subpktlen; + reply_payload_space -= subpktlen; + + send_reply(reply, reply_payload - reply); +} + + +/* + * Called when an ethernet packet is received. + * Return true if we handled it here, otherwise + * it'll be passed on to the DSP Tx pipe + */ +int +eth_pkt_inspector(bsm12_t *sm, int bufno) +{ + u2_eth_packet_t *pkt = (u2_eth_packet_t *) buffer_ram(bufno); + size_t byte_len = (buffer_pool_status->last_line[bufno] - 3) * 4; + + //static size_t last_len = 0; + + // hal_toggle_leds(0x1); + + // inspect rcvd frame and figure out what do do. + + if (pkt->ehdr.ethertype != U2_ETHERTYPE) + return true; // ignore, probably bogus PAUSE frame from MAC + + int chan = u2p_chan(&pkt->fixed); + + switch (chan){ + case CONTROL_CHAN: + handle_control_chan_frame(pkt, byte_len); + return -1; + break; + + case 0: + return 0; // pass it off to DSP TX + + case 1: + return 1; // pass it off to SERDES TX + + default: + abort(); + break; + } +} + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ + link_is_up = speed != 0; + hal_set_leds(link_is_up ? LED_RJ45 : 0x0, LED_RJ45); + printf("\neth link changed: speed = %d\n", speed); +} + + +void +print_tune_result(char *msg, bool tune_ok, + u2_fxpt_freq_t target_freq, struct tune_result *r) +{ +#if 0 + printf("db_tune %s %s\n", msg, tune_ok ? "true" : "false"); + putstr(" target_freq "); print_fxpt_freq(target_freq); newline(); + putstr(" baseband_freq "); print_fxpt_freq(r->baseband_freq); newline(); + putstr(" dxc_freq "); print_fxpt_freq(r->dxc_freq); newline(); + putstr(" residual_freq "); print_fxpt_freq(r->residual_freq); newline(); + printf(" inverted %s\n", r->inverted ? "true" : "false"); +#endif +} diff --git a/firmware/microblaze/apps/mimo_app_common_v2.h b/firmware/microblaze/apps/mimo_app_common_v2.h new file mode 100644 index 000000000..1e62ced37 --- /dev/null +++ b/firmware/microblaze/apps/mimo_app_common_v2.h @@ -0,0 +1,63 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_APP_COMMON_H +#define INCLUDED_APP_COMMON_H + +#include <stdbool.h> +#include "usrp2_eth_packet.h" +#include "bsm12.h" +#include "memory_map.h" +#include "hal_io.h" +#include <stddef.h> +#include <db.h> + +#define CPU_TX_BUF 7 // cpu -> eth + +#define _AL4 __attribute__((aligned (4))) + +extern volatile bool link_is_up; // eth handler sets this + +// If there's a dbsm that sends to the ethernet, put it's address here +extern dbsm_t *ac_could_be_sending_to_eth; + +extern int cpu_tx_buf_dest_port; + +void set_reply_hdr(u2_eth_packet_t *reply_pkt, u2_eth_packet_t const *cmd_pkt); + +/* + * Called when an ethernet packet is received. + */ +int eth_pkt_inspector(bsm12_t *sm, int bufno); + + +void link_changed_callback(int speed); + +void +print_tune_result(char *msg, bool tune_ok, + u2_fxpt_freq_t target_freq, struct tune_result *r); + + +void start_rx_streaming_cmd(const eth_mac_addr_t *host, op_start_rx_streaming_t *p); +void stop_rx_cmd(void); +void restart_streaming(void); +bool is_streaming(void); + +void handle_control_chan_frame(u2_eth_packet_t *pkt, size_t len); + +#endif /* INCLUDED_APP_COMMON_H */ diff --git a/firmware/microblaze/apps/mimo_tx.c b/firmware/microblaze/apps/mimo_tx.c new file mode 100644 index 000000000..e0f8aa6fa --- /dev/null +++ b/firmware/microblaze/apps/mimo_tx.c @@ -0,0 +1,363 @@ +/* + * Copyright 2007,2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * This is a down and dirty test program that confirms that the we can + * coherently transmit different signals to two USRP2s connected via a + * mimo cable. This code runs in the USRP2 connected to the ethernet. + * The other USRP runs mimo_tx_slave. The host runs test_mimo_tx. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "bsm12.h" +#include "mimo_app_common_v2.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include "clocks.h" + +#define FW_SETS_SEQNO 1 // define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno; // used when f/w is filling in sequence numbers +#endif + + +/* + * Experimental code to transmit packets to DSP Tx and SERDES + * + * Hard wire the Tx config so we don't have to deal with control stuff yet. + */ + +#define BUF_BSM12_0 4 +#define BUF_BSM12_1 5 +#define BUF_BSM12_2 6 + +//#define CPU_TX_BUF 7 // cpu -> eth + +// 4 lines of ethernet hdr + 1 line transport hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4) + +// Receive from ethernet +buf_cmd_args_t bsm12_recv_args = { + PORT_ETH, + 0, + BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t bsm12_send0_args = { + PORT_DSP, + DSP_TX_FIRST_LINE, // starts just past transport header + 0 // filled in from last_line register +}; + +// send to SERDES +buf_cmd_args_t bsm12_send1_args = { + PORT_SERDES, + 0, // starts just past transport header + 0 // filled in from last_line register +}; + +bsm12_t bsm12_sm; // the state machine + +#if 0 +/* + * ================================================================ + * configure DSP RX double buffering state machine (dsp -> eth) + * ================================================================ + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4 + 1) + +// receive from DSP +buf_cmd_args_t dsp_rx_recv_args = { + PORT_DSP, + DSP_RX_FIRST_LINE, + BP_LAST_LINE +}; + +// send to ETH +buf_cmd_args_t dsp_rx_send_args = { + PORT_ETH, + 0, // starts with ethernet header in line 0 + 0, // filled in from list_line register +}; + +dbsm_t dsp_rx_sm; // the state machine +#endif + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +// variables for streaming mode + +static bool streaming_p = false; +static unsigned int streaming_items_per_frame = 0; +static int streaming_frame_count = 0; +#define FRAMES_PER_CMD 1000 + +bool is_streaming(void){ return streaming_p; } + + +// ---------------------------------------------------------------- + + +void +restart_streaming(void) +{ +#if 0 + // setup RX DSP regs + dsp_rx_regs->clear_state = 1; // reset + + streaming_p = true; + streaming_frame_count = FRAMES_PER_CMD; + + dsp_rx_regs->rx_command = + MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, + streaming_items_per_frame, + 1, 1); // set "chain" bit + + // kick off the state machine + dbsm_start(&dsp_rx_sm); + + dsp_rx_regs->rx_time = 0; // enqueue first of two commands + + // make sure this one and the rest have the "now" and "chain" bits set. + dsp_rx_regs->rx_command = + MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, + streaming_items_per_frame, + 1, 1); + + dsp_rx_regs->rx_time = 0; // enqueue second command +#endif +} + +void +start_rx_streaming_cmd(const eth_mac_addr_t *host, op_start_rx_streaming_t *p) +{ +#if 0 + host_mac_addr = *host; // remember who we're sending to + + /* + * Construct ethernet header and word0 and preload into two buffers + */ + u2_eth_packet_t pkt; + memset(&pkt, 0, sizeof(pkt)); + pkt.ehdr.dst = *host; + pkt.ehdr.src = *ethernet_mac_addr(); + pkt.ehdr.ethertype = U2_ETHERTYPE; + u2p_set_word0(&pkt.fixed, 0, 0); + // DSP RX will fill in timestamp + + memcpy_wa(buffer_ram(DSP_RX_BUF_0), &pkt, sizeof(pkt)); + memcpy_wa(buffer_ram(DSP_RX_BUF_1), &pkt, sizeof(pkt)); + + + if (FW_SETS_SEQNO) + fw_seqno = 0; + + streaming_items_per_frame = p->items_per_frame; + restart_streaming(); +#endif +} + + +void +stop_rx_cmd(void) +{ +#if 0 + streaming_p = false; + dsp_rx_regs->clear_state = 1; // flush cmd queue + bp_clear_buf(DSP_RX_BUF_0); + bp_clear_buf(DSP_RX_BUF_1); +#endif +} + + +static void +setup_tx() +{ + dsp_tx_regs->clear_state = 1; + + int tx_scale = 2500; + int interp = 8; // * 4 + + // setup some defaults + + dsp_tx_regs->freq = 429496730; // 10MHz + dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; + dsp_tx_regs->interp_rate = (1 << 9) | (1 << 8) | interp; +} + + +#if 0 +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY. This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet. A bit of a kludge + * but it should work. + */ +int +fw_sets_seqno_inspector(bsm12_t *sm, int buf_this) +{ + uint32_t *p = buffer_ram(buf_this); + uint32_t seqno = fw_seqno++; + + // KLUDGE all kinds of nasty magic numbers and embedded knowledge + uint32_t t = p[4]; + t = (t & 0xffff00ff) | ((seqno & 0xff) << 8); + p[4] = t; + + // queue up another rx command when required + if (streaming_p && --streaming_frame_count == 0){ + streaming_frame_count = FRAMES_PER_CMD; + dsp_rx_regs->rx_time = 0; + } + + return false; // we didn't handle the packet +} +#endif +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ + uint32_t status = buffer_pool_status->status; + + bsm12_process_status(&bsm12_sm, status); +} + +int +main(void) +{ + u2_init(); + + putstr("\nMIMO Tx\n"); + print_mac_addr(ethernet_mac_addr()->addr); + newline(); + + ethernet_register_link_changed_callback(link_changed_callback); + ethernet_init(); + + clocks_mimo_config(MC_WE_DONT_LOCK | MC_PROVIDE_CLK_TO_MIMO); + +#if 0 + // make bit 15 of Tx gpio's be a s/w output + hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); + hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + + output_regs->debug_mux_ctrl = 1; +#if 0 + hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); + hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + + // initialize double buffering state machine for ethernet -> DSP Tx + + bsm12_init(&bsm12_sm, BUF_BSM12_0, + &bsm12_recv_args, &bsm12_send0_args, &bsm12_send1_args, + eth_pkt_inspector); + + +#if 0 + // initialize double buffering state machine for DSP RX -> Ethernet + + if (FW_SETS_SEQNO){ + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + fw_sets_seqno_inspector); + } + else { + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + dbsm_nop_inspector); + } + + // tell app_common that this dbsm could be sending to the ethernet + ac_could_be_sending_to_eth = &dsp_rx_sm; +#endif + + + // program tx registers + setup_tx(); + + // kick off the state machine + bsm12_start(&bsm12_sm); + + //int which = 0; + + while(1){ + // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); + // which ^= 0x8000; + + buffer_irq_handler(0); + + int pending = pic_regs->pending; // poll for under or overrun + + if (pending & PIC_UNDERRUN_INT){ + // dbsm_handle_tx_underrun(&dsp_tx_sm); + pic_regs->pending = PIC_UNDERRUN_INT; // clear interrupt + putchar('U'); + } + + if (pending & PIC_OVERRUN_INT){ + // dbsm_handle_rx_overrun(&dsp_rx_sm); + pic_regs->pending = PIC_OVERRUN_INT; // clear pending interrupt + + // FIXME Figure out how to handle this robustly. + // Any buffers that are emptying should be allowed to drain... + + if (streaming_p){ + // restart_streaming(); + // FIXME report error + } + else { + // FIXME report error + } + putchar('O'); + } + } +} diff --git a/firmware/microblaze/apps/mimo_tx_slave.c b/firmware/microblaze/apps/mimo_tx_slave.c new file mode 100644 index 000000000..cdf9c03c2 --- /dev/null +++ b/firmware/microblaze/apps/mimo_tx_slave.c @@ -0,0 +1,376 @@ +/* + * Copyright 2007,2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * This is a down and dirty test program that confirms that the we can + * coherently transmit different signals to two USRP2s connected via a + * mimo cable. This code runs in the USRP2 NOT connected to the + * ethernet. The USRP connected to the ethernet runs mimo_tx. The + * host runs test_mimo_tx. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include "clocks.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +#define FW_SETS_SEQNO 1 // define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno; // used when f/w is filling in sequence numbers +#endif + + +/* + * Full duplex Tx and Rx between serdes and DSP pipelines + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to serdes flow + * Buffers 4 and 5 are used to double-buffer the serdes to DSP Tx flow + */ +//#define CPU_RX_BUF 0 // eth -> cpu + +#define DSP_RX_BUF_0 2 // dsp rx -> serdes (double buffer) +#define DSP_RX_BUF_1 3 // dsp rx -> serdes +#define DSP_TX_BUF_0 4 // serdes -> dsp tx (double buffer) +#define DSP_TX_BUF_1 5 // serdes -> dsp tx + +/* + * ================================================================== + * configure DSP TX double buffering state machine (serdes -> dsp) + * ================================================================== + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4) + +// Receive from serdes +buf_cmd_args_t dsp_tx_recv_args = { + PORT_SERDES, + 0, + BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { + PORT_DSP, + DSP_TX_FIRST_LINE, // starts just past transport header + 0 // filled in from last_line register +}; + +dbsm_t dsp_tx_sm; // the state machine + +/* + * ================================================================= + * configure DSP RX double buffering state machine (dsp -> serdes) + * ================================================================= + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4 + 1) + +// receive from DSP +buf_cmd_args_t dsp_rx_recv_args = { + PORT_DSP, + DSP_RX_FIRST_LINE, + BP_LAST_LINE +}; + +// send to serdes +buf_cmd_args_t dsp_rx_send_args = { + PORT_SERDES, + 0, // starts with ethernet header in line 0 + 0, // filled in from list_line register +}; + +dbsm_t dsp_rx_sm; // the state machine + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +// variables for streaming mode + +static bool streaming_p = false; +static unsigned int streaming_items_per_frame = 0; +static int streaming_frame_count = 0; +#define FRAMES_PER_CMD 1000 + +bool is_streaming(void){ return streaming_p; } + +// ---------------------------------------------------------------- + + +void +restart_streaming(void) +{ + // setup RX DSP regs + dsp_rx_regs->clear_state = 1; // reset + + streaming_p = true; + streaming_frame_count = FRAMES_PER_CMD; + + dsp_rx_regs->rx_command = + MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, + streaming_items_per_frame, + 1, 1); // set "chain" bit + + // kick off the state machine + dbsm_start(&dsp_rx_sm); + + dsp_rx_regs->rx_time = 0; // enqueue first of two commands + + // make sure this one and the rest have the "now" and "chain" bits set. + dsp_rx_regs->rx_command = + MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, + streaming_items_per_frame, + 1, 1); + + dsp_rx_regs->rx_time = 0; // enqueue second command +} + +void +start_rx_streaming_cmd(const eth_mac_addr_t *host, op_start_rx_streaming_t *p) +{ + host_mac_addr = *host; // remember who we're sending to + + /* + * Construct ethernet header and word0 and preload into two buffers + */ + u2_eth_packet_t pkt; + memset(&pkt, 0, sizeof(pkt)); + pkt.ehdr.dst = *host; + pkt.ehdr.src = *ethernet_mac_addr(); + pkt.ehdr.ethertype = U2_ETHERTYPE; + u2p_set_word0(&pkt.fixed, 0, 0); + // DSP RX will fill in timestamp + + memcpy_wa(buffer_ram(DSP_RX_BUF_0), &pkt, sizeof(pkt)); + memcpy_wa(buffer_ram(DSP_RX_BUF_1), &pkt, sizeof(pkt)); + + + if (FW_SETS_SEQNO) + fw_seqno = 0; + + streaming_items_per_frame = p->items_per_frame; + restart_streaming(); +} + + +void +stop_rx_cmd(void) +{ + streaming_p = false; + dsp_rx_regs->clear_state = 1; // flush cmd queue + bp_clear_buf(DSP_RX_BUF_0); + bp_clear_buf(DSP_RX_BUF_1); +} + + +static void +setup_tx() +{ + dsp_tx_regs->clear_state = 1; + bp_clear_buf(DSP_TX_BUF_0); + bp_clear_buf(DSP_TX_BUF_1); + + int tx_scale = 2500; + int interp = 8; // * 4 + + // setup some defaults + + dsp_tx_regs->freq = 429496730; // 10MHz + dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; + dsp_tx_regs->interp_rate = (1 << 9) | (1 << 8) | interp; +} + + +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY. This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet. A bit of a kludge + * but it should work. + */ +bool +fw_sets_seqno_inspector(dbsm_t *sm, int buf_this) // returns false +{ + uint32_t *p = buffer_ram(buf_this); + uint32_t seqno = fw_seqno++; + + // KLUDGE all kinds of nasty magic numbers and embedded knowledge + uint32_t t = p[4]; + t = (t & 0xffff00ff) | ((seqno & 0xff) << 8); + p[4] = t; + + // queue up another rx command when required + if (streaming_p && --streaming_frame_count == 0){ + streaming_frame_count = FRAMES_PER_CMD; + dsp_rx_regs->rx_time = 0; + } + + return false; // we didn't handle the packet +} +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ + // hal_toggle_leds(LED_A); + + uint32_t status = buffer_pool_status->status; + + if (0 && (status & ~BPS_IDLE_ALL)){ + putstr("status = "); + puthex32_nl(status); + } + + dbsm_process_status(&dsp_tx_sm, status); + dbsm_process_status(&dsp_rx_sm, status); +} + +int +main(void) +{ + u2_init(); + + output_regs->led_src = 0x3; // h/w controls bottom two bits + clocks_enable_test_clk(true, 1); + + putstr("\nMIMO Tx Slave\n"); + + cpu_tx_buf_dest_port = PORT_SERDES; + + // ethernet_register_link_changed_callback(link_changed_callback); + // ethernet_init(); + + clocks_mimo_config(MC_WE_LOCK_TO_MIMO); + + // puts("post clocks_mimo_config"); + +#if 0 + // make bit 15 of Tx gpio's be a s/w output + hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); + hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + +#if 0 + output_regs->debug_mux_ctrl = 1; + hal_gpio_set_sels(GPIO_TX_BANK, "0000000000000000"); + hal_gpio_set_sels(GPIO_RX_BANK, "0000000000000000"); + hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); + hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + + // initialize double buffering state machine for ethernet -> DSP Tx + + dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, + &dsp_tx_recv_args, &dsp_tx_send_args, + eth_pkt_inspector); + + + //output_regs->flush_icache = 1; + + // initialize double buffering state machine for DSP RX -> Ethernet + + if (FW_SETS_SEQNO){ + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + fw_sets_seqno_inspector); + } + else { + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + dbsm_nop_inspector); + } + + // puts("post dbsm_init's"); + + // tell app_common that this dbsm could be sending to the ethernet + ac_could_be_sending_to_eth = &dsp_rx_sm; + + + // program tx registers + setup_tx(); + + // puts("post setup_tx"); + + // kick off the state machine + dbsm_start(&dsp_tx_sm); + + // puts("post dbsm_start"); + + //int which = 0; + + while(1){ + // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); + // which ^= 0x8000; + + buffer_irq_handler(0); + + int pending = pic_regs->pending; // poll for under or overrun + + if (pending & PIC_UNDERRUN_INT){ + dbsm_handle_tx_underrun(&dsp_tx_sm); + pic_regs->pending = PIC_UNDERRUN_INT; // clear interrupt + putchar('U'); + } + + if (pending & PIC_OVERRUN_INT){ + dbsm_handle_rx_overrun(&dsp_rx_sm); + pic_regs->pending = PIC_OVERRUN_INT; // clear pending interrupt + + // FIXME Figure out how to handle this robustly. + // Any buffers that are emptying should be allowed to drain... + + if (streaming_p){ + // restart_streaming(); + // FIXME report error + } + else { + // FIXME report error + } + putchar('O'); + } + } +} diff --git a/firmware/microblaze/apps/rcv_eth_packets.c b/firmware/microblaze/apps/rcv_eth_packets.c new file mode 100644 index 000000000..03fc94354 --- /dev/null +++ b/firmware/microblaze/apps/rcv_eth_packets.c @@ -0,0 +1,233 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> + + +// ---------------------------------------------------------------- + +static eth_mac_addr_t dst_mac_addr = + {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}; + + +// ---------------------------------------------------------------- + +#define PACKET_SIZE 1500 // bytes +#define ETH_DATA_RATE 1000000 // 1MB/s +#define ETH_PACKET_RATE (ETH_DATA_RATE/PACKET_SIZE) // 13,3333 pkts/s + +#define TIMER_RATE 100000000 // 100 MHz clock + +static int timer_delta = TIMER_RATE/ETH_PACKET_RATE; // ticks between interrupts + +static volatile bool send_packet_now = false; // timer handler sets this +static volatile bool link_is_up = false; // eth handler sets this + +int packet_number = 0; + +// ---------------------------------------------------------------- + +// debugging output on tx pins +#define LS_MASK 0xE0000 +#define LS_1000 0x80000 +#define LS_100 0x40000 +#define LS_10 0x20000 + + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ + int v = 0; + switch(speed){ + case 10: + v = LS_10; + link_is_up = true; + break; + + case 100: + v = LS_100; + link_is_up = true; + break; + + case 1000: + v = LS_100; + link_is_up = true; + break; + + default: + v = 0; + link_is_up = false; + break; + } + + //hal_gpio_set_tx(v, LS_MASK); /* set debug bits on d'board */ + + putstr("\neth link changed: speed = "); + puthex16_nl(speed); +} + +void +timer_irq_handler(unsigned irq) +{ + hal_set_timeout(timer_delta); // schedule next timeout + send_packet_now = 1; +} + + +void +buffer_irq_handler(unsigned irq) +{ + // FIXME +} + +static void +init_packet(int *buf, const u2_eth_packet_t *pkt, int bufnum) +{ + int i = 0; + int mark = ((bufnum & 0xff) << 24) | 0x005A0000; + + for (i = 0; i < BP_NLINES; i++){ + buf[i] = mark | i; + mark ^= 0x00FF0000; + } + + // copy header into buffer + memcpy_wa(buf, pkt, sizeof(*pkt)); +} + +static void +init_packets(void) +{ + int i; + + u2_eth_packet_t pkt __attribute__((aligned (4))); + + pkt.ehdr.dst = dst_mac_addr; + pkt.ehdr.src = *ethernet_mac_addr(); + pkt.ehdr.ethertype = U2_ETHERTYPE; + + // fill ALL buffers for debugging + for (i = 0; i < 8; i++) + init_packet((void *)buffer_ram(i), &pkt, i); +} + +int +main(void) +{ + u2_init(); + + int prev_leds = -1; + int new_leds = 0x00; + output_regs->leds = 0x00; + + int peak_hold_count = 0; + + // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output + //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + + putstr("\nrcv_eth_packets\n"); + + init_packets(); + + // pic_register_handler(IRQ_BUFFER, buffer_irq_handler); // poll for now + + // FIXME turn off timer since I don't think MTS and MFS instructions are implemented + // pic_register_handler(IRQ_TIMER, timer_irq_handler); + // hal_set_timeout(timer_delta); + + ethernet_register_link_changed_callback(link_changed_callback); + + ethernet_init(); + + //eth_mac->speed = 4; // FIXME hardcode mac speed to 1000 + + // kick off a receive + bp_receive_to_buf(2, PORT_ETH, 1, 0, 511); + + while(1){ + // u2_eth_packet_t pkt; + + new_leds = 0; + if (link_is_up) + new_leds = 0x2; + + if ((buffer_pool_status->status & (BPS_DONE_2|BPS_ERROR_2)) != 0){ + // we've got a packet! + +#if 0 + // copy to stack buffer so we can byte address it + memcpy_wa(&pkt, (void *)buffer_ram(2), sizeof(pkt)); + + putstr("Rx: src: "); + print_mac_addr(pkt.ehdr.dst_addr); + putstr(" dst: "); + print_mac_addr(pkt.ehdr.src_addr); + putstr(" ethtype: "); + puthex16(pkt.ehdr.ethertype); + putstr(" len: "); + int len = (buffer_pool_status->last_line[2] + 1) * 4; + puthex16_nl(len); +#else + volatile int *bp = buffer_ram(2); + int i; + for (i = 0; i < 16; i++){ + puthex8(i); + putchar(':'); + puthex32_nl(bp[i]); + } +#endif + + // kick off next receive + bp_clear_buf(2); + bp_receive_to_buf(2, PORT_ETH, 1, 0, 511); + + peak_hold_count = 2048 * 10; + } + + if (peak_hold_count > 0){ + peak_hold_count--; + new_leds |= 0x1; + } + + if (new_leds != prev_leds){ + prev_leds = new_leds; + output_regs->leds = new_leds; + } + } + + hal_finish(); + return 1; +} diff --git a/firmware/microblaze/apps/read_dbids.c b/firmware/microblaze/apps/read_dbids.c new file mode 100644 index 000000000..24c6d9ab4 --- /dev/null +++ b/firmware/microblaze/apps/read_dbids.c @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <nonstdio.h> +#include <u2_init.h> +#include <stdbool.h> +#include <usrp2_i2c_addr.h> +#include <i2c.h> + + +int main(void) +{ + u2_init(); + + puts("\nread_dbids"); + + unsigned char dbid_tx[2]; + unsigned char dbid_rx[2]; + bool ok; + + ok = eeprom_read(I2C_ADDR_TX_A, 1, dbid_tx, 2); + if (!ok){ + puts("failed to read Tx Daugherboard EEPROM"); + } + else { + putstr("Tx Daugherboard ID: "); + puthex8(dbid_tx[1]); // MSB + puthex8(dbid_tx[0]); // LSB + newline(); + } + + ok = eeprom_read(I2C_ADDR_RX_A, 1, dbid_rx, 2); + if (!ok){ + puts("failed to read Rx Daugherboard EEPROM"); + } + else { + putstr("Rx Daugherboard ID: "); + puthex8(dbid_rx[1]); // MSB + puthex8(dbid_rx[0]); // LSB + newline(); + } + + return 0; +} diff --git a/firmware/microblaze/apps/sd_bounce.c b/firmware/microblaze/apps/sd_bounce.c new file mode 100644 index 000000000..c1b48f170 --- /dev/null +++ b/firmware/microblaze/apps/sd_bounce.c @@ -0,0 +1,153 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Loopback SERDES to SERDES + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "nonstdio.h" +#include "memset_wa.h" +#include <dbsm.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <clocks.h> + + + +// ---------------------------------------------------------------- + +#define SERDES_RX_BUF_0 0 +#define SERDES_RX_BUF_1 1 + +/* + * ================================================================ + * configure SD RX double buffering state machine + * ================================================================ + */ + +// receive from SERDES +buf_cmd_args_t sd_recv_args = { + PORT_SERDES, + 0, + BP_LAST_LINE +}; + +// send to SERDES +buf_cmd_args_t sd_send_args = { + PORT_SERDES, + 0, // starts with packet in line 0 + 0, // filled in from list_line register +}; + +dbsm_t sd_sm; // the state machine + + + + +// ---------------------------------------------------------------- + +#if 0 +static bool +check_packet(int *buf, int nlines) +{ + bool ok = true; + int i = 0; + for (i = 0; i < nlines; i++){ + int expected = ((2*i + 0) << 16) | (2*i+1); + if (buf[i] != expected){ + ok = false; + printf("buf[%d] = 0x%x expected = 0x%x\n", i, buf[i], expected); + } + } + return ok; +} + +static void +zero_buffer(int bufno) +{ + memset_wa(buffer_ram(bufno), 0, BP_NLINES * 4); +} +#endif + + +bool +sd_rx_inspector(dbsm_t *sm, int buf_this) +{ + hal_toggle_leds(0x2); + +#if 0 + int last_line = buffer_pool_status->last_line[buf_this]; + bool ok = check_packet(buffer_ram(buf_this), last_line); + static int good = 0; + static int bad = 0; + + if (ok) + good++; + else + bad++; + + if(good+bad == 10000) { + printf("Good %d\tBad %d\n",good,bad); + good = 0; + bad = 0; + } +#endif + + return false; +} + + +inline static void +buffer_irq_handler(void) +{ + uint32_t status = buffer_pool_status->status; + dbsm_process_status(&sd_sm, status); +} + + +int +main(void) +{ + u2_init(); + + putstr("\nsd_bounce\n"); + + // Get our clock from the mimo interface + clocks_mimo_config(MC_WE_LOCK_TO_MIMO); + + dbsm_init(&sd_sm, SERDES_RX_BUF_0, + &sd_recv_args, &sd_send_args, + sd_rx_inspector); + + // kick off the state machine + dbsm_start(&sd_sm); + + while(1){ + buffer_irq_handler(); + } +} diff --git a/firmware/microblaze/apps/sd_gentest.c b/firmware/microblaze/apps/sd_gentest.c new file mode 100644 index 000000000..35e912615 --- /dev/null +++ b/firmware/microblaze/apps/sd_gentest.c @@ -0,0 +1,269 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "nonstdio.h" +#include "memset_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <clocks.h> +#include <mdelay.h> + +// ---------------------------------------------------------------- + +int packet_number = 0; +volatile bool send_packet_now = 0; + +#define SERDES_TX_BUF 0 +#define SERDES_RX_BUF 1 + + +#define NLINES_PER_PKT 380 + + +// ---------------------------------------------------------------- + +//static int timer_delta = (int)(MASTER_CLK_RATE * 100e-6); +static int timer_delta = 1000000; // .01 second + +void +timer_irq_handler(unsigned irq) +{ + hal_set_timeout(timer_delta); // schedule next timeout + send_packet_now = true; +} + + +static void +init_packet(int *buf) +{ + int i = 0; + for (i = 0; i < BP_NLINES; i++){ + buf[i] = ((2*i + 0) << 16) | (2*i+1); + } +} + +static bool +check_packet(int *buf, int nlines) +{ + bool ok = true; + int i = 0; + for (i = 0; i < nlines; i++){ + int expected = ((2*i + 0) << 16) | (2*i+1); + if (buf[i] != expected){ + ok = false; + printf("buf[%d] = 0x%x expected = 0x%x\n", i, buf[i], expected); + } + } + return ok; +} + +static void +zero_buffer(int bufno) +{ + memset_wa(buffer_ram(bufno), 0, BP_NLINES * 4); +} + +static void +init_packets(void) +{ + // init just the one we're using + init_packet(buffer_ram(SERDES_TX_BUF)); +} + +int +main(void) +{ + u2_init(); + + // We're free running and provide clock to the MIMO interface + clocks_mimo_config(MC_WE_DONT_LOCK | MC_PROVIDE_CLK_TO_MIMO); + + + // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output + // output_regs->debug_mux_ctrl = 1; + // hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + // hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + + putstr("\nsd_gentest\n"); + + // Set up serdes (already enabled) + //output_regs->serdes_ctrl = (SERDES_ENABLE | SERDES_RXEN | SERDES_LOOPEN); + //output_regs->serdes_ctrl = (SERDES_ENABLE | SERDES_RXEN); + + init_packets(); + + // pic_register_handler(IRQ_TIMER, timer_irq_handler); + + //if (hwconfig_simulation_p()) + // timer_delta = sim_timer_delta; + + // start a receive from sd + zero_buffer(SERDES_RX_BUF); + bp_receive_to_buf(SERDES_RX_BUF, PORT_SERDES, 1, 0, BP_LAST_LINE); + + // fire off the first packet + bp_send_from_buf(SERDES_TX_BUF, PORT_SERDES, 1, 0, NLINES_PER_PKT); + hal_set_timeout(timer_delta); + int ready_to_send = 0; + + int counter __attribute__((unused)) = 0; + int sent = 1; + int txerr = 0; + int rxerr = 0; + int rcvd = 0; + int rxcrc = 0; + int sent_acc = 0; + int txerr_acc = 0; + int rxerr_acc = 0; + int rcvd_acc = 0; + int rxcrc_acc = 0; + +#define EXPECTING_PKT() ((counter & 0x1) == 0) +#define SEND_PKT() ((counter & 0x1) != 0) + + bool got_packet = false; + + while(1){ + uint32_t status = buffer_pool_status->status; + + if (status & (BPS_DONE(SERDES_RX_BUF))){ + bp_clear_buf(SERDES_RX_BUF); + got_packet = true; + + //hal_toggle_leds(0x2); + + // check packet + int last_line = buffer_pool_status->last_line[SERDES_RX_BUF]-1; + bool ok = check_packet(buffer_ram(SERDES_RX_BUF), last_line); + + if (ok) { + rcvd++; + //putchar('r'); + } + else { + rcvd++; + rxcrc++; + //putchar('P'); + } + // start a receive from sd + zero_buffer(SERDES_RX_BUF); + bp_receive_to_buf(SERDES_RX_BUF, PORT_SERDES, 1, 0, BP_LAST_LINE); + } + + if (status & (BPS_ERROR(SERDES_RX_BUF))){ + bp_clear_buf(SERDES_RX_BUF); + got_packet = true; + rcvd++; + rxerr++; + //putchar('E'); + + // start a receive from sd + zero_buffer(SERDES_RX_BUF); + bp_receive_to_buf(SERDES_RX_BUF, PORT_SERDES, 1, 0, BP_LAST_LINE); + } + + if (status & (BPS_DONE(SERDES_TX_BUF))){ + bp_clear_buf(SERDES_TX_BUF); + //putchar('t'); + bp_send_from_buf(SERDES_TX_BUF, PORT_SERDES, 1, 0, NLINES_PER_PKT); + //mdelay(1); + int i; + for (i = 0; i < 50; i++){ + asm volatile ("or r0, r0, r0\n\ + or r0, r0, r0\n \ + or r0, r0, r0\n \ + or r0, r0, r0\n \ + or r0, r0, r0\n \ + or r0, r0, r0\n \ + or r0, r0, r0\n"); + } + sent ++; + ready_to_send = 1; + //hal_toggle_leds(0x1); + } + + if (status & BPS_ERROR(SERDES_TX_BUF)){ + bp_clear_buf(SERDES_TX_BUF); + sent++; + txerr++; + ready_to_send = 1; + //putchar('X'); + } + + if(sent >=1000) { + printf("Status\tSENT %d\tTXERR %d\t",sent,txerr); + printf("RX %d\tERR %d\tCRC %d\tMISSED %d\n",rcvd, rxerr, rxcrc, sent-rcvd); + sent_acc += sent; sent = 0; + txerr_acc += txerr; txerr = 0; + rcvd_acc += rcvd; rcvd = 0; + rxerr_acc += rxerr; rxerr = 0; + rxcrc_acc += rxcrc; rxcrc = 0; + } + + if(sent_acc >=10000) { + printf("\nOverall\tSENT %d\tTXERR %d\t",sent_acc,txerr_acc); + printf("RX %d\tERR %d\tCRC %d\tMISSED %d\n\n",rcvd_acc, rxerr_acc, rxcrc_acc, sent_acc-rcvd_acc); + sent_acc = 0; + txerr_acc = 0; + rcvd_acc = 0; + rxerr_acc = 0; + rxcrc_acc = 0; + } +#if 0 + int pending = pic_regs->pending; + if (pending & PIC_TIMER_INT){ + hal_set_timeout(timer_delta); + + /* + if (EXPECTING_PKT()){ + if (!got_packet) + putchar('T'); + got_packet = false; + } + + if (SEND_PKT()){ + if (status & BPS_IDLE(SERDES_TX_BUF)) + bp_send_from_buf(SERDES_TX_BUF, PORT_SERDES, 1, 0, NLINES_PER_PKT); + } + counter++; + */ + + putchar('T'); + if(ready_to_send) { + bp_send_from_buf(SERDES_TX_BUF, PORT_SERDES, 1, 0, NLINES_PER_PKT); + counter++; + ready_to_send = 0; + } + + pic_regs->pending = PIC_TIMER_INT; // clear pending interrupt + } +#endif + } + + return 0; +} diff --git a/firmware/microblaze/apps/serdes_to_dsp.c b/firmware/microblaze/apps/serdes_to_dsp.c new file mode 100644 index 000000000..4994e0a69 --- /dev/null +++ b/firmware/microblaze/apps/serdes_to_dsp.c @@ -0,0 +1,179 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +/* + * This program can respond to queries from the host + * and stream rx samples. + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx eth flow + */ +//#define CPU_RX_BUF 0 // eth -> cpu +//#define CPU_TX_BUF 1 // cpu -> eth + +#define DSP_RX_BUF_0 2 // dsp rx -> eth (double buffer) +#define DSP_RX_BUF_1 3 // dsp rx -> eth +#define DSP_TX_BUF_0 4 // eth -> dsp tx (double buffer) +#define DSP_TX_BUF_1 5 // eth -> dsp tx + +/* + * ================================================================ + * configure DSP TX double buffering state machine + * ================================================================ + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4) + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { + PORT_SERDES, + 0, + BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { + PORT_DSP, + DSP_TX_FIRST_LINE, // starts just past transport header + 0 // filled in from last_line register +}; + +dbsm_t dsp_tx_sm; // the state machine + + +// ---------------------------------------------------------------- + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +void +start_rx_cmd(const eth_mac_addr_t *host, op_start_rx_t *p) +{ +} + +void +stop_rx_cmd(void) +{ +} + +static void +setup_tx() +{ + dsp_tx_regs->clear_state = 1; + bp_clear_buf(DSP_TX_BUF_0); + bp_clear_buf(DSP_TX_BUF_1); + + int tx_scale = 256; + int interp = 32; + + op_config_tx_t def_config; + memset(&def_config, 0, sizeof(def_config)); + def_config.phase_inc = 408021893; // 9.5 MHz [2**32 * fc/fsample] + def_config.scale_iq = (tx_scale << 16) | tx_scale; + def_config.interp = interp; + + // setup Tx DSP regs + config_tx_cmd(&def_config); +} + + +inline static void +buffer_irq_handler(unsigned irq) +{ + //hal_toggle_leds(0x2); + + uint32_t status = buffer_pool_status->status; + + dbsm_process_status(&dsp_tx_sm, status); + + if (status & BPS_DONE(CPU_TX_BUF)){ + bp_clear_buf(CPU_TX_BUF); + } +} + +int +main(void) +{ + u2_init(); + + // Get our clock from the mimo interface + + clocks_enable_test_clk(true,1); + clocks_mimo_config(MC_WE_LOCK_TO_MIMO); + + // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output + //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + + putstr("\nserdes_to_dsp\n"); + + ethernet_register_link_changed_callback(link_changed_callback); + ethernet_init(); + + + // initialize double buffering state machine for ethernet -> DSP Tx + + dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, + &dsp_tx_recv_args, &dsp_tx_send_args, + eth_pkt_inspector); + + // program tx registers + setup_tx(); + + // kick off the state machine + dbsm_start(&dsp_tx_sm); + + while(1){ + buffer_irq_handler(0); + + int pending = pic_regs->pending; // poll for under or overrun + + if (pending & PIC_UNDERRUN_INT){ + dbsm_handle_tx_underrun(&dsp_tx_sm); + pic_regs->pending = PIC_UNDERRUN_INT; // clear interrupt + putchar('U'); + } + } +} + diff --git a/firmware/microblaze/apps/serdes_txrx.c b/firmware/microblaze/apps/serdes_txrx.c new file mode 100644 index 000000000..2c47c9628 --- /dev/null +++ b/firmware/microblaze/apps/serdes_txrx.c @@ -0,0 +1,368 @@ +/* + * Copyright 2007,2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include "clocks.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +#define FW_SETS_SEQNO 1 // define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno; // used when f/w is filling in sequence numbers +#endif + + +/* + * Full duplex Tx and Rx between serdes and DSP pipelines + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to serdes flow + * Buffers 4 and 5 are used to double-buffer the serdes to DSP Tx flow + */ +//#define CPU_RX_BUF 0 // eth -> cpu + +#define DSP_RX_BUF_0 2 // dsp rx -> serdes (double buffer) +#define DSP_RX_BUF_1 3 // dsp rx -> serdes +#define DSP_TX_BUF_0 4 // serdes -> dsp tx (double buffer) +#define DSP_TX_BUF_1 5 // serdes -> dsp tx + +/* + * ================================================================== + * configure DSP TX double buffering state machine (serdes -> dsp) + * ================================================================== + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4) + +// Receive from serdes +buf_cmd_args_t dsp_tx_recv_args = { + PORT_SERDES, + 0, + BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { + PORT_DSP, + DSP_TX_FIRST_LINE, // starts just past transport header + 0 // filled in from last_line register +}; + +dbsm_t dsp_tx_sm; // the state machine + +/* + * ================================================================= + * configure DSP RX double buffering state machine (dsp -> serdes) + * ================================================================= + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4 + 1) + +// receive from DSP +buf_cmd_args_t dsp_rx_recv_args = { + PORT_DSP, + DSP_RX_FIRST_LINE, + BP_LAST_LINE +}; + +// send to serdes +buf_cmd_args_t dsp_rx_send_args = { + PORT_SERDES, + 0, // starts with ethernet header in line 0 + 0, // filled in from list_line register +}; + +dbsm_t dsp_rx_sm; // the state machine + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +// variables for streaming mode + +static bool streaming_p = false; +static unsigned int streaming_items_per_frame = 0; +static int streaming_frame_count = 0; +#define FRAMES_PER_CMD 1000 + +bool is_streaming(void){ return streaming_p; } + +// ---------------------------------------------------------------- + + +void +restart_streaming(void) +{ + // setup RX DSP regs + dsp_rx_regs->clear_state = 1; // reset + + streaming_p = true; + streaming_frame_count = FRAMES_PER_CMD; + + dsp_rx_regs->rx_command = + MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, + streaming_items_per_frame, + 1, 1); // set "chain" bit + + // kick off the state machine + dbsm_start(&dsp_rx_sm); + + dsp_rx_regs->rx_time = 0; // enqueue first of two commands + + // make sure this one and the rest have the "now" and "chain" bits set. + dsp_rx_regs->rx_command = + MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, + streaming_items_per_frame, + 1, 1); + + dsp_rx_regs->rx_time = 0; // enqueue second command +} + +void +start_rx_streaming_cmd(const eth_mac_addr_t *host, op_start_rx_streaming_t *p) +{ + host_mac_addr = *host; // remember who we're sending to + + /* + * Construct ethernet header and word0 and preload into two buffers + */ + u2_eth_packet_t pkt; + memset(&pkt, 0, sizeof(pkt)); + pkt.ehdr.dst = *host; + pkt.ehdr.src = *ethernet_mac_addr(); + pkt.ehdr.ethertype = U2_ETHERTYPE; + u2p_set_word0(&pkt.fixed, 0, 0); + // DSP RX will fill in timestamp + + memcpy_wa(buffer_ram(DSP_RX_BUF_0), &pkt, sizeof(pkt)); + memcpy_wa(buffer_ram(DSP_RX_BUF_1), &pkt, sizeof(pkt)); + + + if (FW_SETS_SEQNO) + fw_seqno = 0; + + streaming_items_per_frame = p->items_per_frame; + restart_streaming(); +} + + +void +stop_rx_cmd(void) +{ + streaming_p = false; + dsp_rx_regs->clear_state = 1; // flush cmd queue + bp_clear_buf(DSP_RX_BUF_0); + bp_clear_buf(DSP_RX_BUF_1); +} + + +static void +setup_tx() +{ + dsp_tx_regs->clear_state = 1; + bp_clear_buf(DSP_TX_BUF_0); + bp_clear_buf(DSP_TX_BUF_1); + + int tx_scale = 256; + int interp = 32; + + // setup some defaults + + dsp_tx_regs->freq = 0; + dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; + dsp_tx_regs->interp_rate = interp; +} + + +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY. This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet. A bit of a kludge + * but it should work. + */ +bool +fw_sets_seqno_inspector(dbsm_t *sm, int buf_this) // returns false +{ + uint32_t *p = buffer_ram(buf_this); + uint32_t seqno = fw_seqno++; + + // KLUDGE all kinds of nasty magic numbers and embedded knowledge + uint32_t t = p[4]; + t = (t & 0xffff00ff) | ((seqno & 0xff) << 8); + p[4] = t; + + // queue up another rx command when required + if (streaming_p && --streaming_frame_count == 0){ + streaming_frame_count = FRAMES_PER_CMD; + dsp_rx_regs->rx_time = 0; + } + + return false; // we didn't handle the packet +} +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ + // hal_toggle_leds(LED_A); + + uint32_t status = buffer_pool_status->status; + + if (0 && (status & ~BPS_IDLE_ALL)){ + putstr("status = "); + puthex32_nl(status); + } + + dbsm_process_status(&dsp_tx_sm, status); + dbsm_process_status(&dsp_rx_sm, status); +} + +int +main(void) +{ + u2_init(); + + output_regs->led_src = 0x3; // h/w controls bottom two bits + clocks_enable_test_clk(true, 1); + + putstr("\nSERDES TxRx\n"); + + cpu_tx_buf_dest_port = PORT_SERDES; + + // ethernet_register_link_changed_callback(link_changed_callback); + // ethernet_init(); + + clocks_mimo_config(MC_WE_LOCK_TO_MIMO); + + // puts("post clocks_mimo_config"); + +#if 0 + // make bit 15 of Tx gpio's be a s/w output + hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); + hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + +#if 0 + output_regs->debug_mux_ctrl = 1; + hal_gpio_set_sels(GPIO_TX_BANK, "0000000000000000"); + hal_gpio_set_sels(GPIO_RX_BANK, "0000000000000000"); + hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); + hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + + // initialize double buffering state machine for ethernet -> DSP Tx + + dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, + &dsp_tx_recv_args, &dsp_tx_send_args, + eth_pkt_inspector); + + + //output_regs->flush_icache = 1; + + // initialize double buffering state machine for DSP RX -> Ethernet + + if (FW_SETS_SEQNO){ + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + fw_sets_seqno_inspector); + } + else { + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + dbsm_nop_inspector); + } + + // puts("post dbsm_init's"); + + // tell app_common that this dbsm could be sending to the ethernet + ac_could_be_sending_to_eth = &dsp_rx_sm; + + + // program tx registers + setup_tx(); + + // puts("post setup_tx"); + + // kick off the state machine + dbsm_start(&dsp_tx_sm); + + // puts("post dbsm_start"); + + //int which = 0; + + while(1){ + // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); + // which ^= 0x8000; + + buffer_irq_handler(0); + + int pending = pic_regs->pending; // poll for under or overrun + + if (pending & PIC_UNDERRUN_INT){ + dbsm_handle_tx_underrun(&dsp_tx_sm); + pic_regs->pending = PIC_UNDERRUN_INT; // clear interrupt + putchar('U'); + } + + if (pending & PIC_OVERRUN_INT){ + dbsm_handle_rx_overrun(&dsp_rx_sm); + pic_regs->pending = PIC_OVERRUN_INT; // clear pending interrupt + + // FIXME Figure out how to handle this robustly. + // Any buffers that are emptying should be allowed to drain... + + if (streaming_p){ + // restart_streaming(); + // FIXME report error + } + else { + // FIXME report error + } + putchar('O'); + } + } +} diff --git a/firmware/microblaze/apps/set_hw_rev.c b/firmware/microblaze/apps/set_hw_rev.c new file mode 100644 index 000000000..d4ac8ff81 --- /dev/null +++ b/firmware/microblaze/apps/set_hw_rev.c @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#include <u2_init.h> +#include <nonstdio.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> + +#define HW_REV_MAJOR 0 +#define HW_REV_MINOR 3 + +int +main(void) +{ + u2_init(); + + putstr("\nset_hw_rev\n"); + + bool ok = true; + unsigned char maj = HW_REV_MAJOR; + unsigned char min = HW_REV_MINOR; + ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_MSB, &maj, 1); + ok &= eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_LSB, &min, 1); + + if (ok) + printf("OK: set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); + else + printf("FAILED to set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); + + return 0; +} diff --git a/firmware/microblaze/apps/test1.c b/firmware/microblaze/apps/test1.c new file mode 100644 index 000000000..c3cc3be56 --- /dev/null +++ b/firmware/microblaze/apps/test1.c @@ -0,0 +1,282 @@ +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include "nonstdio.h" + +// Globals +#define EMPTY 0 +#define FILLING 1 +#define FULL 2 +#define EMPTYING 3 + +#define PORT 2 // ethernet = 2, serdes = 0 +int dsp_rx_buf, dsp_tx_buf, serdes_rx_buf, serdes_tx_buf; +int dsp_rx_idle, dsp_tx_idle, serdes_rx_idle, serdes_tx_idle; + +int buffer_state[4]; + + +void double_buffering(int port); + +// +// We register this in the secondary interrupt vector. +// It's called on buffer manager interrupts +// +void +buffer_irq_handler(unsigned irq) +{ + double_buffering(PORT); +} + +int +main(void) +{ + int i; + + u2_init(); + + // Control LEDs + output_regs->leds = 0x02; + + // Turn on ADCs + output_regs->adc_ctrl = 0x0A; + + // Set up TX Chain + dsp_tx_regs->freq = 0; + dsp_tx_regs->scale_iq = (1 << 16) | 1; + dsp_tx_regs->interp_rate = 8; + + // Set up RX Chain + dsp_rx_regs->freq = 0; + dsp_rx_regs->scale_iq = (1 << 16) | 1; + dsp_rx_regs->decim_rate = 8; + + // Set up buffer control, using only 4 for now + for(i=0;i<4;i++) + buffer_state[i] = EMPTY; + + // Set up DSP RX + buffer_state[0] = FILLING; + serdes_tx_idle = 1; + bp_receive_to_buf(0, 1, 1, 10, 509); // DSP_RX to buffer 0, use 500 lines + + //dsp_rx_regs->run_rx = 1; // Start DSP_RX + putstr("Done DSP RX setup\n"); + + // Set up serdes RX + buffer_state[2] = FILLING; + dsp_tx_idle = 1; + bp_receive_to_buf(2, PORT, 1, 5, 504); + + while (buffer_pool_status->status == 0) // wait for completion of DSP RX + ; + + putstr("Done DSP TX setup\n"); + //dsp_tx_regs->run_tx = 1; + + // register interrupt handler + pic_register_handler(IRQ_BUFFER, buffer_irq_handler); + + while (1) + ; + + hal_finish(); + return 1; +} + +void +double_buffering(int port) { + unsigned int localstatus = buffer_pool_status->status; + + if(localstatus & BPS_DONE_0) { + bp_clear_buf(0); + if(buffer_state[0] == FILLING) { + buffer_state[0] = FULL; + if(buffer_state[1] == EMPTY) { + bp_receive_to_buf(1, 1, 1, 10, 509); // DSP_RX to buffer 1, use 500 lines + buffer_state[1] = FILLING; + } + else + dsp_rx_idle = 1; + if(serdes_tx_idle) { + serdes_tx_idle = 0; + bp_send_from_buf(0, port, 1, 10, 509); // SERDES_TX from buffer 0 + buffer_state[0] = EMPTYING; + } + } + else { // buffer was emptying + buffer_state[0] = EMPTY; + if(dsp_rx_idle) { + dsp_rx_idle = 0; + bp_receive_to_buf(0, 1, 1, 10, 509); // DSP_RX to buffer 0, use 500 lines + buffer_state[0] = FILLING; + } + if(buffer_state[1] == FULL) { + bp_send_from_buf(1, port, 1, 10, 509); // SERDES_TX from buffer 1 + buffer_state[1] = EMPTYING; + } + else + serdes_tx_idle = 1; + } + putstr("Int Proc'ed 0\n"); + } + if(localstatus & BPS_DONE_1) { + bp_clear_buf(1); + if(buffer_state[1] == FILLING) { + buffer_state[1] = FULL; + if(buffer_state[0] == EMPTY) { + bp_receive_to_buf(0, 1, 1, 10, 509); // DSP_RX to buffer 1, use 500 lines + buffer_state[0] = FILLING; + } + else + dsp_rx_idle = 1; + if(serdes_tx_idle) { + serdes_tx_idle = 0; + bp_send_from_buf(1, port, 1, 10, 509); // SERDES_TX from buffer 1 + buffer_state[1] = EMPTYING; + } + } + else { // buffer was emptying + buffer_state[1] = EMPTY; + if(dsp_rx_idle) { + dsp_rx_idle = 0; + bp_receive_to_buf(1, 1, 1, 10, 509); // DSP_RX to buffer 1, use 500 lines + buffer_state[1] = FILLING; + } + if(buffer_state[0] == FULL) { + bp_send_from_buf(0, port, 1, 10, 509); // SERDES_TX from buffer 0 + buffer_state[0] = EMPTYING; + } + else + serdes_tx_idle = 1; + } + putstr("Int Proc'ed 1\n"); + } + if(localstatus & BPS_DONE_2) { + bp_clear_buf(2); + if(buffer_state[2] == FILLING) { + buffer_state[2] = FULL; + if(buffer_state[3] == EMPTY) { + bp_receive_to_buf(3, port, 1, 5, 504); // SERDES_RX to buffer 3, use 500 lines + buffer_state[3] = FILLING; + } + else + serdes_rx_idle = 1; + if(dsp_tx_idle) { + dsp_tx_idle = 0; + bp_send_from_buf(2, 1, 1, 5, 504); // DSP_TX from buffer 2 + buffer_state[2] = EMPTYING; + } + } + else { // buffer was emptying + buffer_state[2] = EMPTY; + if(serdes_rx_idle) { + serdes_rx_idle = 0; + bp_receive_to_buf(2, port, 1, 5, 504); // SERDES_RX to buffer 2 + buffer_state[2] = FILLING; + } + if(buffer_state[3] == FULL) { + bp_send_from_buf(3, 1, 1, 5, 504); // DSP_TX from buffer 3 + buffer_state[3] = EMPTYING; + } + else + dsp_tx_idle = 1; + } + putstr("Int Proc'ed 2\n"); + } + if(localstatus & BPS_DONE_3) { + bp_clear_buf(3); + if(buffer_state[3] == FILLING) { + buffer_state[3] = FULL; + if(buffer_state[2] == EMPTY) { + bp_receive_to_buf(2, port, 1, 5, 504); // SERDES_RX to buffer 2, use 500 lines + buffer_state[2] = FILLING; + } + else + serdes_rx_idle = 1; + if(dsp_tx_idle) { + dsp_tx_idle = 0; + bp_send_from_buf(3, 1, 1, 5, 504); // DSP_TX from buffer 3 + buffer_state[3] = EMPTYING; + } + } + else { // buffer was emptying + buffer_state[3] = EMPTY; + if(serdes_rx_idle) { + serdes_rx_idle = 0; + bp_receive_to_buf(3, port, 1, 5, 504); // SERDES_RX to buffer 3 + buffer_state[3] = FILLING; + } + if(buffer_state[2] == FULL) { + bp_send_from_buf(2, 1, 1, 5, 504); // DSP_TX from buffer 2 + buffer_state[2] = EMPTYING; + } + else + dsp_tx_idle = 1; + } + putstr("Int Proc'ed 3\n"); + } +} + +// Spare Code + +#if 0 + // Set up LSDAC + int i = 0; + while(1) { + int command = (3 << 19) | (0 << 16) | (i & 0xffff); + spi_transact(SPI_TXONLY, SPI_SS_TX_DAC, command, 24, 1); // negate TX phase + i++; + } +#endif + +#if 0 + // Write to buffer 0 + int *buf = (int *)(BUFFER_BASE + BUFFER_0); + puthex_nl((int)buf); + + for(i=0;i<BUFFER_SIZE;i++) + buf[i] = i; + + putstr("Filled buffer 0\n"); + + // Write to buffer 1 + buf = (int *)(BUFFER_BASE + BUFFER_1); + puthex_nl((int)buf); + for(i=0;i<BUFFER_SIZE;i++) + buf[i] = i + ((i^0xFFFF) << 16); + + putstr("Filled buffer 1\n"); + +#endif + +#if 0 + // rx SERDES into buffer #2 (buf,port,step,fl,ll) + bp_receive_to_buf(2, 0, 1, 10, 300); + putstr("SERDES RX buffer setup\n"); + + // send SERDES from buffer #0 (buf,port,step,fl,ll) + bp_send_from_buf(0, 0, 1, 20, 200); + putstr("SERDES TX buffer setup\n"); + +#endif + +#if 0 + // send to DACs from buffer #1 + bp_send_from_buf(1 /*buf#*/, 1 /*port*/, 1 /*step*/, 20 /*fl*/, 250 /*ll*/); + putstr("DAC Buffer setup\n"); +#endif + +#if 0 + //putstr("ENTER INT\n"); + for(i=0;i<8;i++) + if(*status & (1<<i)) { + //putstr("Clearing buf "); + puthex_nl(i); + bp_clear_buf(i); + } + //putstr("EXIT INT\n"); +#endif diff --git a/firmware/microblaze/apps/test_db_spi.c b/firmware/microblaze/apps/test_db_spi.c new file mode 100644 index 000000000..f4fa98ef1 --- /dev/null +++ b/firmware/microblaze/apps/test_db_spi.c @@ -0,0 +1,35 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <u2_init.h> +#include <nonstdio.h> +#include <hal_io.h> +#include <spi.h> + +int +main(void) +{ + u2_init(); + + puts("\ntest_db_spi"); + + while(1){ + spi_transact(SPI_TXONLY, SPI_SS_RX_DB, 0xCC33, 16, SPIF_PUSH_FALL); + spi_transact(SPI_TXONLY, SPI_SS_TX_DB, 0x33CC, 16, SPIF_PUSH_FALL); + } +} diff --git a/firmware/microblaze/apps/test_i2c.c b/firmware/microblaze/apps/test_i2c.c new file mode 100644 index 000000000..f349ead88 --- /dev/null +++ b/firmware/microblaze/apps/test_i2c.c @@ -0,0 +1,108 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <stdint.h> +#include <u2_init.h> /* FIXME */ +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <string.h> +#include <hal_io.h> + + + +#define ASSERT_TRUE(x) \ + do { \ + if (!(x)){ \ + printf("ASSERT_TRUE failed on line %d\n", __LINE__); \ + nerrors++; \ + } \ + } while(0) + +#define ASSERT_FALSE(x) \ + do { \ + if (x){ \ + printf("ASSERT_FALSE failed on line %d\n", __LINE__); \ + nerrors++; \ + } \ + } while(0) + + +#define BUFSIZE 128 + +int +main(void) +{ + int i; + bool ok; + int nerrors = 0; + uint8_t buf[BUFSIZE]; + int not_dev_addr = 0x35; // no device with this address on the i2c bus. + int offset; + int len; + + u2_init(); + + puts("test_i2c\n"); + + // try writing a non-existent device + buf[0] = 0xA5; + ok = i2c_write(not_dev_addr, buf, 1); + ASSERT_FALSE(ok); + + // try read from non-existent device + buf[0] = 0; + ok = i2c_read(not_dev_addr, buf, 1); + ASSERT_FALSE(ok); + + // try writing eeprom + offset = 31; + len = 8; + memset(buf, 0, sizeof(buf)); + for (i = 0; i < len; i++) + buf[i] = i; + ok = eeprom_write(I2C_ADDR_MBOARD, offset, buf, len); + ASSERT_TRUE(ok); + + // now try to read it back + offset = 31; + len = 8; + memset(buf, 0, sizeof(buf)); + ok = eeprom_read(I2C_ADDR_MBOARD, offset, buf, len); + ASSERT_TRUE(ok); + + // check result + for (i = 0; i < len; i++){ + if (buf[i] != i){ + printf("buf[%d] = %d, should be %d\n", i, buf[i], i); + nerrors++; + } + } + + if (nerrors == 0){ + output_regs->leds = 0x3; + puts("PASSED\n"); + } + else { + output_regs->leds = 0x0; + puts("FAILED\n"); + } + + hal_finish(); + return 0; +} + diff --git a/firmware/microblaze/apps/test_lsadc.c b/firmware/microblaze/apps/test_lsadc.c new file mode 100644 index 000000000..5fda29cd7 --- /dev/null +++ b/firmware/microblaze/apps/test_lsadc.c @@ -0,0 +1,57 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <lsadc.h> +#include <lsdac.h> +#include <u2_init.h> +#include <nonstdio.h> +#include <hal_io.h> + +int +main(void) +{ + u2_init(); + + puts("\ntest_lsadc"); + + uint32_t r; + + unsigned int up_counter = 0; + + while (1){ + unsigned int v; + v = up_counter; + + lsdac_write_rx(0, v << 0); + lsdac_write_rx(2, v << 1); + +#if 1 + r = lsadc_read_rx(0); + lsdac_write_rx(1, r & 0x0fff); + //puthex32_nl(r); +#endif + +#if 1 + r = lsadc_read_rx(1); + lsdac_write_rx(3, r & 0x0fff); + //puthex32_nl(r); +#endif + + up_counter++; + } +} diff --git a/firmware/microblaze/apps/test_lsdac.c b/firmware/microblaze/apps/test_lsdac.c new file mode 100644 index 000000000..8c1bf333b --- /dev/null +++ b/firmware/microblaze/apps/test_lsdac.c @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <lsdac.h> +#include <u2_init.h> +#include <nonstdio.h> +#include <hal_io.h> + +int +main(void) +{ + u2_init(); + + puts("\ntest_lsdac"); + + unsigned int up_counter = 0; + unsigned int dn_counter = 0; + + while(1){ + unsigned int v; + v = up_counter; + lsdac_write_rx(0, v << 0); + lsdac_write_rx(1, v << 1); + lsdac_write_rx(2, v << 2); + lsdac_write_rx(3, v << 3); + + v = up_counter; + lsdac_write_tx(0, v << 0); + lsdac_write_tx(1, v << 1); + lsdac_write_tx(2, v << 2); + lsdac_write_tx(3, v << 3); + + up_counter++; + dn_counter--; + } +} diff --git a/firmware/microblaze/apps/test_phy_comm.c b/firmware/microblaze/apps/test_phy_comm.c new file mode 100644 index 000000000..d312ca4cc --- /dev/null +++ b/firmware/microblaze/apps/test_phy_comm.c @@ -0,0 +1,113 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +// check communication with ethernet PHY chip + +#include "u2_init.h" +#include "memory_map.h" +#include "hal_io.h" +#include "ethernet.h" +#include "pic.h" +#include "nonstdio.h" + + +#define DELTA_T 12500000 // .125s (10ns per tick) +//#define DELTA_T 10000 + +// debugging output on tx pins +#define LS_MASK 0xE0000 +#define LS_1000 0x80000 +#define LS_100 0x40000 +#define LS_10 0x20000 + + + +#define U2_ETHERTYPE 0xBEEF + + +static volatile int led_link_up_flag = 0; + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ + int v = 0; + switch(speed){ + case 10: + v = LS_10; + led_link_up_flag = 0x2; + break; + + case 100: + v = LS_100; + led_link_up_flag = 0x2; + break; + + case 1000: + v = LS_100; + led_link_up_flag = 0x2; + break; + + default: + v = 0; + led_link_up_flag = 0; + break; + } + + //hal_gpio_set_tx(v, LS_MASK); /* set debug bits on d'board */ + + putstr("\neth link changed: speed = "); + puthex_nl(speed); +} + +void +timer_handler(unsigned irq) +{ + static int led_counter = 0; + + hal_set_timeout(DELTA_T); // schedule next timeout + output_regs->leds = (led_counter++ & 0x1) | led_link_up_flag; +} + +int +main(void) +{ + u2_init(); + + putstr("\n test_phy_comm\n"); + + pic_register_handler(IRQ_ONETIME, timer_handler); + hal_set_timeout(DELTA_T); // schedule timeout + + // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output + //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + + ethernet_register_link_changed_callback(link_changed_callback); + + output_regs->phy_ctrl = 1; /* reset the eth PHY */ + output_regs->phy_ctrl = 0; + + ethernet_init(); + + while(1) + ; + + return 0; +} diff --git a/firmware/microblaze/apps/test_ram.c b/firmware/microblaze/apps/test_ram.c new file mode 100644 index 000000000..77ee693f6 --- /dev/null +++ b/firmware/microblaze/apps/test_ram.c @@ -0,0 +1,105 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <stdint.h> +#include <u2_init.h> /* FIXME */ +#include <sd.h> +#include <string.h> +#include <hal_io.h> +#include <nonstdio.h> +#include <hal_uart.h> + +#define ASSERT_TRUE(x) \ + do { \ + if (!(x)){ \ + printf("ASSERT_TRUE failed on line %d\n", __LINE__); \ + nerrors++; \ + } \ + } while(0) + +#define ASSERT_FALSE(x) \ + do { \ + if (x){ \ + printf("ASSERT_FALSE failed on line %d\n", __LINE__); \ + nerrors++; \ + } \ + } while(0) + + +#define BUFSIZE 128 + +int test_ram() +{ + int i,j,k; + output_regs->ram_page = 1<<10; + + extram[0] = 0xDEADBEEF; + extram[1] = 0xF00D1234; + extram[7] = 0x76543210; + + output_regs->ram_page = 2<<10; + extram[7] = 0x55555555; + extram[1] = 0xaaaaaaaa; + extram[0] = 0xeeeeeeee; + + output_regs->ram_page = 1<<10; + + i = extram[0]; + k = extram[1]; + j = extram[7]; + + if((i != 0xDEADBEEF)||(j!=0x76543210)||(k!=0xF00D1234)) { + puts("RAM FAIL1!\n"); + puthex32_nl(i); + puthex32_nl(j); + puthex32_nl(k); + return 0; + } + + output_regs->ram_page = 2<<10; + + j = extram[7]; + k = extram[1]; + i = extram[0]; + + if((i != 0xeeeeeeee)||(j!=0x55555555)||(k!=0xaaaaaaaa)) { + puts("RAM FAIL2!\n"); + puthex32_nl(i); + puthex32_nl(j); + puthex32_nl(k); + return 0; + } + return 1; +} + +int +main(void) +{ + + u2_init(); + puts("\ntest_ram\n"); + int success = test_ram(); + if(success) + puts("RAM Passed Tests\n"); + else + puts("RAM Failed\n"); + + hal_finish(); + return 0; +} + diff --git a/firmware/microblaze/apps/test_sd.c b/firmware/microblaze/apps/test_sd.c new file mode 100644 index 000000000..494432d7f --- /dev/null +++ b/firmware/microblaze/apps/test_sd.c @@ -0,0 +1,81 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <stdint.h> +#include <u2_init.h> /* FIXME */ +#include <sd.h> +#include <string.h> +#include <hal_io.h> +#include <nonstdio.h> + + +#define ASSERT_TRUE(x) \ + do { \ + if (!(x)){ \ + printf("ASSERT_TRUE failed on line %d\n", __LINE__); \ + nerrors++; \ + } \ + } while(0) + +#define ASSERT_FALSE(x) \ + do { \ + if (x){ \ + printf("ASSERT_FALSE failed on line %d\n", __LINE__); \ + nerrors++; \ + } \ + } while(0) + + +#define BUFSIZE 128 + +int +main(void) +{ + int i; + unsigned char buf[512]; + + u2_init(); + + puts("\ntest_sd\n"); + + + i = sd_init(); + if(i) + puts("Successfully Init'ed Card\n"); + else + puts("FAILED INIT of Card\n"); + + i = sd_read_block(2048,buf); + if(i) { + puts("READ Command accepted\n"); + for(i=0;i<512;i++) + if((i&15) == 15) + puthex8_nl(buf[i]); + else { + puthex8(buf[i]); + putchar(' '); + } + } + else + puts("READ Command Rejected\n"); + + puts("Done"); + hal_finish(); + return 0; +} + diff --git a/firmware/microblaze/apps/timer_test.c b/firmware/microblaze/apps/timer_test.c new file mode 100644 index 000000000..44e80b5f1 --- /dev/null +++ b/firmware/microblaze/apps/timer_test.c @@ -0,0 +1,57 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "u2_init.h" +#include "memory_map.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include "nonstdio.h" +#include "hal_io.h" + +#define DELTA_T 500 // 5 us (10ns per tick) + + +void +timer_handler(unsigned irq) +{ + hal_set_timeout(DELTA_T); + + putstr("Tick: "); + puthex_nl(0); +} + +int +main(void) +{ + u2_init(); + + // setup timer + + putstr("Setting up timer\n"); + pic_register_handler(IRQ_ONETIME, timer_handler); + + hal_set_timeout(DELTA_T); + + while (1) + ; + + putstr("Done Testing\n"); + + hal_finish(); + return 1; +} diff --git a/firmware/microblaze/apps/tx_standalone.c b/firmware/microblaze/apps/tx_standalone.c new file mode 100644 index 000000000..1645fa8ba --- /dev/null +++ b/firmware/microblaze/apps/tx_standalone.c @@ -0,0 +1,339 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "memcpy_wa.h" +#include "dbsm.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#define _AL4 __attribute__((aligned (4))) + +#define USE_BUFFER_INTERRUPT 0 // 0 or 1 + + +static int timer_delta = MASTER_CLK_RATE/1000; // tick at 1kHz + +/* + * This program can respond to queries from the host + * and stream rx samples. + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx eth flow + */ +//#define CPU_RX_BUF 0 // eth -> cpu +#define CPU_TX_BUF 1 // cpu -> eth + +#define DSP_RX_BUF_0 2 // dsp rx -> eth (double buffer) +#define DSP_RX_BUF_1 3 // dsp rx -> eth +#define DSP_TX_BUF_0 4 // eth -> dsp tx (double buffer) +#define DSP_TX_BUF_1 5 // eth -> dsp tx + + +/* + * ================================================================ + * configure DSP RX double buffering state machine + * ================================================================ + */ + + +// 4 lines of ethernet hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE 5 +#define DSP_RX_SAMPLES_PER_FRAME 128 +#define DSP_RX_EXTRA_LINES 1 // writes timestamp + +// Receive from DSP Rx +buf_cmd_args_t dsp_rx_recv_args = { + PORT_DSP, + DSP_RX_FIRST_LINE, + BP_LAST_LINE +}; + +// send to ethernet +buf_cmd_args_t dsp_rx_send_args = { + PORT_ETH, + 0, // starts with ethernet header in line 0 + 0, // filled in from last_line register +}; + +dbsm_t dsp_rx_sm; // the state machine + +/* + * ================================================================ + * configure DSP TX double buffering state machine + * ================================================================ + */ + +// 4 lines of ethernet hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE 4 +#define DSP_TX_SAMPLES_PER_FRAME 250 // not used except w/ debugging +#define DSP_TX_EXTRA_LINES 2 // reads word0 + timestamp + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { + PORT_ETH, + 0, + BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { + PORT_DSP, + DSP_TX_FIRST_LINE, // starts just past ethernet header + 0 // filled in from last_line register +}; + +dbsm_t dsp_tx_sm; // the state machine + +/* + * send constant buffer to DSP TX + */ +static inline void +SEND_CONST_TO_DSP_TX(void) +{ + bp_send_from_buf(DSP_TX_BUF_0, PORT_DSP, 1, + DSP_TX_FIRST_LINE, + DSP_TX_FIRST_LINE + DSP_TX_EXTRA_LINES + DSP_TX_SAMPLES_PER_FRAME - 1); +} + +// ---------------------------------------------------------------- + + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +void link_changed_callback(int speed); +static volatile bool link_is_up = false; // eth handler sets this + + +void +timer_irq_handler(unsigned irq) +{ + hal_set_timeout(timer_delta); // schedule next timeout +} + +// Tx DSP underrun +void +underrun_irq_handler(unsigned irq) +{ + dsp_tx_regs->clear_state = 1; + bp_clear_buf(DSP_TX_BUF_0); + bp_clear_buf(DSP_TX_BUF_1); + dbsm_stop(&dsp_tx_sm); + + // FIXME anything else? + + putstr("\nirq: underrun\n"); +} + +// Rx DSP overrun +void +overrun_irq_handler(unsigned irq) +{ + dsp_rx_regs->clear_state = 1; + bp_clear_buf(DSP_RX_BUF_0); + bp_clear_buf(DSP_RX_BUF_1); + dbsm_stop(&dsp_rx_sm); + + // FIXME anything else? + + putstr("\nirq: overrun\n"); +} + +static void +start_tx_transfers(void) +{ + bp_clear_buf(DSP_TX_BUF_0); // FIXME, really goes in state machine + bp_clear_buf(DSP_TX_BUF_1); + + // fill everything with a constant 32k + 0j + + uint32_t const_sample = (32000 << 16) | 0; + int i; + for (i = 0; i < BP_NLINES; i++){ + buffer_ram(DSP_TX_BUF_0)[i] = const_sample; + buffer_ram(DSP_TX_BUF_1)[i] = const_sample; + } + + /* + * Construct ethernet header and word0 and preload into two buffers + */ + u2_eth_packet_t pkt; + memset(&pkt, 0, sizeof(pkt)); + //pkt.ehdr.dst = *host; + pkt.ehdr.src = *ethernet_mac_addr(); + pkt.ehdr.ethertype = U2_ETHERTYPE; + u2p_set_word0(&pkt.fixed, + U2P_TX_IMMEDIATE | U2P_TX_START_OF_BURST, 0); + u2p_set_timestamp(&pkt.fixed, T_NOW); + + memcpy_wa(buffer_ram(DSP_TX_BUF_0), &pkt, sizeof(pkt)); + memcpy_wa(buffer_ram(DSP_TX_BUF_1), &pkt, sizeof(pkt)); + + + int tx_scale = 256; + + // setup Tx DSP regs + dsp_tx_regs->clear_state = 1; // reset + dsp_tx_regs->freq = 408021893; // 9.5 MHz [2**32 * fc/fsample] + dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; + dsp_tx_regs->interp_rate = 32; + + // kick off the state machine + // dbsm_start(&dsp_rx_sm); + + SEND_CONST_TO_DSP_TX(); // send constant buffer to DSP TX +} + + +void +buffer_irq_handler(unsigned irq) +{ + uint32_t status = buffer_pool_status->status; + + if (0){ + putstr("irq: "); + puthex32(status); + putchar('\n'); + } + + if (status & BPS_ERROR_ALL){ + // FIXME rare path, handle error conditions + } + + if (status & BPS_DONE(DSP_TX_BUF_0)){ + bp_clear_buf(DSP_TX_BUF_0); + SEND_CONST_TO_DSP_TX(); + hal_toggle_leds(0x1); + } + +} + +int +main(void) +{ + u2_init(); + + // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output + //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + + putstr("\ntx_only\n"); + + // Control LEDs + hal_set_leds(0x0, 0x3); + + if (USE_BUFFER_INTERRUPT) + pic_register_handler(IRQ_BUFFER, buffer_irq_handler); + + pic_register_handler(IRQ_OVERRUN, overrun_irq_handler); + pic_register_handler(IRQ_UNDERRUN, underrun_irq_handler); + + //pic_register_handler(IRQ_TIMER, timer_irq_handler); + //hal_set_timeout(timer_delta); + + ethernet_register_link_changed_callback(link_changed_callback); + + ethernet_init(); + + // initialize double buffering state machine for DSP RX -> Ethernet + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + dbsm_nop_inspector); + + // setup receive from ETH + // bp_receive_to_buf(CPU_RX_BUF, PORT_ETH, 1, 0, BP_LAST_LINE); + +#if 0 + if (hwconfig_simulation_p()){ + // If we're simulating, pretend that we got a start command from the host + eth_mac_addr_t host = {{ 0x00, 0x0A, 0xE4, 0x3E, 0xD2, 0xD5 }}; + start_rx_cmd(&host); + } +#endif + + start_tx_transfers(); // send constant buffers to DSP TX + + while(1){ + if (!USE_BUFFER_INTERRUPT) + buffer_irq_handler(0); + } +} + +// ---------------------------------------------------------------- + +// debugging output on tx pins +#define LS_MASK 0xE0000 +#define LS_1000 0x80000 +#define LS_100 0x40000 +#define LS_10 0x20000 + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ + int v = 0; + switch(speed){ + case 10: + v = LS_10; + link_is_up = true; + break; + + case 100: + v = LS_100; + link_is_up = true; + break; + + case 1000: + v = LS_100; + link_is_up = true; + break; + + default: + v = 0; + link_is_up = false; + break; + } + + //hal_gpio_set_tx(v, LS_MASK); /* set debug bits on d'board */ + + // hal_set_leds(link_is_up ? 0x2 : 0x0, 0x2); + + printf("\neth link changed: speed = %d\n", speed); +} diff --git a/firmware/microblaze/apps/txrx.c b/firmware/microblaze/apps/txrx.c new file mode 100644 index 000000000..d0108c1a5 --- /dev/null +++ b/firmware/microblaze/apps/txrx.c @@ -0,0 +1,414 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "usrp2_ipv4_packet.h" +#include "usrp2_udp_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include "clocks.h" +#include <vrt/bits.h> + +#define FW_SETS_SEQNO 1 // define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno; // used when f/w is filling in sequence numbers +#endif + + +/* + * Full duplex Tx and Rx between ethernet and DSP pipelines + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx eth flow + */ +//#define CPU_RX_BUF 0 // eth -> cpu + +#define DSP_RX_BUF_0 2 // dsp rx -> eth (double buffer) +#define DSP_RX_BUF_1 3 // dsp rx -> eth +#define DSP_TX_BUF_0 4 // eth -> dsp tx (double buffer) +#define DSP_TX_BUF_1 5 // eth -> dsp tx + +/* + * ================================================================ + * configure DSP TX double buffering state machine (eth -> dsp) + * ================================================================ + */ + +// DSP Tx reads ethernet header words +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_ip_udp_t))/4) + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { + PORT_ETH, + 0, + BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { + PORT_DSP, + DSP_TX_FIRST_LINE, // starts just past transport header + 0 // filled in from last_line register +}; + +dbsm_t dsp_tx_sm; // the state machine + +/* + * ================================================================ + * configure DSP RX double buffering state machine (dsp -> eth) + * ================================================================ + */ + +// DSP Rx writes ethernet header words +#define DSP_RX_FIRST_LINE 1 //1 = control stuff to udp sm + +// receive from DSP +buf_cmd_args_t dsp_rx_recv_args = { + PORT_DSP, + DSP_RX_FIRST_LINE, + BP_LAST_LINE +}; + +// send to ETH +buf_cmd_args_t dsp_rx_send_args = { + PORT_ETH, + 0, // starts with ethernet header in line 0 + 0, // filled in from list_line register +}; + +dbsm_t dsp_rx_sm; // the state machine + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + +#define TIME_NOW ((uint32_t)(~0)) + +// variables for streaming mode + +static bool streaming_p = false; +static unsigned int streaming_items_per_frame = 0; +static uint32_t time_secs = TIME_NOW; +static uint32_t time_ticks = TIME_NOW; +static int streaming_frame_count = 0; +#define FRAMES_PER_CMD 2 + +bool is_streaming(void){ return streaming_p; } + + +// ---------------------------------------------------------------- + +#define VRT_HEADER_WORDS 5 +#define VRT_TRAILER_WORDS 1 + +void +restart_streaming(void) +{ + // setup RX DSP regs + sr_rx_ctrl->nsamples_per_pkt = streaming_items_per_frame; + sr_rx_ctrl->nchannels = 1; + sr_rx_ctrl->clear_overrun = 1; // reset + sr_rx_ctrl->vrt_header = (0 + | VRTH_PT_IF_DATA_WITH_SID + | VRTH_HAS_TRAILER + | VRTH_TSI_OTHER + | VRTH_TSF_SAMPLE_CNT + | (VRT_HEADER_WORDS+streaming_items_per_frame+VRT_TRAILER_WORDS)); + sr_rx_ctrl->vrt_stream_id = 0; + sr_rx_ctrl->vrt_trailer = 0; + + streaming_p = true; + streaming_frame_count = FRAMES_PER_CMD; + + sr_rx_ctrl->cmd = + MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, + (time_ticks==TIME_NOW)?1:0, 1); // conditionally set "now" bit, set "chain" bit + + // kick off the state machine + dbsm_start(&dsp_rx_sm); + + sr_rx_ctrl->time_secs = time_secs; + sr_rx_ctrl->time_ticks = time_ticks; // enqueue first of two commands + + // make sure this one and the rest have the "now" and "chain" bits set. + sr_rx_ctrl->cmd = + MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, 1, 1); + + sr_rx_ctrl->time_secs = 0; + sr_rx_ctrl->time_ticks = 0; // enqueue second command +} + +/* + * 1's complement sum for IP and UDP headers + * + * init chksum to zero to start. + */ +static unsigned int +CHKSUM(unsigned int x, unsigned int *chksum) +{ + *chksum += x; + *chksum = (*chksum & 0xffff) + (*chksum>>16); + *chksum = (*chksum & 0xffff) + (*chksum>>16); + return x; +} + +void +start_rx_streaming_cmd(op_start_rx_streaming_t *p) +{ + /* + * Construct ethernet header and preload into two buffers + */ + struct { + uint32_t ctrl_word; + } mem _AL4; + + memset(&mem, 0, sizeof(mem)); + p->items_per_frame = (1500 - sizeof(u2_eth_ip_udp_t))/sizeof(uint32_t) - (VRT_HEADER_WORDS + VRT_TRAILER_WORDS); //FIXME + mem.ctrl_word = (VRT_HEADER_WORDS+p->items_per_frame+VRT_TRAILER_WORDS)*sizeof(uint32_t) | 1 << 16; + + memcpy_wa(buffer_ram(DSP_RX_BUF_0), &mem, sizeof(mem)); + memcpy_wa(buffer_ram(DSP_RX_BUF_1), &mem, sizeof(mem)); + + //setup ethernet header machine + sr_udp_sm->eth_hdr.mac_dst_0_1 = (host_dst_mac_addr.addr[0] << 8) | host_dst_mac_addr.addr[1]; + sr_udp_sm->eth_hdr.mac_dst_2_3 = (host_dst_mac_addr.addr[2] << 8) | host_dst_mac_addr.addr[3]; + sr_udp_sm->eth_hdr.mac_dst_4_5 = (host_dst_mac_addr.addr[4] << 8) | host_dst_mac_addr.addr[5]; + sr_udp_sm->eth_hdr.mac_src_0_1 = (host_src_mac_addr.addr[0] << 8) | host_src_mac_addr.addr[1]; + sr_udp_sm->eth_hdr.mac_src_2_3 = (host_src_mac_addr.addr[2] << 8) | host_src_mac_addr.addr[3]; + sr_udp_sm->eth_hdr.mac_src_4_5 = (host_src_mac_addr.addr[4] << 8) | host_src_mac_addr.addr[5]; + sr_udp_sm->eth_hdr.ether_type = ETHERTYPE_IPV4; + + //setup ip header machine + unsigned int chksum = 0; + sr_udp_sm->ip_hdr.ver_ihl_tos = CHKSUM(0x4500, &chksum); // IPV4, 5 words of header (20 bytes), TOS=0 + sr_udp_sm->ip_hdr.total_length = UDP_SM_INS_IP_LEN; // Don't checksum this line in SW + sr_udp_sm->ip_hdr.identification = CHKSUM(0x0000, &chksum); // ID + sr_udp_sm->ip_hdr.flags_frag_off = CHKSUM(0x4000, &chksum); // don't fragment + sr_udp_sm->ip_hdr.ttl_proto = CHKSUM(0x2011, &chksum); // TTL=32, protocol = UDP (17 decimal) + //sr_udp_sm->ip_hdr.checksum .... filled in below + uint32_t src_ip_addr = host_src_ip_addr.s_addr; + uint32_t dst_ip_addr = host_dst_ip_addr.s_addr; + sr_udp_sm->ip_hdr.src_addr_high = CHKSUM(src_ip_addr >> 16, &chksum); // IP src high + sr_udp_sm->ip_hdr.src_addr_low = CHKSUM(src_ip_addr & 0xffff, &chksum); // IP src low + sr_udp_sm->ip_hdr.dst_addr_high = CHKSUM(dst_ip_addr >> 16, &chksum); // IP dst high + sr_udp_sm->ip_hdr.dst_addr_low = CHKSUM(dst_ip_addr & 0xffff, &chksum); // IP dst low + sr_udp_sm->ip_hdr.checksum = UDP_SM_INS_IP_HDR_CHKSUM | (chksum & 0xffff); + + //setup the udp header machine + sr_udp_sm->udp_hdr.src_port = host_src_udp_port; + sr_udp_sm->udp_hdr.dst_port = host_dst_udp_port; + sr_udp_sm->udp_hdr.length = UDP_SM_INS_UDP_LEN; + sr_udp_sm->udp_hdr.checksum = UDP_SM_LAST_WORD; // zero UDP checksum + + if (FW_SETS_SEQNO) + fw_seqno = 0; + + streaming_items_per_frame = p->items_per_frame; + time_secs = p->time_secs; + time_ticks = p->time_ticks; + restart_streaming(); +} + +void +stop_rx_cmd(void) +{ + if (is_streaming()){ + streaming_p = false; + + // no samples, "now", not chained + sr_rx_ctrl->cmd = MK_RX_CMD(0, 1, 0); + + sr_rx_ctrl->time_secs = 0; + sr_rx_ctrl->time_ticks = 0; // enqueue command + } + +} + + +static void +setup_tx() +{ + sr_tx_ctrl->clear_state = 1; + bp_clear_buf(DSP_TX_BUF_0); + bp_clear_buf(DSP_TX_BUF_1); + + int tx_scale = 256; + int interp = 32; + + // setup some defaults + + dsp_tx_regs->freq = 0; + dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; + dsp_tx_regs->interp_rate = interp; +} + + +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY. This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet. A bit of a kludge + * but it should work. + */ +bool +fw_sets_seqno_inspector(dbsm_t *sm, int buf_this) // returns false +{ + // queue up another rx command when required + if (streaming_p && --streaming_frame_count == 0){ + streaming_frame_count = FRAMES_PER_CMD; + sr_rx_ctrl->time_secs = 0; + sr_rx_ctrl->time_ticks = 0; + } + + return false; // we didn't handle the packet +} +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ + uint32_t status = buffer_pool_status->status; + + dbsm_process_status(&dsp_tx_sm, status); + dbsm_process_status(&dsp_rx_sm, status); +} + +int +main(void) +{ + u2_init(); + + putstr("\nTxRx-NEWETH\n"); + print_mac_addr(ethernet_mac_addr()->addr); + newline(); + + ethernet_register_link_changed_callback(link_changed_callback); + ethernet_init(); + +#if 0 + // make bit 15 of Tx gpio's be a s/w output + hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); + hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + + output_regs->debug_mux_ctrl = 1; +#if 0 + hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); + hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); + hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + + // initialize double buffering state machine for ethernet -> DSP Tx + + dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, + &dsp_tx_recv_args, &dsp_tx_send_args, + eth_pkt_inspector); + + + // initialize double buffering state machine for DSP RX -> Ethernet + + if (FW_SETS_SEQNO){ + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + fw_sets_seqno_inspector); + } + else { + dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, + &dsp_rx_recv_args, &dsp_rx_send_args, + dbsm_nop_inspector); + } + + // tell app_common that this dbsm could be sending to the ethernet + ac_could_be_sending_to_eth = &dsp_rx_sm; + + + // program tx registers + setup_tx(); + + // kick off the state machine + dbsm_start(&dsp_tx_sm); + + //int which = 0; + + while(1){ + // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); + // which ^= 0x8000; + + buffer_irq_handler(0); + + int pending = pic_regs->pending; // poll for under or overrun + + if (pending & PIC_UNDERRUN_INT){ + dbsm_handle_tx_underrun(&dsp_tx_sm); + pic_regs->pending = PIC_UNDERRUN_INT; // clear interrupt + putchar('U'); + } + + if (pending & PIC_OVERRUN_INT){ + dbsm_handle_rx_overrun(&dsp_rx_sm); + pic_regs->pending = PIC_OVERRUN_INT; // clear pending interrupt + + // FIXME Figure out how to handle this robustly. + // Any buffers that are emptying should be allowed to drain... + + if (streaming_p){ + // restart_streaming(); + // FIXME report error + } + else { + // FIXME report error + } + putchar('O'); + } + } +} + +//-------------------compile time checks-------------------------------- +#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;} + +void compile_time_checks(void){ + COMPILE_TIME_ASSERT(sizeof(u2_eth_hdr_t) == 14); + COMPILE_TIME_ASSERT(sizeof(u2_ipv4_hdr_t) == 20); + COMPILE_TIME_ASSERT(sizeof(u2_udp_hdr_t) == 8); +} diff --git a/firmware/microblaze/bootstrap b/firmware/microblaze/bootstrap new file mode 100755 index 000000000..2343025cc --- /dev/null +++ b/firmware/microblaze/bootstrap @@ -0,0 +1,24 @@ +#!/bin/sh +# +# Copyright 2010 Ettus Research LLC +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +rm -rf *.cache + +aclocal +autoconf +libtoolize --automake +automake --add-missing --gnu --warnings=all diff --git a/firmware/microblaze/configure.ac b/firmware/microblaze/configure.ac new file mode 100644 index 000000000..4c96d607e --- /dev/null +++ b/firmware/microblaze/configure.ac @@ -0,0 +1,46 @@ +# +# Copyright 2010 Ettus Research LLC +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +################################################## +## Setup Autotools +################################################## +AC_PREREQ(2.60) +AC_INIT +AM_INIT_AUTOMAKE(uhd-mb, 0) + +################################################## +## Setup Compiler +################################################## +dnl Fix 2.64 cross compile detection for AVR and RTEMS +dnl by not trying to compile fopen. +m4_if(m4_defn([m4_PACKAGE_VERSION]), [2.64], + [m4_foreach([_GCC_LANG], [C, C++, Fortran, Fortran 77], + [m4_define([_AC_LANG_IO_PROGRAM(]_GCC_LANG[)], m4_defn([AC_LANG_PROGRAM(]_GCC_LANG[)]))])]) + +AC_PROG_CC([mb-gcc]) +LT_INIT + +################################################## +## Create Files +################################################## +AC_CONFIG_FILES([ \ + Makefile \ + apps/Makefile \ + include/Makefile \ + lib/Makefile \ +]) +AC_OUTPUT diff --git a/firmware/microblaze/divisors.py b/firmware/microblaze/divisors.py new file mode 100755 index 000000000..d31bd4dad --- /dev/null +++ b/firmware/microblaze/divisors.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +speeds = (9600, 19200, 38400, 57600, 115200, 230400) + +master_clk = 100e6 +wb_clk = master_clk / 2 + +def divisor(speed): + div0 = wb_clk // (speed * 16) + div1 = div0 + 1 + actual0 = actual_speed(div0) + actual1 = actual_speed(div1) + if abs(actual0 - speed) < abs(actual1 - speed): + return div0 + else: + return div1 + +def actual_speed(divisor): + return (wb_clk // divisor) / 16 + +def doit(speed): + div = divisor(speed) + actual = actual_speed(div) + rel_error = (actual - speed) / speed + print "target: %6d divisor: %6d actual: %11.4f %6.3f%%" % (speed, div, actual, rel_error*100) + +def main(): + print "wb_clk = %f" % (wb_clk,) + for s in speeds: + doit(s) + +if __name__ == '__main__': + main() + diff --git a/firmware/microblaze/include/.gitignore b/firmware/microblaze/include/.gitignore new file mode 100644 index 000000000..b336cc7ce --- /dev/null +++ b/firmware/microblaze/include/.gitignore @@ -0,0 +1,2 @@ +/Makefile +/Makefile.in diff --git a/firmware/microblaze/include/Makefile.am b/firmware/microblaze/include/Makefile.am new file mode 100644 index 000000000..1e4eadfe6 --- /dev/null +++ b/firmware/microblaze/include/Makefile.am @@ -0,0 +1,31 @@ +# +# Copyright 2008, 2010 Free Software Foundation, Inc. +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +noinst_HEADERS = \ + usrp2_cdefs.h \ + usrp2_eth_packet.h \ + usrp2_ipv4_packet.h \ + usrp2_udp_packet.h \ + usrp2_fpga_regs.h \ + usrp2_i2c_addr.h \ + usrp2_clock_bits.h \ + usrp2_types.h \ + network.h \ + vrt/bits.h \ + vrt/types.h diff --git a/firmware/microblaze/include/network.h b/firmware/microblaze/include/network.h new file mode 100644 index 000000000..4c6c43585 --- /dev/null +++ b/firmware/microblaze/include/network.h @@ -0,0 +1,82 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/*! + * Various networking related structures and defines + */ + +#ifndef INCLUDED_NETWORK_H +#define INCLUDED_NETWORK_H + +#include <stdint.h> +#include <stdbool.h> + +// Ethernet MAC address + +typedef struct { + uint8_t addr[6]; +} eth_mac_addr_t; + + +// IPv4 address + +#ifndef __cplusplus + +struct in_addr { + uint32_t s_addr; +}; + +static inline struct in_addr +make_in_addr(uint32_t addr) +{ + struct in_addr r; + r.s_addr = addr; + return r; +} + +static inline bool +in_addr_eq(const struct in_addr a, const struct in_addr b) +{ + return a.s_addr == b.s_addr; +} + +#define MK_IP_ADDR(a, b, c, d) \ + (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + + +// IPv4 AF_INET sockets: + +struct sockaddr_in { + short sin_family; // e.g. AF_INET, AF_INET6 + unsigned short sin_port; // e.g. htons(3490) + struct in_addr sin_addr; +}; + +static inline struct sockaddr_in +make_sockaddr_in(struct in_addr addr, int port) +{ + struct sockaddr_in r; + r.sin_family = 0; // not used + r.sin_port = port; + r.sin_addr = addr; + return r; +} + +#endif + +#endif /* INCLUDED_NETWORK_H */ diff --git a/firmware/microblaze/include/usrp2_cdefs.h b/firmware/microblaze/include/usrp2_cdefs.h new file mode 100644 index 000000000..71395cda8 --- /dev/null +++ b/firmware/microblaze/include/usrp2_cdefs.h @@ -0,0 +1,34 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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. + */ + +#ifndef INCLUDED_USRP2_CDEFS_H +#define INCLUDED_USRP2_CDEFS_H + +/* C++ needs to know that types and declarations are C, not C++. */ +#ifdef __cplusplus +# define __U2_BEGIN_DECLS extern "C" { +# define __U2_END_DECLS } +#else +# define __U2_BEGIN_DECLS +# define __U2_END_DECLS +#endif + +#endif /* INCLUDED_USRP2_CDEFS_H */ diff --git a/firmware/microblaze/include/usrp2_clock_bits.h b/firmware/microblaze/include/usrp2_clock_bits.h new file mode 100644 index 000000000..d2052e941 --- /dev/null +++ b/firmware/microblaze/include/usrp2_clock_bits.h @@ -0,0 +1,55 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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. + */ +#ifndef INCLUDED_USRP2_CLOCK_BITS_H +#define INCLUDED_USRP2_CLOCK_BITS_H + +#define _MC_WE_LOCK 0x0001 +#define _MC_MIMO_CLK_INPUT 0x0002 // else SMA input + +/* + * Derived masks (use these): + * + * We get our input from 1 of three places: + * Our free running oscilator, our SMA connector, or from the MIMO connector + */ +#define MC_WE_DONT_LOCK 0x0000 +#define MC_WE_LOCK_TO_SMA (_MC_WE_LOCK | 0) +#define MC_WE_LOCK_TO_MIMO (_MC_WE_LOCK | _MC_MIMO_CLK_INPUT) + +/* + * Independent of the source of the clock, we may or may not drive our + * clock onto the mimo connector. Note that there are dedicated clock + * signals in each direction, so disaster doesn't occurs if we're + * unnecessarily providing clock. + */ +#define MC_PROVIDE_CLK_TO_MIMO 0x0004 + +#define MC_REF_CLK_MASK 0x0f + +#define MC_PPS_SOURCE_SMA (0x00 << 4) +#define MC_PPS_SOURCE_MIMO (0x01 << 4) + +#define MC_PPS_POLARITY_NEG (0x00 << 5) +#define MC_PPS_POLARITY_POS (0x01 << 5) + +#endif /* INCLUDED_USRP2_CLOCK_BITS_H */ diff --git a/firmware/microblaze/include/usrp2_eth_packet.h b/firmware/microblaze/include/usrp2_eth_packet.h new file mode 100644 index 000000000..7d5b84f00 --- /dev/null +++ b/firmware/microblaze/include/usrp2_eth_packet.h @@ -0,0 +1,405 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* + * Copyright 2007,2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_USRP2_ETH_PACKET_H +#define INCLUDED_USRP2_ETH_PACKET_H + +#include "usrp2_cdefs.h" +#include "network.h" + +__U2_BEGIN_DECLS + +#define U2_DATA_ETHERTYPE 0xBEEF // used in our data frames +#define U2_CTRL_ETHERTYPE 0xBEF0 // used in our control frames +#define MAC_CTRL_ETHERTYPE 0x8808 // used in PAUSE frames +#define ETHERTYPE_IPV4 0x0800 // used in ip packets + +/* + * All these data structures are BIG-ENDIAN on the wire + */ + +// FIXME gcc specific. Really ought to come from compiler.h +#define _AL4 __attribute__((aligned (4))) + +/*! + * \brief The classic 14-byte ethernet header + */ +typedef struct { + eth_mac_addr_t dst; + eth_mac_addr_t src; + uint16_t ethertype; +} __attribute__((packed)) u2_eth_hdr_t; + +/*! + * \brief consolidated packet: ethernet header + */ +typedef struct { + u2_eth_hdr_t ehdr; +} u2_eth_packet_t; + +/*! + * \brief consolidated packet: padding + ethernet header + */ +typedef struct { + uint16_t padding; + u2_eth_hdr_t ehdr; +} u2_eth_packet_pad_before_t; + +/* + * Opcodes for control channel + * + * Reply opcodes are the same as the request opcode with the OP_REPLY_BIT set (0x80). + */ +#define OP_REPLY_BIT 0x80 + +#define OP_EOP 0 // marks last subpacket in packet + +#define OP_ID 1 +#define OP_ID_REPLY (OP_ID | OP_REPLY_BIT) +#define OP_BURN_MAC_ADDR 2 +#define OP_BURN_MAC_ADDR_REPLY (OP_BURN_MAC_ADDR | OP_REPLY_BIT) +#define OP_CONFIG_RX_V2 4 +#define OP_CONFIG_RX_REPLY_V2 (OP_CONFIG_RX_V2 | OP_REPLY_BIT) +#define OP_CONFIG_TX_V2 5 +#define OP_CONFIG_TX_REPLY_V2 (OP_CONFIG_TX_V2 | OP_REPLY_BIT) +#define OP_START_RX_STREAMING 6 +#define OP_START_RX_STREAMING_REPLY (OP_START_RX_STREAMING | OP_REPLY_BIT) +#define OP_STOP_RX 7 +#define OP_STOP_RX_REPLY (OP_STOP_RX | OP_REPLY_BIT) +#define OP_CONFIG_CLOCK 8 +#define OP_CONFIG_CLOCK_REPLY (OP_CONFIG_CLOCK | OP_REPLY_BIT) +#define OP_DBOARD_INFO 9 +#define OP_DBOARD_INFO_REPLY (OP_DBOARD_INFO | OP_REPLY_BIT) +#define OP_PEEK 11 +#define OP_PEEK_REPLY (OP_PEEK | OP_REPLY_BIT) +#define OP_POKE 12 +#define OP_POKE_REPLY (OP_POKE | OP_REPLY_BIT) +#define OP_SET_TX_LO_OFFSET 13 +#define OP_SET_TX_LO_OFFSET_REPLY (OP_SET_TX_LO_OFFSET | OP_REPLY_BIT) +#define OP_SET_RX_LO_OFFSET 14 +#define OP_SET_RX_LO_OFFSET_REPLY (OP_SET_RX_LO_OFFSET | OP_REPLY_BIT) +#define OP_RESET_DB 15 +#define OP_RESET_DB_REPLY (OP_RESET_DB | OP_REPLY_BIT) +#define OP_GPIO_SET_DDR 17 +#define OP_GPIO_SET_DDR_REPLY (OP_GPIO_SET_DDR | OP_REPLY_BIT) +#define OP_GPIO_SET_SELS 18 +#define OP_GPIO_SET_SELS_REPLY (OP_GPIO_SET_SELS | OP_REPLY_BIT) +#define OP_GPIO_READ 19 +#define OP_GPIO_READ_REPLY (OP_GPIO_READ | OP_REPLY_BIT) +#define OP_GPIO_WRITE 20 +#define OP_GPIO_WRITE_REPLY (OP_GPIO_WRITE | OP_REPLY_BIT) +#define OP_GPIO_STREAM 21 +#define OP_GPIO_STREAM_REPLY (OP_GPIO_STREAM | OP_REPLY_BIT) +#define OP_SET_TIME 22 +#define OP_SET_TIME_REPLY (OP_SET_TIME | OP_REPLY_BIT) + +/* + * All subpackets are a multiple of 4 bytes long. + * All subpackets start with an 8-bit opcode, an 8-bit len and an 8-bit rid. + */ +#define MAX_SUBPKT_LEN 252 + +/*! + * \brief Generic request and reply packet + * + * Used by: + * OP_EOP, OP_BURN_MAC_ADDR_REPLY, OP_START_RX_STREAMING_REPLY, + * OP_STOP_RX_REPLY, OP_DBOARD_INFO, OP_SYNC_TO_PPS + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t ok; // bool +} _AL4 op_generic_t; + +/*! + * \brief Set the ticks and secs on a usrp2 + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t type; + uint32_t time_secs; + uint32_t time_ticks; +} _AL4 op_set_time_t; + +typedef enum { + OP_SET_TIME_TYPE_NOW, + OP_SET_TIME_TYPE_PPS +} op_set_time_type_t; + +/*! + * \brief Reply info from a USRP2 + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t mbz; + eth_mac_addr_t addr; + uint16_t hw_rev; + uint8_t fpga_md5sum[16]; + uint8_t sw_md5sum[16]; +} _AL4 op_id_reply_t; + +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t mbz; + uint32_t items_per_frame; // # of 32-bit data items; MTU=1500: [9,371] + uint32_t time_secs; + uint32_t time_ticks; +} _AL4 op_start_rx_streaming_t; + +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + eth_mac_addr_t addr; +} _AL4 op_burn_mac_addr_t; + +/*! + * \brief Configure receiver + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t mbz; + // bitmask indicating which of the following fields are valid + uint16_t valid; + uint16_t gain; // fxpt_db (Q9.7) + uint32_t freq_hi; // high 32-bits of 64-bit fxpt_freq (Q44.20) + uint32_t freq_lo; // low 32-bits of 64-bit fxpt_freq (Q44.20) + uint32_t decim; // desired decimation factor (NOT -1) + uint32_t scale_iq; // (scale_i << 16) | scale_q [16.0 format] +} _AL4 op_config_rx_v2_t; + +// bitmask for "valid" field. If the bit is set, there's +// meaningful data in the corresonding field. + +#define CFGV_GAIN 0x0001 // gain field is valid +#define CFGV_FREQ 0x0002 // target_freq field is valid +#define CFGV_INTERP_DECIM 0x0004 // interp or decim is valid +#define CFGV_SCALE_IQ 0x0008 // scale_iq is valid + +/*! + * \brief Reply to receiver configuration + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t mbz; + + uint16_t ok; // config was successful (bool) + uint16_t inverted; // spectrum is inverted (bool) + + // RF frequency that corresponds to DC in the IF (fxpt_freq) + uint32_t baseband_freq_hi; + uint32_t baseband_freq_lo; + // DDC frequency (fxpt_freq) + uint32_t ddc_freq_hi; + uint32_t ddc_freq_lo; + // residual frequency (fxpt_freq) + uint32_t residual_freq_hi; + uint32_t residual_freq_lo; + +} _AL4 op_config_rx_reply_v2_t; + +/*! + * \brief Configure transmitter + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t mbz; + + // bitmask indicating which of the following fields are valid + uint16_t valid; + uint16_t gain; // fxpt_db (Q9.7) + uint32_t freq_hi; // high 32-bits of 64-bit fxpt_freq (Q44.20) + uint32_t freq_lo; // low 32-bits of 64-bit fxpt_freq (Q44.20) + uint32_t interp; // desired interpolation factor (NOT -1) + uint32_t scale_iq; // (scale_i << 16) | scale_q [16.0 format] +} _AL4 op_config_tx_v2_t; + +/*! + * \brief Reply to configure transmitter + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t mbz; + + uint16_t ok; // config was successful (bool) + uint16_t inverted; // spectrum is inverted (bool) + + // RF frequency that corresponds to DC in the IF (fxpt_freq) + uint32_t baseband_freq_hi; + uint32_t baseband_freq_lo; + // DUC frequency (fxpt_freq) + uint32_t duc_freq_hi; + uint32_t duc_freq_lo; + // residual frequency (fxpt_freq) + uint32_t residual_freq_hi; + uint32_t residual_freq_lo; + +} _AL4 op_config_tx_reply_v2_t; + +/*! + * \brief Configure clocking, etc (uses generic reply) + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t flags; +} op_config_clock_t; + +/*! + * \brief High-level information about daughterboards + */ +typedef struct { + int32_t dbid; //< d'board ID (-1 none, -2 invalid eeprom) + uint32_t freq_min_hi; //< high 32-bits of 64-bit fxpt_freq (Q44.20) + uint32_t freq_min_lo; //< low 32-bits of 64-bit fxpt_freq (Q44.20) + uint32_t freq_max_hi; //< high 32-bits of 64-bit fxpt_freq (Q44.20) + uint32_t freq_max_lo; //< low 32-bits of 64-bit fxpt_freq (Q44.20) + uint16_t gain_min; //< min gain that can be set. fxpt_db (Q9.7) + uint16_t gain_max; //< max gain that can be set. fxpt_db (Q9.7) + uint16_t gain_step_size; //< fxpt_db (Q9.7) +} u2_db_info_t; + + +/*! + * \brief Reply to d'board info request + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t ok; // request was successful (bool) + + u2_db_info_t tx_db_info; + u2_db_info_t rx_db_info; +} _AL4 op_dboard_info_reply_t; + +/*! + * \brief Read from Wishbone memory + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t mbz; + uint32_t addr; + uint32_t bytes; +} _AL4 op_peek_t; + +/*! + * \brief Write to Wishbone memory + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t mbz; + uint32_t addr; + // Words follow here +} _AL4 op_poke_t; + +/* + * Common structure for commands with a single frequency param + * (e.g., set_*_lo_offset, set_*_bw) + */ +typedef struct { + uint8_t opcode; + uint8_t len; + uint8_t rid; + uint8_t mbz; + uint32_t freq_hi; //< high 32-bits of 64-bit fxpt_freq (Q44.20) + uint32_t freq_lo; //< low 32-bits of 64-bit fxpt_freq (Q44.20) +} _AL4 op_freq_t; + +/* + * Structures for commands in GPIO system + */ +typedef struct { + uint8_t opcode; // OP_GPIO_SET_DDR, OP_GPIO_WRITE, OP_GPIO_STREAM + uint8_t len; + uint8_t rid; + uint8_t bank; + uint16_t value; + uint16_t mask; +} _AL4 op_gpio_t; + +typedef struct { + uint8_t opcode; // OP_GPIO_SET_SELS + uint8_t len; + uint8_t rid; + uint8_t bank; + uint8_t sels[16]; +} _AL4 op_gpio_set_sels_t; + +typedef struct { + uint8_t opcode; // OP_GPIO_READ_REPLY + uint8_t len; + uint8_t rid; + uint8_t ok; + uint16_t mbz; + uint16_t value; +} _AL4 op_gpio_read_reply_t; + +/* + * ================================================================ + * union of all of subpacket types + * ================================================================ + */ +typedef union { + + op_generic_t op_generic; + op_id_reply_t op_id_reply; + op_start_rx_streaming_t op_start_rx_streaming; + op_burn_mac_addr_t op_burn_mac_addr; + op_config_rx_v2_t op_config_rx_v2; + op_config_rx_reply_v2_t op_config_rx_reply_v2; + op_config_tx_v2_t op_config_tx_v2; + op_config_tx_reply_v2_t op_config_tx_reply_v2; + op_config_clock_t op_config_clock; + op_peek_t op_peek; + op_poke_t op_poke; + op_freq_t op_freq; + op_gpio_t op_gpio; + op_gpio_set_sels_t op_gpio_set_sels; + op_gpio_read_reply_t op_gpio_read_reply; + op_set_time_t op_set_time; + +} u2_subpkt_t; + + +__U2_END_DECLS + +#endif /* INCLUDED_USRP2_ETH_PACKET_H */ diff --git a/firmware/microblaze/include/usrp2_fpga_regs.h b/firmware/microblaze/include/usrp2_fpga_regs.h new file mode 100644 index 000000000..b0f83df60 --- /dev/null +++ b/firmware/microblaze/include/usrp2_fpga_regs.h @@ -0,0 +1,80 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_USRP2_FPGA_REGS_H +#define INCLUDED_USSRP2_FPGA_REGS_H + +#include "usrp2_cdefs.h" + +__U2_BEGIN_DECLS + +// ---------------------------------------------------------------- + +#define DSP_CORE_TX_BASE 128 + +// DUC center frequency tuning word (phase increment) +#define FR_TX_FREQ_0 (0 + DSP_CORE_TX_BASE) + +// I & Q output scaling, 16.0 format ((I_SCALE << 16) | Q_SCALE) +#define FR_TX_SCALE_0 (1 + DSP_CORE_TX_BASE) + +// Tx interpolation rate (set to 1 less than desired rate) +#define FR_TX_INTERP_RATE_0 (2 + DSP_CORE_TX_BASE) + +// Write 1 (actually anything) to clear tx state +#define FR_TX_CLEAR_STATE_0 (3 + DSP_CORE_TX_BASE) + +// ---------------------------------------------------------------- + +#define DSP_CORE_RX_BASE 160 + +// DDC center frequency tuning word (phase increment) +#define FR_RX_FREQ_0 (0 + DSP_CORE_RX_BASE) + +// I & Q input scaling, 16.0 format ((I_SCALE << 16) | Q_SCALE) +#define FR_RX_SCALE_0 (1 + DSP_CORE_RX_BASE) + +// Rx decimation rate (set to 1 less than desired rate) +#define FR_RX_DECIM_RATE_0 (2 + DSP_CORE_RX_BASE) + +// The next two registers concatenated are the Rx command register. +// +// Writing FR_RX_TIME_TO_RX_0 writes the concatenated value into the +// cmd queue. Thus, if you're writing both, be sure to write +// FR_RX_QTY_0 first. +// +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Timestamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +#define FR_RX_TIME_TO_RX (3 + DSP_CORE_RX_BASE) + +// 23-bits 9-bits +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | number_of_lines | lines_per_frame | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +#define FR_RX_QTY_0 (4 + DSP_CORE_RX_BASE) + +// write a 1 (anything actually) to clear the overrun +#define FR_RX_CLR_OVERRUN_0 (5 + DSP_CORE_RX_BASE) + + +__U2_END_DECLS + +#endif /* INCLUDED_USRP2_FPGA_REGS_H */ diff --git a/firmware/microblaze/include/usrp2_i2c_addr.h b/firmware/microblaze/include/usrp2_i2c_addr.h new file mode 100644 index 000000000..f25996903 --- /dev/null +++ b/firmware/microblaze/include/usrp2_i2c_addr.h @@ -0,0 +1,82 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_USRP2_I2C_ADDR_H +#define INCLUDED_USRP2_I2C_ADDR_H + +#include "usrp2_cdefs.h" + +__U2_BEGIN_DECLS + +// I2C addresses + +#define I2C_DEV_EEPROM 0x50 // 24LC02[45]: 7-bits 1010xxx + +#define I2C_ADDR_MBOARD (I2C_DEV_EEPROM | 0x0) +#define I2C_ADDR_TX_A (I2C_DEV_EEPROM | 0x4) +#define I2C_ADDR_RX_A (I2C_DEV_EEPROM | 0x5) + + +// format of USRP2 motherboard rom +// 00: 0x00 h/w rev (LSB) +// 01: 0x00 h/w rev (MSB) +// 02: 0x00 MAC addr 0 +// 03: 0x50 MAC addr 1 +// 04: 0xC2 MAC addr 2 +// 05: 0x85 MAC addr 3 +// 06: 0x3. MAC addr 4 +// 07: 0x.. MAC addr 5 + +#define MBOARD_REV_LSB 0x00 +#define MBOARD_REV_MSB 0x01 +#define MBOARD_MAC_ADDR 0x02 + + +// format of daughterboard EEPROM +// 00: 0xDB code for ``I'm a daughterboard'' +// 01: .. Daughterboard ID (LSB) +// 02: .. Daughterboard ID (MSB) +// 03: .. io bits 7-0 direction (bit set if it's an output from m'board) +// 04: .. io bits 15-8 direction (bit set if it's an output from m'board) +// 05: .. ADC0 DC offset correction (LSB) +// 06: .. ADC0 DC offset correction (MSB) +// 07: .. ADC1 DC offset correction (LSB) +// 08: .. ADC1 DC offset correction (MSB) +// ... +// 1f: .. negative of the sum of bytes [0x00, 0x1e] + +#define DB_EEPROM_MAGIC 0x00 +#define DB_EEPROM_MAGIC_VALUE 0xDB +#define DB_EEPROM_ID_LSB 0x01 +#define DB_EEPROM_ID_MSB 0x02 +#define DB_EEPROM_OE_LSB 0x03 +#define DB_EEPROM_OE_MSB 0x04 +#define DB_EEPROM_OFFSET_0_LSB 0x05 // offset correction for ADC or DAC 0 +#define DB_EEPROM_OFFSET_0_MSB 0x06 +#define DB_EEPROM_OFFSET_1_LSB 0x07 // offset correction for ADC or DAC 1 +#define DB_EEPROM_OFFSET_1_MSB 0x08 +#define DB_EEPROM_CHKSUM 0x1f + +#define DB_EEPROM_CLEN 0x20 // length of common portion of eeprom + +#define DB_EEPROM_CUSTOM_BASE DB_EEPROM_CLEN // first avail offset for + // daughterboard specific use +__U2_END_DECLS + +#endif /* INCLUDED_USRP2_I2C_ADDR_H */ + diff --git a/firmware/microblaze/include/usrp2_ipv4_packet.h b/firmware/microblaze/include/usrp2_ipv4_packet.h new file mode 100644 index 000000000..2822dee89 --- /dev/null +++ b/firmware/microblaze/include/usrp2_ipv4_packet.h @@ -0,0 +1,52 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRP2_IPV4_PACKET_H +#define INCLUDED_USRP2_IPV4_PACKET_H + +#include "usrp2_cdefs.h" +#include "network.h" + +__U2_BEGIN_DECLS + +/*! + * \brief The classic ipv4 header + */ +typedef struct { + unsigned int ip_v:4; /* both fields are 4 bits */ + unsigned int ip_hl:4; + uint8_t ip_tos; + uint16_t ip_len; + uint16_t ip_id; + uint16_t ip_off; + uint8_t ip_ttl; + uint8_t ip_p; + uint16_t ip_sum; + struct in_addr ip_src; + struct in_addr ip_dst; +} __attribute__((packed)) u2_ipv4_hdr_t; + +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + +#define IP_PROTO_UDP 17 + +__U2_END_DECLS + +#endif /* INCLUDED_USRP2_IPV4_PACKET_H */ diff --git a/firmware/microblaze/include/usrp2_types.h b/firmware/microblaze/include/usrp2_types.h new file mode 100644 index 000000000..dd2bcf1ed --- /dev/null +++ b/firmware/microblaze/include/usrp2_types.h @@ -0,0 +1,112 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_USRP2_TYPES_H +#define INCLUDED_USRP2_TYPES_H + +#include <usrp2_cdefs.h> +#include <stdint.h> + +__U2_BEGIN_DECLS + +/*! + * \brief Fixed point representation of a frequency in Hertz (VITA-49 compatible) + * + * 64-bit two's complement, with the radix point 20 bits up from the bottom. + * Q44.20 format (20 bits to the right of the radix point) + * + * Values range from +/- 8.79 terahertz with a resolution of 0.95 microhertz. + */ +typedef int64_t u2_fxpt_freq_t; + +#define U2_FPF_RP 20 // location of radix point in u2_fxpt_freq_t + +// macro so we can init structs at compile time +#define U2_DOUBLE_TO_FXPT_FREQ(f) (int64_t)((f) * (1LL << U2_FPF_RP)) + +static inline u2_fxpt_freq_t +u2_double_to_fxpt_freq(double f) +{ + return U2_DOUBLE_TO_FXPT_FREQ(f); +} + +static inline int +u2_fxpt_freq_round_to_int(u2_fxpt_freq_t fx) +{ + return (int)((fx+(1<<(U2_FPF_RP-1)))>>U2_FPF_RP); +} + +static inline double +u2_fxpt_freq_to_double(u2_fxpt_freq_t fx) +{ + return ((double) fx) * 1.0/(1 << U2_FPF_RP); +} + +static inline uint32_t +u2_fxpt_freq_hi(u2_fxpt_freq_t f) +{ + return ((f >> 32) & 0xffffffff); +} + +static inline uint32_t +u2_fxpt_freq_lo(u2_fxpt_freq_t f) +{ + return (f & 0xffffffff); +} + +static inline u2_fxpt_freq_t +u2_fxpt_freq_from_hilo(uint32_t hi, uint32_t lo) +{ + return (((u2_fxpt_freq_t) hi) << 32) | lo; +} + +/*! + * \brief Fixed point representation of a gain in dB (VITA-49 compatible) + * + * 16-bit two's complement, with the radix point 7 bits up from the bottom. + * Q9.7 format (7 bits to the right of the radix point) + */ +typedef int16_t u2_fxpt_gain_t; + +#define U2_FPG_RP 7 // location of radix point in u2_fxpt_gain_t + +// macro so we can init structs at compile time +#define U2_DOUBLE_TO_FXPT_GAIN(g) (int16_t)((g) * (1 << U2_FPG_RP)) + +static inline u2_fxpt_gain_t +u2_double_to_fxpt_gain(double g) +{ + return U2_DOUBLE_TO_FXPT_GAIN(g); +} + +static inline float +u2_fxpt_gain_to_double(u2_fxpt_gain_t fx) +{ + return ((double) fx) * 1.0/(1 << U2_FPG_RP); +} + +static inline int +u2_fxpt_gain_round_to_int(u2_fxpt_gain_t fx) +{ + return (int)((fx+(1<<(U2_FPG_RP-1)))>>U2_FPG_RP); +} + + +__U2_END_DECLS + + +#endif /* INCLUDED_USRP2_TYPES_H */ diff --git a/firmware/microblaze/include/usrp2_udp_packet.h b/firmware/microblaze/include/usrp2_udp_packet.h new file mode 100644 index 000000000..4739d4c49 --- /dev/null +++ b/firmware/microblaze/include/usrp2_udp_packet.h @@ -0,0 +1,37 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRP2_UDP_PACKET_H +#define INCLUDED_USRP2_UDP_PACKET_H + +#include "usrp2_cdefs.h" + +__U2_BEGIN_DECLS + +/*! + * \brief The classic udp header + */ +typedef struct { + uint16_t src_port; + uint16_t dst_port; + uint16_t length; + uint16_t checksum; +} __attribute__((packed)) u2_udp_hdr_t; + +__U2_END_DECLS + +#endif /* INCLUDED_USRP2_UDP_PACKET_H */ diff --git a/firmware/microblaze/include/vrt/bits.h b/firmware/microblaze/include/vrt/bits.h new file mode 100644 index 000000000..54eeec7b4 --- /dev/null +++ b/firmware/microblaze/include/vrt/bits.h @@ -0,0 +1,92 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_VRT_BITS_H +#define INCLUDED_VRT_BITS_H + +#include <stdint.h> + + +/* VRT Header bits */ + +#define VRTH_PT_MASK (0xf << 28) +#define VRTH_PT_IF_DATA_NO_SID (0x0 << 28) // IF-Data, no stream id +#define VRTH_PT_IF_DATA_WITH_SID (0x1 << 28) // IF-Data, w/ stream id +#define VRTH_PT_EXT_DATA_NO_SID (0x2 << 28) +#define VRTH_PT_EXT_DATA_WITH_SID (0x3 << 28) +#define VRTH_PT_IF_CONTEXT (0x4 << 28) +#define VRTH_PT_EXT_CONTEXT (0x5 << 28) + +#define VRTH_HAS_CLASSID (1 << 27) +#define VRTH_HAS_TRAILER (1 << 26) // Data pkts only +#define VRTH_START_OF_BURST (1 << 25) // Data (Tx) pkts only +#define VRTH_END_OF_BURST (1 << 24) // Data (Tx) pkts only +#define VRTH_TSM (1 << 24) // Context pkts only + +#define VRTH_TSI_MASK (0x3 << 22) +#define VRTH_TSI_NONE (0x0 << 22) +#define VRTH_TSI_UTC (0x1 << 22) +#define VRTH_TSI_GPS (0x2 << 22) +#define VRTH_TSI_OTHER (0x3 << 22) + +#define VRTH_TSF_MASK (0x3 << 20) +#define VRTH_TSF_NONE (0x0 << 20) +#define VRTH_TSF_SAMPLE_CNT (0x1 << 20) +#define VRTH_TSF_REAL_TIME_PS (0x2 << 20) +#define VRTH_TSF_FREE_RUNNING (0x3 << 20) + +#define VRTH_PKT_CNT_SHIFT 16 +#define VRTH_PKT_CNT_MASK (0xf << 16) + +#define VRTH_PKT_SIZE_MASK 0xffff + + +static inline int +vrth_pkt_cnt(uint32_t h) +{ + return (h & VRTH_PKT_CNT_MASK) >> 16; +} + +static inline int +vrth_pkt_size(uint32_t h) +{ + return h & VRTH_PKT_SIZE_MASK; +} + +/* + * Trailer bits + */ +#define TR_E (1 << 8) + +#define TR_ENABLE(x) ((x) << 20) +#define TR_STATE(x) ((x) << 8) + +// Use these with TR_ENABLE and TR_STATE +#define TR_CAL_TIME (1 << 11) +#define TR_VALID_DATA (1 << 10) +#define TR_REF_LOCK (1 << 9) +#define TR_AGC (1 << 8) +#define TR_DETECTED_SIG (1 << 7) +#define TR_SPECTRAL_INVERSION (1 << 6) +#define TR_OVER_RANGE (1 << 5) +#define TR_SAMPLE_LOSS (1 << 4) +#define TR_USER_3 (1 << 3) +#define TR_USER_2 (1 << 2) +#define TR_USER_1 (1 << 1) +#define TR_USER_0 (1 << 0) + +#endif /* INCLUDED_VRT_BITS_H */ diff --git a/firmware/microblaze/include/vrt/types.h b/firmware/microblaze/include/vrt/types.h new file mode 100644 index 000000000..edfa4ec37 --- /dev/null +++ b/firmware/microblaze/include/vrt/types.h @@ -0,0 +1,138 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_VRT_TYPES_H +#define INCLUDED_VRT_TYPES_H + +#include <stdint.h> + +/* macros for dealing with fixed point numbers */ +#define _FXPT_C(_type, _x, _rp) ((_type)((_x)*(1ll << _rp))) +#define _FXPT_TO_INT(_x, _one) (((_x) + ((_one)/2))/(_one)) +#define _FXPT_TO_DOUBLE(_x, _one) ((double)(_x) * (1.0/(_one))) + +/*********************************************************************** + * The VRT Altitude Type (meters) + **********************************************************************/ +typedef int32_t vrt_altitude_t; +#define VRT_ALTITUDE_RP 5 +#define VRT_ALTITUDE_C(_x) _FXPT_C(vrt_altitude_t, _x, VRT_ALTITUDE_RP) + +static inline vrt_altitude_t +double_to_vrt_altitude(double num){ + return VRT_ALTITUDE_C(num); +} + +static inline int32_t +vrt_altitude_round_to_int(vrt_altitude_t fx){ + return _FXPT_TO_INT(fx, VRT_ALTITUDE_C(1)); +} + +static inline double +vrt_altitude_to_double(vrt_altitude_t fx){ + return _FXPT_TO_DOUBLE(fx, VRT_ALTITUDE_C(1)); +} + +/*********************************************************************** + * The VRT Geolocation Angle Type (degrees) + **********************************************************************/ +typedef int32_t vrt_geo_angle_t; +#define VRT_GEO_ANGLE_RP 22 +#define VRT_GEO_ANGLE_C(_x) _FXPT_C(vrt_geo_angle_t, _x, VRT_GEO_ANGLE_RP) + +static inline vrt_geo_angle_t +double_to_vrt_geo_angle(double num){ + return VRT_GEO_ANGLE_C(num); +} + +static inline int16_t +vrt_geo_angle_round_to_int(vrt_geo_angle_t fx){ + return _FXPT_TO_INT(fx, VRT_GEO_ANGLE_C(1)); +} + +static inline double +vrt_geo_angle_to_double(vrt_geo_angle_t fx){ + return _FXPT_TO_DOUBLE(fx, VRT_GEO_ANGLE_C(1)); +} + +/*********************************************************************** + * The VRT Frequency Type (Hz) + **********************************************************************/ +typedef int64_t vrt_freq_t; +#define VRT_FREQ_RP 20 +#define VRT_FREQ_C(_x) _FXPT_C(vrt_freq_t, _x, VRT_FREQ_RP) + +static inline vrt_freq_t +double_to_vrt_freq(double num){ + return VRT_FREQ_C(num); +} + +static inline int64_t +vrt_freq_round_to_int(vrt_freq_t fx){ + return _FXPT_TO_INT(fx, VRT_FREQ_C(1)); +} + +static inline double +vrt_freq_to_double(vrt_freq_t fx){ + return _FXPT_TO_DOUBLE(fx, VRT_FREQ_C(1)); +} + +/*********************************************************************** + * The VRT Gain Type (dB) + **********************************************************************/ +typedef int16_t vrt_gain_t; +#define VRT_GAIN_RP 7 +#define VRT_GAIN_C(_x) _FXPT_C(vrt_gain_t, _x, VRT_GAIN_RP) + +static inline vrt_gain_t +double_to_vrt_gain(double num){ + return VRT_GAIN_C(num); +} + +static inline int16_t +vrt_gain_round_to_int(vrt_gain_t fx){ + return _FXPT_TO_INT(fx, VRT_GAIN_C(1)); +} + +static inline double +vrt_gain_to_double(vrt_gain_t fx){ + return _FXPT_TO_DOUBLE(fx, VRT_GAIN_C(1)); +} + +/*********************************************************************** + * The VRT Temperature Type (Celcius) + **********************************************************************/ +typedef int16_t vrt_temp_t; +#define VRT_TEMP_RP 6 +#define VRT_TEMP_C(_x) _FXPT_C(vrt_temp_t, _x, VRT_TEMP_RP) + +static inline vrt_temp_t +double_to_vrt_temp(double num){ + return VRT_TEMP_C(num); +} + +static inline int16_t +vrt_temp_round_to_int(vrt_temp_t fx){ + return _FXPT_TO_INT(fx, VRT_TEMP_C(1)); +} + +static inline double +vrt_temp_to_double(vrt_temp_t fx){ + return _FXPT_TO_DOUBLE(fx, VRT_TEMP_C(1)); +} + +#endif /* INCLUDED_VRT_TYPES_H */ diff --git a/firmware/microblaze/lib/.gitignore b/firmware/microblaze/lib/.gitignore new file mode 100644 index 000000000..5d838bf6c --- /dev/null +++ b/firmware/microblaze/lib/.gitignore @@ -0,0 +1,40 @@ +*~ +/*-stamp +/*.a +/*.bin +/*.dump +/*.log +/*.rom +/.deps +/Makefile +/Makefile.in +/aclocal.m4 +/autom4te.cache +/blink_leds +/blink_leds2 +/build +/compile +/config.h +/config.h.in +/config.log +/config.status +/configure +/depcomp +/eth_test +/gen_eth_packets +/ibs_rx_test +/ibs_tx_test +/install-sh +/libtool +/ltmain.sh +/missing +/py-compile +/rcv_eth_packets +/run_tests.sh +/stamp-h1 +/test1 +/test_phy_comm +/timer_test +/buf_ram_test +/buf_ram_zero +/hello diff --git a/firmware/microblaze/lib/Makefile.am b/firmware/microblaze/lib/Makefile.am new file mode 100644 index 000000000..9119a4391 --- /dev/null +++ b/firmware/microblaze/lib/Makefile.am @@ -0,0 +1,98 @@ +# +# Copyright 2007 Free Software Foundation, Inc. +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +noinst_LIBRARIES = \ + libu2fw.a + + +libu2fw_a_SOURCES = \ + abort.c \ + ad9510.c \ + ad9777.c \ + bsm12.c \ + buffer_pool.c \ + clocks.c \ + db_basic.c \ + db_dbsrx.c \ + db_init.c \ + db_rfx.c \ + db_tvrx.c \ + db_xcvr2450.c \ + dbsm.c \ + eeprom.c \ + ethernet.c \ + eth_mac.c \ + _exit.c \ + exit.c \ + hal_io.c \ + hal_uart.c \ + i2c.c \ + lsadc.c \ + lsdac.c \ + mdelay.c \ + memcpy_wa.c \ + memset_wa.c \ + nonstdio.c \ + pic.c \ + print_mac_addr.c \ + print_rmon_regs.c \ + print_fxpt.c \ + print_buffer.c \ + printf.c \ + sd.c \ + spi.c \ + u2_init.c + + +noinst_HEADERS = \ + ad9510.h \ + ad9777.h \ + ad9777_regs.h \ + bsm12.h \ + buffer_pool.h \ + clocks.h \ + db.h \ + db_base.h \ + dbsm.h \ + eth_mac.h \ + eth_mac_regs.h \ + eth_phy.h \ + ethernet.h \ + hal_io.h \ + hal_uart.h \ + i2c.h \ + lsadc.h \ + lsdac.h \ + mdelay.h \ + memcpy_wa.h \ + memory_map.h \ + memset_wa.h \ + nonstdio.h \ + pic.h \ + print_rmon_regs.h \ + sd.h \ + spi.h \ + stdint.h \ + stdio.h \ + u2_init.h \ + usrp2_bytesex.h \ + wb16550.h + +EXTRA_DIST = \ + microblaze.ld diff --git a/firmware/microblaze/lib/_exit.c b/firmware/microblaze/lib/_exit.c new file mode 100644 index 000000000..9b40ab2ee --- /dev/null +++ b/firmware/microblaze/lib/_exit.c @@ -0,0 +1,27 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Stub so we can compile using 3.4 based mb-gcc + */ +void +_exit(int status) +{ + while (1) + ; +} diff --git a/firmware/microblaze/lib/abort.c b/firmware/microblaze/lib/abort.c new file mode 100644 index 000000000..d1d709392 --- /dev/null +++ b/firmware/microblaze/lib/abort.c @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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. + */ + +#include <nonstdio.h> + +extern void _exit(int status); + +void +abort(void) +{ + putstr("\n\nabort\n"); + // FIXME loop blinking leds + _exit(-1); +} diff --git a/firmware/microblaze/lib/ad9510.c b/firmware/microblaze/lib/ad9510.c new file mode 100644 index 000000000..4d3acb65d --- /dev/null +++ b/firmware/microblaze/lib/ad9510.c @@ -0,0 +1,42 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "ad9510.h" +#include "spi.h" +#include <memory_map.h> + +#define RD (1 << 15) +#define WR (0 << 15) + +void +ad9510_write_reg(int regno, uint8_t value) +{ + uint32_t inst = WR | (regno & 0xff); + uint32_t v = (inst << 8) | (value & 0xff); + spi_transact(SPI_TXONLY, SPI_SS_AD9510, v, 24, SPIF_PUSH_FALL); +} + +int +ad9510_read_reg(int regno) +{ + uint32_t inst = RD | (regno & 0xff); + uint32_t v = (inst << 8) | 0; + uint32_t r = spi_transact(SPI_TXRX, SPI_SS_AD9510, v, 24, + SPIF_PUSH_FALL | SPIF_LATCH_FALL); + return r & 0xff; +} diff --git a/firmware/microblaze/lib/ad9510.h b/firmware/microblaze/lib/ad9510.h new file mode 100644 index 000000000..a395e5223 --- /dev/null +++ b/firmware/microblaze/lib/ad9510.h @@ -0,0 +1,30 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_AD9510_H +#define INCLUDED_AD9510_H + +#include <stdint.h> + +/* + * Analog Device AD9510 1.2 GHz Clock Distribution IC w/ PLL + */ + +void ad9510_write_reg(int regno, uint8_t value); +int ad9510_read_reg(int regno); + +#endif /* INCLUDED_AD9510_H */ diff --git a/firmware/microblaze/lib/ad9777.c b/firmware/microblaze/lib/ad9777.c new file mode 100644 index 000000000..734ccd7e5 --- /dev/null +++ b/firmware/microblaze/lib/ad9777.c @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "ad9777.h" +#include "memory_map.h" +#include "spi.h" + +#define IB_RD 0x80 +#define IB_WR 0x00 +#define IB_XFER_1 0x00 +#define IB_XFER_2 0x20 +#define IB_XFER_3 0x40 +#define IB_XFER_4 0x60 +#define IB_ADDR_MASK 0x1f + +void +ad9777_write_reg(int regno, uint8_t value) +{ + uint8_t instr = IB_WR | IB_XFER_1 | (regno & IB_ADDR_MASK); + spi_transact(SPI_TXONLY, SPI_SS_AD9777, + (instr << 8) | (value & 0xff), 16, SPIF_PUSH_FALL); +} + +int +ad9777_read_reg(int regno) +{ + uint8_t instr = IB_RD | IB_XFER_1 | (regno & IB_ADDR_MASK); + uint32_t r = spi_transact(SPI_TXRX, SPI_SS_AD9777, + (instr << 8) | 0, 16, + SPIF_PUSH_FALL | SPIF_LATCH_RISE); + return r & 0xff; +} diff --git a/firmware/microblaze/lib/ad9777.h b/firmware/microblaze/lib/ad9777.h new file mode 100644 index 000000000..d4d104910 --- /dev/null +++ b/firmware/microblaze/lib/ad9777.h @@ -0,0 +1,31 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_AD9777_H +#define INCLUDED_AD9777_H + +#include <stdint.h> +#include "ad9777_regs.h" + +/* + * Analog Devices AD9777 16-bit, 160 MS/s, Dual Interpolating TxDAC + */ + +void ad9777_write_reg(int regno, uint8_t value); +int ad9777_read_reg(int regno); + +#endif /* INCLUDED_AD9777_H */ diff --git a/firmware/microblaze/lib/ad9777_regs.h b/firmware/microblaze/lib/ad9777_regs.h new file mode 100644 index 000000000..de2936c15 --- /dev/null +++ b/firmware/microblaze/lib/ad9777_regs.h @@ -0,0 +1,71 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_AD9777_REGS_H +#define INCLUDED_AD9777_REGS_H + +#define R0_SW_RESET (1 << 5) +#define R0_SLEEP (1 << 4) +#define R0_POWER_DN (1 << 3) +#define R0_1R (1 << 2) +#define R0_2R (0 << 2) +#define R0_PLL_LOCKED (1 << 1) + +#define R1_INTERP_1X 0x00 +#define R1_INTERP_2X 0x40 +#define R1_INTERP_4X 0x80 +#define R1_INTERP_8X 0xC0 +#define R1_MOD_NONE 0x00 +#define R1_MOD_FS_2 0x10 // Fs/2 +#define R1_MOD_FS_4 0x20 // Fs/4 +#define R1_MOD_FS_8 0x30 // Fs/8 +#define R1_ZERO_STUFF (1 << 3) // N.B., doubles output rate +#define R1_REAL_MIX (1 << 2) +#define R1_CMPLX_MIX (0 << 2) +#define R1_POS_EXP (1 << 1) // exp(+jwt) +#define R1_NEG_EXP (0 << 1) // exp(-jwt) +#define R1_DATACLK_OUT (1 << 0) + +#define R2_2S_COMP (0 << 7) +#define R2_2PORT_MODE (0 << 6) +#define R2_1PORT_MODE (1 << 6) + +#define R3_PLL_DIV_1 0x00 +#define R3_PLL_DIV_2 0x01 +#define R3_PLL_DIV_4 0x02 +#define R3_PLL_DIV_8 0x03 + +#define R4_PLL_ON (1 << 7) +#define R4_CP_MANUAL (1 << 6) +#define R4_CP_AUTO (0 << 6) +#define R4_CP_50uA (0x00 | R4_CP_MANUAL) +#define R4_CP_100uA (0x01 | R4_CP_MANUAL) +#define R4_CP_200uA (0x02 | R4_CP_MANUAL) +#define R4_CP_400uA (0x03 | R4_CP_MANUAL) +#define R4_CP_800uA (0x07 | R4_CP_MANUAL) + +#define R5_I_FINE_GAIN(g) (g) // 8-bits +#define R6_I_COARSE_GAIN(g) ((g) & 0xf) // low 4-bits + +#define R9_Q_FINE_GAIN(g) (g) // 8-bits +#define R10_Q_COARSE_GAIN(g) ((g) & 0xf) // low 4-bits + + +// FIXME more registers for offset and gain control... + + +#endif /* INCLUDED_AD9777_REGS_H */ diff --git a/firmware/microblaze/lib/bsm12.c b/firmware/microblaze/lib/bsm12.c new file mode 100644 index 000000000..3f17fe42d --- /dev/null +++ b/firmware/microblaze/lib/bsm12.c @@ -0,0 +1,319 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * buffer state machine: 1 input to two outputs + * + * Typically used to read packets from the ethernet and then after inspecting, + * handle the packet in firmware or pass it on to 1 of the 2 buffer destinations. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "bsm12.h" +#include "memory_map.h" +#include "buffer_pool.h" +#include <stdbool.h> +#include "nonstdio.h" +#include <stdlib.h> + +typedef enum { + BS_EMPTY, + BS_FILLING, + BS_FULL, + BS_EMPTYING, +} buffer_state_t; + +static buffer_state_t buffer_state[NBUFFERS]; +static uint32_t last_send_ctrl[NBUFFERS]; +static int8_t buffer_target[NBUFFERS]; // -1, 0 or 1. +static uint8_t buffer_dst[NBUFFERS]; // 0 or 1. Valid only when BF_EMPTYING + +#define ST_IDLE (-1) + +void +bsm12_init(bsm12_t *sm, int buf0, + const buf_cmd_args_t *recv, + const buf_cmd_args_t *send0, + const buf_cmd_args_t *send1, + bsm12_inspector_t inspect) +{ + if (buf0 & 0x3) // precondition: buf0 % 4 == 0 + abort(); + + sm->buf0 = buf0; + sm->running = false; + sm->recv_args = *recv; + sm->send_args[0] = *send0; + sm->send_args[1] = *send1; + + sm->rx_state = ST_IDLE; + sm->tx_state[0] = ST_IDLE; + sm->tx_state[1] = ST_IDLE; + + sm->inspect = inspect; + + sm->bps_error = BPS_ERROR(buf0 + 0) | BPS_ERROR(buf0 + 1) | BPS_ERROR(buf0 + 2); + sm->bps_done = BPS_DONE(buf0 + 0) | BPS_DONE(buf0 + 1) | BPS_DONE(buf0 + 2); + sm->bps_error_or_done = sm->bps_error | sm->bps_done; + + // How much to adjust the last_line register. + // It's 1 for everything but the ethernet. + sm->last_line_adj = recv->port == PORT_ETH ? 3 : 1; + + buffer_state[sm->buf0 + 0] = BS_EMPTY; + buffer_state[sm->buf0 + 1] = BS_EMPTY; + buffer_state[sm->buf0 + 2] = BS_EMPTY; + + buffer_target[sm->buf0 + 0] = -1; + buffer_target[sm->buf0 + 1] = -1; + buffer_target[sm->buf0 + 2] = -1; + + for (int i = 0; i < NBUFFERS; i++) + sm->next_buf[i] = buf0; + + sm->next_buf[buf0 + 0] = buf0 + 1; + sm->next_buf[buf0 + 1] = buf0 + 2; + sm->next_buf[buf0 + 2] = buf0 + 0; + + for (int i = 0; i < 3; i++){ + sm->precomputed_receive_to_buf_ctrl_word[i] = + (BPC_READ + | BPC_BUFFER(sm->buf0 + i) + | BPC_PORT(sm->recv_args.port) + | BPC_STEP(1) + | BPC_FIRST_LINE(sm->recv_args.first_line) + | BPC_LAST_LINE(sm->recv_args.last_line)); + + for (int j = 0; j < 2; j++){ + sm->precomputed_send_from_buf_ctrl_word[i][j] = + (BPC_WRITE + | BPC_BUFFER(sm->buf0 + i) + | BPC_PORT(sm->send_args[j].port) + | BPC_STEP(1) + | BPC_FIRST_LINE(sm->send_args[j].first_line) + | BPC_LAST_LINE(0)); // last line filled in at runtime + } + } +} + +static inline void +bsm12_receive_to_buf(bsm12_t *sm, int bufno) +{ + buffer_pool_ctrl->ctrl = sm->precomputed_receive_to_buf_ctrl_word[bufno & 0x3]; +} + +static inline void +bsm12_send_from_buf(bsm12_t *sm, int bufno, int dst_idx) +{ + dst_idx &= 0x1; + + uint32_t t = + (sm->precomputed_send_from_buf_ctrl_word[bufno & 0x3][dst_idx] + | BPC_LAST_LINE(buffer_pool_status->last_line[bufno] - sm->last_line_adj)); + + buffer_pool_ctrl->ctrl = t; + last_send_ctrl[bufno] = t; + buffer_dst[bufno] = dst_idx; +} + +static inline void +bsm12_resend_from_buf(bsm12_t *sm, int bufno) +{ + buffer_pool_ctrl->ctrl = last_send_ctrl[bufno]; +} + +void +bsm12_start(bsm12_t *sm) +{ + sm->running = true; + + buffer_state[sm->buf0 + 0] = BS_EMPTY; + buffer_state[sm->buf0 + 1] = BS_EMPTY; + buffer_state[sm->buf0 + 2] = BS_EMPTY; + + buffer_target[sm->buf0 + 0] = -1; + buffer_target[sm->buf0 + 1] = -1; + buffer_target[sm->buf0 + 2] = -1; + + bp_clear_buf(sm->buf0 + 0); + bp_clear_buf(sm->buf0 + 1); + bp_clear_buf(sm->buf0 + 2); + + sm->rx_state = 0; + sm->tx_state[0] = ST_IDLE; + sm->tx_state[1] = ST_IDLE; + bsm12_receive_to_buf(sm, sm->buf0); + buffer_state[sm->buf0] = BS_FILLING; +} + +void +bsm12_stop(bsm12_t *sm) +{ + sm->running = false; + bp_clear_buf(sm->buf0 + 0); + bp_clear_buf(sm->buf0 + 1); + bp_clear_buf(sm->buf0 + 2); + buffer_state[sm->buf0 + 0] = BS_EMPTY; + buffer_state[sm->buf0 + 1] = BS_EMPTY; + buffer_state[sm->buf0 + 2] = BS_EMPTY; +} + +static void bsm12_process_helper(bsm12_t *sm, int buf_this); +static void bsm12_error_helper(bsm12_t *sm, int buf_this); + +void +bsm12_process_status(bsm12_t *sm, uint32_t status) +{ + // anything for us? + if ((status & sm->bps_error_or_done) == 0 || !sm->running) + return; + + if (status & sm->bps_error){ + // Most likely an ethernet Rx error. We just restart the transfer. + if (status & (BPS_ERROR(sm->buf0 + 0))) + bsm12_error_helper(sm, sm->buf0 + 0); + + if (status & (BPS_ERROR(sm->buf0 + 1))) + bsm12_error_helper(sm, sm->buf0 + 1); + + if (status & (BPS_ERROR(sm->buf0 + 2))) + bsm12_error_helper(sm, sm->buf0 + 2); + } + + if (status & BPS_DONE(sm->buf0 + 0)) + bsm12_process_helper(sm, sm->buf0 + 0); + + if (status & BPS_DONE(sm->buf0 + 1)) + bsm12_process_helper(sm, sm->buf0 + 1); + + if (status & BPS_DONE(sm->buf0 + 2)) + bsm12_process_helper(sm, sm->buf0 + 2); +} + +static void +bsm12_process_helper(bsm12_t *sm, int buf_this) +{ + bp_clear_buf(buf_this); + + if (buffer_state[buf_this] == BS_FILLING){ + + buffer_state[buf_this] = BS_FULL; + buffer_target[buf_this] = -1; + + // + // where does this packet go? + // + int dst = sm->inspect(sm, buf_this); + if (dst == -1){ + // + // f/w handled the packet; refill the buffer + // + bsm12_receive_to_buf(sm, buf_this); + buffer_state[buf_this] = BS_FILLING; + buffer_target[buf_this] = -1; + sm->rx_state = buf_this & 0x3; + } + else { // goes to dst 0 or 1 + // + // If the next buffer is empty, start a receive on it + // + int t = sm->next_buf[buf_this]; + if (buffer_state[t] == BS_EMPTY){ + bsm12_receive_to_buf(sm, t); + buffer_state[t] = BS_FILLING; + buffer_target[t] = -1; + sm->rx_state = t & 0x3; + } + else + sm->rx_state = ST_IDLE; + + // + // If the destination is idle, start the xfer, othewise remember it + // + if (sm->tx_state[dst] == ST_IDLE){ + bsm12_send_from_buf(sm, buf_this, dst); + sm->tx_state[dst] = buf_this & 0x3; + buffer_state[buf_this] = BS_EMPTYING; + buffer_target[buf_this] = -1; + } + else { + buffer_target[buf_this] = dst; + } + } + } + + else { // BS_EMPTYING + + buffer_state[buf_this] = BS_EMPTY; + buffer_target[buf_this] = -1; + + if (sm->rx_state == ST_IDLE){ // fire off another receive + sm->rx_state = buf_this & 0x3; + bsm12_receive_to_buf(sm, buf_this); + buffer_state[buf_this] = BS_FILLING; + buffer_target[buf_this] = -1; + } + + int dst = buffer_dst[buf_this]; // the dst we were emptying into + // is the next buffer full and for us? + int t = sm->next_buf[buf_this]; + if (buffer_target[t] == dst){ // yes, + bsm12_send_from_buf(sm, t, dst); // send it + buffer_state[t] = BS_EMPTYING; + buffer_target[t] = -1; + sm->tx_state[dst] = t & 0x3; + } + // how about the one after that? + else if (buffer_target[t=sm->next_buf[t]] == dst){ // yes, + bsm12_send_from_buf(sm, t, dst); // send it + buffer_state[t] = BS_EMPTYING; + buffer_target[t] = -1; + sm->tx_state[dst] = t & 0x3; + } + else { + sm->tx_state[dst] = ST_IDLE; + } + } +} + +static void +bsm12_error_helper(bsm12_t *sm, int buf_this) +{ + bp_clear_buf(buf_this); // clears ERROR flag + + if (buffer_state[buf_this] == BS_FILLING){ + bsm12_receive_to_buf(sm, buf_this); // restart the xfer + } + else { // buffer was emptying + bsm12_resend_from_buf(sm, buf_this); // restart the xfer + } +} + + +void +bsm12_handle_tx_underrun(bsm12_t *sm) +{ +} + +void +bsm12_handle_rx_overrun(bsm12_t *sm) +{ +} diff --git a/firmware/microblaze/lib/bsm12.h b/firmware/microblaze/lib/bsm12.h new file mode 100644 index 000000000..b8e576b79 --- /dev/null +++ b/firmware/microblaze/lib/bsm12.h @@ -0,0 +1,83 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_BSM12_H +#define INCLUDED_BSM12_H + +#include "dbsm.h" +#include "memory_map.h" + +/*! + * buffer state machine: 1 input to two outputs + * + * Typically used to read packets from the ethernet and then after inspecting, + * handle the packet in firmware or pass it on to 1 of the 2 buffer destinations. + */ + +struct _bsm12; +typedef struct _bsm12 bsm12_t; + +/*! + * Pointer to function that does packet inspection. + * + * \param sm the state machine + * \param buf_this the index of the buffer to inspect and/or pass on + * + * Returns -1, 0 or 1. If it returns -1, it means that the s/w + * handled that packet, and that it should NOT be passed on to one of + * the buffer endpoints. 0 indicates the first endpoint, 1 the second endpoint. + */ +typedef int (*bsm12_inspector_t)(bsm12_t *sm, int buf_this); + + +/*! + * buffer state machine: 1 input to two outputs + */ +struct _bsm12 +{ + uint8_t buf0; // This machine uses buf0, buf0+1 and buf0+2. buf0 % 4 == 0. + uint8_t running; + int8_t rx_state; // -1, 0, 1, 2 which buffer we're receiving into + int8_t tx_state[2]; // -1, 0, 1, 2 which buffer we're sending from + buf_cmd_args_t recv_args; + buf_cmd_args_t send_args[2]; + bsm12_inspector_t inspect; + int last_line_adj; + uint32_t bps_error; + uint32_t bps_done; + uint32_t bps_error_or_done; + uint8_t next_buf[NBUFFERS]; + uint32_t precomputed_receive_to_buf_ctrl_word[3]; + uint32_t precomputed_send_from_buf_ctrl_word[4][2]; // really only 3, not 4 + // (easier addr comp) +}; + +void bsm12_init(bsm12_t *sm, int buf0, + const buf_cmd_args_t *recv, + const buf_cmd_args_t *send0, + const buf_cmd_args_t *send1, + bsm12_inspector_t inspect); + +void bsm12_start(bsm12_t *sm); +void bsm12_stop(bsm12_t *sm); +void bsm12_process_status(bsm12_t *sm, uint32_t status); +void bsm12_handle_tx_underrun(bsm12_t *sm); +void bsm12_handle_rx_overrun(bsm12_t *sm); + + +#endif /* INCLUDED_BSM12_H */ diff --git a/firmware/microblaze/lib/buffer_pool.c b/firmware/microblaze/lib/buffer_pool.c new file mode 100644 index 000000000..77e7c5213 --- /dev/null +++ b/firmware/microblaze/lib/buffer_pool.c @@ -0,0 +1,72 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "memory_map.h" +#include "buffer_pool.h" +#include "hal_io.h" + +void +bp_init(void) +{ + int i; + bp_disable_port(PORT_SERDES); + bp_disable_port(PORT_DSP); + bp_disable_port(PORT_ETH); + bp_disable_port(PORT_RAM); + + for (i = 0; i < NBUFFERS; i++) + bp_clear_buf(i); +} + +#ifndef INLINE_BUFFER_POOL + +void +bp_clear_buf(int bufnum) +{ + buffer_pool_ctrl->ctrl = BPC_BUFFER(bufnum) | BPC_PORT_NIL | BPC_CLR; +} + +void +bp_disable_port(int portnum) +{ + // disable buffer connections to this port + buffer_pool_ctrl->ctrl = BPC_BUFFER_NIL | BPC_PORT(portnum); +} + +void +bp_receive_to_buf(int bufnum, int port, int step, int fl, int ll) +{ + buffer_pool_ctrl->ctrl = (BPC_READ + | BPC_BUFFER(bufnum) + | BPC_PORT(port) + | BPC_STEP(step) + | BPC_FIRST_LINE(fl) + | BPC_LAST_LINE(ll)); +} + +void +bp_send_from_buf(int bufnum, int port, int step, int fl, int ll) +{ + buffer_pool_ctrl->ctrl = (BPC_WRITE + | BPC_BUFFER(bufnum) + | BPC_PORT(port) + | BPC_STEP(step) + | BPC_FIRST_LINE(fl) + | BPC_LAST_LINE(ll)); +} + +#endif diff --git a/firmware/microblaze/lib/buffer_pool.h b/firmware/microblaze/lib/buffer_pool.h new file mode 100644 index 000000000..145b20f8d --- /dev/null +++ b/firmware/microblaze/lib/buffer_pool.h @@ -0,0 +1,75 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_BUFFER_POOL_H +#define INCLUDED_BUFFER_POOL_H + +#include "memory_map.h" + +// Buffer Pool Management + + +// define to have common buffer operations inlined +#define INLINE_BUFFER_POOL 1 + +void bp_init(void); + +#ifndef INLINE_BUFFER_POOL + +void bp_clear_buf(int bufnum); +void bp_disable_port(int portnum); +void bp_receive_to_buf(int bufnum, int port, int step, int fl, int ll); +void bp_send_from_buf(int bufnum, int port, int step, int fl, int ll); + +#else + +static inline void +bp_clear_buf(int bufnum) +{ + buffer_pool_ctrl->ctrl = BPC_BUFFER(bufnum) | BPC_PORT_NIL | BPC_CLR; +} + +static inline void +bp_disable_port(int portnum) +{ + // disable buffer connections to this port + buffer_pool_ctrl->ctrl = BPC_BUFFER_NIL | BPC_PORT(portnum); +} + +static inline void +bp_receive_to_buf(int bufnum, int port, int step, int fl, int ll) +{ + buffer_pool_ctrl->ctrl = (BPC_READ + | BPC_BUFFER(bufnum) + | BPC_PORT(port) + | BPC_STEP(step) + | BPC_FIRST_LINE(fl) + | BPC_LAST_LINE(ll)); +} + +static inline void +bp_send_from_buf(int bufnum, int port, int step, int fl, int ll) +{ + buffer_pool_ctrl->ctrl = (BPC_WRITE + | BPC_BUFFER(bufnum) + | BPC_PORT(port) + | BPC_STEP(step) + | BPC_FIRST_LINE(fl) + | BPC_LAST_LINE(ll)); +} +#endif +#endif diff --git a/firmware/microblaze/lib/clocks.c b/firmware/microblaze/lib/clocks.c new file mode 100644 index 000000000..d9d4fcd3c --- /dev/null +++ b/firmware/microblaze/lib/clocks.c @@ -0,0 +1,239 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <clocks.h> + +#include "memory_map.h" +#include "ad9510.h" +#include "spi.h" +#include "u2_init.h" +#include "nonstdio.h" + +void +clocks_init(void) +{ + // Set up basic clocking functions in AD9510 + ad9510_write_reg(0x45, 0x00); // CLK2 drives distribution + + clocks_enable_fpga_clk(true, 1); + + spi_wait(); + + // Set up PLL for 10 MHz reference + // Reg 4, A counter, Don't Care + ad9510_write_reg(0x05, 0x00); // Reg 5, B counter MSBs, 0 + ad9510_write_reg(0x06, 0x05); // Reg 6, B counter LSBs, 5 + // Reg 7, Loss of reference detect, doesn't work yet, 0 + ad9510_write_reg(0x5A, 0x01); // Update Regs + + // Primary clock configuration + clocks_mimo_config(MC_WE_DONT_LOCK); + + // Set up other clocks + clocks_enable_test_clk(false, 0); + clocks_enable_tx_dboard(false, 0); + clocks_enable_rx_dboard(false, 0); + clocks_enable_eth_phyclk(false, 0); + + // Enable clock to ADCs and DACs + clocks_enable_dac_clk(true, 1); + clocks_enable_adc_clk(true, 1); +} + + +void +clocks_mimo_config(int flags) +{ + if (flags & _MC_WE_LOCK){ + // Reg 8, Charge pump on, dig lock det, positive PFD, 47 + ad9510_write_reg(0x08, 0x47); + } + else { + // Reg 8, Charge pump off, dig lock det, positive PFD + ad9510_write_reg(0x08, 0x00); + } + + // Reg 9, Charge pump current, 0x40=3mA, 0x00=650uA + ad9510_write_reg(0x09, 0x00); + // Reg A, Prescaler of 2, everything normal 04 + ad9510_write_reg(0x0A, 0x04); + // Reg B, R Div MSBs, 0 + ad9510_write_reg(0x0B, 0x00); + // Reg C, R Div LSBs, 1 + ad9510_write_reg(0x0C, 0x01); + // Reg D, Antibacklash, Digital lock det, 0 + + ad9510_write_reg(0x5A, 0x01); // Update Regs + + spi_wait(); + + // Allow for clock switchover + + if (flags & _MC_WE_LOCK){ // WE LOCK + if (flags & _MC_MIMO_CLK_INPUT) { + // Turn on ref output and choose the MIMO connector + output_regs->clk_ctrl = 0x15; + } + else { + // turn on ref output and choose the SMA + output_regs->clk_ctrl = 0x1C; + } + } + else { // WE DONT LOCK + // Disable both ext clk inputs + output_regs->clk_ctrl = 0x10; + } + + // Do we drive a clock onto the MIMO connector? + if (flags & MC_PROVIDE_CLK_TO_MIMO) + clocks_enable_clkexp_out(true,10); + else + clocks_enable_clkexp_out(false,0); +} + +bool +clocks_lock_detect() +{ + if(pic_regs->pending & PIC_CLKSTATUS) + return true; + return false; +} + +int inline +clocks_gen_div(int divisor) +{ + int L,H; + L = (divisor>>1)-1; + H = divisor-L-2; + return (L<<4)|H; +} + +#define CLOCK_OUT_EN 0x08 +#define CLOCK_OUT_DIS_CMOS 0x01 +#define CLOCK_OUT_DIS_PECL 0x02 +#define CLOCK_DIV_DIS 0x80 +#define CLOCK_DIV_EN 0x00 + +#define CLOCK_MODE_PECL 1 +#define CLOCK_MODE_LVDS 2 +#define CLOCK_MODE_CMOS 3 + +void +clocks_enable_XXX_clk(bool enable, int divisor, int reg_en, int reg_div, int mode) +{ + int enable_word, div_word, div_en_word; + + switch(mode) { + case CLOCK_MODE_PECL : + enable_word = enable ? 0x08 : 0x0A; + break; + case CLOCK_MODE_LVDS : + enable_word = enable ? 0x02 : 0x03; + break; + case CLOCK_MODE_CMOS : + enable_word = enable ? 0x08 : 0x09; + break; + } + if(enable && (divisor>1)) { + div_word = clocks_gen_div(divisor); + div_en_word = CLOCK_DIV_EN; + } + else { + div_word = 0; + div_en_word = CLOCK_DIV_DIS; + } + + ad9510_write_reg(reg_en,enable_word); // Output en/dis + ad9510_write_reg(reg_div,div_word); // Set divisor + ad9510_write_reg(reg_div+1,div_en_word); // Enable or Bypass Divider + ad9510_write_reg(0x5A, 0x01); // Update Regs +} + +// Clock 0 +void +clocks_enable_test_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x3C,0x48,CLOCK_MODE_PECL); +} + +// Clock 1 +void +clocks_enable_fpga_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x3D,0x4A,CLOCK_MODE_PECL); +} + +// Clock 2 on Rev 3, Clock 5 on Rev 4 +void +clocks_enable_clkexp_out(bool enable, int divisor) +{ + if(u2_hw_rev_major == 3) + clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); + else if(u2_hw_rev_major == 4) { + ad9510_write_reg(0x34,0x00); // Turn on fine delay + ad9510_write_reg(0x35,0x00); // Set Full Scale to nearly 10ns + ad9510_write_reg(0x36,0x1c); // Set fine delay. 0x20 is midscale + clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); + + } + else + putstr("ERR: Invalid Rev\n"); +} + +// Clock 5 on Rev 3, none (was 2) on Rev 4 +void +clocks_enable_eth_phyclk(bool enable, int divisor) +{ + if(u2_hw_rev_major == 3) + clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); + else if(u2_hw_rev_major == 4) + clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); + else + putstr("ERR: Invalid Rev\n"); +} + +// Clock 3 +void +clocks_enable_dac_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x3F,0x4E,CLOCK_MODE_PECL); +} + +// Clock 4 +void +clocks_enable_adc_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x40,0x50,CLOCK_MODE_LVDS); +} + +// Clock 6 +void +clocks_enable_tx_dboard(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x42,0x54,CLOCK_MODE_CMOS); +} + +// Clock 7 +void +clocks_enable_rx_dboard(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x43,0x56,CLOCK_MODE_CMOS); +} diff --git a/firmware/microblaze/lib/clocks.h b/firmware/microblaze/lib/clocks.h new file mode 100644 index 000000000..141fc61e0 --- /dev/null +++ b/firmware/microblaze/lib/clocks.h @@ -0,0 +1,95 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_CLOCKS_H +#define INCLUDED_CLOCKS_H + +/* + * Routines to configure our multitude of clocks + */ + +#include <stdbool.h> +#include <usrp2_clock_bits.h> + + +/*! + * One time call to initialize all clocks to a reasonable state. We + * come out of here using our free running 100MHz oscilator and not + * providing a clock to the MIMO connector (CMC_WE_DONT_LOCK) + */ +void clocks_init(void); + + +/*! + * \brief MIMO clock configuration. + * + * Configure our master clock source, and whether or not we drive a + * clock onto the mimo connector. See MC_flags in usrp2_mimo_config.h. + */ +void clocks_mimo_config(int flags); + +/*! + * \brief Lock Detect -- Return True if our PLL is locked + */ +bool clocks_lock_detect(); + +/*! + * \brief Enable or disable test clock (extra clock signal) + */ +void clocks_enable_test_clk(bool enable, int divisor); + +/*! + * \brief Enable or disable fpga clock. Disabling would wedge and require a power cycle. + */ +void clocks_enable_fpga_clk(bool enable, int divisor); + +/*! + * \brief Enable or disable clock output sent to MIMO connector + */ +void clocks_enable_clkexp_out(bool enable, int divisor); + +/*! + * \brief Enable or disable ethernet phyclk, should always be disabled + */ +void clocks_enable_eth_phyclk(bool enable, int divisor); + +/*! + * \brief Enable or disable clock to DAC + */ +void clocks_enable_dac_clk(bool enable, int divisor); + +/*! + * \brief Enable or disable clock to ADC + */ +void clocks_enable_adc_clk(bool enable, int divisor); + +/*! + * \brief Enable or disable clock to Rx daughterboard + */ +void clocks_enable_rx_dboard(bool enable, int divisor); + + +/*! + * \brief Enable or disable clock to Tx daughterboard + */ +void clocks_enable_tx_dboard(bool enable, int divisor); + + +#endif /* INCLUDED_CLOCKS_H */ diff --git a/firmware/microblaze/lib/db.h b/firmware/microblaze/lib/db.h new file mode 100644 index 000000000..cec960267 --- /dev/null +++ b/firmware/microblaze/lib/db.h @@ -0,0 +1,104 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Interface to daughterboard code + */ + +#ifndef INCLUDED_DB_H +#define INCLUDED_DB_H + +#include <usrp2_types.h> + + +struct db_base; + +/* pointers to daughterboard structures */ +extern struct db_base *tx_dboard; +extern struct db_base *rx_dboard; + + +//! Intermediate tuning information + +struct tune_result +{ + //! The RF frequency that corresponds to DC in the IF from the daughterboard + u2_fxpt_freq_t baseband_freq; + + //! The DDC/DUC frequency used to down/up convert to/from the target frequency + u2_fxpt_freq_t dxc_freq; + + //! Any differerence btwn target and actual (typically < 0.01 Hz) + u2_fxpt_freq_t residual_freq; + + //! Is the complex baseband spectrum inverted + bool inverted; +}; + + +/*! + * \brief One-time init at powerup + * + * Sets rx_dboard, tx_dboard and initializes daughterboards. + */ +void +db_init(void); + +/*! + * \brief Set daughterboard LO offset frequency. + * + * \param[in] db is the daughterboard instance + * \param[in] offset is the amount to add to tuning requests + * \param[out] success or failure + */ +bool +db_set_lo_offset(struct db_base *db, u2_fxpt_freq_t offset); + +/*! + * \brief Two stage tuning. Given target_freq, tune LO and DDC/DUC + * + * \param[in] db is the daughterboard instance + * \param[in] target_freq is the freq to translate the complex baseband to/from. + * \param[out] result provides details of the resulting configuration. + * + */ +bool +db_tune(struct db_base *db, u2_fxpt_freq_t target_freq, struct tune_result *result); + + +/* + * Set only the DDC frequency + */ +bool +db_set_ddc_freq(u2_fxpt_freq_t dxc_freq, u2_fxpt_freq_t *actual_dxc_freq); + +/* + * Set only the DUC frequency + */ +bool +db_set_duc_freq(u2_fxpt_freq_t dxc_freq, u2_fxpt_freq_t *actual_dxc_freq); + + +/*! + * \brief Set gain + */ +bool +db_set_gain(struct db_base *db, u2_fxpt_gain_t gain); + + +#endif /* INCLUDED_DB_H */ diff --git a/firmware/microblaze/lib/db_base.h b/firmware/microblaze/lib/db_base.h new file mode 100644 index 000000000..8144c8560 --- /dev/null +++ b/firmware/microblaze/lib/db_base.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_DB_BASE_H +#define INCLUDED_DB_BASE_H + +#include <usrp2_types.h> +#include <stdbool.h> + +/*! + * \brief "base class" for firmware version of daughterboard code + */ +struct db_base { + uint16_t dbid; //< daughterboard ID + + uint16_t is_tx : 1; //< is this a transmit db? + uint16_t is_quadrature : 1; + uint16_t i_and_q_swapped : 1; + uint16_t spectrum_inverted : 1; + + uint16_t output_enables; //< bitmask of which pins should be outputs from FPGA + uint16_t used_pins; //< bitmask of pins used by the daughterboard + + u2_fxpt_freq_t freq_min; //< min freq that can be set (Hz) + u2_fxpt_freq_t freq_max; //< max freq that can be set (Hz) + + u2_fxpt_gain_t gain_min; //< min gain that can be set (dB) + u2_fxpt_gain_t gain_max; //< max gain that can be set (dB) + u2_fxpt_gain_t gain_step_size; //< (dB) + + u2_fxpt_freq_t default_lo_offset; //< offset to add to tune frequency, reset value + u2_fxpt_freq_t current_lo_offset; //< current value of lo_offset + + /* + * Auto T/R control values + */ + uint32_t atr_mask; //< which bits to control + uint32_t atr_txval; //< value to use when transmitting + uint32_t atr_rxval; //< value to use when receiving + + //! delay in clk ticks from when Tx fifo gets data to when T/R switches + // uint32_t atr_tx_delay; + + //! delay in clk ticks from when Tx fifo goes empty to when T/R switches + // uint32_t atr_rx_delay; + + bool (*init)(struct db_base *); + bool (*set_freq)(struct db_base *, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc); + bool (*set_gain)(struct db_base *, u2_fxpt_gain_t gain); + bool (*set_tx_enable)(struct db_base *, bool on); +}; + + +#endif /* INCLUDED_DB_BASE_H */ diff --git a/firmware/microblaze/lib/db_basic.c b/firmware/microblaze/lib/db_basic.c new file mode 100644 index 000000000..2bd4ebfbe --- /dev/null +++ b/firmware/microblaze/lib/db_basic.c @@ -0,0 +1,161 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#include <db_base.h> + +bool db_basic_init(struct db_base *db); +bool db_basic_set_freq(struct db_base *db, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc); +bool db_basic_set_gain(struct db_base *db, u2_fxpt_gain_t gain); +bool db_basic_set_tx_enable(struct db_base *, bool on); + +struct db_basic { + struct db_base base; +}; + + +struct db_basic db_basic_tx = { + .base.dbid = 0x0000, + .base.is_tx = true, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(-90e9), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(90e9), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.atr_mask = 0, + .base.atr_txval = 0, + .base.atr_rxval = 0, + //.base.atr_tx_delay = 0, + //.base.atr_rx_delay = 0, + + .base.init = db_basic_init, + .base.set_freq = db_basic_set_freq, + .base.set_gain = db_basic_set_gain, + .base.set_tx_enable = db_basic_set_tx_enable, +}; + +struct db_basic db_basic_rx = { + .base.dbid = 0x0001, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(-90e9), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(90e9), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.is_quadrature = false, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.atr_mask = 0, + .base.atr_txval = 0, + .base.atr_rxval = 0, + //.base.atr_tx_delay = 0, + //.base.atr_rx_delay = 0, + + .base.init = db_basic_init, + .base.set_freq = db_basic_set_freq, + .base.set_gain = db_basic_set_gain, + .base.set_tx_enable = db_basic_set_tx_enable, +}; + +struct db_basic db_lf_tx = { + .base.dbid = 0x000e, + .base.is_tx = true, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(-32e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(32e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.atr_mask = 0, + .base.atr_txval = 0, + .base.atr_rxval = 0, + //.base.atr_tx_delay = 0, + //.base.atr_rx_delay = 0, + + .base.init = db_basic_init, + .base.set_freq = db_basic_set_freq, + .base.set_gain = db_basic_set_gain, + .base.set_tx_enable = db_basic_set_tx_enable, +}; + +struct db_basic db_lf_rx = { + .base.dbid = 0x000f, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(32e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.is_quadrature = false, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.atr_mask = 0, + .base.atr_txval = 0, + .base.atr_rxval = 0, + //.base.atr_tx_delay = 0, + //.base.atr_rx_delay = 0, + + .base.init = db_basic_init, + .base.set_freq = db_basic_set_freq, + .base.set_gain = db_basic_set_gain, + .base.set_tx_enable = db_basic_set_tx_enable, +}; + + +bool +db_basic_init(struct db_base *db) +{ + return true; +} + +bool +db_basic_set_freq(struct db_base *db, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc) +{ + *dc = 0; + return true; +} + +bool +db_basic_set_gain(struct db_base *db, u2_fxpt_gain_t gain) +{ + return true; +} + +bool +db_basic_set_tx_enable(struct db_base *db, bool on) +{ + return true; +} + diff --git a/firmware/microblaze/lib/db_dbsrx.c b/firmware/microblaze/lib/db_dbsrx.c new file mode 100644 index 000000000..2174a6cd8 --- /dev/null +++ b/firmware/microblaze/lib/db_dbsrx.c @@ -0,0 +1,395 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <i2c.h> +#include <db_base.h> +#include <lsdac.h> +#include <memory_map.h> +#include <clocks.h> +#include <stdio.h> +#include <hal_io.h> + +#define min(X,Y) ((X) < (Y) ? (X) : (Y)) +#define max(X,Y) ((X) > (Y) ? (X) : (Y)) +#define abs(X) ((X) < (0) ? ((-1)*(X)) : (X)) + +#define I2C_ADDR 0x67 +#define REFCLK_DIVISOR 25 // Gives a 4 MHz clock +#define REFCLK_FREQ U2_DOUBLE_TO_FXPT_FREQ(MASTER_CLK_RATE/REFCLK_DIVISOR) +#define REFCLK_FREQ_INT u2_fxpt_freq_round_to_int(REFCLK_FREQ) + +#define VMAXGAIN .75 +#define VMINGAIN 2.6 +#define RFGAINMAX 60 +#define BBGAINMAX 24 +#define DACFULLSCALE 3.3 + +bool db_dbsrx_init(struct db_base *db); +bool db_dbsrx_set_freq(struct db_base *db, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc); +bool db_dbsrx_set_gain(struct db_base *db, u2_fxpt_gain_t gain); + +struct db_dbsrx_common { + int d_n; + int d_div2; + int d_osc; + int d_cp; + int d_r_reg; + int d_fdac; + int d_m; + int d_dl; + int d_ade; + int d_adl; + int d_gc2; + int d_diag; +}; + +struct db_dbsrx_dummy { + struct db_base base; + struct db_dbsrx_common common; +}; + +struct db_dbsrx { + struct db_base base; + struct db_dbsrx_common common; +}; + +struct db_dbsrx db_dbsrx = { + .base.dbid = 0x000d, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(500e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(2.6e9), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(RFGAINMAX+BBGAINMAX), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(1), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = db_dbsrx_init, + .base.set_freq = db_dbsrx_set_freq, + .base.set_gain = db_dbsrx_set_gain, + .base.set_tx_enable = 0, + .base.atr_mask = 0x0000, + .base.atr_txval = 0, + .base.atr_rxval = 0, + //.base.atr_tx_delay = + //.base.atr_rx_delay = + .common.d_n = 950, + .common.d_div2 = 0, + .common.d_osc = 5, + .common.d_cp = 3, + .common.d_r_reg = 1, + .common.d_fdac = 127, + .common.d_m = 2, + .common.d_dl = 1, + .common.d_ade = 0, + .common.d_adl = 0, + .common.d_gc2 = 31, + .common.d_diag = 0, +}; + +bool +db_dbsrx_init(struct db_base *dbb){ + struct db_dbsrx_dummy *db = (struct db_dbsrx_dummy *) dbb; + db->base.set_gain(dbb, (db->base.gain_max + db->base.gain_min)/2); + clocks_enable_rx_dboard(true, REFCLK_DIVISOR); // Gives 4 MHz clock + + return true; +} + +/************************************************** + * Registers + **************************************************/ +static int +_read_adc (void){ + unsigned char readback[2]; + i2c_read(I2C_ADDR, readback, 2*sizeof(unsigned char)); + int adc_val = (readback[0] >> 2)&7; + //printf("READBACK[0] %d, [1] %d\n",readback[0],readback[1]); + //printf("ADC: %d\n",adc_val); + return adc_val; +} + +static void +_write_reg (int regno, int v){ + //regno is in [0,5], v is value to write to register""" + unsigned char args[2]; + args[0] = (unsigned char)regno; + args[1] = (unsigned char)v; + i2c_write(I2C_ADDR, args, 2*sizeof(unsigned char)); + //printf("Reg %d, Val %x\n",regno,v); +} + +static void _send_reg_0(struct db_dbsrx_dummy *db){ + _write_reg(0,(db->common.d_div2<<7) + (db->common.d_n>>8)); +} + +static void _send_reg_1(struct db_dbsrx_dummy *db){ + _write_reg(1,db->common.d_n & 255); +} + +static void _send_reg_2(struct db_dbsrx_dummy *db){ + _write_reg(2,db->common.d_osc + (db->common.d_cp<<3) + (db->common.d_r_reg<<5)); +} + +static void _send_reg_3(struct db_dbsrx_dummy *db){ + _write_reg(3,db->common.d_fdac); +} + +static void _send_reg_4(struct db_dbsrx_dummy *db){ + _write_reg(4,db->common.d_m + (db->common.d_dl<<5) + (db->common.d_ade<<6) + (db->common.d_adl<<7)); +} + +static void _send_reg_5(struct db_dbsrx_dummy *db){ + _write_reg(5,db->common.d_gc2 + (db->common.d_diag<<5)); +} + +/************************************************** + * Helpers for setting the freq + **************************************************/ +static void +_set_div2(struct db_dbsrx_dummy *db, int div2){ + db->common.d_div2 = div2; + _send_reg_0(db); +} + +// FIXME How do we handle ADE and ADL properly? +static void +_set_ade(struct db_dbsrx_dummy *db, int ade){ + db->common.d_ade = ade; + _send_reg_4(db); +} + +static void +_set_r(struct db_dbsrx_dummy *db, int r){ + db->common.d_r_reg = r; + _send_reg_2(db); +} + +static void +_set_n(struct db_dbsrx_dummy *db, int n){ + db->common.d_n = n; + _send_reg_0(db); + _send_reg_1(db); +} + +static void +_set_osc(struct db_dbsrx_dummy *db, int osc){ + db->common.d_osc = osc; + _send_reg_2(db); +} + +static void +_set_cp(struct db_dbsrx_dummy *db, int cp){ + db->common.d_cp = cp; + _send_reg_2(db); +} + +/************************************************** + * Set the freq + **************************************************/ + + +bool +db_dbsrx_set_freq(struct db_base *dbb, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc){ + struct db_dbsrx_dummy *db = (struct db_dbsrx_dummy *) dbb; + + if(!(freq>=db->base.freq_min && freq<=db->base.freq_max)) { + return false; + } + + u2_fxpt_freq_t vcofreq; + if(freq < U2_DOUBLE_TO_FXPT_FREQ(1150e6)) { + _set_div2(db, 0); + vcofreq = 4 * freq; + } + else { + _set_div2(db, 1); + vcofreq = 2 * freq; + } + + _set_ade(db, 1); + int rmin = max(2, u2_fxpt_freq_round_to_int(REFCLK_FREQ/2e6)); //TODO? remove max() + //int rmax = min(128, u2_fxpt_freq_round_to_int(REFCLK_FREQ/500e3)); //TODO? remove min() + int n = 0; + u2_fxpt_freq_t best_delta = U2_DOUBLE_TO_FXPT_FREQ(10e6); + u2_fxpt_freq_t delta; + + int r_reg = 0; + while ((r_reg<7) && ((2<<r_reg) < rmin)) { + r_reg++; + } + //printf ("r_reg = %d, r = %d\n",r_reg,2<<r_reg); + int best_r = r_reg; + int best_n = 0; + + while(r_reg <= 7) { + n = u2_fxpt_freq_round_to_int(freq/REFCLK_FREQ_INT*(2<<r_reg)); + //printf("LOOP: r_reg %d, best_r %d, best_n %d, best_delta %d\n", + //r_reg,best_r,best_n,u2_fxpt_freq_round_to_int(best_delta)); + + //printf("N: %d\n",n); + if(n<256) { + r_reg++; + continue; + } + delta = abs(n*REFCLK_FREQ/(2<<r_reg) - freq); + if(delta < best_delta) { + best_r = r_reg; + best_n = n; + best_delta = delta; + } + if(best_delta < U2_DOUBLE_TO_FXPT_FREQ(75e3)) { + break; + } + r_reg++; + } + + //printf("BEST R: %d Best Delta %d Best N %d\n", + // best_r,u2_fxpt_freq_round_to_int(best_delta),best_n); + _set_r(db, best_r); + _set_n(db, best_n); + + int vco; + if(vcofreq < U2_DOUBLE_TO_FXPT_FREQ(2433e6)) + vco = 0; + else if(vcofreq < U2_DOUBLE_TO_FXPT_FREQ(2711e6)) + vco=1; + else if(vcofreq < U2_DOUBLE_TO_FXPT_FREQ(3025e6)) + vco=2; + else if(vcofreq < U2_DOUBLE_TO_FXPT_FREQ(3341e6)) + vco=3; + else if(vcofreq < U2_DOUBLE_TO_FXPT_FREQ(3727e6)) + vco=4; + else if(vcofreq < U2_DOUBLE_TO_FXPT_FREQ(4143e6)) + vco=5; + else if(vcofreq < U2_DOUBLE_TO_FXPT_FREQ(4493e6)) + vco=6; + else + vco=7; + //printf("Initial VCO choice %d\n",vco); + _set_osc(db, vco); + + + int adc_val = 0; + while(adc_val == 0 || adc_val == 7) { + adc_val = _read_adc(); + //printf("adc %d\n",adc_val); + + if(adc_val == 0) { + if(vco <= 0) { + return false; + } + else { + vco = vco - 1; + } + } + else if(adc_val == 7) { + if(vco >= 7) { + return false; + } + else { + vco = vco + 1; + } + } + _set_osc(db, vco); + } + + if(adc_val == 1 || adc_val == 2) { + _set_cp(db, 1); + } + else if(adc_val == 3 || adc_val == 4) { + _set_cp(db, 2); + } + else { + _set_cp(db, 3); + } + //printf("Final VCO choice %d\n",vco); + + *dc = db->common.d_n * REFCLK_FREQ / (2<<db->common.d_r_reg); + return true; + +} + +/************************************************** + * Helpers for setting the gain + **************************************************/ + +static void +_set_gc2(struct db_dbsrx_dummy *db, int gc2){ + db->common.d_gc2 = gc2; + _send_reg_5(db); +} + +/************************************************** + * Set the gain + **************************************************/ +bool +db_dbsrx_set_gain(struct db_base *dbb, u2_fxpt_gain_t gain){ + struct db_dbsrx_dummy *db = (struct db_dbsrx_dummy *) dbb; + + u2_fxpt_gain_t rfgain, bbgain; + + if(!(gain >= db->base.gain_min && gain <= db->base.gain_max)) { + return false; + } + + if(gain < U2_DOUBLE_TO_FXPT_GAIN(RFGAINMAX)) { + rfgain = gain; + bbgain = 0; + } + else { + rfgain = U2_DOUBLE_TO_FXPT_GAIN(RFGAINMAX); + bbgain = gain - U2_DOUBLE_TO_FXPT_GAIN(RFGAINMAX); + } + + int rf_gain_slope_q8 = 256 * 4096 * (VMAXGAIN-VMINGAIN) / RFGAINMAX / DACFULLSCALE; + int rf_gain_offset_q8 = 128 * 256 * 4096 * VMINGAIN / DACFULLSCALE; + + int rfdac = (rfgain*rf_gain_slope_q8 + rf_gain_offset_q8)>>15; + + //printf("Set RF Gain %d, %d\n",rfgain,rfdac); + lsdac_write_rx(1,rfdac); + + // Set GC2 + int bb_gain_slope_q8 = 256*(0-31)/(BBGAINMAX-0); + + int gc2 = u2_fxpt_gain_round_to_int((bb_gain_slope_q8 * bbgain)>>8) + 31; + //printf("Set BB Gain: %d, gc2 %d\n",bbgain,gc2); + + _set_gc2(db, gc2); + + return true; +} + +/************************************************** + * Helpers for setting the bw + **************************************************/ +static void +_set_m(struct db_dbsrx_dummy *db, int m){ + db->common.d_m = m; + _send_reg_4(db); +} + +static void +_set_fdac(struct db_dbsrx_dummy *db, int fdac){ + db->common.d_fdac = fdac; + _send_reg_3(db); +} diff --git a/firmware/microblaze/lib/db_init.c b/firmware/microblaze/lib/db_init.c new file mode 100644 index 000000000..a0bfc5cfb --- /dev/null +++ b/firmware/microblaze/lib/db_init.c @@ -0,0 +1,428 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#include <memory_map.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <string.h> +#include <stdio.h> +#include <db.h> +#include <db_base.h> +#include <hal_io.h> +#include <nonstdio.h> + + +struct db_base *tx_dboard; // the tx daughterboard that's installed +struct db_base *rx_dboard; // the rx daughterboard that's installed + +extern struct db_base db_basic_tx; +extern struct db_base db_basic_rx; +extern struct db_base db_lf_tx; +extern struct db_base db_lf_rx; +extern struct db_base db_rfx_400_tx; +extern struct db_base db_rfx_400_rx; +extern struct db_base db_rfx_900_tx; +extern struct db_base db_rfx_900_rx; +extern struct db_base db_rfx_1200_tx; +extern struct db_base db_rfx_1200_rx; +extern struct db_base db_rfx_1800_tx; +extern struct db_base db_rfx_1800_rx; +extern struct db_base db_rfx_2400_tx; +extern struct db_base db_rfx_2400_rx; +extern struct db_base db_tvrx1; +extern struct db_base db_tvrx2; +extern struct db_base db_tvrx3; +extern struct db_base db_dbsrx; + +extern struct db_base db_xcvr2450_tx; +extern struct db_base db_xcvr2450_rx; + +struct db_base *all_dboards[] = { + &db_basic_tx, + &db_basic_rx, + &db_lf_tx, + &db_lf_rx, + &db_rfx_400_tx, + &db_rfx_400_rx, + &db_rfx_900_tx, + &db_rfx_900_rx, + &db_rfx_1200_tx, + &db_rfx_1200_rx, + &db_rfx_1800_tx, + &db_rfx_1800_rx, + &db_rfx_2400_tx, + &db_rfx_2400_rx, + &db_tvrx1, +#if 0 + &db_tvrx2, +#endif + &db_tvrx3, + &db_dbsrx, + &db_xcvr2450_tx, + &db_xcvr2450_rx, + 0 +}; + + +typedef enum { UDBE_OK, UDBE_NO_EEPROM, UDBE_INVALID_EEPROM } usrp_dbeeprom_status_t; + +static usrp_dbeeprom_status_t +read_raw_dboard_eeprom (unsigned char *buf, int i2c_addr) +{ + if (!eeprom_read (i2c_addr, 0, buf, DB_EEPROM_CLEN)) + return UDBE_NO_EEPROM; + + if (buf[DB_EEPROM_MAGIC] != DB_EEPROM_MAGIC_VALUE) + return UDBE_INVALID_EEPROM; + + int sum = 0; + unsigned int i; + for (i = 0; i < DB_EEPROM_CLEN; i++) + sum += buf[i]; + + if ((sum & 0xff) != 0) + return UDBE_INVALID_EEPROM; + + return UDBE_OK; +} + + +/* + * Return DBID, -1 <none> or -2 <invalid eeprom contents> + */ +int +read_dboard_eeprom(int i2c_addr) +{ + unsigned char buf[DB_EEPROM_CLEN]; + + usrp_dbeeprom_status_t s = read_raw_dboard_eeprom (buf, i2c_addr); + + //printf("\nread_raw_dboard_eeprom: %d\n", s); + + switch (s){ + case UDBE_OK: + return (buf[DB_EEPROM_ID_MSB] << 8) | buf[DB_EEPROM_ID_LSB]; + + case UDBE_NO_EEPROM: + default: + return -1; + + case UDBE_INVALID_EEPROM: + return -2; + } +} + + +static struct db_base * +lookup_dbid(int dbid) +{ + if (dbid < 0) + return 0; + + int i; + for (i = 0; all_dboards[i]; i++) + if (all_dboards[i]->dbid == dbid) + return all_dboards[i]; + + return 0; +} + +static struct db_base * +lookup_dboard(int i2c_addr, struct db_base *default_db, char *msg) +{ + struct db_base *db; + int dbid = read_dboard_eeprom(i2c_addr); + + // FIXME removing this printf has the system hang if there are two d'boards + // installed. (I think the problem is in i2c_read/write or the way + // I kludge the zero-byte write to set the read address in eeprom_read.) + printf("%s dbid: 0x%x\n", msg, dbid); + + if (dbid < 0){ // there was some kind of problem. Treat as Basic Tx + return default_db; + } + else if ((db = lookup_dbid(dbid)) == 0){ + printf("No daugherboard code for dbid = 0x%x\n", dbid); + return default_db; + } + return db; +} + +void +set_atr_regs(int bank, struct db_base *db) +{ + uint32_t val[4]; + int shift; + int mask; + int i; + + val[ATR_IDLE] = db->atr_rxval; + val[ATR_RX] = db->atr_rxval; + val[ATR_TX] = db->atr_txval; + val[ATR_FULL] = db->atr_txval; + + if (bank == GPIO_TX_BANK){ + mask = 0xffff0000; + shift = 16; + } + else { + mask = 0x0000ffff; + shift = 0; + } + + for (i = 0; i < 4; i++){ + int t = (atr_regs->v[i] & ~mask) | ((val[i] << shift) & mask); + //printf("atr_regs[%d] = 0x%x\n", i, t); + atr_regs->v[i] = t; + } +} + +static void +set_gpio_mode(int bank, struct db_base *db) +{ + int i; + + hal_gpio_set_ddr(bank, db->output_enables, 0xffff); + set_atr_regs(bank, db); + + for (i = 0; i < 16; i++){ + if (db->used_pins & (1 << i)){ + // set to either GPIO_SEL_SW or GPIO_SEL_ATR + hal_gpio_set_sel(bank, i, (db->atr_mask & (1 << i)) ? 'a' : 's'); + } + } +} + +static int __attribute__((unused)) +determine_tx_mux_value(struct db_base *db) +{ + if (db->i_and_q_swapped) + return 0x01; + else + return 0x10; +} + +static int +determine_rx_mux_value(struct db_base *db) +{ +#define ADC0 0x0 +#define ADC1 0x1 +#define ZERO 0x2 + + static int truth_table[8] = { + /* swap_iq, uses */ + /* 0, 0x0 */ (ZERO << 2) | ZERO, // N/A + /* 0, 0x1 */ (ZERO << 2) | ADC0, + /* 0, 0x2 */ (ZERO << 2) | ADC1, + /* 0, 0x3 */ (ADC1 << 2) | ADC0, + /* 1, 0x0 */ (ZERO << 2) | ZERO, // N/A + /* 1, 0x1 */ (ZERO << 2) | ADC0, + /* 1, 0x2 */ (ZERO << 2) | ADC1, + /* 1, 0x3 */ (ADC0 << 2) | ADC1, + }; + + int subdev0_uses; + int subdev1_uses; + int uses; + + if (db->is_quadrature) + subdev0_uses = 0x3; // uses A/D 0 and 1 + else + subdev0_uses = 0x1; // uses A/D 0 only + + // FIXME second subdev on Basic Rx, LF RX + // if subdev2 exists + // subdev1_uses = 0x2; + subdev1_uses = 0; + + uses = subdev0_uses; + + int swap_iq = db->i_and_q_swapped & 0x1; + int index = (swap_iq << 2) | uses; + + return truth_table[index]; +} + + +void +db_init(void) +{ + int m; + + tx_dboard = lookup_dboard(I2C_ADDR_TX_A, &db_basic_tx, "Tx"); + //printf("db_init: tx dbid = 0x%x\n", tx_dboard->dbid); + set_gpio_mode(GPIO_TX_BANK, tx_dboard); + tx_dboard->init(tx_dboard); + m = determine_tx_mux_value(tx_dboard); + dsp_tx_regs->tx_mux = m; + //printf("tx_mux = 0x%x\n", m); + tx_dboard->current_lo_offset = tx_dboard->default_lo_offset; + + rx_dboard = lookup_dboard(I2C_ADDR_RX_A, &db_basic_rx, "Rx"); + //printf("db_init: rx dbid = 0x%x\n", rx_dboard->dbid); + set_gpio_mode(GPIO_RX_BANK, rx_dboard); + rx_dboard->init(rx_dboard); + m = determine_rx_mux_value(rx_dboard); + dsp_rx_regs->rx_mux = m; + //printf("rx_mux = 0x%x\n", m); + rx_dboard->current_lo_offset = rx_dboard->default_lo_offset; +} + +/*! + * Calculate the frequency to use for setting the digital down converter. + * + * \param[in] target_freq desired RF frequency (Hz) + * \param[in] baseband_freq the RF frequency that corresponds to DC in the IF. + * + * \param[out] dxc_freq is the value for the ddc + * \param[out] inverted is true if we're operating in an inverted Nyquist zone. +*/ +void +calc_dxc_freq(u2_fxpt_freq_t target_freq, u2_fxpt_freq_t baseband_freq, + u2_fxpt_freq_t *dxc_freq, bool *inverted) +{ + u2_fxpt_freq_t fs = U2_DOUBLE_TO_FXPT_FREQ(100e6); // converter sample rate + u2_fxpt_freq_t delta = target_freq - baseband_freq; + +#if 0 + printf("calc_dxc_freq\n"); + printf(" fs = "); print_fxpt_freq(fs); newline(); + printf(" target = "); print_fxpt_freq(target_freq); newline(); + printf(" baseband = "); print_fxpt_freq(baseband_freq); newline(); + printf(" delta = "); print_fxpt_freq(delta); newline(); +#endif + + if (delta >= 0){ + while (delta > fs) + delta -= fs; + if (delta <= fs/2){ // non-inverted region + *dxc_freq = -delta; + *inverted = false; + } + else { // inverted region + *dxc_freq = delta - fs; + *inverted = true; + } + } + else { + while (delta < -fs) + delta += fs; + if (delta >= -fs/2){ // non-inverted region + *dxc_freq = -delta; + *inverted = false; + } + else { // inverted region + *dxc_freq = delta + fs; + *inverted = true; + } + } +} + +bool +db_set_lo_offset(struct db_base *db, u2_fxpt_freq_t offset) +{ + db->current_lo_offset = offset; + return true; +} + +bool +db_tune(struct db_base *db, u2_fxpt_freq_t target_freq, struct tune_result *result) +{ + memset(result, 0, sizeof(*result)); + bool inverted = false; + u2_fxpt_freq_t dxc_freq; + u2_fxpt_freq_t actual_dxc_freq; + + // Ask the d'board to tune as closely as it can to target_freq+lo_offset + bool ok = db->set_freq(db, target_freq+db->current_lo_offset, &result->baseband_freq); + + // Calculate the DDC setting that will downconvert the baseband from the + // daughterboard to our target frequency. + calc_dxc_freq(target_freq, result->baseband_freq, &dxc_freq, &inverted); + + // If the spectrum is inverted, and the daughterboard doesn't do + // quadrature downconversion, we can fix the inversion by flipping the + // sign of the dxc_freq... (This only happens using the basic_rx board) + + if (db->spectrum_inverted) + inverted = !inverted; + + if (inverted && !db->is_quadrature){ + dxc_freq = -dxc_freq; + inverted = !inverted; + } + + if (db->is_tx){ + dxc_freq = -dxc_freq; // down conversion versus up conversion + ok &= db_set_duc_freq(dxc_freq, &actual_dxc_freq); + } + else { + ok &= db_set_ddc_freq(dxc_freq, &actual_dxc_freq); + } + + result->dxc_freq = dxc_freq; + result->residual_freq = dxc_freq - actual_dxc_freq; + result->inverted = inverted; + return ok; +} + +static int32_t +compute_freq_control_word(u2_fxpt_freq_t target_freq, u2_fxpt_freq_t *actual_freq) +{ + // If we were using floating point, we'd calculate + // master = 100e6; + // v = (int) rint(target_freq / master_freq) * pow(2.0, 32.0); + + //printf("compute_freq_control_word\n"); + //printf(" target_freq = "); print_fxpt_freq(target_freq); newline(); + + int32_t master_freq = 100000000; // 100M + + int32_t v = ((target_freq << 12)) / master_freq; + //printf(" fcw = %d\n", v); + + *actual_freq = (v * (int64_t) master_freq) >> 12; + + //printf(" actual = "); print_fxpt_freq(*actual_freq); newline(); + + return v; +} + + +bool +db_set_ddc_freq(u2_fxpt_freq_t dxc_freq, u2_fxpt_freq_t *actual_dxc_freq) +{ + int32_t v = compute_freq_control_word(dxc_freq, actual_dxc_freq); + dsp_rx_regs->freq = v; + return true; +} + +bool +db_set_duc_freq(u2_fxpt_freq_t dxc_freq, u2_fxpt_freq_t *actual_dxc_freq) +{ + int32_t v = compute_freq_control_word(dxc_freq, actual_dxc_freq); + dsp_tx_regs->freq = v; + return true; +} + +bool +db_set_gain(struct db_base *db, u2_fxpt_gain_t gain) +{ + return db->set_gain(db, gain); +} diff --git a/firmware/microblaze/lib/db_rfx.c b/firmware/microblaze/lib/db_rfx.c new file mode 100644 index 000000000..ff12f9e32 --- /dev/null +++ b/firmware/microblaze/lib/db_rfx.c @@ -0,0 +1,590 @@ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <spi.h> +#include <memory_map.h> +#include <db_base.h> +#include <hal_io.h> +#include <stdio.h> +#include <mdelay.h> +#include <lsdac.h> +#include <clocks.h> + + +bool rfx_init_rx(struct db_base *db); +bool rfx_init_tx(struct db_base *db); +bool rfx_set_freq(struct db_base *db, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc); +bool rfx_set_gain_rx(struct db_base *db, u2_fxpt_gain_t gain); +bool rfx_set_gain_tx(struct db_base *db, u2_fxpt_gain_t gain); +bool rfx_set_tx_enable(struct db_base *, bool on); + +// Control Latch Defines +#define P 0 // Prescalar value for setting in regs, must match the next line... +#define PRESCALER 8 // Presacalar value for computations +#define PD 0 // Power down, 0 = normal operation +#define PL 0 // PLL power output +#define MTLD 1 // Mute till lock detect +#define CPGAIN 0 // Charge pump gain, use setting 1, also in N-reg +#define CP3S 0 // Charge pump tri-state, 0 = normal operation +#define PDP 1 // Phase detector polarity +#define MUXOUT 1 // Digital lock detect, active high +#define CR 0 // normal operation +#define PC 1 // core power + +// N Latch Defines +#define DIVSEL 0 // N Counter always operates on full rate +#define N_RSV 0 + +// R Latch Defines +#define R_RSV 0 +#define R_BSC 3 +#define R_TMB 0 +#define R_LDP 1 +#define R_ABP 0 +#define R_DIV 16 + +#define phdet_freq (U2_DOUBLE_TO_FXPT_FREQ(100e6/R_DIV)) + +// IO Pin functions +#define POWER_UP (1 << 7) // Low enables power supply +#define ANT_SW (1 << 6) // On TX DB, 0 = TX, 1 = RX, on RX DB 0 = main ant, 1 = RX2 +#define MIX_EN (1 << 5) // Enable appropriate mixer +#define LOCKDET_MASK (1 << 2) // Input pin + +struct db_rfx_common { + // RFX common stuff + unsigned char DIV2; + unsigned char CP1; + unsigned char CP2; + int freq_mult; + int spi_mask; +}; + +struct db_rfx_dummy { + struct db_base base; + struct db_rfx_common common; +}; + + +struct db_rfx_400_rx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_400_tx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_900_rx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_900_tx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_1200_rx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_1200_tx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_1800_rx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_1800_tx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_2400_rx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_2400_tx { + struct db_base base; + struct db_rfx_common common; +}; + + +/* + * The class instances + */ +struct db_rfx_400_rx db_rfx_400_rx = { + .base.dbid = 0x0024, + .base.is_tx = false, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(400e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(500e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(45), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0.022), + .base.is_quadrature = true, + .base.i_and_q_swapped = true, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = rfx_init_rx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_rx, + .base.set_tx_enable = 0, + .base.atr_mask = 0x00E0, + .base.atr_txval = POWER_UP, + .base.atr_rxval = POWER_UP|MIX_EN, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 0, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_RX_DB, + .common.freq_mult = 2 +}; + + +struct db_rfx_400_tx db_rfx_400_tx = { + .base.dbid = 0x0028, + .base.is_tx = true, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(400e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(500e6), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(12.5e6), + .base.init = rfx_init_tx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_tx, + .base.set_tx_enable = rfx_set_tx_enable, + .base.atr_mask = 0x00E0, + .base.atr_txval = POWER_UP|MIX_EN, + .base.atr_rxval = POWER_UP|ANT_SW, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 1, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_TX_DB, + .common.freq_mult = 2 +}; + +struct db_rfx_900_rx db_rfx_900_rx = { + .base.dbid = 0x0025, + .base.is_tx = false, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(750e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(1050e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(70), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0.034), + .base.is_quadrature = true, + .base.i_and_q_swapped = true, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = rfx_init_rx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_rx, + .base.set_tx_enable = 0, + .base.atr_mask = 0x00E0, + .base.atr_txval = 0, + .base.atr_rxval = MIX_EN, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 1, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_RX_DB, + .common.freq_mult = 2 +}; + + +struct db_rfx_900_tx db_rfx_900_tx = { + .base.dbid = 0x0029, + .base.is_tx = true, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(750e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(1050e6), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(12.5e6), + .base.init = rfx_init_tx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_tx, + .base.set_tx_enable = rfx_set_tx_enable, + .base.atr_mask = 0x00E0, + .base.atr_txval = MIX_EN, + .base.atr_rxval = ANT_SW, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 1, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_TX_DB, + .common.freq_mult = 2 +}; + +struct db_rfx_1200_rx db_rfx_1200_rx = { + .base.dbid = 0x0026, + .base.is_tx = false, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(1150e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(1450e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(70), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0.034), + .base.is_quadrature = true, + .base.i_and_q_swapped = true, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = rfx_init_rx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_rx, + .base.set_tx_enable = 0, + .base.atr_mask = 0x00E0, + .base.atr_txval = 0, + .base.atr_rxval = MIX_EN, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 1, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_RX_DB, + .common.freq_mult = 2 +}; + + +struct db_rfx_1200_tx db_rfx_1200_tx = { + .base.dbid = 0x002a, + .base.is_tx = true, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(1150e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(1450e6), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(12.5e6), + .base.init = rfx_init_tx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_tx, + .base.set_tx_enable = rfx_set_tx_enable, + .base.atr_mask = 0x00E0, + .base.atr_txval = MIX_EN, + .base.atr_rxval = ANT_SW, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 1, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_TX_DB, + .common.freq_mult = 2 +}; + +struct db_rfx_1800_rx db_rfx_1800_rx = { + .base.dbid = 0x0034, + .base.is_tx = false, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(1500e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(2100e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(70), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0.034), + .base.is_quadrature = true, + .base.i_and_q_swapped = true, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = rfx_init_rx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_rx, + .base.set_tx_enable = 0, + .base.atr_mask = 0x00E0, + .base.atr_txval = 0, + .base.atr_rxval = MIX_EN, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 0, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_RX_DB, + .common.freq_mult = 1 +}; + + +struct db_rfx_1800_tx db_rfx_1800_tx = { + .base.dbid = 0x0035, + .base.is_tx = true, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(1500e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(2100e6), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(12.5e6), + .base.init = rfx_init_tx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_tx, + .base.set_tx_enable = rfx_set_tx_enable, + .base.atr_mask = 0x00E0, + .base.atr_txval = MIX_EN, + .base.atr_rxval = ANT_SW, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 0, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_TX_DB, + .common.freq_mult = 1 +}; + + +struct db_rfx_2400_rx db_rfx_2400_rx = { + .base.dbid = 0x0027, + .base.is_tx = false, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(2300e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(2900e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(70), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0.034), + .base.is_quadrature = true, + .base.i_and_q_swapped = true, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = rfx_init_rx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_rx, + .base.set_tx_enable = 0, + .base.atr_mask = 0x00E0, + .base.atr_txval = 0, + .base.atr_rxval = MIX_EN, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 0, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_RX_DB, + .common.freq_mult = 1 +}; + + +struct db_rfx_2400_tx db_rfx_2400_tx = { + .base.dbid = 0x002b, + .base.is_tx = true, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(2300e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(2900e6), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(12.5e6), + .base.init = rfx_init_tx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_tx, + .base.set_tx_enable = rfx_set_tx_enable, + .base.atr_mask = 0x00E0, + .base.atr_txval = MIX_EN, + .base.atr_rxval = ANT_SW, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 0, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_TX_DB, + .common.freq_mult = 1 +}; + + +bool +rfx_init_tx(struct db_base *dbb) +{ + //struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + clocks_enable_tx_dboard(true, 0); + + // Set the freq now to get the one time 10ms delay out of the way. + u2_fxpt_freq_t dc; + dbb->set_freq(dbb, dbb->freq_min, &dc); + return true; +} + +bool +rfx_init_rx(struct db_base *dbb) +{ + //struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + clocks_enable_rx_dboard(true, 0); + + // test gain + dbb->set_gain(dbb,U2_DOUBLE_TO_FXPT_GAIN(45.0)); + + // Set the freq now to get the one time 10ms delay out of the way. + u2_fxpt_freq_t dc; + dbb->set_freq(dbb, dbb->freq_min, &dc); + + return true; +} + +bool +rfx_set_freq(struct db_base *dbb, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc) +{ + static unsigned char first = true; + + *dc = 0; + struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + u2_fxpt_freq_t desired_n = (U2_DOUBLE_TO_FXPT_FREQ(1.0)*db->common.freq_mult*freq)/phdet_freq; + int N_DIV = u2_fxpt_freq_round_to_int(desired_n); + int B = N_DIV/PRESCALER; + int A = N_DIV - PRESCALER*B; + + if(B<A) + return false; + + int R = (R_RSV<<22)|(R_BSC<<20)|(R_TMB<<19)|(R_LDP<<18)|(R_ABP<<16)|(R_DIV<<2)|1; + int N = (DIVSEL<<23)|(db->common.DIV2<<22)|(CPGAIN<<21)|(B<<8)|(N_RSV<<7)|(A<<2)|2; + int C = (P<<22)|(PD<<20)|(db->common.CP2<<17)|(db->common.CP1<<14)|(PL<<12)| + (MTLD<<11)|(CPGAIN<<10)|(CP3S<<9)|(PDP<<8)|(MUXOUT<<5)|(CR<<4)|(PC<<2)|0; + + spi_transact(SPI_TXONLY,db->common.spi_mask,R,24,SPIF_PUSH_FALL); + spi_transact(SPI_TXONLY,db->common.spi_mask,C,24,SPIF_PUSH_FALL); + if (first){ + first = false; + mdelay(10); + } + spi_transact(SPI_TXONLY,db->common.spi_mask,N,24,SPIF_PUSH_FALL); + + //printf("A = %d, B = %d, N_DIV = %d\n",A, B, N_DIV); + *dc = (N_DIV * phdet_freq) / db->common.freq_mult; + return true; +} + +bool +rfx_set_gain_tx(struct db_base *dbb, u2_fxpt_gain_t gain) +{ + // There is no analog gain control on TX + return true; +} + +bool +rfx_set_gain_rx(struct db_base *dbb, u2_fxpt_gain_t gain) +{ + struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + + int offset_q8 = (int)(1.2/3.3*4096*(1<<15)); + int range_q15 = (int)(-1.0*4096/3.3*256*128); + int slope_q8 = range_q15/db->base.gain_max; + + int dacword = ((slope_q8 * gain) + offset_q8)>>15; + //printf("DACWORD %d\n",dacword); + lsdac_write_rx(1,dacword); + return true; +} + + +bool +rfx_set_tx_enable(struct db_base *dbb, bool on) +{ + struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + + // FIXME + + return false; +} + +bool +rfx_lock_detect(struct db_base *dbb) +{ + struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + int pins; + pins = hal_gpio_read( db->base.is_tx ? GPIO_TX_BANK : GPIO_RX_BANK ); + if(pins & LOCKDET_MASK) + return true; + return false; +} + +/* + def select_rx_antenna(self, which_antenna): + """ + Specify which antenna port to use for reception. + @param which_antenna: either 'TX/RX' or 'RX2' + """ + if which_antenna in (0, 'TX/RX'): + self._u.write_io(self._which, 0, RX2_RX1N) + elif which_antenna in (1, 'RX2'): + self._u.write_io(self._which, RX2_RX1N, RX2_RX1N) + else: + raise ValueError, "which_antenna must be either 'TX/RX' or 'RX2'" + + def set_gain(self, gain): + """ + Set the gain. + + @param gain: gain in decibels + @returns True/False + """ + maxgain = self.gain_range()[1] - self._u.pga_max() + mingain = self.gain_range()[0] + if gain > maxgain: + pga_gain = gain-maxgain + assert pga_gain <= self._u.pga_max() + agc_gain = maxgain + else: + pga_gain = 0 + agc_gain = gain + V_maxgain = .2 + V_mingain = 1.2 + V_fullscale = 3.3 + dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale + assert dac_value>=0 and dac_value<4096 + return self._u.write_aux_dac(self._which, 0, int(dac_value)) and \ + self._set_pga(int(pga_gain)) + + def gain_range(self): + return (self._u.pga_min(), self._u.pga_max() + 70, 0.05) -- For 900-2400 + return (self._u.pga_min(), self._u.pga_max() + 45, 0.035) -- For 400 + +*/ diff --git a/firmware/microblaze/lib/db_tvrx.c b/firmware/microblaze/lib/db_tvrx.c new file mode 100644 index 000000000..49bf75883 --- /dev/null +++ b/firmware/microblaze/lib/db_tvrx.c @@ -0,0 +1,244 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <i2c.h> +#include <lsdac.h> +#include <memory_map.h> +#include <db_base.h> +#include <hal_io.h> +#include <stdio.h> + +bool tvrx_init(struct db_base *db); +bool tvrx_set_freq(struct db_base *db, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc); +bool tvrx_set_gain(struct db_base *db, u2_fxpt_gain_t gain); + +#define I2C_ADDR 0x60 +#define REF_FREQ (U2_DOUBLE_TO_FXPT_FREQ(4e6)/640*8) + +#define ref_div 640 /* choices are 640, 512, 1024 */ + +#if (ref_div == 640) +#define ref_div_byte 0 +#else +#if (ref_div == 512) +#define ref_div_byte 0x6 +#else +#define ref_div_byte 0x2 +#endif +#endif + +#define fast_tuning 0x40 + +#define control_byte_1 (0x88|fast_tuning|ref_div_byte) + + +struct db_tvrx_common { + // TVRX common stuff + u2_fxpt_freq_t first_if; + u2_fxpt_freq_t second_if; +}; + +struct db_tvrx_dummy { + struct db_base base; + struct db_tvrx_common common; +}; + +struct db_tvrx1 { + struct db_base base; + struct db_tvrx_common common; +}; + +struct db_tvrx2 { + struct db_base base; + struct db_tvrx_common common; +}; + +struct db_tvrx3 { + struct db_base base; + struct db_tvrx_common common; +}; + +/* The class instances */ +struct db_tvrx1 db_tvrx1 = { + .base.dbid = 0x0003, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(50e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(860e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(95), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(1), + .base.is_quadrature = false, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = tvrx_init, + .base.set_freq = tvrx_set_freq, + .base.set_gain = tvrx_set_gain, + .base.set_tx_enable = 0, + .base.atr_mask = 0x0000, + .base.atr_txval = 0, + .base.atr_rxval = 0, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.first_if = U2_DOUBLE_TO_FXPT_FREQ(43.75e6), + .common.second_if = U2_DOUBLE_TO_FXPT_FREQ(5.75e6), +}; + +#if 0 +struct db_tvrx2 db_tvrx2 = { + .base.dbid = 0x000c, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(50e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(860e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(95), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(1), + .base.is_quadrature = false, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = tvrx_init, + .base.set_freq = tvrx_set_freq, + .base.set_gain = tvrx_set_gain, + .base.set_tx_enable = 0, + .base.atr_mask = 0x0000, + .base.atr_txval = 0, + .base.atr_rxval = 0, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.first_if = U2_DOUBLE_TO_FXPT_FREQ(44e6), + .common.second_if = U2_DOUBLE_TO_FXPT_FREQ(56e6), // Fs - 44e6 +}; +#endif + +struct db_tvrx3 db_tvrx3 = { + .base.dbid = 0x0040, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(50e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(860e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(95), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(1), + .base.is_quadrature = false, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = tvrx_init, + .base.set_freq = tvrx_set_freq, + .base.set_gain = tvrx_set_gain, + .base.set_tx_enable = 0, + .base.atr_mask = 0x0000, + .base.atr_txval = 0, + .base.atr_rxval = 0, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.first_if = U2_DOUBLE_TO_FXPT_FREQ(44e6), + .common.second_if = U2_DOUBLE_TO_FXPT_FREQ(56e6), // Fs - 44e6 +}; + +bool +tvrx_init(struct db_base *dbb) +{ + struct db_tvrx_dummy *db = (struct db_tvrx_dummy *) dbb; + db->base.set_gain(dbb,U2_DOUBLE_TO_FXPT_GAIN(94.0)); + return true; +} + +bool +tvrx_set_freq(struct db_base *dbb, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc) +{ + *dc = 0; + if (freq < dbb->freq_min || freq > dbb->freq_max) + return false; + + struct db_tvrx_dummy *db = (struct db_tvrx_dummy *) dbb; + + u2_fxpt_freq_t target_lo_freq = freq + db->common.first_if; + int n_div = u2_fxpt_freq_round_to_int(((1LL<<20) * target_lo_freq)/REF_FREQ); + + u2_fxpt_freq_t actual_lo_freq = REF_FREQ * n_div; + u2_fxpt_freq_t actual_freq = actual_lo_freq - db->common.first_if; + if(n_div > 32767) + return false; + + if (0) + printf("n_div = %d, actual_freq = %d, actual_lo_freq = %d\n", + n_div, u2_fxpt_freq_round_to_int(actual_freq), + u2_fxpt_freq_round_to_int(actual_lo_freq)); + + unsigned char buf[4]; + buf[0] = (n_div>>8) & 0xff; + buf[1] = n_div & 0xff; + buf[2] = control_byte_1; + buf[3] = ((actual_freq < U2_DOUBLE_TO_FXPT_FREQ(158e6)) ? 0xa8 : // VHF LOW + (actual_freq < U2_DOUBLE_TO_FXPT_FREQ(464e6)) ? 0x98 : // VHF HIGH + 0x38); // UHF + + *dc = actual_freq - db->common.second_if; + return i2c_write(I2C_ADDR,buf,4); +} + +bool +tvrx_set_gain(struct db_base *dbb, u2_fxpt_gain_t gain) +{ + //struct db_tvrx_dummy *db = (struct db_tvrx_dummy *) dbb; + int rfgain; + int ifgain; + if(gain>U2_DOUBLE_TO_FXPT_GAIN(95.0)) + return false; + if(gain<0) + return false; + + if(gain>U2_DOUBLE_TO_FXPT_GAIN(60.0)) { + rfgain = U2_DOUBLE_TO_FXPT_GAIN(60.0); + ifgain = gain-U2_DOUBLE_TO_FXPT_GAIN(60.0); + } else { + rfgain = gain; + ifgain = 0; + } + + int rf_slope_q8 = 256 * 4096 * 2.5 / 60.0 / 1.22 / 3.3; + int rf_offset_q8 = 128 * 256 * 4096 * 1.25 / 1.22 / 3.3; + int if_slope_q8 = 256 * 4096 * 2.25 / 35.0 / 1.22 / 3.3; + int if_offset_q8 = 128 * 256 * 4096 * 1.4 / 1.22 / 3.3; + + + int rfdac = (rfgain*rf_slope_q8 + rf_offset_q8)>>15; + int ifdac = (ifgain*if_slope_q8 + if_offset_q8)>>15; + lsdac_write_rx(0,rfdac); + lsdac_write_rx(1,ifdac); + + if (0) + printf("Setting gain %d, rf %d, if %d\n",gain,rfdac,ifdac); + + return true; +} + + +bool +tvrx_lock_detect(struct db_base *dbb) +{ + // struct db_tvrx_dummy *db = (struct db_tvrx_dummy *) dbb; + return true; +} diff --git a/firmware/microblaze/lib/db_xcvr2450.c b/firmware/microblaze/lib/db_xcvr2450.c new file mode 100644 index 000000000..d7ab728e7 --- /dev/null +++ b/firmware/microblaze/lib/db_xcvr2450.c @@ -0,0 +1,496 @@ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <memory_map.h> +#include <db_base.h> +#include <stdio.h> +#include <spi.h> +#include <hal_io.h> +#include <clocks.h> +#include <mdelay.h> + +void set_atr_regs(int bank, struct db_base *db); //FIXME I need to be in a header + +// RX IO Pins +#define LOCKDET (1 << 15) // This is an INPUT!!! +#define EN (1 << 14) +#define RX_EN (1 << 13) // 1 = RX on, 0 = RX off +#define RX_HP (1 << 12) +#define B1 (1 << 11) +#define B2 (1 << 10) +#define B3 (1 << 9) +#define B4 (1 << 8) +#define B5 (1 << 7) +#define B6 (1 << 6) +#define B7 (1 << 5) +#define RX_OE_MASK EN|RX_EN|RX_HP|B1|B2|B3|B4|B5|B6|B7 +#define RX_SAFE_IO EN +#define RX_ATR_MASK EN|RX_EN|RX_HP + +// TX IO Pins +#define HB_PA_OFF (1 << 15) // 5GHz PA, 1 = off, 0 = on +#define LB_PA_OFF (1 << 14) // 2.4GHz PA, 1 = off, 0 = on +#define ANTSEL_TX1_RX2 (1 << 13) // 1 = Ant 1 to TX, Ant 2 to RX +#define ANTSEL_TX2_RX1 (1 << 12) // 1 = Ant 2 to TX, Ant 1 to RX +#define TX_EN (1 << 11) // 1 = TX on, 0 = TX off +#define AD9515DIV (1 << 4) // 1 = Div by 3, 0 = Div by 2 +#define TX_OE_MASK HB_PA_OFF|LB_PA_OFF|ANTSEL_TX1_RX2|ANTSEL_TX2_RX1|TX_EN|AD9515DIV +#define TX_SAFE_IO HB_PA_OFF|LB_PA_OFF|ANTSEL_TX1_RX2|AD9515DIV +#define TX_ATR_MASK HB_PA_OFF|LB_PA_OFF|ANTSEL_TX1_RX2|ANTSEL_TX2_RX1|TX_EN|AD9515DIV + +#define LB_FREQ_MIN U2_DOUBLE_TO_FXPT_FREQ(2.3e9) +#define LB_FREQ_MAX U2_DOUBLE_TO_FXPT_FREQ(2.6e9) +#define HB_FREQ_MIN U2_DOUBLE_TO_FXPT_FREQ(4.8e9) +#define HB_FREQ_MAX U2_DOUBLE_TO_FXPT_FREQ(6.1e9) +#define MASTER_REF_CLK_DIV 1 +#define N_DIV_MIN_Q16 (131 << 16) + +bool xcvr2450_init(struct db_base *db); +bool xcvr2450_set_freq(struct db_base *db, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc); +bool xcvr2450_set_gain_rx(struct db_base *db, u2_fxpt_gain_t gain); +bool xcvr2450_set_gain_tx(struct db_base *db, u2_fxpt_gain_t gain); +bool xcvr2450_set_tx_enable(struct db_base *db, bool on); + +struct db_xcvr2450_common { + int d_mimo, d_int_div, d_frac_div, d_highband, d_five_gig; + int d_cp_current, d_ref_div, d_rssi_hbw; + int d_txlpf_bw, d_rxlpf_bw, d_rxlpf_fine, d_rxvga_ser; + int d_rssi_range, d_rssi_mode, d_rssi_mux; + int d_rx_hp_pin, d_rx_hpf, d_rx_ant; + int d_tx_ant, d_txvga_ser, d_tx_driver_lin; + int d_tx_vga_lin, d_tx_upconv_lin, d_tx_bb_gain; + int d_pabias_delay, d_pabias; + int d_rx_rf_gain, d_rx_bb_gain, d_txgain; + int d_ad9515_div; + int d_tx_enb; +}; + +struct db_xcvr2450_dummy { + struct db_base base; + struct db_xcvr2450_common *common; +}; + +struct db_xcvr2450_rx { + struct db_base base; + struct db_xcvr2450_common *common; +}; + +struct db_xcvr2450_tx { + struct db_base base; + struct db_xcvr2450_common *common; +}; + +/* + * shared common between rx and tx db + */ +struct db_xcvr2450_common db_xcvr2450_common = { + /* set sane defaults */ + .d_mimo = 1, // 0 = OFF, 1 = ON + .d_int_div = 192, // 128 = min, 255 = max + .d_frac_div = 0, // 0 = min, 65535 = max + .d_highband = 0, // 0 = freq <= 5.4e9, 1 = freq > 5.4e9 + .d_five_gig = 0, // 0 = freq <= 3.e9, 1 = freq > 3e9 + .d_cp_current = 0, // 0 = 2mA, 1 = 4mA + .d_ref_div = 1, // 1 to 7 + .d_rssi_hbw = 0, // 0 = 2 MHz, 1 = 6 MHz + .d_txlpf_bw = 1, // 1 = 12 MHz, 2 = 18 MHz, 3 = 24 MHz + .d_rxlpf_bw = 1, // 0 = 7.5 MHz, 1 = 9.5 MHz, 2 = 14 MHz, 3 = 18 MHz + .d_rxlpf_fine = 2, // 0 = 90%, 1 = 95%, 2 = 100%, 3 = 105%, 4 = 110% + .d_rxvga_ser = 1, // 0 = RXVGA controlled by B7:1, 1=controlled serially + .d_rssi_range = 1, // 0 = low range (datasheet typo), 1=high range (0.5V - 2.0V) + .d_rssi_mode = 1, // 0 = enable follows RXHP, 1 = enabled + .d_rssi_mux = 0, // 0 = RSSI, 1 = TEMP + .d_rx_hp_pin = 0, // 0 = Fc set by rx_hpf, 1 = 600 KHz + .d_rx_hpf = 0, // 0 = 100Hz, 1 = 30KHz + .d_rx_ant = 0, // 0 = Ant. #1, 1 = Ant. #2 + .d_tx_ant = 0, // 0 = Ant. #1, 1 = Ant. #2 + .d_txvga_ser = 1, // 0 = TXVGA controlled by B6:1, 1=controlled serially + .d_tx_driver_lin = 2, // 0=50% (worst linearity), 1=63%, 2=78%, 3=100% (best lin) + .d_tx_vga_lin = 2, // 0=50% (worst linearity), 1=63%, 2=78%, 3=100% (best lin) + .d_tx_upconv_lin = 2, // 0=50% (worst linearity), 1=63%, 2=78%, 3=100% (best lin) + .d_tx_bb_gain = 3, // 0=maxgain-5dB, 1=max-3dB, 2=max-1.5dB, 3=max + .d_pabias_delay = 15, // 0 = 0, 15 = 7uS + .d_pabias = 0, // 0 = 0 uA, 63 = 315uA + .d_rx_rf_gain = 0, // 0 = 0dB, 1 = 0dB, 2 = 15dB, 3 = 30dB + .d_rx_bb_gain = 16, // 0 = min, 31 = max (0 - 62 dB) + .d_txgain = 63, // 0 = min, 63 = max + .d_tx_enb = 1, // 0 = disabled, 1 = enabled +}; + +/* + * The class instances + */ +struct db_xcvr2450_rx db_xcvr2450_rx = { + .base.dbid = 0x0061, + .base.is_tx = false, + .base.output_enables = RX_OE_MASK, + .base.used_pins = 0xFFFF, + .base.freq_min = LB_FREQ_MIN, + .base.freq_max = HB_FREQ_MAX, + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(92), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(1), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = xcvr2450_init, + .base.set_freq = xcvr2450_set_freq, + .base.set_gain = xcvr2450_set_gain_rx, + .base.atr_mask = RX_ATR_MASK, + .base.atr_txval = 0x0, + .base.atr_rxval = 0x0, + .common = &db_xcvr2450_common, +}; + +struct db_xcvr2450_tx db_xcvr2450_tx = { + .base.dbid = 0x0060, + .base.is_tx = true, + .base.output_enables = TX_OE_MASK, + .base.used_pins = 0xFFFF, + .base.freq_min = LB_FREQ_MIN, + .base.freq_max = HB_FREQ_MAX, + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(30), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(30.0/63.0), + .base.is_quadrature = true, + .base.i_and_q_swapped = true, + .base.spectrum_inverted = false, + .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.init = xcvr2450_init, + .base.set_freq = xcvr2450_set_freq, + .base.set_gain = xcvr2450_set_gain_tx, + .base.set_tx_enable = xcvr2450_set_tx_enable, + .base.atr_mask = TX_ATR_MASK, + .base.atr_txval = 0x0, + .base.atr_rxval = 0x0, + .common = &db_xcvr2450_common, +}; + +/************************************************** + * Set Registers + **************************************************/ +static void +send_reg(int v){ + // Send 24 bits, it keeps last 18 clocked in + spi_transact(SPI_TXONLY,SPI_SS_RX_DB,v,24,SPIF_PUSH_FALL); + //printf("xcvr2450: Setting reg %d to %x\n", (v&15), v); +} + +static void +set_reg_standby(struct db_xcvr2450_dummy *db){ + int reg_standby = ( + (db->common->d_mimo<<17) | + (1<<16) | + (1<<6) | + (1<<5) | + (1<<4) | 2); + send_reg(reg_standby); +} + +static void +set_reg_int_divider(struct db_xcvr2450_dummy *db){ + int reg_int_divider = (( + (db->common->d_frac_div & 0x03)<<16) | + (db->common->d_int_div<<4) | 3); + send_reg(reg_int_divider); +} + +static void +set_reg_frac_divider(struct db_xcvr2450_dummy *db){ + int reg_frac_divider = ((db->common->d_frac_div & 0xfffc)<<2) | 4; + send_reg(reg_frac_divider); +} + +static void +set_reg_bandselpll(struct db_xcvr2450_dummy *db){ + int reg_bandselpll = ((db->common->d_mimo<<17) | + (1<<16) | + (1<<15) | + (0<<11) | //this bit gets toggled + (db->common->d_highband<<10) | + (db->common->d_cp_current<<9) | + (db->common->d_ref_div<<5) | + (db->common->d_five_gig<<4) | 5); + send_reg(reg_bandselpll); + reg_bandselpll = ((db->common->d_mimo<<17) | + (1<<16) | + (1<<15) | + (1<<11) | + (db->common->d_highband<<10) | + (db->common->d_cp_current<<9) | + (db->common->d_ref_div<<5) | + (db->common->d_five_gig<<4) | 5); + send_reg(reg_bandselpll); +} + +static void +set_reg_cal(struct db_xcvr2450_dummy *db){ + // FIXME do calibration + int reg_cal = ( + (1<<14) | 6); + send_reg(reg_cal); +} + +static void +set_reg_lpf(struct db_xcvr2450_dummy *db){ + int reg_lpf = ( + (db->common->d_rssi_hbw<<15) | + (db->common->d_txlpf_bw<<9) | + (db->common->d_rxlpf_bw<<7) | + (db->common->d_rxlpf_fine<<4) | 7); + send_reg(reg_lpf); +} + +static void +set_reg_rxrssi_ctrl(struct db_xcvr2450_dummy *db){ + int reg_rxrssi_ctrl = ( + (db->common->d_rxvga_ser<<16) | + (db->common->d_rssi_range<<15) | + (db->common->d_rssi_mode<<14) | + (db->common->d_rssi_mux<<12) | + (1<<9) | + (db->common->d_rx_hpf<<6) | + (1<<4) | 8); + send_reg(reg_rxrssi_ctrl); +} + +static void +set_reg_txlin_gain(struct db_xcvr2450_dummy *db){ + int reg_txlin_gain = ( + (db->common->d_txvga_ser<<14) | + (db->common->d_tx_driver_lin<<12) | + (db->common->d_tx_vga_lin<<10) | + (db->common->d_tx_upconv_lin<<6) | + (db->common->d_tx_bb_gain<<4) | 9); + send_reg(reg_txlin_gain); +} + +static void +set_reg_pabias(struct db_xcvr2450_dummy *db){ + int reg_pabias = ( + (db->common->d_pabias_delay<<10) | + (db->common->d_pabias<<4) | 10); + send_reg(reg_pabias); +} + +static void +set_reg_rxgain(struct db_xcvr2450_dummy *db){ + int reg_rxgain = ( + (db->common->d_rx_rf_gain<<9) | + (db->common->d_rx_bb_gain<<4) | 11); + send_reg(reg_rxgain); +} + +static void +set_reg_txgain(struct db_xcvr2450_dummy *db){ + int reg_txgain = ( + (db->common->d_txgain<<4) | 12); + send_reg(reg_txgain); +} + +/************************************************** + * GPIO + **************************************************/ +static void +set_gpio(struct db_xcvr2450_dummy *db){ + //set tx/rx gpio pins for auto tr + int tx_enb_sel = (db->common->d_tx_enb)? TX_EN:0; + int ad9515_sel = (db->common->d_ad9515_div == 3)? AD9515DIV:0; + int rx_hp = (db->common->d_rx_hp_pin)? RX_HP:0; + int tx_antsel = (db->common->d_tx_ant)? ANTSEL_TX2_RX1:ANTSEL_TX1_RX2; + int rx_antsel = (db->common->d_rx_ant)? ANTSEL_TX2_RX1:ANTSEL_TX1_RX2; + int tx_pa_sel = (db->common->d_five_gig)? LB_PA_OFF:HB_PA_OFF; + + /* FIXME better way to set rx and tx val for RX and TX banks */ + /* set rx bank */ + db->base.atr_rxval = EN|rx_hp|RX_EN; + db->base.atr_txval = EN|rx_hp; + set_atr_regs(GPIO_RX_BANK, (struct db_base *)db); + /* set tx bank */ + db->base.atr_rxval = HB_PA_OFF|LB_PA_OFF|rx_antsel|ad9515_sel; + db->base.atr_txval = tx_pa_sel|tx_antsel|tx_enb_sel|ad9515_sel; + set_atr_regs(GPIO_TX_BANK, (struct db_base *)db); +} + +/************************************************** + * Init for TX and RX + **************************************************/ +bool +xcvr2450_init(struct db_base *dbb){ + struct db_xcvr2450_dummy *db = (struct db_xcvr2450_dummy *) dbb; + /* Initialize chipset */ + clocks_enable_tx_dboard(true, MASTER_REF_CLK_DIV); + set_gpio(db); + set_reg_standby(db); + set_reg_bandselpll(db); + set_reg_cal(db); + set_reg_lpf(db); + set_reg_rxrssi_ctrl(db); + set_reg_txlin_gain(db); + set_reg_pabias(db); + set_reg_rxgain(db); + set_reg_txgain(db); + //u2_fxpt_freq_t dc; + //db->base.set_freq(dbb, U2_DOUBLE_TO_FXPT_FREQ(2.434e9), &dc); + return true; +} + +/************************************************** + * Lock detect + **************************************************/ +static bool +lock_detect(){ + //true when the VCO/PLL lock detect bit is set. + if(hal_gpio_read(GPIO_RX_BANK) & LOCKDET) { + return true; + } + else { // Give it a second chance + mdelay(1); + if(hal_gpio_read(GPIO_RX_BANK) & LOCKDET) + return true; + else + return false; + } +} + +/************************************************** + * Set the freq + **************************************************/ +bool +xcvr2450_set_freq(struct db_base *dbb, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc){ + unsigned int scaler, div_factor, actual_div_q16; + struct db_xcvr2450_dummy *db = (struct db_xcvr2450_dummy *) dbb; + /* determine if the freq range is in low or high band */ + if (freq >= LB_FREQ_MIN && freq <= LB_FREQ_MAX) { + db->common->d_five_gig = 0; + scaler = 3; + //printf("2.4-GHZ\n"); + } else if (freq >= HB_FREQ_MIN && freq <= HB_FREQ_MAX) { + db->common->d_five_gig = 1; + scaler = 5; + //printf("5-GHZ\n"); + } else { + printf("Out of range\n"); + return false; + } + /* set the highband bit */ + if(freq > U2_DOUBLE_TO_FXPT_FREQ(5.408e9)) { + db->common->d_highband = 1; + //printf("5-HB\n"); + } + else { + db->common->d_highband = 0; + //printf("5-LB\n"); + } + unsigned int loop_iter = 0; + do { /* set the dividers so that the n divider is above the practical minimum */ + switch(loop_iter){ + case 0: + db->common->d_ad9515_div = 3; + db->common->d_ref_div = 1; + break; + case 1: + db->common->d_ad9515_div = 2; + db->common->d_ref_div = 2; + break; + default: + db->common->d_ad9515_div = 3; + db->common->d_ref_div = loop_iter; + } + loop_iter++; + div_factor = db->common->d_ref_div*db->common->d_ad9515_div*4*MASTER_REF_CLK_DIV; + actual_div_q16 = ((freq*div_factor)/(scaler*MASTER_CLK_RATE)) >> (U2_FPF_RP-16); + } while (actual_div_q16 < N_DIV_MIN_Q16); + /* calculate the divisors */ + db->common->d_int_div = actual_div_q16 >> 16; + db->common->d_frac_div = actual_div_q16 & 0xffff; //isolate lower 16 bits + /* calculate the dc freq */ + *dc = ((((u2_fxpt_freq_t)MASTER_CLK_RATE)*actual_div_q16*scaler) / div_factor) << (U2_FPF_RP-16); + /*printf("scaler %d, div(int) %u, div_factor %d, ad9515_div %u, ref_div %u\n", + scaler, db->common->d_int_div, div_factor, db->common->d_ad9515_div, db->common->d_ref_div); + printf("actual div %u, Target Freq %uKHz, Actual Freq %uKHz\n", + actual_div_q16, u2_fxpt_freq_round_to_int(freq/1000), u2_fxpt_freq_round_to_int(*dc/1000)); + */ + set_gpio(db); + set_reg_int_divider(db); + set_reg_frac_divider(db); + set_reg_bandselpll(db); + + bool ok = lock_detect(); + if(!ok){ + //printf("Fail lock detect %uKHz\n", u2_fxpt_freq_round_to_int(freq/1000)); + } + return ok; +} + +/************************************************** + * Set RX Gain + **************************************************/ +bool +xcvr2450_set_gain_rx(struct db_base *dbb, u2_fxpt_gain_t gain){ + struct db_xcvr2450_dummy *db = (struct db_xcvr2450_dummy *) dbb; + //ensure gain is within range + if(!(gain >= db->base.gain_min && gain <= db->base.gain_max)) { + return false; + } + // Split the gain between RF and baseband + // This is experimental, not prescribed + if(gain < U2_DOUBLE_TO_FXPT_GAIN(30.0)) { + db->common->d_rx_rf_gain = 0; // 0 dB RF gain + db->common->d_rx_bb_gain = u2_fxpt_gain_round_to_int(gain/2); + } + else if(gain >= U2_DOUBLE_TO_FXPT_GAIN(30.0) && gain < U2_DOUBLE_TO_FXPT_GAIN(61.0)) { + db->common->d_rx_rf_gain = 2; // 15 dB RF gain + db->common->d_rx_bb_gain = u2_fxpt_gain_round_to_int((gain-U2_DOUBLE_TO_FXPT_GAIN(15.0))/2); + } + else if(gain >= U2_DOUBLE_TO_FXPT_GAIN(61.0)) { + db->common->d_rx_rf_gain = 3; // 30.5 dB RF gain + db->common->d_rx_bb_gain = u2_fxpt_gain_round_to_int((gain-U2_DOUBLE_TO_FXPT_GAIN(30.5))/2); + } + //printf("RX RF Gain %u, RX BB Gain %u\n", db->common->d_rx_rf_gain, db->common->d_rx_bb_gain); + set_reg_rxgain(db); + return true; +} + +/************************************************** + * Set TX Gain + **************************************************/ +bool +xcvr2450_set_gain_tx(struct db_base *dbb, u2_fxpt_gain_t gain){ + struct db_xcvr2450_dummy *db = (struct db_xcvr2450_dummy *) dbb; + //ensure gain in within range + if(!(gain >= db->base.gain_min && gain <= db->base.gain_max)) { + return false; + } + //scale for register and set + db->common->d_txgain = (gain*63)/db->base.gain_max; + //printf("TX Gain %u, TX Reg %u\n", u2_fxpt_gain_round_to_int(gain), db->common->d_txgain); + set_reg_txgain(db); + return true; +} + +/************************************************** + * Set TX Enable + **************************************************/ +bool +xcvr2450_set_tx_enable(struct db_base *dbb, bool on){ + struct db_xcvr2450_dummy *db = (struct db_xcvr2450_dummy *) dbb; + db->common->d_tx_enb = on; + set_gpio(db); + return true; +} diff --git a/firmware/microblaze/lib/dbsm.c b/firmware/microblaze/lib/dbsm.c new file mode 100644 index 000000000..9d66ec39c --- /dev/null +++ b/firmware/microblaze/lib/dbsm.c @@ -0,0 +1,297 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Double Buffering State Machine + */ + +#include "dbsm.h" +#include "memory_map.h" +#include "buffer_pool.h" +#include <stdbool.h> +#include "nonstdio.h" +#include <stdlib.h> + +typedef enum { + BS_EMPTY, + BS_FILLING, + BS_FULL, + BS_EMPTYING, +} buffer_state_t; + +static buffer_state_t buffer_state[NBUFFERS]; + +bool +dbsm_nop_inspector(dbsm_t *sm, int buf_this) +{ + return false; +} + +void +dbsm_init(dbsm_t *sm, int buf0, + const buf_cmd_args_t *recv, const buf_cmd_args_t *send, + inspector_t inspect) +{ + if (buf0 & 0x1) // must be even + abort(); + + sm->buf0 = buf0; + sm->running = false; + sm->recv_args = *recv; + sm->send_args = *send; + + sm->rx_idle = true; + sm->tx_idle = true; + + sm->inspect = inspect; + + // How much to adjust the last_line register. + // It's 1 for everything but the ethernet. + //sm->last_line_adj = recv->port == PORT_ETH ? 3 : 1; + sm->last_line_adj = 1; + + buffer_state[sm->buf0] = BS_EMPTY; + buffer_state[sm->buf0 ^ 1] = BS_EMPTY; + + sm->precomputed_receive_to_buf_ctrl_word[0] = + (BPC_READ + | BPC_BUFFER(sm->buf0) + | BPC_PORT(sm->recv_args.port) + | BPC_STEP(1) + | BPC_FIRST_LINE(sm->recv_args.first_line) + | BPC_LAST_LINE(sm->recv_args.last_line)); + + sm->precomputed_receive_to_buf_ctrl_word[1] = + (BPC_READ + | BPC_BUFFER(sm->buf0 ^ 1) + | BPC_PORT(sm->recv_args.port) + | BPC_STEP(1) + | BPC_FIRST_LINE(sm->recv_args.first_line) + | BPC_LAST_LINE(sm->recv_args.last_line)); + + sm->precomputed_send_from_buf_ctrl_word[0] = + (BPC_WRITE + | BPC_BUFFER(sm->buf0) + | BPC_PORT(sm->send_args.port) + | BPC_STEP(1) + | BPC_FIRST_LINE(sm->send_args.first_line) + | BPC_LAST_LINE(0)); // last line filled in at runtime + + sm->precomputed_send_from_buf_ctrl_word[1] = + (BPC_WRITE + | BPC_BUFFER(sm->buf0 ^ 1) + | BPC_PORT(sm->send_args.port) + | BPC_STEP(1) + | BPC_FIRST_LINE(sm->send_args.first_line) + | BPC_LAST_LINE(0)); // last line filled in at runtime + +} + +static inline void +dbsm_receive_to_buf(dbsm_t *sm, int bufno) +{ + buffer_pool_ctrl->ctrl = sm->precomputed_receive_to_buf_ctrl_word[bufno & 1]; +} + +static inline void +dbsm_send_from_buf(dbsm_t *sm, int bufno) +{ + buffer_pool_ctrl->ctrl = + (sm->precomputed_send_from_buf_ctrl_word[bufno & 1] + | BPC_LAST_LINE(buffer_pool_status->last_line[bufno] - sm->last_line_adj)); +} + +void +dbsm_start(dbsm_t *sm) +{ + // printf("dbsm_start: buf0 = %d, recv_port = %d\n", sm->buf0, sm->recv_args.port); + + sm->running = true; + + buffer_state[sm->buf0] = BS_EMPTY; + buffer_state[sm->buf0 ^ 1] = BS_EMPTY; + + bp_clear_buf(sm->buf0); + bp_clear_buf(sm->buf0 ^ 1); + + sm->tx_idle = true; + sm->rx_idle = false; + dbsm_receive_to_buf(sm, sm->buf0); + buffer_state[sm->buf0] = BS_FILLING; + +} + + +void +dbsm_stop(dbsm_t *sm) +{ + sm->running = false; + bp_clear_buf(sm->buf0); + bp_clear_buf(sm->buf0 ^ 1); + buffer_state[sm->buf0] = BS_EMPTY; + buffer_state[sm->buf0 ^ 1] = BS_EMPTY; +} + +static void dbsm_process_helper(dbsm_t *sm, int buf_this); +static void dbsm_error_helper(dbsm_t *sm, int buf_this); + +void +dbsm_process_status(dbsm_t *sm, uint32_t status) +{ + if (!sm->running) + return; + + if (status & (BPS_ERROR(sm->buf0) | BPS_ERROR(sm->buf0 ^ 1))){ + putchar('E'); + // Most likely an ethernet Rx error. We just restart the transfer. + if (status & (BPS_ERROR(sm->buf0))) + dbsm_error_helper(sm, sm->buf0); + + if (status & (BPS_ERROR(sm->buf0 ^ 1))) + dbsm_error_helper(sm, sm->buf0 ^ 1); + } + + if (status & BPS_DONE(sm->buf0)) + dbsm_process_helper(sm, sm->buf0); + + if (status & BPS_DONE(sm->buf0 ^ 1)) + dbsm_process_helper(sm, sm->buf0 ^ 1); +} + +static void +dbsm_process_helper(dbsm_t *sm, int buf_this) +{ + int buf_other = buf_this ^ 1; + + bp_clear_buf(buf_this); + + if (buffer_state[buf_this] == BS_FILLING){ + buffer_state[buf_this] = BS_FULL; + // + // does s/w handle this packet? + // + if (sm->inspect(sm, buf_this)){ + // s/w handled the packet; refill the buffer + dbsm_receive_to_buf(sm, buf_this); + buffer_state[buf_this] = BS_FILLING; + } + + else { // s/w didn't handle this; pass it on + + if(buffer_state[buf_other] == BS_EMPTY){ + dbsm_receive_to_buf(sm, buf_other); + buffer_state[buf_other] = BS_FILLING; + } + else + sm->rx_idle = true; + + if (sm->tx_idle){ + sm->tx_idle = false; + dbsm_send_from_buf(sm, buf_this); + buffer_state[buf_this] = BS_EMPTYING; + } + } + } + else { // buffer was emptying + buffer_state[buf_this] = BS_EMPTY; + if (sm->rx_idle){ + sm->rx_idle = false; + dbsm_receive_to_buf(sm, buf_this); + buffer_state[buf_this] = BS_FILLING; + } + if (buffer_state[buf_other] == BS_FULL){ + dbsm_send_from_buf(sm, buf_other); + buffer_state[buf_other] = BS_EMPTYING; + } + else + sm->tx_idle = true; + } +} + +static void +dbsm_error_helper(dbsm_t *sm, int buf_this) +{ + bp_clear_buf(buf_this); // clears ERROR flag + + if (buffer_state[buf_this] == BS_FILLING){ + dbsm_receive_to_buf(sm, buf_this); // restart the xfer + } + else { // buffer was emptying + dbsm_send_from_buf(sm, buf_this); // restart the xfer + } +} + +/* + * Handle DSP Tx underrun + */ +void +dbsm_handle_tx_underrun(dbsm_t *sm) +{ + // clear the DSP Tx state machine + sr_tx_ctrl->clear_state = 1; + + // If there's a buffer that's empyting, clear it & flush xfer + + if (buffer_state[sm->buf0] == BS_EMPTYING){ + bp_clear_buf(sm->buf0); + sr_tx_ctrl->clear_state = 1; // flush partial packet + // drop frame in progress on ground. Pretend it finished + dbsm_process_helper(sm, sm->buf0); + } + else if (buffer_state[sm->buf0 ^ 1] == BS_EMPTYING){ + bp_clear_buf(sm->buf0 ^ 1); + sr_tx_ctrl->clear_state = 1; // flush partial packet + // drop frame in progress on ground. Pretend it finished + dbsm_process_helper(sm, sm->buf0 ^ 1); + } +} + +/* + * Handle DSP Rx overrun + */ +void +dbsm_handle_rx_overrun(dbsm_t *sm) +{ + sr_rx_ctrl->clear_overrun = 1; + + // If there's a buffer that's filling, clear it. + // Any restart will be the job of the caller. + + if (buffer_state[sm->buf0] == BS_FILLING) + bp_clear_buf(sm->buf0); + + if (buffer_state[sm->buf0 ^1] == BS_FILLING) + bp_clear_buf(sm->buf0 ^ 1); +} + +void +dbsm_wait_for_opening(dbsm_t *sm) +{ + if (buffer_state[sm->buf0] == BS_EMPTYING){ + // wait for xfer to complete + int mask = BPS_DONE(sm->buf0) | BPS_ERROR(sm->buf0) | BPS_IDLE(sm->buf0); + while ((buffer_pool_status->status & mask) == 0) + ; + } + else if (buffer_state[sm->buf0 ^ 1] == BS_EMPTYING){ + // wait for xfer to complete + int mask = BPS_DONE(sm->buf0 ^ 1) | BPS_ERROR(sm->buf0 ^ 1) | BPS_IDLE(sm->buf0 ^ 1); + while ((buffer_pool_status->status & mask) == 0) + ; + } +} diff --git a/firmware/microblaze/lib/dbsm.h b/firmware/microblaze/lib/dbsm.h new file mode 100644 index 000000000..cb7e12fc3 --- /dev/null +++ b/firmware/microblaze/lib/dbsm.h @@ -0,0 +1,90 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_DBSM_H +#define INCLUDED_DBSM_H + +/* + * Double Buffering State Machine + */ + +#include <stdint.h> +#include <stdbool.h> + +struct _dbsm; +typedef struct _dbsm dbsm_t; + +/* + * pointer to function that does packet inspection. + * + * If one of these returns true, it means that the s/w + * handled that packet, and that it should NOT be passed + * on to the normal destination port. + */ +typedef bool (*inspector_t)(dbsm_t *sm, int buf_this); + +bool dbsm_nop_inspector(dbsm_t *sm, int buf_this); // returns false + + +typedef struct +{ + uint16_t port; + uint16_t first_line; + uint16_t last_line; +} buf_cmd_args_t; + +/*! + * double buffer state machine + */ +struct _dbsm +{ + uint8_t buf0; // Must be even. This machine uses buf0 and buf0+1 + uint8_t running; + uint8_t rx_idle; + uint8_t tx_idle; + buf_cmd_args_t recv_args; + buf_cmd_args_t send_args; + inspector_t inspect; + uint32_t precomputed_receive_to_buf_ctrl_word[2]; + uint32_t precomputed_send_from_buf_ctrl_word[2]; + int last_line_adj; +}; + +void dbsm_init(dbsm_t *sm, int buf0, + const buf_cmd_args_t *recv, const buf_cmd_args_t *send, + inspector_t inspect); + +void dbsm_start(dbsm_t *sm); +void dbsm_stop(dbsm_t *sm); +void dbsm_process_status(dbsm_t *sm, uint32_t status); +void dbsm_handle_tx_underrun(dbsm_t *sm); +void dbsm_handle_rx_overrun(dbsm_t *sm); + +/* + * The cpu calls this when it want to ensure that it can send a buffer + * to the same destination being used by this state machine. + * + * If neither buffer is EMPTYING it returns immediately. If a buffer + * is EMPYTING, it waits for the h/w to transition to the DONE or + * ERROR state. + * + * When this function returns, the caller queues it's buffer and busy + * waits for it to complete. + */ +void dbsm_wait_for_opening(dbsm_t *sm); + +#endif /* INCLUDED_DBSM_H */ diff --git a/firmware/microblaze/lib/eeprom.c b/firmware/microblaze/lib/eeprom.c new file mode 100644 index 000000000..b12ffe082 --- /dev/null +++ b/firmware/microblaze/lib/eeprom.c @@ -0,0 +1,69 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "i2c.h" +#include "mdelay.h" + +static const int EEPROM_PAGESIZE = 16; + +bool +eeprom_write (int i2c_addr, int eeprom_offset, const void *buf, int len) +{ + unsigned char cmd[2]; + const unsigned char *p = (unsigned char *) buf; + + // The simplest thing that could possibly work: + // all writes are single byte writes. + // + // We could speed this up using the page write feature, + // but we write so infrequently, why bother... + + while (len-- > 0){ + cmd[0] = eeprom_offset++; + cmd[1] = *p++; + bool r = i2c_write (i2c_addr, cmd, sizeof (cmd)); + mdelay (10); // delay 10ms worst case write time + if (!r) + return false; + } + return true; +} + +bool +eeprom_read (int i2c_addr, int eeprom_offset, void *buf, int len) +{ + unsigned char *p = (unsigned char *) buf; + + // We setup a random read by first doing a "zero byte write". + // Writes carry an address. Reads use an implicit address. + + unsigned char cmd[1]; + cmd[0] = eeprom_offset; + if (!i2c_write (i2c_addr, cmd, sizeof (cmd))) + return false; + + while (len > 0){ + // int n = std::min (len, MAX_EP0_PKTSIZE); + int n = len; + if (!i2c_read (i2c_addr, p, n)) + return false; + len -= n; + p += n; + } + return true; +} + diff --git a/firmware/microblaze/lib/eth_mac.c b/firmware/microblaze/lib/eth_mac.c new file mode 100644 index 000000000..375d3f4d4 --- /dev/null +++ b/firmware/microblaze/lib/eth_mac.c @@ -0,0 +1,127 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "eth_mac.h" +#include "memory_map.h" +#include <stdbool.h> +#include "eth_phy.h" // for simulation constants +#include "mdelay.h" +#include "stdio.h" + +#define PHY_ADDR 1 + +void +eth_mac_set_addr(const eth_mac_addr_t *src) +{ + eth_mac->ucast_hi = + (((unsigned int)src->addr[0])<<8) + + ((unsigned int)src->addr[1]); + eth_mac->ucast_lo = + (((unsigned int)src->addr[2])<<24) + + (((unsigned int)src->addr[3])<<16) + + (((unsigned int)src->addr[4])<<8) + + (((unsigned int)src->addr[5])); +} + + +void +eth_mac_init(const eth_mac_addr_t *src) +{ + eth_mac->miimoder = 25; // divider from CPU clock (50MHz/25 = 2MHz) + + eth_mac_set_addr(src); + eth_mac->settings = MAC_SET_PAUSE_EN | MAC_SET_PASS_BCAST | MAC_SET_PASS_UCAST | MAC_SET_PAUSE_SEND_EN; + + eth_mac->pause_time = 38; + eth_mac->pause_thresh = 1200; + + // set rx flow control high and low water marks + // unsigned int lwmark = (2*2048 + 64)/4; // 2 * 2048-byte frames + 1 * 64-byte pause frame + // eth_mac->fc_hwmark = lwmark + 2048/4; // plus a 2048-byte frame + + // eth_mac->fc_lwmark = 600; // there are currently 2047 lines in the fifo + // eth_mac->fc_hwmark = 1200; + //eth_mac->fc_padtime = 1700; // how long before flow control runs out do we + // request a re-pause. Units of 8ns (bytes) + + //eth_mac->tx_pause_en = 0; // pay attn to pause frames sent to us + //eth_mac->pause_quanta_set = 38; // a bit more than 1 max frame 16kb/512 + fudge + //eth_mac->pause_frame_send_en = 0; // enable sending pause frames +} + +int +eth_mac_read_rmon(int addr) +{ + int t = 0; + /* + eth_mac->rmon_rd_addr = addr; + eth_mac->rmon_rd_apply = 1; + while(eth_mac->rmon_rd_grant == 0) + ; + + t = eth_mac->rmon_rd_dout; + eth_mac->rmon_rd_apply = 0; + */ + return t; +} + +int +eth_mac_miim_read(int addr) +{ + if (hwconfig_simulation_p()){ + switch(addr){ + case PHY_LINK_AN: + return LANSR_MASTER | LANSR_LINK_GOOD | LANSR_SPEED_1000; + default: + return 0; + } + } + + int phy_addr = PHY_ADDR; + eth_mac->miiaddress = ((addr & 0x1f) << 8) | phy_addr; + eth_mac->miicommand = MIIC_RSTAT; + + while((eth_mac->miistatus & MIIS_BUSY) != 0) + ; + + int r = eth_mac->miirx_data; + //printf("MIIM-READ ADDR 0x%x DATA 0x%x\n",addr, r); + return r; +} + +void +eth_mac_miim_write(int addr, int value) +{ + int phy_addr = PHY_ADDR; + eth_mac->miiaddress = ((addr & 0x1f) << 8) | phy_addr; + eth_mac->miitx_data = value; + eth_mac->miicommand = MIIC_WCTRLDATA; + + //printf("MIIM-WRITE ADDR 0x%x VAL 0x%x\n",addr,value); + while((eth_mac->miistatus & MIIS_BUSY) != 0) + ; +} + +int +eth_mac_miim_read_status(void) +{ + if (hwconfig_simulation_p()) + return 0; + + return eth_mac->miistatus; +} diff --git a/firmware/microblaze/lib/eth_mac.h b/firmware/microblaze/lib/eth_mac.h new file mode 100644 index 000000000..65717f6f8 --- /dev/null +++ b/firmware/microblaze/lib/eth_mac.h @@ -0,0 +1,32 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_ETH_MAC_H +#define INCLUDED_ETH_MAC_H + +#include "network.h" + +void eth_mac_init(const eth_mac_addr_t *src); + +void eth_mac_set_addr(const eth_mac_addr_t *src); +int eth_mac_read_rmon(int addr); +int eth_mac_miim_read(int addr); +void eth_mac_miim_write(int addr, int value); +int eth_mac_miim_read_status(void); + +#endif /* INCLUDED_ETH_MAC_H */ diff --git a/firmware/microblaze/lib/eth_mac_regs.h b/firmware/microblaze/lib/eth_mac_regs.h new file mode 100644 index 000000000..d680f8de0 --- /dev/null +++ b/firmware/microblaze/lib/eth_mac_regs.h @@ -0,0 +1,62 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_ETH_MAC_REGS_H +#define INCLUDED_ETH_MAC_REGS_H + +/* + * Simple GEMAC + * + */ +typedef struct { + volatile int settings; + volatile int ucast_hi; + volatile int ucast_lo; + volatile int mcast_hi; + volatile int mcast_lo; + volatile int miimoder; + volatile int miiaddress; + volatile int miitx_data; + volatile int miicommand; + volatile int miistatus; + volatile int miirx_data; + volatile int pause_time; + volatile int pause_thresh; +} eth_mac_regs_t; + +// settings register +#define MAC_SET_PAUSE_EN (1 << 0) // Makes us respect received pause frames (normally on) +#define MAC_SET_PASS_ALL (1 << 1) // Enables promiscuous mode, currently broken +#define MAC_SET_PASS_PAUSE (1 << 2) // Sends pause frames through (normally off) +#define MAC_SET_PASS_BCAST (1 << 3) // Sends broadcast frames through (normally on) +#define MAC_SET_PASS_MCAST (1 << 4) // Sends multicast frames that match mcast addr (normally off) +#define MAC_SET_PASS_UCAST (1 << 5) // Sends unicast (normal) frames through if they hit in address filter (normally on) +#define MAC_SET_PAUSE_SEND_EN (1 << 6) // Enables sending pause frames + +// miicommand register +#define MIIC_SCANSSTAT (1 << 0) // Scan status +#define MIIC_RSTAT (1 << 1) // Read status +#define MIIC_WCTRLDATA (1 << 2) // Write control data + +// miistatus register +#define MIIS_LINKFAIL (1 << 0) // The link failed +#define MIIS_BUSY (1 << 1) // The MII is busy (operation in progress) +#define MIIS_NVALID (1 << 2) // The data in the status register is invalid + // This it is only valid when the scan status is active. + +#endif /* INCLUDED_ETH_MAC_REGS_H */ diff --git a/firmware/microblaze/lib/eth_phy.h b/firmware/microblaze/lib/eth_phy.h new file mode 100644 index 000000000..6c16f97b7 --- /dev/null +++ b/firmware/microblaze/lib/eth_phy.h @@ -0,0 +1,219 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* Much of this was extracted from the Linux e1000_hw.h file */ + +#ifndef INCLUDED_ETH_PHY_H +#define INCLUDED_ETH_PHY_H + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ + +#define PHY_CTRL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Regiser */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +/* PHY 1000 MII Register additions in DP83856 */ +/* The part implements 0x00 thru 0x1f; we use these. */ + +#define PHY_LINK_AN 0x11 /* Link and Auto Negotiation Status Reg */ +#define PHY_INT_STATUS 0x14 /* Interupt Status Reg (RO) */ +#define PHY_INT_MASK 0x15 /* Interrupt Mask Reg (RW) */ +#define PHY_INT_CLEAR 0x17 /* Interrupt Clear Reg (RW) */ + + +/* Bit definitions for some of the registers above */ + +/* PHY Control Register (PHY_CTRL) */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ + +/* PHY Status Register (PHY_STATUS) */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register (PHY_AUTONEG_ADV) */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) (PHY_LP_ABILITY) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register (PHY_AUTONEG_EXP) */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */ + +/* Next Page TX Register (PHY_NEXT_PAGE_TX) */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* Link Partner Next Page Register (PHY_LP_NEXT_PAGE) */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* 1000BASE-T Control Register (PHY_1000T_CTRL) */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ +#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register (PHY_1000T_STATUS) */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 +#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100 + +/* Extended Status Register (PHY_EXT_STATUS) */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ + +#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ + /* (0=enable, 1=disable) */ + +/* Link and Auto Negotiation Status Reg (PHY_LINK_AN) [READ-ONLY] */ +#define LANSR_MASTER 0x0001 /* 1=PHY is currently in master mode */ +#define LANSR_FULL_DUPLEX 0x0002 /* 1=PHY is currently full duplex */ +#define LANSR_LINK_GOOD 0x0004 /* 1=a good link is established */ +#define LANSR_SPEED_MASK 0x0018 +#define LANSR_SPEED_10 0x0000 /* 10Mb/s */ +#define LANSR_SPEED_100 0x0008 /* 100Mb/s */ +#define LANSR_SPEED_1000 0x0010 /* 1000Mb/s */ +#define LANSR_SPEED_RSRVD 0x0018 /* reserved */ +#define LANSR_NON_COMP_MODE 0x0020 /* 1=detects only in non-compliant mode */ +#define LANSR_DEEP_LOOPBACK 0x0040 /* 1=the PHY operates in deep loopback mode */ +#define LANSR_SHALLOW_LOOPBACK 0x0080 /* 1=the PHY operates in shallow loopback mode */ +#define LANSR_RSRVD_8 0x0100 /* reserved */ +#define LANSR_FIFO_ERR 0x0200 /* 1=FIFO error occurred */ +#define LANSR_MDIX_XOVER 0x0400 /* 1=PHY's MDI is in cross-over mode */ +#define LANSR_RSRVD_11 0x0800 /* resevered */ +#define LANSR_TP_POLARITY_REV 0xf000 /* Twisted pair polarity status A:D([15:12]) 1=reversed */ + +/* Interrupt status, mask and clear regs (PHY_INT_{STATUS,MASK,CLEAR}) */ +#define PHY_INT_RSRVD_0 0x0001 /* reserved */ +#define PHY_INT_RSRVD_1 0x0002 /* reserved */ +#define PHY_INT_RSRVD_2 0x0004 /* reserved */ +#define PHY_INT_REM_FLT_CNG 0x0008 /* Remote Fault Changed */ +#define PHY_INT_AN_CMPL 0x0010 /* Auto-negotiation completion */ +#define PHY_INT_NXT_PG_RCVD 0x0020 /* Next Page Received */ +#define PHY_INT_JABBER_CNG 0x0040 /* Jabber Changed */ +#define PHY_INT_NO_LINK 0x0080 /* No link after auto-negotiation */ +#define PHY_INT_NO_HCD 0x0100 /* AN couldn't determine highest common denominator */ +#define PHY_INT_MAS_SLA_ERR 0x0200 /* Master / Slave Error: couldn't resolve */ +#define PHY_INT_PRL_DET_FLT 0x0400 /* Parallel detection fault */ +#define PHY_INT_POL_CNG 0x0800 /* Polarity of any channel changed */ +#define PHY_INT_MDIX_CNG 0x1000 /* MDIX changed. A pair swap occurred. */ +#define PHY_INT_DPLX_CNG 0x2000 /* Duplex changed */ +#define PHY_INT_LNK_CNG 0x4000 /* Link changed (asserted when a link is established or broken) */ +#define PHY_INT_SPD_CNG 0x8000 /* Speed changed */ + +#endif /* INCLUDED_ETH_PHY_H */ diff --git a/firmware/microblaze/lib/ethernet.c b/firmware/microblaze/lib/ethernet.c new file mode 100644 index 000000000..5402a6c9f --- /dev/null +++ b/firmware/microblaze/lib/ethernet.c @@ -0,0 +1,348 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "ethernet.h" +#include "memory_map.h" +#include "eth_phy.h" +#include "eth_mac.h" +#include "eth_mac_regs.h" +#include "pic.h" +#include "hal_io.h" +#include "nonstdio.h" +#include <stdbool.h> +#include "i2c.h" +#include "usrp2_i2c_addr.h" + + +#define VERBOSE 0 + +static ethernet_t ed_state; +static ethernet_link_changed_callback_t ed_callback = 0; + +void +ethernet_register_link_changed_callback(ethernet_link_changed_callback_t new_callback) +{ + ed_callback = new_callback; +} + + +static void +ed_set_mac_speed(int speed) +{ + printf("Speed set to %d\n",speed); + /* + switch(speed){ + case 10: + eth_mac->speed = 1; + break; + case 100: + eth_mac->speed = 2; + break; + case 1000: + eth_mac->speed = 4; + break; + default: + break; + } + */ +} + +static void +ed_link_up(int speed) +{ + // putstr("ed_link_up: "); puthex16_nl(speed); + + ed_set_mac_speed(speed); + + if (ed_callback) // fire link changed callback + (*ed_callback)(speed); +} + +static void +ed_link_down(void) +{ + // putstr("ed_link_down\n"); + + if (ed_callback) // fire link changed callback + (*ed_callback)(0); +} + + +static void +ed_link_speed_change(int speed) +{ + ed_link_down(); + ed_link_up(speed); +} + +static void +print_flow_control(int flow_control) +{ + static const char *flow_control_msg[4] = { + "NONE", "WE_TX", "WE_RX", "SYMMETRIC" + }; + putstr("ethernet flow control: "); + puts(flow_control_msg[flow_control & 0x3]); +} + +static void +check_flow_control_resolution(void) +{ + static const unsigned char table[16] = { + // index = {local_asm, local_pause, partner_asm, partner_pause} + FC_NONE, FC_NONE, FC_NONE, FC_NONE, + FC_NONE, FC_SYMM, FC_NONE, FC_SYMM, + FC_NONE, FC_NONE, FC_NONE, FC_WE_TX, + FC_NONE, FC_SYMM, FC_WE_RX, FC_SYMM + }; + + int us = eth_mac_miim_read(PHY_AUTONEG_ADV); + int lp = eth_mac_miim_read(PHY_LP_ABILITY); + int index = (((us >> 10) & 0x3) << 2) | ((lp >> 10) & 0x3); + ed_state.flow_control = table[index]; + + if (1) + print_flow_control(ed_state.flow_control); +} + +/* + * Read the PHY state register to determine link state and speed + */ +static void +ed_check_phy_state(void) +{ + int lansr = eth_mac_miim_read(PHY_LINK_AN); + eth_link_state_t new_state = LS_UNKNOWN; + int new_speed = S_UNKNOWN; + + if (VERBOSE){ + putstr("LANSR: "); + puthex16_nl(lansr); + } + + if (lansr & LANSR_LINK_GOOD){ // link's up + if (VERBOSE) + puts(" LINK_GOOD"); + + new_state = LS_UP; + switch (lansr & LANSR_SPEED_MASK){ + case LANSR_SPEED_10: + new_speed = 10; + break; + + case LANSR_SPEED_100: + new_speed = 100; + break; + + case LANSR_SPEED_1000: + new_speed = 1000; + break; + + default: + new_speed = S_UNKNOWN; + break; + } + + check_flow_control_resolution(); + } + else { // link's down + if (VERBOSE) + puts(" NOT LINK_GOOD"); + + new_state = LS_DOWN; + new_speed = S_UNKNOWN; + } + + if (new_state != ed_state.link_state){ + ed_state.link_state = new_state; // remember new state + if (new_state == LS_UP) + ed_link_up(new_speed); + else if (new_state == LS_DOWN) + ed_link_down(); + } + else if (new_state == LS_UP && new_speed != ed_state.link_speed){ + ed_state.link_speed = new_speed; // remember new speed + ed_link_speed_change(new_speed); + } +} + +/* + * This is fired when the ethernet PHY state changes + */ +static void +eth_phy_irq_handler(unsigned irq) +{ + ed_check_phy_state(); + eth_mac_miim_write(PHY_INT_CLEAR, ~0); // clear all ints +} + +void +ethernet_init(void) +{ + eth_mac_init(ethernet_mac_addr()); + + ed_state.link_state = LS_UNKNOWN; + ed_state.link_speed = S_UNKNOWN; + + // initialize MAC registers + // eth_mac->tx_hwmark = 0x1e; + //eth_mac->tx_lwmark = 0x19; + + //eth_mac->crc_chk_en = 1; + //eth_mac->rx_max_length = 2048; + + // configure PAUSE frame stuff + //eth_mac->tx_pause_en = 1; // pay attn to pause frames sent to us + + //eth_mac->pause_quanta_set = 38; // a bit more than 1 max frame 16kb/512 + fudge + //eth_mac->pause_frame_send_en = 1; // enable sending pause frames + + + // setup PHY to interrupt on changes + + unsigned mask = + (PHY_INT_AN_CMPL // auto-neg completed + | PHY_INT_NO_LINK // no link after auto-neg + | PHY_INT_NO_HCD // no highest common denominator + | PHY_INT_MAS_SLA_ERR // couldn't resolve master/slave + | PHY_INT_PRL_DET_FLT // parallel detection fault + | PHY_INT_LNK_CNG // link established or broken + | PHY_INT_SPD_CNG // speed changed + ); + + eth_mac_miim_write(PHY_INT_CLEAR, ~0); // clear all pending interrupts + eth_mac_miim_write(PHY_INT_MASK, mask); // enable the ones we want + + pic_register_handler(IRQ_PHY, eth_phy_irq_handler); + + // Advertise our flow control configuation. + // + // We and the link partner each specify two bits in the base page + // related to autoconfiguration: NWAY_AR_PAUSE and NWAY_AR_ASM_DIR. + // The bits say what a device is "willing" to do, not what may actually + // happen as a result of the negotiation. There are 4 cases: + // + // PAUSE ASM_DIR + // + // 0 0 I have no flow control capability. + // + // 1 0 I both assert and respond to flow control. + // + // 0 1 I assert flow control, but cannot respond. That is, + // I want to be able to send PAUSE frames, but will ignore any + // you send to me. (This is our configuration.) + // + // 1 1 I can both assert and respond to flow control AND I am willing + // to operate symmetrically OR asymmetrically in EITHER direction. + // (We hope the link partner advertises this, otherwise we don't + // get what we want.) + + int t = eth_mac_miim_read(PHY_AUTONEG_ADV); + t &= ~(NWAY_AR_PAUSE | NWAY_AR_ASM_DIR); + t |= NWAY_AR_ASM_DIR; + + // Say we can't to 10BASE-T or 100BASE-TX, half or full duplex + t &= ~(NWAY_AR_10T_HD_CAPS | NWAY_AR_10T_FD_CAPS | NWAY_AR_100TX_HD_CAPS | NWAY_AR_100TX_FD_CAPS); + + eth_mac_miim_write(PHY_AUTONEG_ADV, t); + int r = eth_mac_miim_read(PHY_AUTONEG_ADV); // DEBUG, read back + if (t != r){ + printf("PHY_AUTONEG_ADV: wrote 0x%x, got 0x%x\n", t, r); + } + + // Restart autonegotation. + // We want to ensure that we're advertising our PAUSE capabilities. + t = eth_mac_miim_read(PHY_CTRL); + eth_mac_miim_write(PHY_CTRL, t | MII_CR_RESTART_AUTO_NEG); +} + +static bool +unprogrammed(const eth_mac_addr_t *t) +{ + int i; + bool all_zeros = true; + bool all_ones = true; + for (i = 0; i < 6; i++){ + all_zeros &= t->addr[i] == 0x00; + all_ones &= t->addr[i] == 0xff; + } + return all_ones | all_zeros; +} + +static int8_t src_addr_initialized = false; +static eth_mac_addr_t src_addr = {{ + 0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff + }}; + +const eth_mac_addr_t * +ethernet_mac_addr(void) +{ + if (!src_addr_initialized){ // fetch from eeprom + src_addr_initialized = true; + + // if we're simulating, don't read the EEPROM model, it's REALLY slow + if (hwconfig_simulation_p()) + return &src_addr; + + eth_mac_addr_t tmp; + bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &tmp.addr[0], 6); + if (!ok || unprogrammed(&tmp)){ + // use the default + } + else + src_addr = tmp; + } + + return &src_addr; +} + +bool +ethernet_set_mac_addr(const eth_mac_addr_t *t) +{ + bool ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &t->addr[0], 6); + if (ok){ + src_addr = *t; + src_addr_initialized = true; + eth_mac_set_addr(t); + } + + return ok; +} + +int +ethernet_check_errors(void) +{ + // these registers are reset when read + + int r = 0; + /* + if (eth_mac_read_rmon(0x05) != 0) + r |= RME_RX_CRC; + if (eth_mac_read_rmon(0x06) != 0) + r |= RME_RX_FIFO_FULL; + if (eth_mac_read_rmon(0x07) != 0) + r |= RME_RX_2SHORT_2LONG; + + if (eth_mac_read_rmon(0x25) != 0) + r |= RME_TX_JAM_DROP; + if (eth_mac_read_rmon(0x26) != 0) + r |= RME_TX_FIFO_UNDER; + if (eth_mac_read_rmon(0x27) != 0) + r |= RME_TX_FIFO_OVER; + */ + return r; +} diff --git a/firmware/microblaze/lib/ethernet.h b/firmware/microblaze/lib/ethernet.h new file mode 100644 index 000000000..0e42d9b8d --- /dev/null +++ b/firmware/microblaze/lib/ethernet.h @@ -0,0 +1,82 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_ETHERNET_H +#define INCLUDED_ETHERNET_H + +#include "network.h" +#include <stdbool.h> + +typedef void (*ethernet_link_changed_callback_t)(int speed); + + +/*! + * \brief one time call to initialize ethernet + */ +void ethernet_init(void); + +/*! + * \brief Specify the function to call on link state changes. + * + * When the link comes up, speed is the link speed in Mbit/s. + * When the link goes down, speed is 0. + */ +void ethernet_register_link_changed_callback(ethernet_link_changed_callback_t cb); + +/*! + * \returns ethernet MAC address + */ +const eth_mac_addr_t *ethernet_mac_addr(void); + +/*! + * \brief write mac address to eeprom and begin using it + */ +bool ethernet_set_mac_addr(const eth_mac_addr_t *t); + + +/* + * \brief read RMON regs and return error mask + */ +int ethernet_check_errors(void); + +#define RME_RX_CRC 0x0001 +#define RME_RX_FIFO_FULL 0x0002 +#define RME_RX_2SHORT_2LONG 0x0004 + +#define RME_TX_JAM_DROP 0x0010 +#define RME_TX_FIFO_UNDER 0x0020 +#define RME_TX_FIFO_OVER 0x0040 + + +typedef enum { LS_UNKNOWN, LS_DOWN, LS_UP } eth_link_state_t; + +// flow control bitmasks +#define FC_NONE 0x0 +#define FC_WE_TX 0x1 // we send PAUSE frames +#define FC_WE_RX 0x2 // we honor received PAUSE frames +#define FC_SYMM (FC_WE_TX | FC_WE_RX) + +#define S_UNKNOWN (-1) // unknown link speed + +typedef struct { + eth_link_state_t link_state; + int link_speed; // in Mb/s + int flow_control; +} ethernet_t; + +#endif /* INCLUDED_ETHERNET_H */ diff --git a/firmware/microblaze/lib/exit.c b/firmware/microblaze/lib/exit.c new file mode 100644 index 000000000..95a3bf4de --- /dev/null +++ b/firmware/microblaze/lib/exit.c @@ -0,0 +1,28 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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. + */ + +extern void _exit(int status); + +void +exit(int status) +{ + _exit(status); +} diff --git a/firmware/microblaze/lib/hal_io.c b/firmware/microblaze/lib/hal_io.c new file mode 100644 index 000000000..fdfa15000 --- /dev/null +++ b/firmware/microblaze/lib/hal_io.c @@ -0,0 +1,323 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +// conditionalized on HAL_IO_USES_DBOARD_PINS && HAL_IO_USES_UART + +#include "hal_io.h" +#include "memory_map.h" +#include "hal_uart.h" +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +//#include <assert.h> + +/* + * ======================================================================== + * GPIOS + * ======================================================================== + */ +void +hal_gpio_set_ddr(int bank, int value, int mask) +{ + bank &= 0x1; + + if (bank == GPIO_TX_BANK){ // tx in top half + value <<= 16; + mask <<= 16; + } + else { + value &= 0xffff; + mask &= 0xffff; + } + + int ei = hal_disable_ints(); + gpio_base->ddr = (gpio_base->ddr & ~mask) | (value & mask); + hal_restore_ints(ei); +} + +static bool +code_to_int(char code, int *val) +{ + switch(code){ + case 's': *val = GPIO_SEL_SW; return true; + case 'a': *val = GPIO_SEL_ATR; return true; + case '0': *val = GPIO_SEL_DEBUG_0; return true; + case '1': *val = GPIO_SEL_DEBUG_1; return true; + case '.': + default: + return false; + } +} + +void +hal_gpio_set_sel(int bank, int bitno, char code) +{ + bank &= 0x1; + int t; + + if (!code_to_int(code, &t)) + return; + + int val = t << (2 * bitno); + int mask = 0x3 << (2 * bitno); + + volatile uint32_t *sel = bank == GPIO_TX_BANK ? &gpio_base->tx_sel : &gpio_base->rx_sel; + int ei = hal_disable_ints(); + int v = (*sel & ~mask) | (val & mask); + *sel = v; + hal_restore_ints(ei); + + if (0) + printf("hal_gpio_set_sel(bank=%d, bitno=%d, code=%c) *sel = 0x%x\n", + bank, bitno, code, v); +} + +void +hal_gpio_set_sels(int bank, char *codes) +{ + //assert(strlen(codes) == 16); + + int val = 0; + int mask = 0; + int i; + + for (i = 15; i >= 0; i--){ + val <<= 2; + mask <<= 2; + int t; + if (code_to_int(codes[i], &t)){ + val |= t; + mask |= 0x3; + } + } + + volatile uint32_t *sel = bank == GPIO_TX_BANK ? &gpio_base->tx_sel : &gpio_base->rx_sel; + int ei = hal_disable_ints(); + *sel = (*sel & ~mask) | (val & mask); + hal_restore_ints(ei); +} + + +/*! + * \brief write \p value to gpio pins specified by \p mask. + */ +void +hal_gpio_write(int bank, int value, int mask) +{ + static uint32_t _gpio_io_shadow; + + bank &= 0x1; + + if (bank == GPIO_TX_BANK){ // tx in top half + value <<= 16; + mask <<= 16; + } + else { + value &= 0xffff; + mask &= 0xffff; + } + + //int ei = hal_disable_ints(); + _gpio_io_shadow = (_gpio_io_shadow & ~mask) | (value & mask); + gpio_base->io = _gpio_io_shadow; + //hal_restore_ints(ei); +} + + +/*! + * \brief read GPIO bits + */ +int +hal_gpio_read(int bank) +{ + bank &= 0x1; + int r = gpio_base->io; + if (bank == GPIO_TX_BANK) + r >>= 16; + + return r & 0xffff; +} + +/* + * ======================================================================== + * leds + * ======================================================================== + */ + +static unsigned long leds_shadow = 0; +static unsigned long led_src_shadow = 0; + +void +hal_set_leds(int value, int mask) +{ + int ei = hal_disable_ints(); + leds_shadow = (leds_shadow & ~mask) | (value & mask); + output_regs->leds = leds_shadow; + hal_restore_ints(ei); +} + +// Allow hardware control over leds. 1 = hardware, 0 = software +void +hal_set_led_src(int value, int mask) +{ + int ei = hal_disable_ints(); + led_src_shadow = (led_src_shadow & ~mask) | (value & mask); + output_regs->led_src = led_src_shadow; + hal_restore_ints(ei); +} + +void +hal_toggle_leds(int mask) +{ + int ei = hal_disable_ints(); + leds_shadow ^= mask; + output_regs->leds = leds_shadow; + hal_restore_ints(ei); +} + + +// ================================================================ +// primitives +// ================================================================ + +#if defined(HAL_IO_USES_DBOARD_PINS) +// +// Does i/o using high 9-bits of rx daughterboard pins. +// +// 1 1 1 1 1 1 +// 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | char |W| | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// Asserts W when writing char +// + +#define W 0x0080 + +void +hal_io_init(void) +{ + // make high 9 bits of tx daughterboard outputs + hal_gpio_set_rx_mode(15, 7, GPIOM_OUTPUT); + + // and set them to zero + hal_gpio_set_rx(0x0000, 0xff80); +} + +void +hal_finish(void) +{ + volatile unsigned long *p = (unsigned long *) 0xC2F0; + *p = 0; +} + +// %c +inline int +putchar(int ch) +{ + hal_gpio_set_rx((s << 8) | W, 0xff80); + hal_gpio_set_rx(0, 0xff80); + return ch; +} + +#elif defined(HAL_IO_USES_UART) + +void +hal_io_init(void) +{ + hal_uart_init(); +} + +void +hal_finish(void) +{ +} + +// %c +inline int +putchar(int ch) +{ + hal_uart_putc(ch); + return ch; +} + +int +getchar(void) +{ + return hal_uart_getc(); +} + +#else // nop all i/o + +void +hal_io_init(void) +{ +} + +void +hal_finish(void) +{ +} + +// %c +inline int +putchar(int ch) +{ + return ch; +} + +int +getchar(void) +{ + return EOF; +} + +#endif + +// ================================================================ +// (slightly) higher level functions +// +// These are here so we can inline the calls to putchar. +// The rest of the stuff was moved to nonstdio.c +// ================================================================ + +// \n +inline void +newline(void) +{ + putchar('\n'); +} + +int +putstr(const char *s) +{ + while (*s) + putchar(*s++); + + return 0; +} + +int +puts(const char *s) +{ + putstr(s); + putchar('\n'); + return 0; +} diff --git a/firmware/microblaze/lib/hal_io.h b/firmware/microblaze/lib/hal_io.h new file mode 100644 index 000000000..f8ec617f8 --- /dev/null +++ b/firmware/microblaze/lib/hal_io.h @@ -0,0 +1,166 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_HAL_IO_H +#define INCLUDED_HAL_IO_H + +#include "memory_map.h" + +void hal_io_init(void); +void hal_finish(); + + +/* + * ------------------------------------------------------------------------ + * The GPIO pins are organized into two banks of 16-bits. + * Bank 0 goes to the Tx daughterboard, Bank 1 goes to the Rx daughterboard. + * + * Each pin may be configured as an input or an output from the FPGA. + * For output pins, there are four signals which may be routed to the + * pin. The four signals are the value written by s/w, the output of + * the ATR controller, or two different sources of debug info from the + * FPGA fabric. + * ------------------------------------------------------------------------ + */ + +#define GPIO_TX_BANK 0 // pins that connect to the Tx daughterboard +#define GPIO_RX_BANK 1 // pins that connect to the Rx daughterboard + + +/*! + * \brief Set the data direction for GPIO pins + * + * If the bit is set, it's an output from the FPGA. + * \param value is a 16-bit bitmask of values + * \param mask is a 16-bit bitmask of which bits to effect. + */ +void hal_gpio_set_ddr(int bank, int value, int mask); + +/*! + * \brief Select the source of the signal for an output pin. + * + * \param code is is one of 's', 'a', '0', '1' + * where 's' selects software output, 'a' selects ATR output, '0' selects + * debug 0, '1' selects debug 1. + */ +void hal_gpio_set_sel(int bank, int bitno, char code); + +/*! + * \brief Select the source of the signal for the output pins. + * + * \param codes is is a string of 16 characters composed of '.', 's', + * 'a', '0', or '1' where '.' means "don't change", 's' selects + * software output, 'a' selects ATR output, '0' selects debug 0, '1' + * selects debug 1. + */ +void hal_gpio_set_sels(int bank, char *codes); + + +/*! + * \brief write \p value to gpio pins specified by \p mask. + */ +void hal_gpio_write(int bank, int value, int mask); + +/*! + * \brief read GPIO bits + */ +int hal_gpio_read(int bank); + + +/* + * ------------------------------------------------------------------------ + * control the leds + * + * Low 4-bits are the general purpose leds on the board + * The next bit is the led on the ethernet connector + * ------------------------------------------------------------------------ + */ + +void hal_set_leds(int value, int mask); +void hal_set_led_src(int value, int mask); +void hal_toggle_leds(int mask); + +/* + * ------------------------------------------------------------------------ + * simple timeouts + * ------------------------------------------------------------------------ + */ + + + +static inline void +hal_set_timeout(int delta_ticks) +{ + sr_simple_timer->onetime = delta_ticks; +} + +/* + * ------------------------------------------------------------------------ + * interrupt enable/disable + * ------------------------------------------------------------------------ + */ + +/*! + * \brief Disable interrupts and return previous interrupt enable state. + * [Microblaze specific] + */ +static inline int +hal_disable_ints(void) +{ + int result, t0; + + asm volatile("mfs %0, rmsr \n\ + andni %1, %0, 0x2 \n\ + mts rmsr, %1" + : "=r" (result), "=r" (t0)); + return result; +} + +/*! + * \brief Enable interrupts and return previous interrupt enable state. + * [Microblaze specific] + */ +static inline int +hal_enable_ints(void) +{ + int result, t0; + + asm volatile("mfs %0, rmsr \n\ + ori %1, %0, 0x2 \n\ + mts rmsr, %1" + : "=r" (result), "=r" (t0)); + return result; +} + +/*! + * \brief Set interrupt enable state to \p prev_state. + * [Microblaze specific] + */ +static inline void +hal_restore_ints(int prev_state) +{ + int t0, t1; + asm volatile("andi %0, %2, 0x2 \n\ + mfs %1, rmsr \n\ + andni %1, %1, 0x2 \n\ + or %1, %1, %0 \n\ + mts rmsr, %1" + : "=r" (t0), "=r"(t1) : "r" (prev_state)); +} + +#endif /* INCLUDED_HAL_IO_H */ diff --git a/firmware/microblaze/lib/hal_uart.c b/firmware/microblaze/lib/hal_uart.c new file mode 100644 index 000000000..75b12b432 --- /dev/null +++ b/firmware/microblaze/lib/hal_uart.c @@ -0,0 +1,77 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "hal_uart.h" +#include "hal_io.h" +#include "memory_map.h" + +// First pass, no interrupts + +// Replaced with divisors.py which generates best divisor +//#define CALC_DIVISOR(rate) (WISHBONE_CLK_RATE / ((rate) * 16)) + +#define NSPEEDS 6 +#define MAX_WB_DIV 4 + +static const uint16_t +divisor_table[MAX_WB_DIV+1][NSPEEDS] = { + { 2, 2, 2, 2, 2, 2}, // 0: can't happen + { 651, 326, 163, 109, 54, 27 }, // 1: 100 MHz + { 326, 163, 81, 54, 27, 14 }, // 2: 50 MHz + { 217, 109, 54, 36, 18, 9 }, // 3: 33.3333 MHz + { 163, 81, 41, 27, 14, 7 }, // 4: 25 MHz +}; + +#define u uart_regs + +void +hal_uart_init(void) +{ + u->clkdiv = 217; // 230400 bps +} + +void +hal_uart_putc(int ch) +{ + if (ch == '\n') // FIXME for now map \n -> \r\n + hal_uart_putc('\r'); + + while (u->txlevel == 0) // wait for fifo to have space + ; + + u->txchar = ch; +} + +void +hal_uart_putc_nowait(int ch) +{ + if (ch == '\n') // FIXME for now map \n -> \r\n + hal_uart_putc('\r'); + + if(u->txlevel) // If fifo has space + u->txchar = ch; +} + +int +hal_uart_getc(void) +{ + while ((u->rxlevel) == 0) // wait for data to be ready + ; + + return u->rxchar; +} diff --git a/firmware/microblaze/lib/hal_uart.h b/firmware/microblaze/lib/hal_uart.h new file mode 100644 index 000000000..2ddfa6259 --- /dev/null +++ b/firmware/microblaze/lib/hal_uart.h @@ -0,0 +1,68 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_HAL_UART_H +#define INCLUDED_HAL_UART_H + + +/*! + * \brief one-time call to init + */ +void hal_uart_init(void); + +typedef enum { + US_9600, + US_19200, + US_38400, + US_57600, + US_115200, + US_230400, +} hal_uart_speed_t; + +typedef struct { + hal_uart_speed_t speed; +} hal_uart_config_t; + +/*! + * \brief Set uart parameters + * Default is 115,200 bps, 8N1. + */ +void hal_uart_set_config(const hal_uart_config_t *c); + +/*! + * \brief Get uart configuation. + */ +void hal_uart_get_config(hal_uart_config_t *c); + +/*! + * \brief Enqueue \p ch for output over serial port + */ +void hal_uart_putc(int ch); + +/*! + * \brief Enqueue \p ch for output over serial port, silent fail if queue is full + */ +void hal_uart_putc_nowait(int ch); + +/* + * \brief Blocking read of next char from serial port + */ +int hal_uart_getc(void); + + +#endif /* INCLUDED_HAL_UART_H */ diff --git a/firmware/microblaze/lib/i2c.c b/firmware/microblaze/lib/i2c.c new file mode 100644 index 000000000..3f738733b --- /dev/null +++ b/firmware/microblaze/lib/i2c.c @@ -0,0 +1,127 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "i2c.h" +#include "memory_map.h" +#include "stdint.h" + +#define MAX_WB_DIV 4 // maximum wishbone divisor (from 100 MHz MASTER_CLK) + +// prescaler divisor values for 100 kHz I2C [uses 5 * SCLK internally] + +#define PRESCALER(wb_div) (((MASTER_CLK_RATE/(wb_div)) / (5 * 100000)) - 1) + +static uint16_t prescaler_values[MAX_WB_DIV+1] = { + 0xffff, // 0: can't happen + PRESCALER(1), // 1: 100 MHz + PRESCALER(2), // 2: 50 MHz + PRESCALER(3), // 3: 33.333 MHz + PRESCALER(4), // 4: 25 MHz +}; + +void +i2c_init(void) +{ + i2c_regs->ctrl = 0; // disable core + + // setup prescaler depending on wishbone divisor + int wb_div = hwconfig_wishbone_divisor(); + if (wb_div > MAX_WB_DIV) + wb_div = MAX_WB_DIV; + + i2c_regs->prescaler_lo = prescaler_values[wb_div] & 0xff; + i2c_regs->prescaler_hi = (prescaler_values[wb_div] >> 8) & 0xff; + + i2c_regs->ctrl = I2C_CTRL_EN; // enable core + + // FIXME interrupt driven? +} + +static inline void +wait_for_xfer(void) +{ + while (i2c_regs->cmd_status & I2C_ST_TIP) // wait for xfer to complete + ; +} + +static inline bool +wait_chk_ack(void) +{ + wait_for_xfer(); + + if ((i2c_regs->cmd_status & I2C_ST_RXACK) != 0){ // target NAK'd + return false; + } + return true; +} + +bool +i2c_read (unsigned char i2c_addr, unsigned char *buf, unsigned int len) +{ + if (len == 0) // reading zero bytes always works + return true; + + while (i2c_regs->cmd_status & I2C_ST_BUSY) + ; + + i2c_regs->data = (i2c_addr << 1) | 1; // 7 bit address and read bit (1) + // generate START and write addr + i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; + if (!wait_chk_ack()) + goto fail; + + for (; len > 0; buf++, len--){ + i2c_regs->cmd_status = I2C_CMD_RD | (len == 1 ? (I2C_CMD_NACK | I2C_CMD_STOP) : 0); + wait_for_xfer(); + *buf = i2c_regs->data; + } + return true; + + fail: + i2c_regs->cmd_status = I2C_CMD_STOP; // generate STOP + return false; +} + + +bool +i2c_write(unsigned char i2c_addr, const unsigned char *buf, unsigned int len) +{ + while (i2c_regs->cmd_status & I2C_ST_BUSY) + ; + + i2c_regs->data = (i2c_addr << 1) | 0; // 7 bit address and write bit (0) + + // generate START and write addr (and maybe STOP) + i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START | (len == 0 ? I2C_CMD_STOP : 0); + if (!wait_chk_ack()) + goto fail; + + for (; len > 0; buf++, len--){ + i2c_regs->data = *buf; + i2c_regs->cmd_status = I2C_CMD_WR | (len == 1 ? I2C_CMD_STOP : 0); + if (!wait_chk_ack()) + goto fail; + } + return true; + + fail: + i2c_regs->cmd_status = I2C_CMD_STOP; // generate STOP + return false; +} + + diff --git a/firmware/microblaze/lib/i2c.h b/firmware/microblaze/lib/i2c.h new file mode 100644 index 000000000..ecb6aa0d2 --- /dev/null +++ b/firmware/microblaze/lib/i2c.h @@ -0,0 +1,39 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_I2C_H +#define INCLUDED_I2C_H + +#include <stdbool.h> + +void i2c_init(void); +bool i2c_read (unsigned char i2c_addr, unsigned char *buf, unsigned int len); +bool i2c_write(unsigned char i2c_addr, const unsigned char *buf, unsigned int len); + + +// Write 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. +// Which EEPROM is determined by i2c_addr. See i2c_addr.h + +bool eeprom_write (int i2c_addr, int eeprom_offset, const void *buf, int len); + +// Read 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. +// Which EEPROM is determined by i2c_addr. See i2c_addr.h + +bool eeprom_read (int i2c_addr, int eeprom_offset, void *buf, int len); + +#endif /* INCLUDED_I2C_H */ diff --git a/firmware/microblaze/lib/lsadc.c b/firmware/microblaze/lib/lsadc.c new file mode 100644 index 000000000..7983552e7 --- /dev/null +++ b/firmware/microblaze/lib/lsadc.c @@ -0,0 +1,73 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "lsadc.h" +#include "spi.h" +#include "memory_map.h" + + +// AD9712 or AD7922 1 MS/s, 10-/12-bit ADCs + +//#define SPI_SS_DEBUG SPI_SS_RX_DB +#define SPI_SS_DEBUG 0 + + +void +lsadc_init(void) +{ + // nop +} + +/* + * The ADC's are pipelined. That is, you have to tell them + * which of the two inputs you want one cycle ahead of time. + * We could optimize and keep track of which one we used last + * time, but for simplicity we'll always tell it which + * one we want. This takes 2 16-bit xfers, one to set the + * input and one to read the one we want. + */ + +int +_lsadc_read(int which_adc, int slave_select) +{ + uint32_t r; + int channel = which_adc & 0x1; + + // Set CHN and STY equal to channel number. We don't want "daisy chain mode" + uint16_t cmd = (channel << 13) | (channel << 12); + + spi_transact(SPI_TXONLY, slave_select | SPI_SS_DEBUG, + cmd, 16, SPIF_PUSH_RISE | SPIF_LATCH_RISE); + + r = spi_transact(SPI_TXRX, slave_select | SPI_SS_DEBUG, + cmd, 16, SPIF_PUSH_RISE | SPIF_LATCH_RISE); + + return r & 0x0fff; +} + +int +lsadc_read_rx(int which_adc) +{ + return _lsadc_read(which_adc, SPI_SS_RX_ADC); +} + +int +lsadc_read_tx(int which_adc) +{ + return _lsadc_read(which_adc, SPI_SS_TX_ADC); +} diff --git a/firmware/microblaze/lib/lsadc.h b/firmware/microblaze/lib/lsadc.h new file mode 100644 index 000000000..319f34d91 --- /dev/null +++ b/firmware/microblaze/lib/lsadc.h @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_LSADC_H +#define INCLUDED_LSADC_H + +#include "memory_map.h" + +/*! + * \brief One time call to initialize low-speed ADCs. + */ +void lsadc_init(void); + +/*! + * \brief Read one of the low-speed Rx daughterboard ADCs. + * \param which_adc in [0, 1] + * + * \returns 12-bit value in [0,4095] + */ +int lsadc_read_rx(int which_adc); + +/*! + * \brief Read one of the low-speed Tx daughterboard ADCs. + * \param which_adc in [0, 1] + * + * \returns 12-bit value in [0,4095] + */ +int lsadc_read_tx(int which_adc); + + +#endif /* INCLUDED_LSADC_H */ diff --git a/firmware/microblaze/lib/lsdac.c b/firmware/microblaze/lib/lsdac.c new file mode 100644 index 000000000..6bc2e5cb5 --- /dev/null +++ b/firmware/microblaze/lib/lsdac.c @@ -0,0 +1,68 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "lsdac.h" +#include "spi.h" +#include "memory_map.h" + +// AD5624, AD5623 + +#define CMD(x) ((x) << 19) +#define CMD_WR_INPUT_N CMD(0) // write input N +#define CMD_UP_DAC_N CMD(1) // update DAC N from input reg +#define CMD_WR_INPUT_N_LDAC CMD(2) // write input N, update all +#define CMD_WR_UP_DAC_N CMD(3) // write and update N +#define CMD_WR_PWR_CONFIG CMD(4) // write power up/down config reg +#define CMD_SW_RESET CMD(5) // force s/w reset +#define CMD_WR_LDAC_CFG CMD(6) // write LDAC config reg +#define CMD_WR_INT_REF_CFG CMD(7) // write internal ref cfg reg (AD5623R only) + + +//#define SPI_SS_DEBUG SPI_SS_TX_DB +#define SPI_SS_DEBUG 0 + +inline static void +_write_rx(uint32_t v) +{ + spi_transact(SPI_TXONLY, SPI_SS_RX_DAC | SPI_SS_DEBUG, v, 24, SPIF_PUSH_RISE); +} + +inline static void +_write_tx(uint32_t v) +{ + spi_transact(SPI_TXONLY, SPI_SS_TX_DAC | SPI_SS_DEBUG, v, 24, SPIF_PUSH_RISE); +} + +void +lsdac_init(void) +{ + _write_tx(CMD_SW_RESET | 0x1); // power-on reset + _write_rx(CMD_SW_RESET | 0x1); // power-on reset +} + +void +lsdac_write_rx(int which_dac, int value) +{ + _write_rx(CMD_WR_UP_DAC_N | ((which_dac & 0x7) << 16) | ((value << 4) & 0xffff)); +} + +void +lsdac_write_tx(int which_dac, int value) +{ + _write_tx(CMD_WR_UP_DAC_N | ((which_dac & 0x7) << 16) | ((value << 4) & 0xffff)); +} diff --git a/firmware/microblaze/lib/lsdac.h b/firmware/microblaze/lib/lsdac.h new file mode 100644 index 000000000..9cad917e3 --- /dev/null +++ b/firmware/microblaze/lib/lsdac.h @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_LSDAC_H +#define INCLUDED_LSDAC_H + +#include "memory_map.h" + +/*! + * \brief One time call to initialize low-speed DACs. + */ +void lsdac_init(void); + +/*! + * \brief Write one of the low-speed Rx daughterboard DACs. + * \param which_dac in [0, 3] + * \param unsigned 12-bit value in [0, 4095] + * + * value maps linearly to output voltage from 0 to 3.3V + */ +void lsdac_write_rx(int which_dac, int value); + +/*! + * \brief Write one of the low-speed Tx daughterboard DACs. + * \param which_dac in [0, 3] + * \param unsigned 12-bit value in [0, 4095] + * + * value maps linearly to output voltage from 0 to 3.3V + */ +void lsdac_write_tx(int which_dac, int value); + + +#endif /* INCLUDED_LSDAC_H */ diff --git a/firmware/microblaze/lib/mdelay.c b/firmware/microblaze/lib/mdelay.c new file mode 100644 index 000000000..c8c119b1a --- /dev/null +++ b/firmware/microblaze/lib/mdelay.c @@ -0,0 +1,73 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "mdelay.h" +#include "memory_map.h" + +// Delay about one millisecond. +// +// Need 33,333 cycles at 33 MHz. +// Each time around the loop is 10 cycles +// + +#define LOOPCNT(wb_div) (MASTER_CLK_RATE/(wb_div) / 10000) + +inline static void +delay_1ms(int loop_count) +{ + int i; + for (i = 0; i < loop_count; i++){ + asm volatile ("or r0, r0, r0\n\ + or r0, r0, r0\n\ + or r0, r0, r0\n\ + or r0, r0, r0\n\ + or r0, r0, r0\n\ + or r0, r0, r0\n\ + or r0, r0, r0\n"); + } +} + +// delay about ms milliseconds +void +mdelay(int ms) +{ + static int loop_count = -1; + + if (hwconfig_simulation_p()) + return; + + if (loop_count < 0){ + // set correct loop_count + static unsigned short lc[8] = { + 0, + LOOPCNT(1), + LOOPCNT(2), + LOOPCNT(3), + LOOPCNT(4), + LOOPCNT(5), + LOOPCNT(6), + LOOPCNT(7) + }; + + loop_count = lc[hwconfig_wishbone_divisor() & 0x7]; + } + + int i; + for (i = 0; i < ms; i++) + delay_1ms(loop_count); +} diff --git a/firmware/microblaze/lib/mdelay.h b/firmware/microblaze/lib/mdelay.h new file mode 100644 index 000000000..226bbb3f7 --- /dev/null +++ b/firmware/microblaze/lib/mdelay.h @@ -0,0 +1,29 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_MDELAY_H +#define INCLUDED_MDELAY_H + +/*! + * \brief Delay about ms milliseconds + * + * If simulating, _very_ short delay + */ +void mdelay(int ms); + +#endif /* INCLUDED_MDELAY_H */ diff --git a/firmware/microblaze/lib/memcpy_wa.c b/firmware/microblaze/lib/memcpy_wa.c new file mode 100644 index 000000000..ef20efaa9 --- /dev/null +++ b/firmware/microblaze/lib/memcpy_wa.c @@ -0,0 +1,42 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "memcpy_wa.h" +#include <stdint.h> +#include <stdlib.h> + +/* + * For copying to/from non-byte-adressable memory, such as + * the buffers. dst, src, and nbytes must all satisfy (x % 4 == 0) + */ +void +memcpy_wa(void *dst, const void *src, size_t nbytes) +{ + if (((intptr_t) dst & 0x3) + || ((intptr_t) src & 0x3) + || (nbytes & 0x3)) + exit(1); /* die! */ + + int *dp = (int *) dst; + int *sp = (int *) src; + unsigned nw = nbytes/4; + + unsigned i; + for (i = 0; i < nw; i++) + dp[i] = sp[i]; +} diff --git a/firmware/microblaze/lib/memcpy_wa.h b/firmware/microblaze/lib/memcpy_wa.h new file mode 100644 index 000000000..072fc148f --- /dev/null +++ b/firmware/microblaze/lib/memcpy_wa.h @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_MEMCPY_WA_H +#define INCLUDED_MEMCPY_WA_H + +#include <stddef.h> + +/* + * For copying to/from non-byte-adressable memory, such as + * the buffers. dst, src, and nbytes must all satisfy (x % 4 == 0) + */ +void memcpy_wa(void *dst, const void *src, size_t nbytes); + +#endif /* INCLUDED_MEMCPY_WA_H */ + + diff --git a/firmware/microblaze/lib/memory_map.h b/firmware/microblaze/lib/memory_map.h new file mode 100644 index 000000000..bb6a1036d --- /dev/null +++ b/firmware/microblaze/lib/memory_map.h @@ -0,0 +1,795 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2008,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* Overall Memory Map + * 0000-7FFF 32K RAM space (16K on 1500, 24K on 2000, 32K on DSP) + * 8000-BFFF 16K Buffer Pool + * C000-FFFF 16K Peripherals + */ + + +#ifndef INCLUDED_MEMORY_MAP_H +#define INCLUDED_MEMORY_MAP_H + +#include <stdint.h> + + +#define MASTER_CLK_RATE 100000000 // 100 MHz + + +//////////////////////////////////////////////////////////////// +// +// Memory map for embedded wishbone bus +// +//////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////// +// Main RAM, Slave 0 + +#define RAM_BASE 0x0000 + +//////////////////////////////////////////////////////////////// +// Buffer Pool RAM, Slave 1 +// +// The buffers themselves are located in Slave 1, Buffer Pool RAM. +// The status registers are in Slave 5, Buffer Pool Status. +// The control register is in Slave 7, Settings Bus. + +#define BUFFER_POOL_RAM_BASE 0x8000 + +#define NBUFFERS 8 +#define BP_NLINES 0x0200 // number of 32-bit lines in a buffer +#define BP_LAST_LINE (BP_NLINES - 1) // last line in a buffer + +#define buffer_pool_ram \ + ((uint32_t *) BUFFER_POOL_RAM_BASE) + +#define buffer_ram(n) (&buffer_pool_ram[(n) * BP_NLINES]) + + +///////////////////////////////////////////////////// +// SPI Core, Slave 2. See core docs for more info +#define SPI_BASE 0xC000 // Base address (16-bit) + +typedef struct { + volatile uint32_t txrx0; + volatile uint32_t txrx1; + volatile uint32_t txrx2; + volatile uint32_t txrx3; + volatile uint32_t ctrl; + volatile uint32_t div; + volatile uint32_t ss; +} spi_regs_t; + +#define spi_regs ((spi_regs_t *) SPI_BASE) + + +// Masks for controlling different peripherals +#define SPI_SS_AD9510 1 +#define SPI_SS_AD9777 2 +#define SPI_SS_RX_DAC 4 +#define SPI_SS_RX_ADC 8 +#define SPI_SS_RX_DB 16 +#define SPI_SS_TX_DAC 32 +#define SPI_SS_TX_ADC 64 +#define SPI_SS_TX_DB 128 + +// Masks for different parts of CTRL reg +#define SPI_CTRL_ASS (1<<13) +#define SPI_CTRL_IE (1<<12) +#define SPI_CTRL_LSB (1<<11) +#define SPI_CTRL_TXNEG (1<<10) +#define SPI_CTRL_RXNEG (1<< 9) +#define SPI_CTRL_GO_BSY (1<< 8) +#define SPI_CTRL_CHAR_LEN_MASK 0x7F + +//////////////////////////////////////////////// +// I2C, Slave 3 +// See Wishbone I2C-Master Core Specification. + +#define I2C_BASE 0xC400 + +typedef struct { + volatile uint32_t prescaler_lo; // r/w + volatile uint32_t prescaler_hi; // r/w + volatile uint32_t ctrl; // r/w + volatile uint32_t data; // wr = transmit reg; rd = receive reg + volatile uint32_t cmd_status; // wr = command reg; rd = status reg +} i2c_regs_t; + +#define i2c_regs ((i2c_regs_t *) I2C_BASE) + +#define I2C_CTRL_EN (1 << 7) // core enable +#define I2C_CTRL_IE (1 << 6) // interrupt enable + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// +#define I2C_CMD_START (1 << 7) // generate (repeated) start condition +#define I2C_CMD_STOP (1 << 6) // generate stop condition +#define I2C_CMD_RD (1 << 5) // read from slave +#define I2C_CMD_WR (1 << 4) // write to slave +#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2 (1 << 2) // reserved +#define I2C_CMD_RSVD_1 (1 << 1) // reserved +#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt + +#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected +#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration +#define I2C_ST_RSVD_4 (1 << 4) // reserved +#define I2C_ST_RSVD_3 (1 << 3) // reserved +#define I2C_ST_RSVD_2 (1 << 2) // reserved +#define I2C_ST_TIP (1 << 1) // Transfer-in-progress +#define I2C_ST_IP (1 << 0) // Interrupt pending + + +//////////////////////////////////////////////// +// GPIO, Slave 4 +// +// These go to the daughterboard i/o pins + +#define GPIO_BASE 0xC800 + +typedef struct { + volatile uint32_t io; // tx data in high 16, rx in low 16 + volatile uint32_t ddr; // 32 bits, 1 means output. tx in high 16, rx in low 16 + volatile uint32_t tx_sel; // 16 2-bit fields select which source goes to TX DB + volatile uint32_t rx_sel; // 16 2-bit fields select which source goes to RX DB +} gpio_regs_t; + +// each 2-bit sel field is layed out this way +#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg +#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic +#define GPIO_SEL_DEBUG_0 2 // if pin is an output, debug lines from FPGA fabric +#define GPIO_SEL_DEBUG_1 3 // if pin is an output, debug lines from FPGA fabric + +#define gpio_base ((gpio_regs_t *) GPIO_BASE) + +/////////////////////////////////////////////////// +// Buffer Pool Status, Slave 5 +// +// The buffers themselves are located in Slave 1, Buffer Pool RAM. +// The status registers are in Slave 5, Buffer Pool Status. +// The control register is in Slave 7, Settings Bus. + +#define BUFFER_POOL_STATUS_BASE 0xCC00 + +typedef struct { + volatile uint32_t last_line[NBUFFERS]; // last line xfer'd in buffer + volatile uint32_t status; // error and done flags + volatile uint32_t hw_config; // see below + volatile uint32_t dummy[3]; + volatile uint32_t irqs; + volatile uint32_t pri_enc_bp_status; + volatile uint32_t cycle_count; +} buffer_pool_status_t; + +#define buffer_pool_status ((buffer_pool_status_t *) BUFFER_POOL_STATUS_BASE) + +/* + * Buffer n's xfer is done. + * Clear this bit by issuing bp_clear_buf(n) + */ +#define BPS_DONE(n) (0x00000001 << (n)) +#define BPS_DONE_0 BPS_DONE(0) +#define BPS_DONE_1 BPS_DONE(1) +#define BPS_DONE_2 BPS_DONE(2) +#define BPS_DONE_3 BPS_DONE(3) +#define BPS_DONE_4 BPS_DONE(4) +#define BPS_DONE_5 BPS_DONE(5) +#define BPS_DONE_6 BPS_DONE(6) +#define BPS_DONE_7 BPS_DONE(7) + +/* + * Buffer n's xfer had an error. + * Clear this bit by issuing bp_clear_buf(n) + */ +#define BPS_ERROR(n) (0x00000100 << (n)) +#define BPS_ERROR_0 BPS_ERROR(0) +#define BPS_ERROR_1 BPS_ERROR(1) +#define BPS_ERROR_2 BPS_ERROR(2) +#define BPS_ERROR_3 BPS_ERROR(3) +#define BPS_ERROR_4 BPS_ERROR(4) +#define BPS_ERROR_5 BPS_ERROR(5) +#define BPS_ERROR_6 BPS_ERROR(6) +#define BPS_ERROR_7 BPS_ERROR(7) + +/* + * Buffer n is idle. A buffer is idle if it's not + * DONE, ERROR, or processing a transaction. If it's + * IDLE, it's safe to start a new transaction. + * + * Clear this bit by starting a xfer with + * bp_send_from_buf or bp_receive_to_buf. + */ +#define BPS_IDLE(n) (0x00010000 << (n)) +#define BPS_IDLE_0 BPS_IDLE(0) +#define BPS_IDLE_1 BPS_IDLE(1) +#define BPS_IDLE_2 BPS_IDLE(2) +#define BPS_IDLE_3 BPS_IDLE(3) +#define BPS_IDLE_4 BPS_IDLE(4) +#define BPS_IDLE_5 BPS_IDLE(5) +#define BPS_IDLE_6 BPS_IDLE(6) +#define BPS_IDLE_7 BPS_IDLE(7) + +/* + * Buffer n has a "slow path" packet in it. + * This bit is orthogonal to the bits above and indicates that + * the FPGA ethernet rx protocol engine has identified this packet + * as one requiring firmware intervention. + */ +#define BPS_SLOWPATH(n) (0x01000000 << (n)) +#define BPS_SLOWPATH_0 BPS_SLOWPATH(0) +#define BPS_SLOWPATH_1 BPS_SLOWPATH(1) +#define BPS_SLOWPATH_2 BPS_SLOWPATH(2) +#define BPS_SLOWPATH_3 BPS_SLOWPATH(3) +#define BPS_SLOWPATH_4 BPS_SLOWPATH(4) +#define BPS_SLOWPATH_5 BPS_SLOWPATH(5) +#define BPS_SLOWPATH_6 BPS_SLOWPATH(6) +#define BPS_SLOWPATH_7 BPS_SLOWPATH(7) + + +#define BPS_DONE_ALL 0x000000ff // mask of all dones +#define BPS_ERROR_ALL 0x0000ff00 // mask of all errors +#define BPS_IDLE_ALL 0x00ff0000 // mask of all idles +#define BPS_SLOWPATH_ALL 0xff000000 // mask of all slowpaths + +// The hw_config register + +#define HWC_SIMULATION 0x80000000 +#define HWC_WB_CLK_DIV_MASK 0x0000000f + +/*! + * \brief return non-zero if we're running under the simulator + */ +inline static int +hwconfig_simulation_p(void) +{ + return buffer_pool_status->hw_config & HWC_SIMULATION; +} + +/*! + * \brief Return Wishbone Clock divisor. + * The processor runs at the Wishbone Clock rate which is MASTER_CLK_RATE / divisor. + */ +inline static int +hwconfig_wishbone_divisor(void) +{ + return buffer_pool_status->hw_config & HWC_WB_CLK_DIV_MASK; +} + +/////////////////////////////////////////////////// +// Ethernet Core, Slave 6 + +#define ETH_BASE 0xD000 + +#include "eth_mac_regs.h" + +#define eth_mac ((eth_mac_regs_t *) ETH_BASE) + +//////////////////////////////////////////////////// +// Settings Bus, Slave #7, Not Byte Addressable! +// +// Output-only from processor point-of-view. +// 1KB of address space (== 256 32-bit write-only regs) + + +#define MISC_OUTPUT_BASE 0xD400 +#define TX_PROTOCOL_ENGINE_BASE 0xD480 +#define RX_PROTOCOL_ENGINE_BASE 0xD4C0 +#define BUFFER_POOL_CTRL_BASE 0xD500 +#define LAST_SETTING_REG 0xD7FC // last valid setting register + +#define SR_MISC 0 +#define SR_TX_PROT_ENG 32 +#define SR_RX_PROT_ENG 48 +#define SR_BUFFER_POOL_CTRL 64 +#define SR_UDP_SM 96 +#define SR_TX_DSP 208 +#define SR_TX_CTRL 224 +#define SR_RX_DSP 160 +#define SR_RX_CTRL 176 +#define SR_TIME64 192 +#define SR_SIMTIMER 198 +#define SR_LAST 255 + +#define _SR_ADDR(sr) (MISC_OUTPUT_BASE + (sr) * sizeof(uint32_t)) + +// --- buffer pool control regs --- + +typedef struct { + volatile uint32_t ctrl; +} buffer_pool_ctrl_t; + +// buffer pool ports + +#define PORT_SERDES 0 // serial/deserializer +#define PORT_DSP 1 // DSP tx or rx pipeline +#define PORT_ETH 2 // ethernet tx or rx +#define PORT_RAM 3 // RAM tx or rx + +// the buffer pool ctrl register fields + +#define BPC_BUFFER(n) (((n) & 0xf) << 28) +#define BPC_BUFFER_MASK BPC_BUFFER(~0) +#define BPC_BUFFER_0 BPC_BUFFER(0) +#define BPC_BUFFER_1 BPC_BUFFER(1) +#define BPC_BUFFER_2 BPC_BUFFER(2) +#define BPC_BUFFER_3 BPC_BUFFER(3) +#define BPC_BUFFER_4 BPC_BUFFER(4) +#define BPC_BUFFER_5 BPC_BUFFER(5) +#define BPC_BUFFER_6 BPC_BUFFER(6) +#define BPC_BUFFER_7 BPC_BUFFER(7) +#define BPC_BUFFER_NIL BPC_BUFFER(0x8) // disable + +#define BPC_PORT(n) (((n) & 0x7) << 25) +#define BPC_PORT_MASK BPC_PORT(~0) +#define BPC_PORT_SERDES BPC_PORT(PORT_SERDES) +#define BPC_PORT_DSP BPC_PORT(PORT_DSP) +#define BPC_PORT_ETH BPC_PORT(PORT_ETH) +#define BPC_PORT_RAM BPC_PORT(PORT_RAM) +#define BPC_PORT_NIL BPC_PORT(0x4) // disable + +#define BPC_CLR (1 << 24) // mutually excl commands +#define BPC_READ (1 << 23) +#define BPC_WRITE (1 << 22) + +#define BPC_STEP(step) (((step) & 0xf) << 18) +#define BPC_STEP_MASK BPC_STEP(~0) +#define BPC_LAST_LINE(line) (((line) & 0x1ff) << 9) +#define BPC_LAST_LINE_MASK BPC_LAST_LINE(~0) +#define BPC_FIRST_LINE(line) (((line) & 0x1ff) << 0) +#define BPC_FIRST_LINE_MASK BPC_FIRST_LINE(~0) + +#define buffer_pool_ctrl ((buffer_pool_ctrl_t *) BUFFER_POOL_CTRL_BASE) + +// --- misc outputs --- + +typedef struct { + volatile uint32_t clk_ctrl; + volatile uint32_t serdes_ctrl; + volatile uint32_t adc_ctrl; + volatile uint32_t leds; + volatile uint32_t phy_ctrl; // LSB is reset line to eth phy + volatile uint32_t debug_mux_ctrl; + volatile uint32_t ram_page; // FIXME should go somewhere else... + volatile uint32_t flush_icache; // Flush the icache + volatile uint32_t led_src; // HW or SW control for LEDs +} output_regs_t; + +#define SERDES_ENABLE 8 +#define SERDES_PRBSEN 4 +#define SERDES_LOOPEN 2 +#define SERDES_RXEN 1 + +#define ADC_CTRL_ON 0x0F +#define ADC_CTRL_OFF 0x00 + +// crazy order that matches the labels on the case + +#define LED_A (1 << 4) +#define LED_B (1 << 1) +#define LED_C (1 << 3) +#define LED_D (1 << 0) +#define LED_E (1 << 2) +// LED_F // controlled by CPLD +#define LED_RJ45 (1 << 5) + +#define output_regs ((output_regs_t *) MISC_OUTPUT_BASE) + +// --- udp tx regs --- + +typedef struct { + // Bits 19:16 are control info; bits 15:0 are data (see below) + // First two words are unused. + volatile uint32_t _nope[2]; + //--- ethernet header - 14 bytes--- + volatile struct{ + uint32_t mac_dst_0_1; //word 2 + uint32_t mac_dst_2_3; + uint32_t mac_dst_4_5; + uint32_t mac_src_0_1; + uint32_t mac_src_2_3; + uint32_t mac_src_4_5; + uint32_t ether_type; //word 8 + } eth_hdr; + //--- ip header - 20 bytes --- + volatile struct{ + uint32_t ver_ihl_tos; //word 9 + uint32_t total_length; + uint32_t identification; + uint32_t flags_frag_off; + uint32_t ttl_proto; + uint32_t checksum; + uint32_t src_addr_high; + uint32_t src_addr_low; + uint32_t dst_addr_high; + uint32_t dst_addr_low; //word 18 + } ip_hdr; + //--- udp header - 8 bytes --- + volatile struct{ + uint32_t src_port; //word 19 + uint32_t dst_port; + uint32_t length; + uint32_t checksum; //word 22 + } udp_hdr; + volatile uint32_t _pad[32-23]; +} sr_udp_sm_t; + +// control bits (all expect UDP_SM_LAST_WORD are mutually exclusive) + +// This is the last word of the header +#define UDP_SM_LAST_WORD (1 << 19) + +// Insert IP header checksum here. Data is the xor of 16'hFFFF and +// the values written into regs 9-13 and 15-18. +#define UDP_SM_INS_IP_HDR_CHKSUM (1 << 18) + +// Insert IP Length here (data ignored) +#define UDP_SM_INS_IP_LEN (1 << 17) + +// Insert UDP Length here (data ignore) +#define UDP_SM_INS_UDP_LEN (1 << 16) + +#define sr_udp_sm ((sr_udp_sm_t *) _SR_ADDR(SR_UDP_SM)) + +// --- dsp tx regs --- + +#define MIN_CIC_INTERP 1 +#define MAX_CIC_INTERP 128 + +typedef struct { + volatile uint32_t num_chan; + volatile uint32_t clear_state; // clears out state machine, fifos, +} sr_tx_ctrl_t; + +#define sr_tx_ctrl ((sr_tx_ctrl_t *) _SR_ADDR(SR_TX_CTRL)) + +typedef struct { + volatile int32_t freq; + volatile uint32_t scale_iq; // {scale_i,scale_q} + volatile uint32_t interp_rate; + volatile uint32_t _padding0; // padding for the tx_mux + // NOT freq, scale, interp + /*! + * \brief output mux configuration. + * + * <pre> + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-------------------------------+-------+-------+-------+-------+ + * | | DAC1 | DAC0 | + * +-------------------------------+-------+-------+-------+-------+ + * + * There are N DUCs (1 now) with complex inputs and outputs. + * There are two DACs. + * + * Each 4-bit DACx field specifies the source for the DAC + * Each subfield is coded like this: + * + * 3 2 1 0 + * +-------+ + * | N | + * +-------+ + * + * N specifies which DUC output is connected to this DAC. + * + * N which interp output + * --- ------------------- + * 0 DUC 0 I + * 1 DUC 0 Q + * 2 DUC 1 I + * 3 DUC 1 Q + * F All Zeros + * + * The default value is 0x10 + * </pre> + */ + volatile uint32_t tx_mux; + +} dsp_tx_regs_t; + +#define dsp_tx_regs ((dsp_tx_regs_t *) _SR_ADDR(SR_TX_DSP)) + +// --- VITA RX CTRL regs --- +typedef struct { + // The following 3 are logically a single command register. + // They are clocked into the underlying fifo when time_ticks is written. + volatile uint32_t cmd; // {now, chain, num_samples(30) + volatile uint32_t time_secs; + volatile uint32_t time_ticks; + + volatile uint32_t clear_overrun; // write anything to clear overrun + volatile uint32_t vrt_header; // word 0 of packet. FPGA fills in packet counter + volatile uint32_t vrt_stream_id; // word 1 of packet. + volatile uint32_t vrt_trailer; + volatile uint32_t nsamples_per_pkt; + volatile uint32_t nchannels; // 1 in basic case, up to 4 for vector sources + volatile uint32_t pad[7]; // Make each structure 16 elements long +} sr_rx_ctrl_t; + +#define MK_RX_CMD(nsamples, now, chain) \ + ((((now) & 0x1) << 31) | ((chain & 0x1) << 30) | ((nsamples) & 0x3fffffff)) + +#define sr_rx_ctrl ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL)) + +// --- dsp rx regs --- +#define MIN_CIC_DECIM 1 +#define MAX_CIC_DECIM 128 + +typedef struct { + volatile int32_t freq; + volatile uint32_t scale_iq; // {scale_i,scale_q} + volatile uint32_t decim_rate; + volatile uint32_t dcoffset_i; // Bit 31 high sets fixed offset mode, using lower 14 bits, + // otherwise it is automatic + volatile uint32_t dcoffset_q; // Bit 31 high sets fixed offset mode, using lower 14 bits + + /*! + * \brief input mux configuration. + * + * This determines which ADC (or constant zero) is connected to + * each DDC input. There are N DDCs (1 now). Each has two inputs. + * + * <pre> + * Mux value: + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | |Q0 |I0 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * Each 2-bit I field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) + * Each 2-bit Q field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) + * + * The default value is 0x4 + * </pre> + */ + volatile uint32_t rx_mux; // called adc_mux in dsp_core_rx.v + + /*! + * \brief Streaming GPIO configuration + * + * This determines whether the LSBs of I and Q samples come from the DSP + * pipeline or from the io_rx GPIO pins. To stream GPIO, one must first + * set the GPIO data direction register to have io_rx[15] and/or io_rx[14] + * configured as inputs. The GPIO pins will be sampled at the time the + * remainder of the DSP sample is strobed into the RX sample FIFO. There + * will be a decimation-dependent fixed time offset between the GPIO + * sample stream and the associated RF samples. + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | MBZ |Q|I| + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * I 0=LSB comes from DSP pipeline (default) + * 1=LSB comes from io_rx[15] + * + * Q 0=LSB comes from DSP pipeline (default) + * 1=LSB comes from io_rx[14] + */ + volatile uint32_t gpio_stream_enable; + +} dsp_rx_regs_t; + +#define dsp_rx_regs ((dsp_rx_regs_t *) _SR_ADDR(SR_RX_DSP)) + +// ---------------------------------------------------------------- +// VITA49 64 bit time (write only) + /*! + * \brief Time 64 flags + * + * <pre> + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------------------------------------------------------+-+-+ + * | |S|P| + * +-----------------------------------------------------------+-+-+ + * + * P - PPS edge selection (0=negedge, 1=posedge, default=0) + * S - Source (0=sma, 1=mimo, 0=default) + * + * </pre> + */ +typedef struct { + volatile uint32_t secs; // value to set absolute secs to on next PPS + volatile uint32_t ticks; // value to set absolute ticks to on next PPS + volatile uint32_t flags; // flags - see chart above + volatile uint32_t imm; // set immediate (0=latch on next pps, 1=latch immediate, default=0) +} sr_time64_t; + +#define sr_time64 ((sr_time64_t *) _SR_ADDR(SR_TIME64)) + + +/* + * --- ethernet tx protocol engine regs (write only) --- + * + * These registers control the transmit portion of the ethernet + * protocol engine (out of USRP2). The protocol engine handles fifo + * status and sequence number insertion in outgoing packets, and + * automagically generates status packets when required to inform the + * host of changes in fifo availability. + * + * All outgoing packets have their fifo_status field set to the number + * of 32-bit lines of fifo available in the ethernet Rx fifo (see + * usrp2_eth_packet.h). Seqno's are set if FIXME, else 0. + * + * FIXME clean this up once we know how it's supposed to behave. + */ + +typedef struct { + volatile uint32_t flags; // not yet fully defined (channel?) + volatile uint32_t mac_dst0123; // 4 bytes of destination mac addr + volatile uint32_t mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr + volatile uint32_t mac_src2345; // 4 bytes of destination mac addr + volatile uint32_t seqno; // Write to init seqno. It autoincs on match +} tx_proto_engine_regs_t; + +#define tx_proto_engine ((tx_proto_engine_regs_t *) TX_PROTOCOL_ENGINE_BASE) + +/* + * --- ethernet rx protocol engine regs (write only) --- + * + * These registers control the receive portion of the ethernet + * protocol engine (into USRP2). The protocol engine offloads common + * packet inspection operations so that firmware has less to do on + * "fast path" packets. + * + * The registers define conditions which must be matched for a packet + * to be considered a "fast path" packet. If a received packet + * matches the src and dst mac address, ethertype, flags field, and + * expected seqno number it is considered a "fast path" packet, and + * the expected seqno is updated. If the packet fails to satisfy any + * of the above conditions it's a "slow path" packet, and the + * corresponding SLOWPATH flag will be set buffer_status register. + */ + +typedef struct { + volatile uint32_t flags; // not yet fully defined (channel?) + volatile uint32_t mac_dst0123; // 4 bytes of destination mac addr + volatile uint32_t mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr + volatile uint32_t mac_src2345; // 4 bytes of destination mac addr + volatile uint32_t ethertype_pad; // ethertype in high 16-bits +} rx_proto_engine_regs_t; + +#define rx_proto_engine ((rx_proto_engine_regs_t *) RX_PROTOCOL_ENGINE_BASE) + + + +/////////////////////////////////////////////////// +// Simple Programmable Interrupt Controller, Slave 8 + +#define PIC_BASE 0xD800 + +// Interrupt request lines +// Bit numbers (LSB == 0) that correpond to interrupts into PIC + +#define IRQ_BUFFER 0 // buffer manager +#define IRQ_ONETIME 1 +#define IRQ_SPI 2 +#define IRQ_I2C 3 +#define IRQ_PHY 4 // ethernet PHY +#define IRQ_UNDERRUN 5 +#define IRQ_OVERRUN 6 +#define IRQ_PPS 7 // pulse per second +#define IRQ_UART_RX 8 +#define IRQ_UART_TX 9 +#define IRQ_SERDES 10 +#define IRQ_CLKSTATUS 11 +#define IRQ_PERIODIC 12 + +#define IRQ_TO_MASK(x) (1 << (x)) + +#define PIC_BUFFER_INT IRQ_TO_MASK(IRQ_BUFFER) +#define PIC_ONETIME_INT IRQ_TO_MASK(IRQ_ONETIME) +#define PIC_SPI_INT IRQ_TO_MASK(IRQ_SPI) +#define PIC_I2C_INT IRQ_TO_MASK(IRQ_I2C) +#define PIC_PHY_INT IRQ_TO_MASK(IRQ_PHY) +#define PIC_UNDERRUN_INT IRQ_TO_MASK(IRQ_UNDERRUN) +#define PIC_OVERRUN_INT IRQ_TO_MASK(IRQ_OVERRUN) +#define PIC_PPS_INT IRQ_TO_MASK(IRQ_PPS) +#define PIC_UART_RX_INT IRQ_TO_MASK(IRQ_UART_RX) +#define PIC_UART_TX_INT IRQ_TO_MASK(IRQ_UART_TX) +#define PIC_SERDES IRQ_TO_MASK(IRQ_SERDES) +#define PIC_CLKSTATUS IRQ_TO_MASK(IRQ_CLKSTATUS) + +typedef struct { + volatile uint32_t edge_enable; // mask: 1 -> edge triggered, 0 -> level + volatile uint32_t polarity; // mask: 1 -> rising edge + volatile uint32_t mask; // mask: 1 -> disabled + volatile uint32_t pending; // mask: 1 -> pending; write 1's to clear pending ints +} pic_regs_t; + +#define pic_regs ((pic_regs_t *) PIC_BASE) + +// ---------------------------------------------------------------- +// WB_CLK_RATE is the time base for this +typedef struct { + volatile uint32_t onetime; // Number of wb clk cycles till the onetime interrupt + volatile uint32_t periodic; // Repeat rate of periodic interrupt +} sr_simple_timer_t; + +#define sr_simple_timer ((sr_simple_timer_t *) _SR_ADDR(SR_SIMTIMER)) + +/////////////////////////////////////////////////// +// UART, Slave 10 + +#define UART_BASE 0xE000 + +typedef struct { + // All elements are 8 bits except for clkdiv (16), but we use uint32 to make + // the hardware for decoding easier + volatile uint32_t clkdiv; // Set to 50e6 divided by baud rate (no x16 factor) + volatile uint32_t txlevel; // Number of spaces in the FIFO for writes + volatile uint32_t rxlevel; // Number of available elements in the FIFO for reads + volatile uint32_t txchar; // Write characters to be sent here + volatile uint32_t rxchar; // Read received characters here +} uart_regs_t; + +#define uart_regs ((uart_regs_t *) UART_BASE) + +/////////////////////////////////////////////////// +// ATR Controller, Slave 11 + +#define ATR_BASE 0xE400 + +typedef struct { + volatile uint32_t v[16]; +} atr_regs_t; + +#define ATR_IDLE 0x0 // indicies into v +#define ATR_TX 0x1 +#define ATR_RX 0x2 +#define ATR_FULL 0x3 + +#define atr_regs ((atr_regs_t *) ATR_BASE) + +/////////////////////////////////////////////////// +// SD Card SPI interface, Slave 13 +// All regs are 8 bits wide, but are accessed as if they are 32 bits + +#define SDSPI_BASE 0xEC00 + +typedef struct { + volatile uint32_t status; // Write a 1 or 0 for controlling CS + volatile uint32_t clkdiv; + volatile uint32_t send_dat; + volatile uint32_t receive_dat; +} sdspi_regs_t; + +#define sdspi_regs ((sdspi_regs_t *) SDSPI_BASE) + +/////////////////////////////////////////////////// +// External RAM interface, Slave 14 +// Pages are 1K. Page is 10 bits, set by a control register +// output_regs->ram_page + +#define EXTRAM_BASE 0xF000 +#define extram ((volatile uint32_t *) EXTRAM_BASE) + + +/////////////////////////////////////////////////// + +#endif + diff --git a/firmware/microblaze/lib/memset_wa.c b/firmware/microblaze/lib/memset_wa.c new file mode 100644 index 000000000..da5da21ab --- /dev/null +++ b/firmware/microblaze/lib/memset_wa.c @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "memset_wa.h" +#include <stdint.h> +#include <stdlib.h> + +/* + * For setting non-byte-adressable memory, such as + * the buffers. dst and nbytes must all satisfy (x % 4 == 0) + */ +void * +memset_wa(void *dst, int c, size_t nbytes) +{ + if (((intptr_t) dst & 0x3) + || (nbytes & 0x3)) + exit(1); /* die! */ + + int *dp = (int *) dst; + + c &= 0xff; + int v = (c << 24) | (c << 16) | (c << 8) | c; + unsigned nw = nbytes/4; + + unsigned i; + for (i = 0; i < nw; i++) + dp[i] = v; + + return dst; +} diff --git a/firmware/microblaze/lib/memset_wa.h b/firmware/microblaze/lib/memset_wa.h new file mode 100644 index 000000000..46d903d53 --- /dev/null +++ b/firmware/microblaze/lib/memset_wa.h @@ -0,0 +1,27 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_MEMSET_WA_H +#define INCLUDED_MEMSET_WA_H + +#include <stdlib.h> + +void *memset_wa(void *s, int c, size_t n); + + +#endif /* INCLUDED_MEMSET_WA_H */ diff --git a/firmware/microblaze/lib/microblaze.ld b/firmware/microblaze/lib/microblaze.ld new file mode 100644 index 000000000..ef816cf2e --- /dev/null +++ b/firmware/microblaze/lib/microblaze.ld @@ -0,0 +1,163 @@ +OUTPUT_FORMAT("elf32-microblaze", "", "") +/* SEARCH_DIR(/home/jwilliams/tmp/microblaze-toolchain-sources/release/lin/microblaze//lib);*/ + +ENTRY(_start) + +MEMORY { + flatmem : ORIGIN = 0x0, LENGTH = 0x00008000 /* 32KB */ +} + +SECTIONS +{ + _TEXT_START_ADDR = DEFINED(_TEXT_START_ADDR) ? _TEXT_START_ADDR : 0x50; + .vectors.reset 0x0 : { *(.vectors.reset) } > flatmem + .vectors.sw_exception 0x8 : { *(.vectors.sw_exception) } > flatmem + .vectors.interrupt 0x10 : { *(.vectors.interrupt) } > flatmem + .vectors.hw_exception 0x20 : { *(.vectors.hw_exception) } >flatmem + . = _TEXT_START_ADDR; + _ftext = .; + .text : { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + } > flatmem + _etext = .; + .init : { KEEP (*(.init)) } > flatmem =0 + .fini : { KEEP (*(.fini)) } > flatmem =0 + PROVIDE (__CTOR_LIST__ = .); + PROVIDE (___CTOR_LIST__ = .); + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } > flatmem + PROVIDE (__CTOR_END__ = .); + PROVIDE (___CTOR_END__ = .); + PROVIDE (__DTOR_LIST__ = .); + PROVIDE (___DTOR_LIST__ = .); + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } > flatmem + PROVIDE (__DTOR_END__ = .); + PROVIDE (___DTOR_END__ = .); + . = ALIGN(4); + _frodata = . ; + .rodata : { + *(.rodata) + *(.gnu.linkonce.r.*) + CONSTRUCTORS; /* Is this needed? */ + } > flatmem + _erodata = .; + /* Alignments by 8 to ensure that _SDA2_BASE_ on a word boundary */ + /* Note that .sdata2 and .sbss2 must be contiguous */ + . = ALIGN(8); + _ssrw = .; + .sdata2 : { + *(.sdata2) + *(.gnu.linkonce.s2.*) + } > flatmem + . = ALIGN(4); + .sbss2 : { + PROVIDE (__sbss2_start = .); + *(.sbss2) + *(.gnu.linkonce.sb2.*) + PROVIDE (__sbss2_end = .); + } > flatmem + . = ALIGN(8); + _essrw = .; + _ssrw_size = _essrw - _ssrw; + PROVIDE (_SDA2_BASE_ = _ssrw + (_ssrw_size / 2 )); + . = ALIGN(4); + _fdata = .; + .data : { + *(.data) + *(.gnu.linkonce.d.*) + CONSTRUCTORS; /* Is this needed? */ + } > flatmem + _edata = . ; + /* Added to handle pic code */ + .got : { + *(.got) + } > flatmem + .got1 : { + *(.got1) + } > flatmem + .got2 : { + *(.got2) + } > flatmem + /* Added by Sathya to handle C++ exceptions */ + .eh_frame : { + *(.eh_frame) + } > flatmem + .jcr : { + *(.jcr) + } > flatmem + .gcc_except_table : { + *(.gcc_except_table) + } > flatmem + /* Alignments by 8 to ensure that _SDA_BASE_ on a word boundary */ + /* Note that .sdata and .sbss must be contiguous */ + . = ALIGN(8); + _ssro = .; + .sdata : { + *(.sdata) + *(.gnu.linkonce.s.*) + } > flatmem + . = ALIGN(4); + .sbss : { + PROVIDE (__sbss_start = .); + *(.sbss) + *(.gnu.linkonce.sb.*) + PROVIDE (__sbss_end = .); + } > flatmem + . = ALIGN(8); + _essro = .; + _ssro_size = _essro - _ssro; + PROVIDE (_SDA_BASE_ = _ssro + (_ssro_size / 2 )); + . = ALIGN(4); + _fbss = .; + .bss : { + PROVIDE (__bss_start = .); + *(.bss) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + PROVIDE (__bss_end = .); + _heap = .; + _HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x0; + _STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x800; + . += _HEAP_SIZE; + _heap_end = .; + . += _STACK_SIZE; + . = ALIGN(8); + _stack = .; + _end = .; + } > flatmem + .tdata : { + *(.tdata) + *(.gnu.linkonce.td.*) + } > flatmem + .tbss : { + *(.tbss) + *(.gnu.linkonce.tb.*) + } > flatmem +} diff --git a/firmware/microblaze/lib/nonstdio.c b/firmware/microblaze/lib/nonstdio.c new file mode 100644 index 000000000..1c991afee --- /dev/null +++ b/firmware/microblaze/lib/nonstdio.c @@ -0,0 +1,80 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <nonstdio.h> + +static const char hex[16] = "0123456789ABCDEF"; + +// %x +void +puthex4(unsigned long x) +{ + putchar(hex[x & 0xf]); +} + +// %02x +void +puthex8(unsigned long x) +{ + putchar(hex[(x >> 4) & 0xf]); + putchar(hex[x & 0xf]); +} + +// %04x +void +puthex16(unsigned long x) +{ + puthex8(x >> 8); + puthex8(x); +} + +// %08x +void +puthex32(unsigned long x) +{ + puthex16(x >> 16); + puthex16(x); +} + +void +puthex4_nl(unsigned long x) +{ + puthex4(x); + newline(); +} + +void +puthex8_nl(unsigned long x) +{ + puthex8(x); + newline(); +} + +void +puthex16_nl(unsigned long x) +{ + puthex16(x); + newline(); +} + +void +puthex32_nl(unsigned long x) +{ + puthex32(x); + newline(); +} diff --git a/firmware/microblaze/lib/nonstdio.h b/firmware/microblaze/lib/nonstdio.h new file mode 100644 index 000000000..3fd9e39bb --- /dev/null +++ b/firmware/microblaze/lib/nonstdio.h @@ -0,0 +1,46 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_NONSTDIO_H +#define INCLUDED_NONSTDIO_H + +#include <stdio.h> +#include <usrp2_types.h> +#include <stddef.h> + +void putstr(const char *s); // cf puts, no added newline +void puthex4(unsigned long x); // output 1 hex digit +void puthex8(unsigned long x); // output 2 hex digits +void puthex16(unsigned long x); // output 4 hex digits +void puthex32(unsigned long x); // output 8 hex digits +void puthex4_nl(unsigned long x); // ... followed by newline +void puthex8_nl(unsigned long x); +void puthex16_nl(unsigned long x); +void puthex32_nl(unsigned long x); +#define puthex puthex32 +#define puthex_nl puthex32_nl +void newline(); // putchar('\n') + +void print_mac_addr(const unsigned char addr[6]); +void print_fxpt_freq(u2_fxpt_freq_t v); +void print_fxpt_gain(u2_fxpt_gain_t v); +void print_uint64(uint64_t v); + +void print_buffer(uint32_t *buf, size_t n); + +#endif /* INCLUDED_NONSTDIO_H */ diff --git a/firmware/microblaze/lib/pic.c b/firmware/microblaze/lib/pic.c new file mode 100644 index 000000000..4575bd775 --- /dev/null +++ b/firmware/microblaze/lib/pic.c @@ -0,0 +1,94 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "pic.h" +#include "hal_io.h" +#include "memory_map.h" + + +#define NVECTORS 8 + +/* + * Our secondary interrupt vector. + */ +irq_handler_t pic_vector[NVECTORS] = { + nop_handler, + nop_handler, + nop_handler, + nop_handler, + nop_handler, + nop_handler, + nop_handler, + nop_handler +}; + + +void +pic_init(void) +{ + // uP is level triggered + + pic_regs->mask = ~0; // mask all interrupts + pic_regs->edge_enable = PIC_ONETIME_INT | PIC_PHY_INT; + pic_regs->polarity = ~0 & ~PIC_PHY_INT; // rising edge + pic_regs->pending = ~0; // clear all pending ints +} + +/* + * This magic gets pic_interrupt_handler wired into the + * system interrupt handler with the appropriate prologue and + * epilogue. + */ +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ + // pending and not masked interrupts + int live = pic_regs->pending & ~pic_regs->mask; + + // FIXME loop while there are interrupts to service. + // That will reduce our overhead. + + // handle the first one set + int i; + int mask; + for (i=0, mask=1; i < NVECTORS; i++, mask <<= 1){ + if (mask & live){ // handle this one + // puthex_nl(i); + (*pic_vector[i])(i); + pic_regs->pending = mask; // clear pending interrupt + return; + } + } +} + +void +pic_register_handler(unsigned irq, irq_handler_t handler) +{ + if (irq >= NVECTORS) + return; + pic_vector[irq] = handler; + + pic_regs->mask &= ~IRQ_TO_MASK(irq); +} + +void +nop_handler(unsigned irq) +{ + // nop +} diff --git a/firmware/microblaze/lib/pic.h b/firmware/microblaze/lib/pic.h new file mode 100644 index 000000000..68918f9ad --- /dev/null +++ b/firmware/microblaze/lib/pic.h @@ -0,0 +1,35 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_PIC_H +#define INCLUDED_PIC_H + +typedef void (*irq_handler_t)(unsigned irq); + +void pic_init(void); +void pic_register_handler(unsigned irq, irq_handler_t handler); + +void nop_handler(unsigned irq); // default handler does nothing + +// FIXME inline assembler +int pic_disable_interrupts(); +int pic_enable_interrupts(); +void pic_restore_interrupts(int prev_status); + + +#endif /* INCLUDED_PIC_H */ diff --git a/firmware/microblaze/lib/print_buffer.c b/firmware/microblaze/lib/print_buffer.c new file mode 100644 index 000000000..9f9104bb5 --- /dev/null +++ b/firmware/microblaze/lib/print_buffer.c @@ -0,0 +1,36 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#include <nonstdio.h> + +void +print_buffer(uint32_t *buf, size_t n) +{ + size_t i; + for (i = 0; i < n; i++){ + if (i % 4 == 0) + puthex16(i * 4); + + putchar(' '); + puthex32(buf[i]); + if (i % 4 == 3) + newline(); + } + + newline(); +} + diff --git a/firmware/microblaze/lib/print_fxpt.c b/firmware/microblaze/lib/print_fxpt.c new file mode 100644 index 000000000..185bbc51b --- /dev/null +++ b/firmware/microblaze/lib/print_fxpt.c @@ -0,0 +1,83 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#include <nonstdio.h> + +/* + * print uint64_t + */ +void +print_uint64(uint64_t u) +{ + const char *_hex = "0123456789ABCDEF"; + if (u >= 10) + print_uint64(u/10); + putchar(_hex[u%10]); +} + +static void +print_thousandths(int thousandths) +{ + putchar('.'); + if (thousandths < 100) + putchar('0'); + if (thousandths < 10) + putchar('0'); + printf("%d", thousandths); +} + + +void +print_fxpt_freq(u2_fxpt_freq_t v) +{ + if (v < 0){ + v = -v; + putchar('-'); + } + + int64_t int_part = v >> 20; + int32_t frac_part = v & ((1 << 20) - 1); + +#if 0 + // would work, if we had it + printf("%lld.%03d", int_part, (frac_part * 1000) >> 20); +#else + print_uint64(int_part); + print_thousandths((frac_part * 1000) >> 20); +#endif +} + +void +print_fxpt_gain(u2_fxpt_gain_t v) +{ + if (v < 0){ + v = -v; + putchar('-'); + } + + int32_t int_part = v >> 7; + int32_t frac_part = v & ((1 << 7) - 1); + +#if 0 + // would work, if we had it + printf("%d.%03d", int_part, (frac_part * 1000) >> 7); +#else + printf("%d", int_part); + print_thousandths((frac_part * 1000) >> 7); +#endif +} + diff --git a/firmware/microblaze/lib/print_mac_addr.c b/firmware/microblaze/lib/print_mac_addr.c new file mode 100644 index 000000000..838fd614a --- /dev/null +++ b/firmware/microblaze/lib/print_mac_addr.c @@ -0,0 +1,30 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#include "nonstdio.h" + +void +print_mac_addr(const unsigned char addr[6]) +{ + puthex8(addr[0]); putchar(':'); + puthex8(addr[1]); putchar(':'); + puthex8(addr[2]); putchar(':'); + puthex8(addr[3]); putchar(':'); + puthex8(addr[4]); putchar(':'); + puthex8(addr[5]); +} + diff --git a/firmware/microblaze/lib/print_rmon_regs.c b/firmware/microblaze/lib/print_rmon_regs.c new file mode 100644 index 000000000..6d9986909 --- /dev/null +++ b/firmware/microblaze/lib/print_rmon_regs.c @@ -0,0 +1,44 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "print_rmon_regs.h" +#include "eth_mac.h" +#include "nonstdio.h" + +void +print_rmon_regs(void) +{ + int i; + for (i=0; i <= 0x10; i++){ + putstr("RMON[0x"); + puthex8(i); + putstr("] = "); + printf("%d\n", eth_mac_read_rmon(i)); + } + + for (i=0x20; i <= 0x30; i++){ + putstr("RMON[0x"); + puthex8(i); + putstr("] = "); + printf("%d\n", eth_mac_read_rmon(i)); + } +} diff --git a/firmware/microblaze/lib/print_rmon_regs.h b/firmware/microblaze/lib/print_rmon_regs.h new file mode 100644 index 000000000..44e52da84 --- /dev/null +++ b/firmware/microblaze/lib/print_rmon_regs.h @@ -0,0 +1,24 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_PRINT_RMON_REGS_H +#define INCLUDED_PRINT_RMON_REGS_H + +void print_rmon_regs(void); + +#endif /* INCLUDED_PRINT_RMON_REGS_H */ diff --git a/firmware/microblaze/lib/printf.c b/firmware/microblaze/lib/printf.c new file mode 100644 index 000000000..45bd57cb9 --- /dev/null +++ b/firmware/microblaze/lib/printf.c @@ -0,0 +1,134 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Based on code from the SDCC z80 library ;) + */ + +#include <stdarg.h> +#include <stdio.h> + +static void +_printn(unsigned u, unsigned base, char issigned, + void (*emitter)(char, void *), void *pData) +{ + const char *_hex = "0123456789ABCDEF"; + if (issigned && ((int)u < 0)) { + (*emitter)('-', pData); + u = (unsigned)-((int)u); + } + if (u >= base) + _printn(u/base, base, 0, emitter, pData); + (*emitter)(_hex[u%base], pData); +} + +static void +_printf(const char *format, void (*emitter)(char, void *), + void *pData, va_list va) +{ + while (*format) { + if (*format != '%') + (*emitter)(*format, pData); + else { + switch (*++format) { + case 0: /* hit end of format string */ + return; + case 'c': + { + char c = (char)va_arg(va, int); + (*emitter)(c, pData); + break; + } + case 'u': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 10, 0, emitter, pData); + break; + } + case 'd': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 10, 1, emitter, pData); + break; + } + case 'x': + case 'p': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 16, 0, emitter, pData); + break; + } + case 's': + { + char *s = va_arg(va, char *); + while (*s) { + (*emitter)(*s, pData); + s++; + } + break; + } + } + } + format++; + } +} + +static void +_char_emitter(char c, void *pData __attribute__((unused))) +{ + putchar(c); +} + +int +printf(const char *format, ...) +{ + va_list va; + va_start(va, format); + + _printf(format, _char_emitter, NULL, va); + + va_end(va); + + // wrong return value... + return 0; +} + + +#if 0 + +// Totally dangerous. Don't use +static void +_buf_emitter(char c, void *pData) +{ + *((*((char **)pData)))++ = c; +} + +int sprintf(char *pInto, const char *format, ...) +{ + va_list va; + va_start(va, format); + + _printf(format, _buf_emitter, &pInto, va); + *pInto++ = '\0'; + + va_end(va); + + // FIXME wrong return value + return 0; +} +#endif diff --git a/firmware/microblaze/lib/printf.c.smaller b/firmware/microblaze/lib/printf.c.smaller new file mode 100644 index 000000000..4d858648d --- /dev/null +++ b/firmware/microblaze/lib/printf.c.smaller @@ -0,0 +1,134 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Based on code from the SDCC z80 library ;) + */ + +#include <stdarg.h> +#include <stdio.h> +#include <hal_io.h> /* FIXME refactor into stdio */ + +#define PUTCHAR(x) hal_putc(x) + + +static void +_printn(unsigned u, unsigned base, char issigned) +{ + const char *_hex = "0123456789ABCDEF"; + if (issigned && ((int)u < 0)) { + PUTCHAR('-'); + u = (unsigned)-((int)u); + } + if (u >= base) + _printn(u/base, base, 0); + PUTCHAR(_hex[u%base]); +} + +static void +_printf(const char *format, va_list va) +{ + while (*format) { + if (*format != '%') + PUTCHAR(*format); + else { + switch (*++format) { + case 0: /* hit end of format string */ + return; + case 'c': + { + char c = (char)va_arg(va, int); + PUTCHAR(c); + break; + } + case 'u': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 10, 0); + break; + } + case 'd': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 10, 1); + break; + } + case 'x': + case 'p': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 16, 0); + break; + } + case 's': + { + char *s = va_arg(va, char *); + while (*s) { + PUTCHAR(*s); + s++; + } + break; + } + } + } + format++; + } +} + +#if 0 +static void +_char_emitter(char c, void *pData __attribute__((unused))) +{ + hal_putc(c); +} +#endif + +int +printf(const char *format, ...) +{ + va_list va; + va_start(va, format); + + _printf(format, va); + + // wrong return value... + return 0; +} + + +#if 0 + +// Totally dangerous. Don't use +static void +_buf_emitter(char c, void *pData) +{ + *((*((char **)pData)))++ = c; +} + +int sprintf(char *pInto, const char *format, ...) +{ + va_list va; + va_start(va, format); + + _printf(format, _buf_emitter, &pInto, va); + *pInto++ = '\0'; + + // FIXME wrong return value + return 0; +} +#endif diff --git a/firmware/microblaze/lib/sd.c b/firmware/microblaze/lib/sd.c new file mode 100644 index 000000000..d000b28ae --- /dev/null +++ b/firmware/microblaze/lib/sd.c @@ -0,0 +1,197 @@ +/* -*- c -*- */ +/* + * Copyright 2008 Ettus Research LLC + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "sd.h" +#include "memory_map.h" +#include "stdint.h" +#include "stdio.h" + +static inline void +sd_packarg(unsigned char *argument,unsigned int value) +{ + argument[3] = (unsigned char)(value >> 24); + argument[2] = (unsigned char)(value >> 16); + argument[1] = (unsigned char)(value >> 8); + argument[0] = (unsigned char)(value); +} + +int +sd_init(void) +{ + unsigned char response[5]; + unsigned char argument[4]; + int i,j; + + for(i=0;i<4;i++) + argument[i] = 0; + + // Set clock to less than 400 kHz to start out + sdspi_regs->clkdiv = 128; + + // Delay at least 74 cycles + sd_assert_cs(); + for(i = 0; i < 100; i++) + sd_send_byte(SD_IDLE); + sd_deassert_cs(); + + // Initialization Sequence -- CMD0 CMD55 ACMD41 CMD58 + // Put card in idle state + if(sd_send_command(SD_CMD0,SD_CMD0_R,response,argument)==0) + return 0; // Something went wrong in command + + j = 0; + do { + j++; + if(sd_send_command(SD_CMD55,SD_CMD55_R,response,argument)==1) + sd_send_command(SD_ACMD41,SD_ACMD41_R,response,argument); + else + j = SD_IDLE_WAIT_MAX; + } + while(((response[0] & SD_MSK_IDLE) == SD_MSK_IDLE) && (j < SD_IDLE_WAIT_MAX)); + + if(j>= SD_IDLE_WAIT_MAX) // IDLE timeout exceeded, card asleep + return 0; + + // CMD58 reads the SD card capabilities + if(sd_send_command(SD_CMD58,SD_CMD58_R,response,argument)==0) + return 0; // CMD58 FAIL + + if((response[2] & SD_MSK_OCR_33) != SD_MSK_OCR_33) + return 0; // Card doesn't do 3.3V + + //printf("OCR = %x %x %x %x\n",response[0],response[1],response[2],response[3]); + + // Set blocklen here + sd_packarg(argument,SD_BLOCKLEN); + if(sd_send_command(SD_CMD16,SD_CMD16_R,response,argument)==0) + return 0; // Set Blocklen failed + + // Reset back to high speed + sdspi_regs->clkdiv = 4; + //puts("finished init\n"); + return 1; +} + +int sd_send_command(unsigned char cmd,unsigned char response_type, + unsigned char *response,unsigned char *argument) +{ + int i; + char response_length; + unsigned char tmp; + + sd_assert_cs(); + sd_send_byte((cmd & 0x3F) | 0x40); + for(i=3;i>=0;i--) + sd_send_byte(argument[i]); + sd_send_byte(SD_CRC); // Always the same + + response_length = 0; + switch(response_type) + { + case SD_R1: + case SD_R1B: + response_length = 1; + break; + case SD_R2: + response_length = 2; + break; + case SD_R3: + response_length = 5; + break; + default: + break; + } + + // Wait for a response, which will have a 0 start bit + i = 0; + do + { + tmp = sd_rcv_byte(); + i++; + } + while(((tmp & 0x80) != 0) && i < SD_CMD_TIMEOUT); + + if(i>= SD_CMD_TIMEOUT) + { + sd_deassert_cs(); + //puts("cmd send timeout\n"); + return 0; + } + + for(i=response_length-1; i>=0; i--) + { + response[i] = tmp; + tmp = sd_rcv_byte(); + } + i = 0; + if(response_type == SD_R1B) + { + do + { + i++; + tmp = sd_rcv_byte(); + } + while(tmp != SD_IDLE); + sd_send_byte(SD_IDLE); + } + + //puts("send cmd success\n"); + sd_deassert_cs(); + return 1; +} + +int +sd_read_block (unsigned int blockaddr, unsigned char *buf) +{ + unsigned char response[5]; + unsigned char argument[4]; + unsigned int i = 0; + unsigned char tmp; + + blockaddr <<= SD_BLOCKLEN_NBITS; + sd_packarg(argument,blockaddr); + if(sd_send_command(SD_CMD17,SD_CMD17_R,response,argument)==0) + return 0; //Failed READ; + if(response[0] != 0) + return 0; //Misaligned READ + + sd_assert_cs(); + i = 0; + do + { + tmp = sd_rcv_byte(); + i++; + } + while((tmp == 0xFF) && (i < SD_RD_TIMEOUT)); + if((i>= SD_RD_TIMEOUT) ||((tmp & SD_MSK_TOK_DATAERROR) == 0)) + { + sd_send_byte(SD_IDLE); // Send a dummy before quitting + return 0; // Data ERROR + } + for(i=0;i<SD_BLOCKLEN;i++) + buf[i] = sd_rcv_byte(); + return 1; + +} + +int +sd_write_block(unsigned int blockaddr, const unsigned char *buf) +{ + // FIXME not implemented yet + return 0; +} diff --git a/firmware/microblaze/lib/sd.h b/firmware/microblaze/lib/sd.h new file mode 100644 index 000000000..e2d0ae38e --- /dev/null +++ b/firmware/microblaze/lib/sd.h @@ -0,0 +1,122 @@ +/* -*- c -*- */ +/* + * Copyright 2008 Ettus Research LLC + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_SD_H +#define INCLUDED_SD_H + +#include "memory_map.h" + +#define SD_READY 1 +#define SD_IDLE_WAIT_MAX 100 +#define SD_CMD_TIMEOUT 100 +#define SD_RD_TIMEOUT 1000 + +#define SD_CMD0 0 +#define SD_CMD1 1 +#define SD_CMD9 9 +#define SD_CMD10 10 +#define SD_CMD12 12 +#define SD_CMD13 13 +#define SD_CMD16 16 +#define SD_CMD17 17 +#define SD_CMD18 18 +#define SD_CMD24 24 +#define SD_CMD25 25 +#define SD_CMD27 27 +#define SD_CMD28 28 +#define SD_CMD29 29 +#define SD_CMD30 30 +#define SD_CMD32 32 +#define SD_CMD33 33 +#define SD_CMD38 38 +#define SD_CMD55 55 +#define SD_CMD58 58 +#define SD_CMD59 59 +#define SD_ACMD41 41 +#define SD_IDLE 0xFF +#define SD_CRC 0x95 + +#define SD_R1 1 +#define SD_R1B 2 +#define SD_R2 3 +#define SD_R3 4 + +#define SD_CMD0_R SD_R1 +#define SD_CMD16_R SD_R1 +#define SD_CMD17_R SD_R1 +#define SD_CMD55_R SD_R1 +#define SD_ACMD41_R SD_R1 +#define SD_CMD58_R SD_R3 + +#define SD_BLOCKLEN 512 +#define SD_BLOCKLEN_NBITS 9 + +#define SD_MSK_IDLE 0x01 +#define SD_MSK_OCR_33 0xC0 +#define SD_MSK_TOK_DATAERROR 0xE0 + + +int sd_init(void); + +static inline void +sd_assert_cs(void) +{ + // Wait for idle before doing anything + while(sdspi_regs->status != SD_READY) + ; + sdspi_regs->status = 1; +} + +static inline void +sd_deassert_cs(void) +{ + // Wait for idle before doing anything + while(sdspi_regs->status != SD_READY) + ; + sdspi_regs->status = 0; +} + +static inline char +sd_rcv_byte(void) +{ + // Wait for idle before doing anything + while(sdspi_regs->status != SD_READY) + ; + sdspi_regs->send_dat = SD_IDLE; + while(sdspi_regs->status != SD_READY) + ; + return sdspi_regs-> receive_dat; +} + +static inline void +sd_send_byte(char dat) +{ + // Wait for idle before doing anything + while(sdspi_regs->status != SD_READY) + ; // Wait for status = 1 (ready) + sdspi_regs->send_dat = dat; +} + + +int sd_send_command(unsigned char cmd,unsigned char response_type, + unsigned char *response,unsigned char *argument); + +int sd_read_block (unsigned int blockaddr, unsigned char *buf); +int sd_write_block(unsigned int blockaddr, const unsigned char *buf); + +#endif /* INCLUDED_SD_H */ diff --git a/firmware/microblaze/lib/spi.c b/firmware/microblaze/lib/spi.c new file mode 100644 index 000000000..937397df6 --- /dev/null +++ b/firmware/microblaze/lib/spi.c @@ -0,0 +1,61 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "spi.h" +#include "memory_map.h" + +void +spi_init(void) +{ + /* + * f_sclk = f_wb / ((div + 1) * 2) + */ + spi_regs->div = 1; // 0 = Div by 2 (25 MHz); 1 = Div-by-4 (12.5 MHz) +} + +void +spi_wait(void) +{ + while (spi_regs->ctrl & SPI_CTRL_GO_BSY) + ; +} + +uint32_t +spi_transact(bool readback, int slave, uint32_t data, int length, uint32_t flags) +{ + flags &= (SPI_CTRL_TXNEG | SPI_CTRL_RXNEG); + int ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & length) | flags; + + spi_wait(); + + // Tell it which SPI slave device to access + spi_regs->ss = slave & 0xff; + + // Data we will send + spi_regs->txrx0 = data; + + // Run it -- write once and rewrite with GO set + spi_regs->ctrl = ctrl; + spi_regs->ctrl = ctrl | SPI_CTRL_GO_BSY; + + if(readback) { + spi_wait(); + return spi_regs->txrx0; + } + else + return 0; +} diff --git a/firmware/microblaze/lib/spi.h b/firmware/microblaze/lib/spi.h new file mode 100644 index 000000000..f5b69b270 --- /dev/null +++ b/firmware/microblaze/lib/spi.h @@ -0,0 +1,52 @@ +/* -*- c -*- */ +/* + * Copyright 2006,2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_SPI_H +#define INCLUDED_SPI_H + +#include <memory_map.h> +#include <stdbool.h> + +/*! + * \brief One time call to initialize SPI + */ +void spi_init(void); + +/*! + * \brief Wait for last SPI transaction to complete. + * Unless you need to know it completed, it's not necessary to call this. + */ +void spi_wait(void); + +#define SPI_TXONLY false // readback: no +#define SPI_TXRX true // readback: yes + +/* + * Flags for spi_transact + */ +#define SPIF_PUSH_RISE 0 // push tx data on rising edge of SCLK +#define SPIF_PUSH_FALL SPI_CTRL_TXNEG // push tx data on falling edge of SCLK +#define SPIF_LATCH_RISE 0 // latch rx data on rising edge of SCLK +#define SPIF_LATCH_FALL SPI_CTRL_RXNEG // latch rx data on falling edge of SCLK + + +uint32_t +spi_transact(bool readback, int slave, uint32_t data, int length, uint32_t flags); + + +#endif /* INCLUDED_SPI_H */ diff --git a/firmware/microblaze/lib/stdint.h b/firmware/microblaze/lib/stdint.h new file mode 100644 index 000000000..b5a8611a9 --- /dev/null +++ b/firmware/microblaze/lib/stdint.h @@ -0,0 +1,34 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2009 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_STDINT_H +#define INCLUDED_STDINT_H + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef long long int int64_t; +typedef unsigned long long int uint64_t; + +typedef int intptr_t; +typedef unsigned int uintptr_t; + +#endif /* INCLUDED_STDINT_H */ diff --git a/firmware/microblaze/lib/stdio.h b/firmware/microblaze/lib/stdio.h new file mode 100644 index 000000000..12a7ed0bb --- /dev/null +++ b/firmware/microblaze/lib/stdio.h @@ -0,0 +1,38 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_STDIO_H +#define INCLUDED_STDIO_H + +// very trimmed down stdio.h See also nonstdio.h + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef EOF +#define EOF (-1) +#endif + +int putchar(int c); +int puts(const char *s); +int printf(const char *format, ...); + +int getchar(void); + +#endif /* INCLUDED_STDIO_H */ diff --git a/firmware/microblaze/lib/u2_init.c b/firmware/microblaze/lib/u2_init.c new file mode 100644 index 000000000..2e2e6a0fb --- /dev/null +++ b/firmware/microblaze/lib/u2_init.c @@ -0,0 +1,129 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "pic.h" +#include "hal_io.h" +#include "lsadc.h" +#include "lsdac.h" +#include "buffer_pool.h" +#include "hal_uart.h" +#include "i2c.h" +#include "mdelay.h" +#include "ad9777.h" +#include "clocks.h" +#include "db.h" +#include "usrp2_i2c_addr.h" + +//#include "nonstdio.h" + +unsigned char u2_hw_rev_major; +unsigned char u2_hw_rev_minor; + +static inline void +get_hw_rev(void) +{ + bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_REV_LSB, &u2_hw_rev_minor, 1); + ok &= eeprom_read(I2C_ADDR_MBOARD, MBOARD_REV_MSB, &u2_hw_rev_major, 1); +} + +/* + * We ought to arrange for this to be called before main, but for now, + * we require that the user's main call u2_init as the first thing... + */ +bool +u2_init(void) +{ + // Set GPIOs to inputs, disable GPIO streaming + hal_gpio_set_ddr(GPIO_TX_BANK, 0x0000, 0xffff); + hal_gpio_set_ddr(GPIO_RX_BANK, 0x0000, 0xffff); + + hal_gpio_write(GPIO_TX_BANK, 0x0000, 0xffff); // init s/w output value to zero + hal_gpio_write(GPIO_RX_BANK, 0x0000, 0xffff); + + dsp_rx_regs->gpio_stream_enable = 0; // I, Q LSBs come from DSP + + hal_io_init(); + + // init spi, so that we can switch over to the high-speed clock + spi_init(); + + // init i2c so we can read our rev + i2c_init(); + get_hw_rev(); + + // set up the default clocks + clocks_init(); + + // clocks_enable_test_clk(true,1); + + // Enable ADCs + output_regs->adc_ctrl = ADC_CTRL_ON; + + // Set up AD9777 DAC + ad9777_write_reg(0, R0_1R); + ad9777_write_reg(1, R1_INTERP_4X | R1_REAL_MIX); + ad9777_write_reg(2, 0); + ad9777_write_reg(3, R3_PLL_DIV_1); + ad9777_write_reg(4, R4_PLL_ON | R4_CP_AUTO); + ad9777_write_reg(5, R5_I_FINE_GAIN(0)); + ad9777_write_reg(6, R6_I_COARSE_GAIN(0xf)); + ad9777_write_reg(7, 0); // I dac offset + ad9777_write_reg(8, 0); + ad9777_write_reg(9, R9_Q_FINE_GAIN(0)); + ad9777_write_reg(10, R10_Q_COARSE_GAIN(0xf)); + ad9777_write_reg(11, 0); // Q dac offset + ad9777_write_reg(12, 0); + + // Initial values for tx and rx mux registers + dsp_tx_regs->tx_mux = 0x10; + dsp_rx_regs->rx_mux = 0x44444444; + + // Set up serdes + output_regs->serdes_ctrl = (SERDES_ENABLE | SERDES_RXEN); + + pic_init(); // progammable interrupt controller + bp_init(); // buffer pool + lsadc_init(); // low-speed ADCs + lsdac_init(); // low-speed DACs + db_init(); // daughterboard init + + hal_enable_ints(); + + // flash all leds to let us know board is alive + hal_set_leds(0x0, 0x1f); + mdelay(100); + hal_set_leds(0x1f, 0x1f); + mdelay(100); + hal_set_leds(0x1, 0x1f); // Leave the first one on + +#if 0 + // test register readback + int rr, vv; + vv = ad9777_read_reg(0); + printf("ad9777 reg[0] = 0x%x\n", vv); + + for (rr = 0x04; rr <= 0x0d; rr++){ + vv = ad9510_read_reg(rr); + printf("ad9510 reg[0x%x] = 0x%x\n", rr, vv); + } +#endif + + return true; +} diff --git a/firmware/microblaze/lib/u2_init.h b/firmware/microblaze/lib/u2_init.h new file mode 100644 index 000000000..334791189 --- /dev/null +++ b/firmware/microblaze/lib/u2_init.h @@ -0,0 +1,31 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_U2_INIT_H +#define INCLUDED_U2_INIT_H + +#include <stdbool.h> + +extern unsigned char u2_hw_rev_major; +extern unsigned char u2_hw_rev_minor; + +/*! + * one-time init + */ +bool u2_init(void); + +#endif /* INCLUDED_U2_INIT_H */ diff --git a/firmware/microblaze/lib/usrp2_bytesex.h b/firmware/microblaze/lib/usrp2_bytesex.h new file mode 100644 index 000000000..2b74f2a0b --- /dev/null +++ b/firmware/microblaze/lib/usrp2_bytesex.h @@ -0,0 +1,66 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_USRP2_BYTESEX_H +#define INCLUDED_USRP2_BYTESEX_H + +// The USRP2 speaks big-endian... +// Use the standard include files or provide substitutions for +// htons and friends + +#if defined(HAVE_ARPA_INET_H) +#include <arpa/inet.h> +#elif defined(HAVE_NETINET_IN_H) +#include <netinet/in.h> +#else +#include <stdint.h> + +#ifdef WORDS_BIGENDIAN // nothing to do... + +static inline uint32_t htonl(uint32_t x){ return x; } +static inline uint16_t htons(uint16_t x){ return x; } +static inline uint32_t ntohl(uint32_t x){ return x; } +static inline uint16_t ntohs(uint16_t x){ return x; } + +#else + +#ifdef HAVE_BYTESWAP_H +#include <byteswap.h> +#else + +static inline uint16_t +bswap_16 (uint16_t x) +{ + return ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)); +} + +static inline uint32_t +bswap_32 (uint32_t x) +{ + return ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) \ + | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)); +} +#endif + +static inline uint32_t htonl(uint32_t x){ return bswap_32(x); } +static inline uint16_t htons(uint16_t x){ return bswap_16(x); } +static inline uint32_t ntohl(uint32_t x){ return bswap_32(x); } +static inline uint16_t ntohs(uint16_t x){ return bswap_16(x); } + +#endif +#endif +#endif /* INCLUDED_USRP2_BYTESEX_H */ diff --git a/firmware/microblaze/lib/wb16550.h b/firmware/microblaze/lib/wb16550.h new file mode 100644 index 000000000..7522f4438 --- /dev/null +++ b/firmware/microblaze/lib/wb16550.h @@ -0,0 +1,98 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +// Wishbone National Semiconductor 16550A compatible UART + +#ifndef INCLUDED_WB16550_H +#define INCLUDED_WB16550_H + +#include <stdint.h> + +typedef struct { + volatile uint8_t data; // 0 r/w: r: rx fifo, w: tx fifo (if DLAB: LSB of divisor) + volatile uint8_t ier; // 1 r/w: Interrupt Enable Register (if DLAB: MSB of divisor) + volatile uint8_t iir_fcr; // 2 r/w: r: Interrupt ID Register, + // w: Fifo Control Register + volatile uint8_t lcr; // 3 r/w: Line Control Register + volatile uint8_t mcr; // 4 w: Modem Control Register + volatile uint8_t lsr; // 5 r: Line Status Register + volatile uint8_t msr; // 6 r: Modem Status Register + +} wb16550_reg_t; + +#define UART_IER_RDI 0x01 // Enable received data interrupt +#define UART_IER_THRI 0x02 // Enable transmitter holding reg empty int. +#define UART_IER_RLSI 0x04 // Enable receiver line status interrupt +#define UART_IER_MSI 0x08 // Enable modem status interrupt + +#define UART_IIR_NO_INT 0x01 // No interrupts pending +#define UART_IIR_ID_MASK 0x06 // Mask for interrupt ID +#define UART_IIR_MSI 0x00 // Modem status interrupt +#define UART_IIR_THRI 0x02 // Tx holding register empty int +#define UART_IIR_RDI 0x04 // Rx data available int +#define UART_IIR_RLSI 0x06 // Receiver line status int + +#define UART_FCR_ENABLE_FIFO 0x01 // ignore, always enabled +#define UART_FCR_CLEAR_RCVR 0x02 // Clear the RCVR FIFO +#define UART_FCR_CLEAR_XMIT 0x04 // Clear the XMIT FIFO +#define UART_FCR_TRIGGER_MASK 0xC0 // Mask for FIFO trigger range +#define UART_FCR_TRIGGER_1 0x00 // Rx fifo trigger level: 1 byte +#define UART_FCR_TRIGGER_4 0x40 // Rx fifo trigger level: 4 bytes +#define UART_FCR_TRIGGER_8 0x80 // Rx fifo trigger level: 8 bytes +#define UART_FCR_TRIGGER_14 0xC0 // Rx fifo trigger level: 14 bytes + +#define UART_LCR_DLAB 0x80 // Divisor latch access bit +#define UART_LCR_SBC 0x40 // Set break control +#define UART_LCR_SPAR 0x20 // Stick parity +#define UART_LCR_EPAR 0x10 // Even parity select +#define UART_LCR_PARITY 0x08 // Parity Enable +#define UART_LCR_STOP 0x04 // Stop bits: 0=1 bit, 1=2 bits +#define UART_LCR_WLEN5 0x00 // Wordlength: 5 bits +#define UART_LCR_WLEN6 0x01 // Wordlength: 6 bits +#define UART_LCR_WLEN7 0x02 // Wordlength: 7 bits +#define UART_LCR_WLEN8 0x03 // Wordlength: 8 bits + +#define UART_MCR_LOOP 0x10 // Enable loopback test mode +#define UART_MCR_OUT2n 0x08 // Out2 complement (loopback mode) +#define UART_MCR_OUT1n 0x04 // Out1 complement (loopback mode) +#define UART_MCR_RTSn 0x02 // RTS complement +#define UART_MCR_DTRn 0x01 // DTR complement + +#define UART_LSR_TEMT 0x40 // Transmitter empty +#define UART_LSR_THRE 0x20 // Transmit-hold-register empty +#define UART_LSR_BI 0x10 // Break interrupt indicator +#define UART_LSR_FE 0x08 // Frame error indicator +#define UART_LSR_PE 0x04 // Parity error indicator +#define UART_LSR_OE 0x02 // Overrun error indicator +#define UART_LSR_DR 0x01 // Receiver data ready +#define UART_LSR_BRK_ERROR_BITS 0x1E // BI, FE, PE, OE bits +#define UART_LSR_ERROR 0x80 // At least 1 PE, FE or BI are in the fifo + +#define UART_MSR_DCD 0x80 // Data Carrier Detect +#define UART_MSR_RI 0x40 // Ring Indicator +#define UART_MSR_DSR 0x20 // Data Set Ready +#define UART_MSR_CTS 0x10 // Clear to Send +#define UART_MSR_DDCD 0x08 // Delta DCD +#define UART_MSR_TERI 0x04 // Trailing edge ring indicator +#define UART_MSR_DDSR 0x02 // Delta DSR +#define UART_MSR_DCTS 0x01 // Delta CTS +#define UART_MSR_ANY_DELTA 0x0F // Any of the delta bits! + + +#endif // INCLUDED_WB16550_H diff --git a/firmware/microblaze/u2_flash_tool b/firmware/microblaze/u2_flash_tool new file mode 100755 index 000000000..2b66a4ac0 --- /dev/null +++ b/firmware/microblaze/u2_flash_tool @@ -0,0 +1,116 @@ +#!/usr/bin/env python + +import sys +from optparse import OptionParser + +SECTOR_SIZE = 512 # bytes +MAX_FILE_SIZE = 1 * (2**20) # maximum number of bytes we'll burn to a slot + +FPGA_OFFSET = 0 # offset in flash to fpga image +FIRMWARE_OFFSET = 1 * (2**20) # offset in flash to firmware image + +def read_file_data(filename): + f = open(filename, "rb") + file_data = f.read(MAX_FILE_SIZE) + t = len(file_data) % SECTOR_SIZE + if t != 0: + file_data += (SECTOR_SIZE - t)*chr(0) # pad to an even sector size w/ zeros + return file_data + + +def write_flash(offset, filename, devname): + file_data = read_file_data(filename) + dev = open(devname, "wb") + dev.seek(offset, 0) # seek to absolute byte offset + dev.write(file_data) + dev.flush() + dev.close() + return True + + +def verify_flash(offset, filename, devname): + file_data = read_file_data(filename) + dev = open(devname, "rb") + dev.seek(offset, 0) # seek to absolute byte offset + dev_data = dev.read(len(file_data)) + if len(dev_data) != len(file_data): + sys.stderr.write("short read on device %s\n" % (devname,)) + return False + + if file_data == dev_data: + return True + + # doesn't match + nwrong = 0 + for i in range(len(file_data)): + if dev_data[i] != file_data[i]: + sys.stderr.write("mismatch at offset %7d. Expected 0x%02x, got 0x%02x\n" % ( + i, ord(file_data[i]), ord(dev_data[i]))) + nwrong += 1 + if nwrong > 16: + sys.stderr.write("> 16 errors, stopping comparison\n") + break + return False + +def read_flash(offset, filename, devname): + dev = open(devname, "rb") + dev.seek(offset, 0) # seek to absolute byte offset + dev_data = dev.read(MAX_FILE_SIZE) + dev.close() + open(filename, "wb").write(dev_data) + + +def main(): + parser = OptionParser(usage="%prog: [options] filename") + parser.add_option("-w", "--write", action="store_const", const="write", dest="mode", + help="write FILE to TARGET slot") + parser.add_option("-v", "--verify", action="store_const", const="verify", dest="mode", + help="verify FILE against TARGET slot") + parser.add_option("-r", "--read", action="store_const", const="read", dest="mode", + help="read TARGET slot, write to FILE") + parser.add_option("-t", "--target", type="choice", choices=("fpga", "s/w"), default="s/w", + help="select TARGET slot from: fpga, s/w [default=%default]") + parser.add_option("", "--dev", default=None, + help="specify flash device file, e.g., /dev/sdb. Be careful!") + parser.set_defaults(target="s/w", mode=None) + + (options, args) = parser.parse_args() + if len(args) != 1: + parser.print_help() + raise SystemExit + + filename = args[0] + + if options.mode is None: + sys.stderr.write("specify mode with -w, -v or -r\n") + parser.print_help() + raise SystemExit + + if options.dev is None: + sys.stderr.write("specify the device file with --dev\n") + parser.print_help() + raise SystemExit + + offset = { "fpga" : FPGA_OFFSET, "s/w" : FIRMWARE_OFFSET }[options.target] + + if options.mode == "write": + r = (write_flash(offset, filename, options.dev) + and verify_flash(offset, filename, options.dev)) + elif options.mode == "verify": + r = verify_flash(offset, filename, options.dev) + elif options.mode == "read": + r = read_flash(offset, filename, options.dev) + else: + raise NotImplemented + + if not r: + raise SystemExit, 1 + + +if __name__ == "__main__": + main() + + + + + |