diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a0c8ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,63 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex +*.bin + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# debug information files +*.dwo + +*.cache +compile_commands.json + +tests/core/test_core + +*.pdf diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1341e78 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# wolfHAL + +wolfHAL is a lightweight, OS-agnostic, compiler-agnostic hardware abstraction +layer for embedded targets written in C. It provides a uniform driver model +based on vtable dispatch. + +## Repository layout + +``` +wolfHAL/ Public headers (API surface) + platform/ Platform-specific device macros and definitions +src/ Driver implementations (generic + platform) +boards/ Example board configurations used for testing and examples +examples/ Example applications +tests/ Test framework and test suites +``` + +## Further reading + +- [Getting Started](docs/getting_started.md) — Integrating wolfHAL into your project +- [Boards](boards/README.md) — Example board configurations +- [Examples](examples/README.md) — Example applications +- [Tests](tests/README.md) — Test framework and test suites +- [Writing a Driver](docs/writing_a_driver.md) — How to implement a driver for a new platform +- [Adding a Board](docs/adding_a_board.md) — How to add a new board configuration +- [Adding a Peripheral](docs/adding_a_peripheral.md) — How to add an external peripheral device +- [Adding an Example](docs/adding_an_example.md) — How to add a new example application +- [Adding a Test](docs/adding_a_test.md) — How to add hardware tests + +## License + +GPLv3 -- see [LICENSE](LICENSE) for details. diff --git a/boards/README.md b/boards/README.md new file mode 100644 index 0000000..f8e1887 --- /dev/null +++ b/boards/README.md @@ -0,0 +1,78 @@ +# wolfHAL Example Board Definitions + +The board definitions in this directory are **examples** for use with +wolfHAL's tests and sample applications. They are configured for specific +development boards and are not intended for production use. Users should +create their own board support packages tailored to their hardware. + +Each subdirectory contains a board support package (BSP) for a specific +development board. A BSP provides everything needed to build wolfHAL for a +given target: startup code, peripheral initialization, linker script, and +build configuration. + +## Supported Boards + +| Board | Platform | CPU | Directory | +|-------|----------|-----|-----------| +| Microchip PIC32CZ CA Curiosity Ultra | PIC32CZ | Cortex-M7 | `pic32cz_curiosity_ultra/` | +| ST NUCLEO-C031C6 | STM32C0 | Cortex-M0+ | `stm32c031_nucleo/` | +| ST NUCLEO-F091RC | STM32F0 | Cortex-M0 | `stm32f091rc_nucleo/` | +| ST NUCLEO-F302R8 | STM32F3 | Cortex-M4 | `stm32f302r8_nucleo/` | +| WeAct BlackPill STM32F411 | STM32F4 | Cortex-M4 | `stm32f411_blackpill/` | +| ST NUCLEO-H563ZI | STM32H5 | Cortex-M33 | `stm32h563zi_nucleo/` | +| ST NUCLEO-WB55RG | STM32WB | Cortex-M4 | `stm32wb55xx_nucleo/` | +| ST NUCLEO-L152RE | STM32L1 | Cortex-M3 | `stm32l152re_nucleo/` | +| ST NUCLEO-N657X0-Q | STM32N6 | Cortex-M55 | `stm32n657a0_nucleo/` | +| ST NUCLEO-WBA55CG | STM32WBA | Cortex-M33 | `stm32wba55cg_nucleo/` | + +## Board Directory Contents + +Each board directory contains: + +- **`board.mk`** - Build configuration: toolchain, CPU flags, platform + drivers, and linker script. Included by application Makefiles via + `include $(BOARD_DIR)/board.mk`. +- **`board.h`** - Board-level declarations: global peripheral instances, + pin definitions, and `Board_Init()`/`Board_Deinit()` prototypes. +- **`board.c`** - Peripheral instantiation and `Board_Init()` implementation + (power, clock, GPIO, UART, flash, timer). +- **`linker.ld`** - Linker script defining memory regions (flash, RAM). +- Any additional board-specific source files (e.g. interrupt vector table, + architecture-specific startup code). + +## board.mk Convention + +Board `board.mk` files use a self-referencing pattern so that they can be +included from any directory: + +```makefile +_BOARD_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) +``` + +`_BOARD_DIR` points to the board's own directory, while the application +Makefile sets `BOARD_DIR` which may point elsewhere (e.g. a private board +overlay). This enables private repositories to extend a board by including +the base `board.mk` and adding additional sources. + +### What `BOARD_SOURCE` includes + +Board `board.mk` populates `BOARD_SOURCE` with all of the sources +required to build the wolfHAL tests and sample applications for that board: + +- Board files: `board.c` and any additional board-specific source files +- Platform / SoC drivers: e.g. `pic32cz_*.c`, `stm32wb_*.c` +- Architecture support: `systick.c` and any related startup / vector code +- Core wolfHAL modules and common sources: generic drivers such as + `gpio.c`, `clock.c`, `uart.c`, and other files under `src/*.c` + +In your own projects you may either reuse these defaults by including the +board `board.mk` as-is, or define your own `BOARD_SOURCE` in your +application Makefile to select a different set of modules. + +## Adding a New Board + +1. Create a new directory: `boards/_/` +2. Add `board.mk` following the `_BOARD_DIR` pattern above +3. Implement `board.h`, `board.c`, and `linker.ld` +4. Set `PLATFORM`, `TESTS`, toolchain variables, `CFLAGS`, and `BOARD_SOURCE` +5. Build with `make BOARD=` diff --git a/boards/stm32l152re_nucleo/board.c b/boards/stm32l152re_nucleo/board.c new file mode 100644 index 0000000..fb1b3c3 --- /dev/null +++ b/boards/stm32l152re_nucleo/board.c @@ -0,0 +1,225 @@ +/* Example board configuration for the NUCLEO-L152RE dev board */ + +#include +#include +#include "board.h" +#include + +volatile uint32_t g_tick = 0; + +void SysTick_Handler(void) +{ + g_tick++; +} + +uint32_t Board_GetTick(void) +{ + return g_tick; +} + +whal_Timeout g_whalTimeout = { + .timeoutTicks = 1000, /* 1s */ + .GetTick = Board_GetTick, +}; + +/* Clock */ +whal_Clock g_whalClock = { + .regmap = { WHAL_STM32L152_RCC_REGMAP }, +}; + +static const whal_Stm32l1_Rcc_PeriphClk g_pwrClock = {WHAL_STM32L152_PWR_CLOCK}; + +static const whal_Stm32l1_Rcc_PeriphClk g_periphClks[] = { + {WHAL_STM32L152_GPIOA_CLOCK}, + {WHAL_STM32L152_USART2_CLOCK}, +}; +#define PERIPH_CLK_COUNT (sizeof(g_periphClks) / sizeof(g_periphClks[0])) + +/* Power */ +whal_Power g_whalPower = { + .regmap = { WHAL_STM32L152_PWR_REGMAP }, +}; + +/* GPIO */ +whal_Gpio g_whalGpio = { + .regmap = { WHAL_STM32L152_GPIO_REGMAP }, + + .cfg = &(whal_Stm32l1_Gpio_Cfg) { + .pinCfg = (whal_Stm32l1_Gpio_PinCfg[PIN_COUNT]) { + /* LD2 Green LED on PA5 (NUCLEO-L152RE / UM1724) */ + [LED_PIN] = WHAL_STM32L1_GPIO_PIN( + WHAL_STM32L1_GPIO_PORT_A, 5, WHAL_STM32L1_GPIO_MODE_OUT, + WHAL_STM32L1_GPIO_OUTTYPE_PUSHPULL, WHAL_STM32L1_GPIO_SPEED_LOW, + WHAL_STM32L1_GPIO_PULL_NONE, 0), + /* USART2 TX on PA2, AF7 (ST-Link VCP) */ + [UART_TX_PIN] = WHAL_STM32L1_GPIO_PIN( + WHAL_STM32L1_GPIO_PORT_A, 2, WHAL_STM32L1_GPIO_MODE_ALTFN, + WHAL_STM32L1_GPIO_OUTTYPE_PUSHPULL, WHAL_STM32L1_GPIO_SPEED_FAST, + WHAL_STM32L1_GPIO_PULL_UP, 7), + /* USART2 RX on PA3, AF7 (ST-Link VCP) */ + [UART_RX_PIN] = WHAL_STM32L1_GPIO_PIN( + WHAL_STM32L1_GPIO_PORT_A, 3, WHAL_STM32L1_GPIO_MODE_ALTFN, + WHAL_STM32L1_GPIO_OUTTYPE_PUSHPULL, WHAL_STM32L1_GPIO_SPEED_FAST, + WHAL_STM32L1_GPIO_PULL_UP, 7), + }, + .pinCount = PIN_COUNT, + }, +}; + +/* UART */ +whal_Uart g_whalUart = { + .regmap = { WHAL_STM32L152_USART2_REGMAP }, + + .cfg = &(whal_Stm32l1_Uart_Cfg) { + .timeout = &g_whalTimeout, + .brr = WHAL_STM32L1_UART_BRR(32000000, 115200), + }, +}; + +/* Timer (SysTick) */ +whal_Timer g_whalTimer = { + .regmap = { WHAL_CORTEX_M3_SYSTICK_REGMAP }, + .driver = WHAL_CORTEX_M3_SYSTICK_DRIVER, + + .cfg = &(whal_SysTick_Cfg) { + .cyclesPerTick = 32000000 / 1000, + .clkSrc = WHAL_SYSTICK_CLKSRC_SYSCLK, + .tickInt = WHAL_SYSTICK_TICKINT_ENABLED, + }, +}; + +/* Flash */ +whal_Flash g_whalFlash = { + .regmap = { WHAL_STM32L152_FLASH_REGMAP }, + .driver = WHAL_STM32L152_FLASH_DRIVER, + + .cfg = &(whal_Stm32l1_Flash_Cfg) { + .timeout = &g_whalTimeout, + .startAddr = 0x08000000, + .size = 0x80000, /* 512 KB */ + }, +}; + +void Board_WaitMs(size_t ms) +{ + uint32_t startCount = g_tick; + while ((g_tick - startCount) < ms) + ; +} + +whal_Error Board_Init(void) +{ + whal_Error err; + + /* Enable PWR peripheral clock so the power driver can program VOS. + * Calling Enable on the RCC handle before whal_Clock_Init is safe — + * RCC is a board-level driver and only uses regmap.base. */ + err = whal_Stm32l1_Rcc_EnablePeriphClk(&g_whalClock, &g_pwrClock); + if (err) + return err; + + /* Switch regulator to range 1 (1.8 V) so the PLL can reach 32 MHz. + * Reset default is range 2, which caps SYSCLK at 16 MHz. */ + err = whal_Stm32l1_Pwr_SetVosRange(&g_whalPower, + WHAL_STM32L1_PWR_VOS_RANGE_1, + &g_whalTimeout); + if (err) + return err; + + /* Set flash latency before increasing clock speed. + * STM32L1: 0 WS for HCLK <= 16 MHz, 1 WS for 16 < HCLK <= 32 MHz. */ + err = whal_Stm32l1_Flash_Ext_SetLatency(WHAL_STM32L1_FLASH_LATENCY_1); + if (err) + return err; + + /* HSI 16 MHz -> PLL (HSI * 4 / 2 = 32 MHz) -> SYSCLK = PLL */ + err = whal_Stm32l1_Rcc_EnableOsc(&g_whalClock, + &(whal_Stm32l1_Rcc_OscCfg){WHAL_STM32L1_RCC_HSI_CFG}); + if (err) + return err; + + err = whal_Stm32l1_Rcc_EnablePll(&g_whalClock, &(whal_Stm32l1_Rcc_PllCfg){ + .clkSrc = WHAL_STM32L1_RCC_PLLSRC_HSI, + .pllmul = WHAL_STM32L1_RCC_PLLMUL_4, + .plldiv = WHAL_STM32L1_RCC_PLLDIV_2, + }); + if (err) + return err; + + err = whal_Stm32l1_Rcc_SetSysClock(&g_whalClock, WHAL_STM32L1_RCC_SYSCLK_SRC_PLL); + if (err) + return err; + + for (size_t i = 0; i < PERIPH_CLK_COUNT; i++) { + err = whal_Stm32l1_Rcc_EnablePeriphClk(&g_whalClock, &g_periphClks[i]); + if (err) + return err; + } + + err = whal_Gpio_Init(&g_whalGpio); + if (err) + return err; + + err = whal_Uart_Init(&g_whalUart); + if (err) + return err; + + err = whal_Flash_Init(&g_whalFlash); + if (err) + return err; + + err = whal_Timer_Init(&g_whalTimer); + if (err) + return err; + + err = whal_Timer_Start(&g_whalTimer); + if (err) + return err; + + return WHAL_SUCCESS; +} + +whal_Error Board_Deinit(void) +{ + whal_Error err; + + err = whal_Timer_Stop(&g_whalTimer); + if (err) + return err; + + err = whal_Timer_Deinit(&g_whalTimer); + if (err) + return err; + + err = whal_Flash_Deinit(&g_whalFlash); + if (err) + return err; + + err = whal_Uart_Deinit(&g_whalUart); + if (err) + return err; + + err = whal_Gpio_Deinit(&g_whalGpio); + if (err) + return err; + + for (size_t i = PERIPH_CLK_COUNT; i-- > 0; ) { + err = whal_Stm32l1_Rcc_DisablePeriphClk(&g_whalClock, &g_periphClks[i]); + if (err) + return err; + } + + err = whal_Stm32l1_Rcc_DisablePeriphClk(&g_whalClock, &g_pwrClock); + if (err) + return err; + + err = whal_Stm32l1_Rcc_SetSysClock(&g_whalClock, WHAL_STM32L1_RCC_SYSCLK_SRC_MSI); + if (err) + return err; + + err = whal_Stm32l1_Rcc_DisablePll(&g_whalClock); + if (err) + return err; + + return WHAL_SUCCESS; +} diff --git a/boards/stm32l152re_nucleo/board.h b/boards/stm32l152re_nucleo/board.h new file mode 100644 index 0000000..c82e44e --- /dev/null +++ b/boards/stm32l152re_nucleo/board.h @@ -0,0 +1,31 @@ +#ifndef BOARD_H +#define BOARD_H + +#include +#include +#include + +extern whal_Clock g_whalClock; +extern whal_Gpio g_whalGpio; +extern whal_Timer g_whalTimer; +extern whal_Uart g_whalUart; +extern whal_Flash g_whalFlash; +extern whal_Power g_whalPower; + +extern whal_Timeout g_whalTimeout; +extern volatile uint32_t g_tick; + +enum { + LED_PIN, + UART_TX_PIN, + UART_RX_PIN, + PIN_COUNT, +}; + +#define BOARD_LED_PIN 0 + +whal_Error Board_Init(void); +whal_Error Board_Deinit(void); +void Board_WaitMs(size_t ms); + +#endif /* BOARD_H */ diff --git a/boards/stm32l152re_nucleo/board.mk b/boards/stm32l152re_nucleo/board.mk new file mode 100644 index 0000000..158ca07 --- /dev/null +++ b/boards/stm32l152re_nucleo/board.mk @@ -0,0 +1,40 @@ +_BOARD_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) + +PLATFORM = stm32l1 +TESTS ?= clock gpio uart + +GCC = $(GCC_PATH)arm-none-eabi-gcc +LD = $(GCC_PATH)arm-none-eabi-gcc +OBJCOPY = $(GCC_PATH)arm-none-eabi-objcopy + +CFLAGS += -Wall -Werror $(INCLUDE) -g3 \ + -ffreestanding -nostdlib \ + -mcpu=cortex-m3 -mthumb \ + -DPLATFORM_STM32L1 -MMD -MP \ + -DWHAL_CFG_STM32L1_GPIO_DIRECT_API_MAPPING \ + -DWHAL_CFG_STM32L1_UART_DIRECT_API_MAPPING \ + -DWHAL_CFG_STM32L1_RCC_DIRECT_API_MAPPING +LDFLAGS = -mcpu=cortex-m3 -mthumb \ + -ffreestanding -nostartfiles -Wl,--omagic -static + +LINKER_SCRIPT ?= $(_BOARD_DIR)/linker.ld + +INCLUDE += -I$(_BOARD_DIR) + +BOARD_SOURCE = $(_BOARD_DIR)/ivt.c +BOARD_SOURCE += $(_BOARD_DIR)/board.c +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/timer.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/flash.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/stm32l1_*.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/systick.c) + +# Flash via openocd: make flash BOARD= IMAGE= +OPENOCD ?= /opt/openocd/bin/openocd +OPENOCD_INTERFACE ?= interface/stlink.cfg +OPENOCD_TARGET ?= target/stm32l1.cfg + +.PHONY: flash +flash: + @test -n "$(IMAGE)" || { echo "IMAGE= required" >&2; exit 1; } + $(OPENOCD) -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) \ + -c "program $(IMAGE) verify reset exit" diff --git a/boards/stm32l152re_nucleo/ivt.c b/boards/stm32l152re_nucleo/ivt.c new file mode 100644 index 0000000..adac400 --- /dev/null +++ b/boards/stm32l152re_nucleo/ivt.c @@ -0,0 +1,207 @@ +#include +#include + +extern uint32_t _estack[]; +extern uint32_t _sidata[]; +extern uint32_t _sdata[]; +extern uint32_t _edata[]; +extern uint32_t _sbss[]; +extern uint32_t _ebss[]; + +extern void main(); + +void __attribute__((naked,noreturn)) Default_Handler() +{ + while(1); +} + +void Reset_Handler() __attribute__((weak)); +void NMI_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void HardFault_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void MemManage_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void BusFault_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void UsageFault_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SVC_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DebugMon_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void PendSV_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SysTick_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); + +/* STM32L1xx peripheral interrupts (RM0038 Table 51, Cat.4/5/6) */ +void WWDG_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void PVD_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TAMPER_STAMP_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void RTC_WKUP_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void FLASH_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void RCC_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI0_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI3_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI4_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel3_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel4_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel5_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel6_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel7_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void ADC1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void USB_HP_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void USB_LP_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DAC_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void COMP_CA_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI9_5_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void LCD_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM9_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM10_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM11_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM3_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM4_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void I2C1_EV_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void I2C1_ER_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void I2C2_EV_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void I2C2_ER_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SPI1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SPI2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void USART1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void USART2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void USART3_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI15_10_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void RTC_Alarm_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void USB_FS_WKUP_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM6_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM7_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SDIO_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM5_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SPI3_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void UART4_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void UART5_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel3_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel4_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel5_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void AES_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void COMP_ACQ_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); + +#define RESERVED Default_Handler + +void *memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *d = dest; + const unsigned char *s = src; + + for (size_t i = 0; i < n; i++) + d[i] = s[i]; + + return dest; +} + +void *memset(void *s, int c, size_t n) +{ + unsigned char *p = s; + unsigned char v = (unsigned char)c; + + for (size_t i = 0; i < n; i++) + p[i] = v; + + return s; +} + +void (* const interrupt_vector_table[])() __attribute__((section(".isr_vector"))) = { + (void (*)())_estack, + Reset_Handler, + NMI_Handler, + HardFault_Handler, + MemManage_Handler, + BusFault_Handler, + UsageFault_Handler, + RESERVED, /* Reserved */ + RESERVED, /* Reserved */ + RESERVED, /* Reserved */ + RESERVED, /* Reserved */ + SVC_Handler, + DebugMon_Handler, + RESERVED, /* Reserved */ + PendSV_Handler, + SysTick_Handler, + /* STM32L1xx peripheral interrupts (position 0-56) */ + WWDG_IRQHandler, /* 0 */ + PVD_IRQHandler, /* 1 */ + TAMPER_STAMP_IRQHandler, /* 2 */ + RTC_WKUP_IRQHandler, /* 3 */ + FLASH_IRQHandler, /* 4 */ + RCC_IRQHandler, /* 5 */ + EXTI0_IRQHandler, /* 6 */ + EXTI1_IRQHandler, /* 7 */ + EXTI2_IRQHandler, /* 8 */ + EXTI3_IRQHandler, /* 9 */ + EXTI4_IRQHandler, /* 10 */ + DMA1_Channel1_IRQHandler, /* 11 */ + DMA1_Channel2_IRQHandler, /* 12 */ + DMA1_Channel3_IRQHandler, /* 13 */ + DMA1_Channel4_IRQHandler, /* 14 */ + DMA1_Channel5_IRQHandler, /* 15 */ + DMA1_Channel6_IRQHandler, /* 16 */ + DMA1_Channel7_IRQHandler, /* 17 */ + ADC1_IRQHandler, /* 18 */ + USB_HP_IRQHandler, /* 19 */ + USB_LP_IRQHandler, /* 20 */ + DAC_IRQHandler, /* 21 */ + COMP_CA_IRQHandler, /* 22 */ + EXTI9_5_IRQHandler, /* 23 */ + LCD_IRQHandler, /* 24 */ + TIM9_IRQHandler, /* 25 */ + TIM10_IRQHandler, /* 26 */ + TIM11_IRQHandler, /* 27 */ + TIM2_IRQHandler, /* 28 */ + TIM3_IRQHandler, /* 29 */ + TIM4_IRQHandler, /* 30 */ + I2C1_EV_IRQHandler, /* 31 */ + I2C1_ER_IRQHandler, /* 32 */ + I2C2_EV_IRQHandler, /* 33 */ + I2C2_ER_IRQHandler, /* 34 */ + SPI1_IRQHandler, /* 35 */ + SPI2_IRQHandler, /* 36 */ + USART1_IRQHandler, /* 37 */ + USART2_IRQHandler, /* 38 */ + USART3_IRQHandler, /* 39 */ + EXTI15_10_IRQHandler, /* 40 */ + RTC_Alarm_IRQHandler, /* 41 */ + USB_FS_WKUP_IRQHandler, /* 42 */ + TIM6_IRQHandler, /* 43 */ + TIM7_IRQHandler, /* 44 */ + SDIO_IRQHandler, /* 45 */ + TIM5_IRQHandler, /* 46 */ + SPI3_IRQHandler, /* 47 */ + UART4_IRQHandler, /* 48 */ + UART5_IRQHandler, /* 49 */ + DMA2_Channel1_IRQHandler, /* 50 */ + DMA2_Channel2_IRQHandler, /* 51 */ + DMA2_Channel3_IRQHandler, /* 52 */ + DMA2_Channel4_IRQHandler, /* 53 */ + DMA2_Channel5_IRQHandler, /* 54 */ + AES_IRQHandler, /* 55 */ + COMP_ACQ_IRQHandler, /* 56 */ +}; + +void __attribute__((naked)) Reset_Handler() +{ + __asm__("ldr r0, =_estack\n\t" + "mov sp, r0"); + + /* Copy data section from flash to RAM */ + uint32_t data_section_size = _edata - _sdata; + memcpy(_sdata, _sidata, data_section_size * 4); + + /* Zero out bss */ + uint32_t bss_section_size = _ebss - _sbss; + memset(_sbss, 0, bss_section_size * 4); + + /* Set Interrupt Vector Table Offset */ + uint32_t *vtor = (uint32_t *)0xE000ED08; + *vtor = (uint32_t)interrupt_vector_table; + + main(); +} diff --git a/boards/stm32l152re_nucleo/linker.ld b/boards/stm32l152re_nucleo/linker.ld new file mode 100644 index 0000000..e8a116b --- /dev/null +++ b/boards/stm32l152re_nucleo/linker.ld @@ -0,0 +1,121 @@ +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x20014000; /* end of 80 KB RAM */ +/* Generate a link error if the stack don't fit into RAM */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K +RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 80K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data goes into FLASH */ + .rodata : + { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM AT> FLASH + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss section */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >RAM +} diff --git a/boards/stm32wb55xx_nucleo/board.c b/boards/stm32wb55xx_nucleo/board.c new file mode 100644 index 0000000..d4fa48e --- /dev/null +++ b/boards/stm32wb55xx_nucleo/board.c @@ -0,0 +1,197 @@ +/* Example board configuration for the STM32WB55 Nucleo dev board */ + +#include +#include +#include "board.h" +#include + +/* SysTick timing */ +volatile uint32_t g_tick = 0; + +void SysTick_Handler(void) +{ + g_tick++; +} + +uint32_t Board_GetTick(void) +{ + return g_tick; +} + +whal_Timeout g_whalTimeout = { + .timeoutTicks = 1000, /* 1s timeout */ + .GetTick = Board_GetTick, +}; + +/* Clock */ +whal_Clock g_whalClock = { + .regmap = { WHAL_STM32WB55_RCC_REGMAP }, +}; + +static const whal_Stm32wb_Rcc_PeriphClk g_periphClks[] = { + {WHAL_STM32WB55_GPIOA_GATE}, + {WHAL_STM32WB55_GPIOB_GATE}, + {WHAL_STM32WB55_UART1_GATE}, +}; +#define PERIPH_CLK_COUNT (sizeof(g_periphClks) / sizeof(g_periphClks[0])) + +/* GPIO */ +whal_Gpio g_whalGpio = { + .regmap = { WHAL_STM32WB55_GPIO_REGMAP }, + + .cfg = &(whal_Stm32wb_Gpio_Cfg) { + .pinCfg = (whal_Stm32wb_Gpio_PinCfg[PIN_COUNT]) { + /* LED: PB5, output, push-pull, low speed, pull-up */ + [LED_PIN] = WHAL_STM32WB_GPIO_PIN( + WHAL_STM32WB_GPIO_PORT_B, 5, WHAL_STM32WB_GPIO_MODE_OUT, + WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL, WHAL_STM32WB_GPIO_SPEED_LOW, + WHAL_STM32WB_GPIO_PULL_UP, 0), + /* UART1 TX: PB6, AF7 */ + [UART_TX_PIN] = WHAL_STM32WB_GPIO_PIN( + WHAL_STM32WB_GPIO_PORT_B, 6, WHAL_STM32WB_GPIO_MODE_ALTFN, + WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL, WHAL_STM32WB_GPIO_SPEED_FAST, + WHAL_STM32WB_GPIO_PULL_UP, 7), + /* UART1 RX: PB7, AF7 */ + [UART_RX_PIN] = WHAL_STM32WB_GPIO_PIN( + WHAL_STM32WB_GPIO_PORT_B, 7, WHAL_STM32WB_GPIO_MODE_ALTFN, + WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL, WHAL_STM32WB_GPIO_SPEED_FAST, + WHAL_STM32WB_GPIO_PULL_UP, 7), + }, + .pinCount = PIN_COUNT, + }, +}; + +/* UART */ +whal_Uart g_whalUart = { + .regmap = { WHAL_STM32WB55_UART1_REGMAP }, + + .cfg = &(whal_Stm32wb_Uart_Cfg) { + .timeout = &g_whalTimeout, + .brr = WHAL_STM32WB_UART_BRR(64000000, 115200), + }, +}; + +/* Timer (SysTick) */ +whal_Timer g_whalTimer = { + .regmap = { WHAL_CORTEX_M4_SYSTICK_REGMAP }, + .driver = WHAL_CORTEX_M4_SYSTICK_DRIVER, + + .cfg = &(whal_SysTick_Cfg) { + .cyclesPerTick = 64000000 / 1000, + .clkSrc = WHAL_SYSTICK_CLKSRC_SYSCLK, + .tickInt = WHAL_SYSTICK_TICKINT_ENABLED, + }, +}; + +/* Flash */ +whal_Flash g_whalFlash = { + .regmap = { WHAL_STM32WB55_FLASH_REGMAP }, + .driver = WHAL_STM32WB55_FLASH_DRIVER, + + .cfg = &(whal_Stm32wb_Flash_Cfg) { + .timeout = &g_whalTimeout, + .startAddr = 0x08000000, + .size = 0x80000, /* 512 KB (upper half reserved for BLE stack) */ + }, +}; + +void Board_WaitMs(size_t ms) +{ + uint32_t startCount = g_tick; + while (g_tick - startCount < ms); +} + +whal_Error Board_Init(void) +{ + whal_Error err; + + /* Flash latency must be set before SYSCLK rises above 16 MHz. */ + err = whal_Stm32wb_Flash_Ext_SetLatency(&g_whalFlash, WHAL_STM32WB_FLASH_LATENCY_3); + if (err) + return err; + + /* MSI 4 MHz -> PLL VCO 128 MHz -> PLLR /2 = 64 MHz -> SYSCLK */ + err = whal_Stm32wb_Rcc_EnableMsi(&g_whalClock, WHAL_STM32WB_RCC_MSIRANGE_4MHz); + if (err) + return err; + + err = whal_Stm32wb_Rcc_EnablePll(&g_whalClock, &(whal_Stm32wb_Rcc_PllCfg){ + .clkSrc = WHAL_STM32WB_RCC_PLLCLK_SRC_MSI, + .n = 32, .m = 0, .r = 1, .q = 0, .p = 0, + }); + if (err) + return err; + + err = whal_Stm32wb_Rcc_SetSysClock(&g_whalClock, WHAL_STM32WB_RCC_SYSCLK_SRC_PLL); + if (err) + return err; + + for (size_t i = 0; i < PERIPH_CLK_COUNT; i++) { + err = whal_Stm32wb_Rcc_EnablePeriphClk(&g_whalClock, &g_periphClks[i]); + if (err) + return err; + } + + err = whal_Gpio_Init(&g_whalGpio); + if (err) + return err; + + err = whal_Uart_Init(&g_whalUart); + if (err) + return err; + + err = whal_Flash_Init(&g_whalFlash); + if (err) + return err; + + err = whal_Timer_Init(&g_whalTimer); + if (err) + return err; + + err = whal_Timer_Start(&g_whalTimer); + if (err) + return err; + + return WHAL_SUCCESS; +} + +whal_Error Board_Deinit(void) +{ + whal_Error err; + + err = whal_Timer_Stop(&g_whalTimer); + if (err) + return err; + + err = whal_Timer_Deinit(&g_whalTimer); + if (err) + return err; + + err = whal_Flash_Deinit(&g_whalFlash); + if (err) + return err; + + err = whal_Uart_Deinit(&g_whalUart); + if (err) + return err; + + err = whal_Gpio_Deinit(&g_whalGpio); + if (err) + return err; + + for (size_t i = PERIPH_CLK_COUNT; i-- > 0; ) { + err = whal_Stm32wb_Rcc_DisablePeriphClk(&g_whalClock, &g_periphClks[i]); + if (err) + return err; + } + + err = whal_Stm32wb_Rcc_SetSysClock(&g_whalClock, WHAL_STM32WB_RCC_SYSCLK_SRC_MSI); + if (err) + return err; + + err = whal_Stm32wb_Rcc_DisablePll(&g_whalClock); + if (err) + return err; + + return WHAL_SUCCESS; +} diff --git a/boards/stm32wb55xx_nucleo/board.h b/boards/stm32wb55xx_nucleo/board.h new file mode 100644 index 0000000..cafcbef --- /dev/null +++ b/boards/stm32wb55xx_nucleo/board.h @@ -0,0 +1,30 @@ +#ifndef BOARD_H +#define BOARD_H + +#include +#include +#include + +extern whal_Clock g_whalClock; +extern whal_Gpio g_whalGpio; +extern whal_Timer g_whalTimer; +extern whal_Uart g_whalUart; +extern whal_Flash g_whalFlash; + +extern whal_Timeout g_whalTimeout; +extern volatile uint32_t g_tick; + +enum { + LED_PIN, + UART_TX_PIN, + UART_RX_PIN, + PIN_COUNT, +}; + +#define BOARD_LED_PIN 0 + +whal_Error Board_Init(void); +whal_Error Board_Deinit(void); +void Board_WaitMs(size_t ms); + +#endif /* BOARD_H */ diff --git a/boards/stm32wb55xx_nucleo/board.mk b/boards/stm32wb55xx_nucleo/board.mk new file mode 100644 index 0000000..34a84a6 --- /dev/null +++ b/boards/stm32wb55xx_nucleo/board.mk @@ -0,0 +1,38 @@ +_BOARD_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) + +PLATFORM = stm32wb +TESTS ?= clock gpio uart + +GCC = $(GCC_PATH)arm-none-eabi-gcc +LD = $(GCC_PATH)arm-none-eabi-ld +OBJCOPY = $(GCC_PATH)arm-none-eabi-objcopy + +CFLAGS += -Wall -Werror $(INCLUDE) -g3 \ + -ffreestanding -nostdlib -mcpu=cortex-m4 \ + -DPLATFORM_STM32WB -MMD -MP \ + -DWHAL_CFG_STM32WB_GPIO_DIRECT_API_MAPPING \ + -DWHAL_CFG_STM32WB_UART_DIRECT_API_MAPPING \ + -DWHAL_CFG_STM32WB_RCC_DIRECT_API_MAPPING +LDFLAGS = --omagic -static + +LINKER_SCRIPT ?= $(_BOARD_DIR)/linker.ld + +INCLUDE += -I$(_BOARD_DIR) + +BOARD_SOURCE = $(_BOARD_DIR)/ivt.c +BOARD_SOURCE += $(_BOARD_DIR)/board.c +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/timer.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/flash.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/stm32wb_*.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/systick.c) + +# Flash via openocd: make flash BOARD= IMAGE= +OPENOCD ?= /opt/openocd/bin/openocd +OPENOCD_INTERFACE ?= interface/stlink.cfg +OPENOCD_TARGET ?= target/stm32wbx.cfg + +.PHONY: flash +flash: + @test -n "$(IMAGE)" || { echo "IMAGE= required" >&2; exit 1; } + $(OPENOCD) -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) \ + -c "program $(IMAGE) verify reset exit" diff --git a/boards/stm32wb55xx_nucleo/ivt.c b/boards/stm32wb55xx_nucleo/ivt.c new file mode 100644 index 0000000..a8b8d89 --- /dev/null +++ b/boards/stm32wb55xx_nucleo/ivt.c @@ -0,0 +1,216 @@ +#include +#include + +extern uint32_t _estack[]; +extern uint32_t _sidata[]; +extern uint32_t _sdata[]; +extern uint32_t _edata[]; +extern uint32_t _sbss[]; +extern uint32_t _ebss[]; + +extern void main(); + +void __attribute__((naked,noreturn)) Default_Handler() +{ + while(1); +} + +void Reset_Handler() __attribute__((weak)); +void NMI_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void HardFault_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void MemManage_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void BusFault_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void UsageFault_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SVC_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DebugMon_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void PendSV_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SysTick_Handler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void WWDG_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void PVD_PVM_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TAMP_STAMP_LSECSS_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void RTC_WKUP_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void FLASH_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void RCC_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI0_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI3_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI4_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel3_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel4_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel5_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel6_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA1_Channel7_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void ADC1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void USB_HP_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void USB_LP_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void C2SEV_PWR_C2H_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void COMP_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI9_5_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM1_BRK_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM1_UP_TIM16_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM1_TRG_COM_TIM17_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM1_CC_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TIM2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void PKA_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void I2C1_EV_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void I2C1_ER_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void I2C3_EV_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void I2C3_ER_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SPI1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SPI2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void USART1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void LPUART1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void SAI1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void TSC_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void EXTI15_10_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void RTC_Alarm_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void CRS_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void IPCC_C1_RX_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void IPCC_C1_TX_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void HSEM_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void LPTIM1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void LPTIM2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void LCD_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void QUADSPI_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void AES1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void AES2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void RNG_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void FPU_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel1_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel2_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel3_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel4_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel5_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel6_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMA2_Channel7_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); +void DMAMUX1_OVR_IRQHandler() __attribute__((weak, noreturn, alias("Default_Handler"))); + +#define RESERVED Default_Handler + +void (* const interrupt_vector_table[])() __attribute__((section(".isr_vector"))) = { + (void (*)())_estack, + Reset_Handler, + NMI_Handler, + HardFault_Handler, + MemManage_Handler, + BusFault_Handler, + UsageFault_Handler, + RESERVED, + RESERVED, + RESERVED, + RESERVED, + SVC_Handler, + DebugMon_Handler, + RESERVED, + PendSV_Handler, + SysTick_Handler, + WWDG_IRQHandler, + PVD_PVM_IRQHandler, + TAMP_STAMP_LSECSS_IRQHandler, + RTC_WKUP_IRQHandler, + FLASH_IRQHandler, + RCC_IRQHandler, + EXTI0_IRQHandler, + EXTI1_IRQHandler, + EXTI2_IRQHandler, + EXTI3_IRQHandler, + EXTI4_IRQHandler, + DMA1_Channel1_IRQHandler, + DMA1_Channel2_IRQHandler, + DMA1_Channel3_IRQHandler, + DMA1_Channel4_IRQHandler, + DMA1_Channel5_IRQHandler, + DMA1_Channel6_IRQHandler, + DMA1_Channel7_IRQHandler, + ADC1_IRQHandler, + USB_HP_IRQHandler, + USB_LP_IRQHandler, + C2SEV_PWR_C2H_IRQHandler, + COMP_IRQHandler, + EXTI9_5_IRQHandler, + TIM1_BRK_IRQHandler, + TIM1_UP_TIM16_IRQHandler, + TIM1_TRG_COM_TIM17_IRQHandler, + TIM1_CC_IRQHandler, + TIM2_IRQHandler, + PKA_IRQHandler, + I2C1_EV_IRQHandler, + I2C1_ER_IRQHandler, + I2C3_EV_IRQHandler, + I2C3_ER_IRQHandler, + SPI1_IRQHandler, + SPI2_IRQHandler, + USART1_IRQHandler, + LPUART1_IRQHandler, + SAI1_IRQHandler, + TSC_IRQHandler, + EXTI15_10_IRQHandler, + RTC_Alarm_IRQHandler, + CRS_IRQHandler, + PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler, + IPCC_C1_RX_IRQHandler, + IPCC_C1_TX_IRQHandler, + HSEM_IRQHandler, + LPTIM1_IRQHandler, + LPTIM2_IRQHandler, + LCD_IRQHandler, + QUADSPI_IRQHandler, + AES1_IRQHandler, + AES2_IRQHandler, + RNG_IRQHandler, + FPU_IRQHandler, + DMA2_Channel1_IRQHandler, + DMA2_Channel2_IRQHandler, + DMA2_Channel3_IRQHandler, + DMA2_Channel4_IRQHandler, + DMA2_Channel5_IRQHandler, + DMA2_Channel6_IRQHandler, + DMA2_Channel7_IRQHandler, + DMAMUX1_OVR_IRQHandler, +}; + +void *memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *d = dest; + const unsigned char *s = src; + + for (size_t i = 0; i < n; i++) + d[i] = s[i]; + + return dest; +} + +void *memset(void *s, int c, size_t n) +{ + unsigned char *p = s; + unsigned char v = (unsigned char)c; + + for (size_t i = 0; i < n; i++) + p[i] = v; + + return s; +} + +void __attribute__((naked)) Reset_Handler() +{ + __asm__("ldr r0, =_estack\n\t" + "mov sp, r0"); + + // Copy data section from flash memory to ram + uint32_t data_section_size = _edata - _sdata; + memcpy(_sdata, _sidata, data_section_size*4); + + // Zero out bss + uint32_t bss_section_size = _ebss - _sbss; + memset(_sbss, 0, bss_section_size*4); + + // Set Interrupt Vector Table Offset + uint32_t *vtor = (uint32_t *)0xE000ED08; + *vtor = (uint32_t)interrupt_vector_table; + + main(); +} diff --git a/boards/stm32wb55xx_nucleo/linker.ld b/boards/stm32wb55xx_nucleo/linker.ld new file mode 100644 index 0000000..5de0dd2 --- /dev/null +++ b/boards/stm32wb55xx_nucleo/linker.ld @@ -0,0 +1,120 @@ +ENTRY(Reset_Handler) + +_estack = 0x20030000; /* end of RAM */ +_Min_Stack_Size = 0x500; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K +RAM1 (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00030000 +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data goes into FLASH */ + .rodata : + { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM1 AT> FLASH + + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss section */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM1 + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >RAM1 + +} diff --git a/docs/adding_a_board.md b/docs/adding_a_board.md new file mode 100644 index 0000000..619aad7 --- /dev/null +++ b/docs/adding_a_board.md @@ -0,0 +1,182 @@ +# Adding a New Board + +This guide covers adding a new board configuration to wolfHAL. + +## Overview + +A board ties a platform to concrete hardware by defining peripheral instances, +pin assignments, clock settings, and startup code. Each board lives in its own +directory under `boards/` named `_/`. + +## Required Files + +### board.h + +Exports global peripheral instances and board-specific constants: + +```c +#pragma once + +#include + +extern whal_Clock g_whalClock; +extern whal_Gpio g_whalGpio; +extern whal_Uart g_whalUart; +extern whal_Timer g_whalTimer; +extern whal_Flash g_whalFlash; +extern whal_Timeout g_whalTimeout; + +#define BOARD_LED_PIN 0 + +whal_Error Board_Init(void); +whal_Error Board_Deinit(void); +void Board_WaitMs(size_t ms); +``` + +### board.c + +Defines global device instances with their configurations and implements +`Board_Init()` and `Board_Deinit()`. + +`Board_Init()` is responsible for initializing all peripherals in dependency +order. For example, the clock controller must be initialized before peripherals +that depend on it, and a power supply controller (if present) may need to come +before the clock. It should return `WHAL_SUCCESS` on success or an error code +on failure. + +`Board_Deinit()` tears down peripherals in reverse order. + +```c +#include "board.h" +#include + +static whal_Myplatform_Gpio_PinCfg pinCfg[] = { /* ... */ }; + +static whal_Myplatform_Gpio_Cfg gpioConfig = { + .pinCfg = pinCfg, + .pinCount = sizeof(pinCfg) / sizeof(pinCfg[0]), +}; + +whal_Gpio g_whalGpio = { + .regmap = { WHAL_MYPLATFORM_GPIO_REGMAP }, + .cfg = &gpioConfig, +}; + +static const whal_Myplatform_Clock_PeriphClk g_periphClks[] = { + {MY_PLATFORM_GPIO_CLOCK}, + {MY_PLATFORM_UART_CLOCK}, +}; +#define PERIPH_CLK_COUNT \ + (sizeof(g_periphClks) / sizeof(g_periphClks[0])) + +whal_Error Board_Init(void) +{ + whal_Error err; + + /* Bring up the clock tree imperatively. The chip's clock driver + * exposes Enable*/Disable*/Set* helpers; boards call them in order. + * The exact sequence is chip-specific — see the chip's clock header. */ + err = whal_Myplatform_Clock_EnableOsc(&g_whalClock, + &(whal_Myplatform_Clock_OscCfg){WHAL_MYPLATFORM_CLOCK_OSC0_CFG}); + if (err) + return err; + err = whal_Myplatform_Clock_SetSysClock(&g_whalClock, + WHAL_MYPLATFORM_CLOCK_SYSCLK_SRC_OSC0); + if (err) + return err; + + for (size_t i = 0; i < PERIPH_CLK_COUNT; i++) { + err = whal_Myplatform_Clock_EnablePeriphClk(&g_whalClock, + &g_periphClks[i]); + if (err) + return err; + } + + err = whal_Gpio_Init(&g_whalGpio); + if (err) + return err; + + err = whal_Uart_Init(&g_whalUart); + if (err) + return err; + + err = whal_Timer_Init(&g_whalTimer); + if (err) + return err; + + err = whal_Timer_Start(&g_whalTimer); + if (err) + return err; + + return WHAL_SUCCESS; +} +``` + +```c +whal_Error Board_Deinit(void) +{ + whal_Error err; + + whal_Timer_Stop(&g_whalTimer); + whal_Timer_Deinit(&g_whalTimer); + whal_Uart_Deinit(&g_whalUart); + whal_Gpio_Deinit(&g_whalGpio); + + for (size_t i = PERIPH_CLK_COUNT; i-- > 0; ) { + err = whal_Myplatform_Clock_DisablePeriphClk(&g_whalClock, + &g_periphClks[i]); + if (err) + return err; + } + + return WHAL_SUCCESS; +} +``` + +### board.mk + +Defines the toolchain, compiler flags, and source file list: + +```makefile +_BOARD_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) + +PLATFORM = myplatform +TESTS = gpio clock uart flash timer + +GCC = arm-none-eabi-gcc +LD = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy + +CFLAGS = -mcpu=cortex-m4 -mthumb -Os -Wall -MMD $(INCLUDE) -I$(_BOARD_DIR) \ + -DWHAL_CFG_GPIO_API_MAPPING_MYPLATFORM \ + -DWHAL_CFG_CLOCK_API_MAPPING_MYPLATFORM \ + -DWHAL_CFG_UART_API_MAPPING_MYPLATFORM +LDFLAGS = -mcpu=cortex-m4 -mthumb -nostdlib -lgcc + +LINKER_SCRIPT = $(_BOARD_DIR)/linker.ld + +INCLUDE += -I$(_BOARD_DIR) + +BOARD_SOURCE = $(_BOARD_DIR)/board.c +BOARD_SOURCE += $(_BOARD_DIR)/ivt.c +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*.c) +# Dispatch sources for mapped types are excluded; keep dispatch sources +# for types whose drivers don't use direct API mapping (e.g., flash on +# boards that mix on-chip flash with external SPI NOR). +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/timer.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/flash.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/myplatform_*.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/systick.c) +``` + +### linker.ld + +Linker script defining the memory layout for your board's MCU. Must define +FLASH and RAM regions, place `.isr_vector` at the start of FLASH, and set up +`.text`, `.data`, and `.bss` sections. + +### ivt.c (ARM targets) + +Interrupt vector table and `Reset_Handler`. The reset handler copies `.data` +from FLASH to RAM, zeroes `.bss`, and calls `main()`. This file is specific to +ARM Cortex-M targets. Other architectures will need their own startup code. diff --git a/docs/adding_a_test.md b/docs/adding_a_test.md new file mode 100644 index 0000000..0bc8b2d --- /dev/null +++ b/docs/adding_a_test.md @@ -0,0 +1,130 @@ +# Adding a Test + +This guide covers how to add hardware tests to the wolfHAL test suite. + +## Test Framework + +Tests use the macros defined in `tests/test.h`: + +- `WHAL_TEST_SUITE_START(name)` — print a suite header +- `WHAL_TEST_SUITE_END()` — end a suite +- `WHAL_TEST(fn)` — run a test function and report PASS/FAIL +- `WHAL_ASSERT_EQ(a, b)` — assert equal, prints line number and values on + failure, then returns from the test function +- `WHAL_ASSERT_NEQ(a, b)` — assert not equal +- `WHAL_ASSERT_MEM_EQ(a, b, len)` — assert memory regions are equal, reports + the byte offset of the first mismatch +- `WHAL_SKIP()` — skip the current test (marks it as SKIP instead of + PASS/FAIL and returns early) + +Test functions take no arguments and return void. On assertion failure the +function returns early and the test is marked as failed. + +## Generic Tests + +Generic tests exercise the wolfHAL API using only the public interface. They +rely on global peripheral instances and board constants from `board.h` (e.g., +`g_whalGpio`, `BOARD_LED_PIN`), so they run on any board that supports the +device type without modification. + +Generic tests should verify the API contract — that functions return the +expected error codes, that Set followed by Get produces the correct value, that +data written can be read back, etc. They should not include platform-specific +headers or make assumptions about register layouts. + +Create `tests//test_.c`: + +```c +#include +#include "board.h" +#include "test.h" + +static void Test_Foo_BasicOperation(void) +{ + WHAL_ASSERT_EQ(whal_Foo_Init(&g_whalFoo), WHAL_SUCCESS); +} + +void whal_Test_Foo(void) +{ + WHAL_TEST_SUITE_START("foo"); + WHAL_TEST(Test_Foo_BasicOperation); + WHAL_TEST_SUITE_END(); +} +``` + +The entry point function must be named `whal_Test_` (capitalized device +name). Individual test functions are static and named `Test__`. + +## Platform-Specific Tests + +Platform-specific tests verify that the driver correctly manipulates hardware +registers. They include platform-specific headers and read registers directly +using `whal_Reg_Get()` to confirm that Init configured the right fields, that +Set wrote to the correct output register, etc. + +These tests are automatically detected and compiled when building for the +matching platform. The Makefile looks for files named +`test__.c` and compiles them when the board's `PLATFORM` +variable matches the `` portion of the filename. + +Create `tests//test__.c`: + +```c +#include +#include /_.h> +#include +#include "board.h" +#include "test.h" + +static void Test_Foo_SomeRegister(void) +{ + size_t val = 0; + whal_Reg_Get(g_whalFoo.regmap.base, REG_OFFSET, MASK, POS, &val); + WHAL_ASSERT_EQ(val, EXPECTED); +} + +void whal_Test_Foo_Platform(void) +{ + WHAL_TEST(Test_Foo_SomeRegister); +} +``` + +The entry point must be named `whal_Test__Platform`. No +`WHAL_TEST_SUITE_START` is needed — platform tests run as a continuation of +the generic suite. + +## Hooking into main.c + +Add your test to `tests/main.c`: + +1. Add a conditional declaration block: + +```c +#ifdef WHAL_TEST_ENABLE_FOO +void whal_Test_Foo(void); +#ifdef WHAL_TEST_ENABLE_FOO_PLATFORM +void whal_Test_Foo_Platform(void); +#endif +#endif +``` + +2. Add the corresponding call block in `main()`: + +```c +#ifdef WHAL_TEST_ENABLE_FOO + whal_Test_Foo(); +#ifdef WHAL_TEST_ENABLE_FOO_PLATFORM + whal_Test_Foo_Platform(); +#endif +#endif +``` + +## Enabling on Boards + +Add `foo` to the `TESTS` list in each board's `board.mk` that supports this +device type. The Makefile automatically: + +- Defines `-DWHAL_TEST_ENABLE_FOO` so the test compiles in +- Compiles `foo/test_foo.c` +- Detects and compiles `foo/test__foo.c` if it exists (and defines + `-DWHAL_TEST_ENABLE_FOO_PLATFORM`) diff --git a/docs/adding_an_example.md b/docs/adding_an_example.md new file mode 100644 index 0000000..8dc7827 --- /dev/null +++ b/docs/adding_an_example.md @@ -0,0 +1,104 @@ +# Adding a New Example + +This guide covers adding a new example application to wolfHAL. + +## Overview + +Each example is a standalone application with its own `main.c` and `Makefile`. +Board support is pulled in from the top-level `boards/` directory. + +Examples may be platform-specific if they exercise platform-specific features, +but you should aim to make them as general as possible so they can run on +multiple boards. + +## Step 1: Create the Directory + +Create `examples//` with two files: + +### main.c + +```c +#include +#include "board.h" + +void main(void) +{ + if (Board_Init() != WHAL_SUCCESS) + goto loop; + + /* Application code using g_whal* peripherals from board.h */ + +loop: + while (1); +} +``` + +Key points: +- Include `wolfHAL/wolfHAL.h` for the wolfHAL API +- Include `board.h` for peripheral instances and board constants +- Call `Board_Init()` before using any peripherals +- Use the global peripheral instances (e.g., `g_whalGpio`, `g_whalUart`) +- Use board constants (e.g., `BOARD_LED_PIN`) for portability across boards + +### Makefile + +```makefile +WHAL_DIR = $(CURDIR)/../.. + +BOARD ?= stm32wb55xx_nucleo +BOARD_DIR = $(WHAL_DIR)/boards/$(BOARD) +BUILD_DIR = build/$(BOARD) + +INCLUDE = -I$(WHAL_DIR) + +include $(BOARD_DIR)/board.mk + +SOURCE = main.c +SOURCE += $(BOARD_SOURCE) + +OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(SOURCE)) +DEPENDS = $(OBJECTS:.o=.d) + +all: $(BUILD_DIR)/$(notdir $(CURDIR)).bin + +$(BUILD_DIR)/%.o: %.c Makefile + @mkdir -p $(dir $@) + $(GCC) $(CFLAGS) -c -o $@ $< + +.SECONDARY: +$(BUILD_DIR)/%.elf: $(OBJECTS) $(LINKER_SCRIPT) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS) -T $(LINKER_SCRIPT) -o $@ $(OBJECTS) + +$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf + $(OBJCOPY) $^ -O binary $@ + +.PHONY: clean +clean: + rm -rf build + +-include $(DEPENDS) +``` + +This Makefile is the same for all examples. The board's `board.mk` provides +the toolchain, flags, and platform sources. + +## Step 2: Build + +```sh +cd examples/ +make BOARD= +``` + +The output binary is placed in `build//.bin`. + +## Adding Extra Source Files + +If your example needs additional source files, add them to `SOURCE` in the +Makefile: + +```makefile +SOURCE = main.c +SOURCE += helper.c +SOURCE += $(BOARD_SOURCE) +``` diff --git a/docs/getting_started.md b/docs/getting_started.md new file mode 100644 index 0000000..047c4ea --- /dev/null +++ b/docs/getting_started.md @@ -0,0 +1,381 @@ +# Getting Started + +This guide walks through integrating wolfHAL into a bare-metal embedded project. + +## Project Layout + +A typical project using wolfHAL looks like this: + +``` +my_project/ + wolfHAL/ wolfHAL repository (submodule, copy, etc.) + boards/ + / + board.h Peripheral externs and board constants + board.c Device instances, configuration, and Board_Init + ivt.c Interrupt vector table and Reset_Handler + linker.ld Linker script for your MCU + board.mk Toolchain and source list + src/ + main.c Application entry point + ... Additional application sources + Makefile +``` + +The key idea is that your project provides the board-level glue (device +instances, pin assignments, clock config, startup code) and wolfHAL provides +the driver implementations and API. + +## Adding wolfHAL to Your Project + +wolfHAL is a source-level library with no external dependencies beyond a C +compiler and standard headers (`stdint.h`, `stddef.h`). To use it: + +1. Add the wolfHAL repository root to your include path (e.g., `-I/path/to/wolfHAL`) +2. Compile the generic dispatch sources for the modules you need: + +``` +src/gpio/gpio.c +src/uart/uart.c +src/flash/flash.c +... +``` + +3. Compile the platform-specific driver sources for your target: + +``` +src/clock/_rcc.c +src/gpio/_gpio.c +src/uart/_uart.c +src/flash/_flash.c +... +``` + +You only need to include the modules and platform drivers your project actually +uses. + +## The Device Model + +wolfHAL has three driver categories — platform, peripheral, and board-level +(see `docs/writing_a_driver.md`). Their device structs have slightly +different shapes. + +### Platform drivers + +Platform drivers operate directly on SoC registers. The device struct has +three fields: + +```c +struct whal_Gpio { + const whal_Regmap regmap; /* base address and size */ + const whal_GpioDriver *driver; /* vtable of function pointers */ + const void *cfg; /* platform-specific configuration */ +}; +``` + +- **regmap** — identifies the peripheral's memory-mapped register block +- **driver** — points to the driver implementation (the vtable) +- **cfg** — points to a driver-specific configuration struct that the + driver reads during Init + +Platform headers provide `_REGMAP` and `_DRIVER` macros for each peripheral, +so you only need to fill in the `cfg`: + +```c +#include + +whal_Gpio g_whalGpio = { + .regmap = { WHAL_STM32WB55_GPIO_REGMAP }, + .driver = WHAL_STM32WB55_GPIO_DRIVER, + .cfg = &gpioConfig, +}; +``` + +When direct API mapping is active for a device type, the `.driver` field is +omitted since calls go directly to the driver implementation. + +### Peripheral drivers + +Peripheral drivers talk to external chips over a bus (SPI, I2C, MDIO). The +device struct shape mirrors platform drivers — same vtable + cfg pattern — +but instead of a register block the device carries the bus handle and any +chip-specific addressing inside its cfg: + +```c +whal_Flash g_whalSpiFlash = { + .driver = WHAL_SPI_NOR_W25Q64_FLASH_DRIVER, + .cfg = &(whal_SpiNor_W25q64_Cfg) { + .spi = &g_whalSpi, /* underlying bus driver */ + .csPin = SPI_FLASH_CS_PIN, + /* ...chip-specific fields... */ + }, +}; +``` + +The application calls the same generic API (`whal_Flash_Read(&g_whalSpiFlash, ...)`) +whether the flash is on-chip (platform driver) or external SPI NOR (peripheral +driver) — the device pointer determines which implementation runs. + +### Board-level drivers + +Board-level drivers (clock, power) only expose chip-specific helpers — no +vtable, no generic `whal__*` API. The device struct is just a regmap: + +```c +whal_Clock g_whalClock = { + .regmap = { WHAL_STM32WB55_RCC_REGMAP }, +}; +``` + +There is no `.driver` or `.cfg` field to fill in. Boards bring up the clock +tree imperatively in `Board_Init` by calling chip-specific helpers like +`whal_Stm32wb_Rcc_EnableOsc()`, `EnablePll()`, `SetSysClock()`, etc., in the +required order. Applications that need to trigger board-level behavior at +runtime (e.g., enter low-power mode) call a board-provided wrapper such as +`Board_Sleep()` rather than a generic `whal_X` function. + +## Configuring Devices + +Each platform driver defines its own configuration struct with the parameters +it needs. For example, a GPIO driver takes a pin configuration table; the +platform usually provides a `WHAL__GPIO_PIN(...)` macro to populate +each entry compactly: + +```c +whal_Gpio g_whalGpio = { + .regmap = { WHAL_STM32WB55_GPIO_REGMAP }, + /* .driver: direct API mapping */ + + .cfg = &(whal_Stm32wb_Gpio_Cfg) { + .pinCfg = (whal_Stm32wb_Gpio_PinCfg[PIN_COUNT]) { + [LED_PIN] = WHAL_STM32WB_GPIO_PIN( + WHAL_STM32WB_GPIO_PORT_B, 5, WHAL_STM32WB_GPIO_MODE_OUT, + WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL, WHAL_STM32WB_GPIO_SPEED_LOW, + WHAL_STM32WB_GPIO_PULL_UP, 0), + }, + .pinCount = PIN_COUNT, + }, +}; +``` + +A UART driver typically takes a pre-computed baud rate register value and a +timeout: + +```c +whal_Uart g_whalUart = { + .regmap = { WHAL_STM32WB55_UART1_REGMAP }, + /* .driver: direct API mapping */ + + .cfg = &(whal_Stm32wb_Uart_Cfg) { + .brr = WHAL_STM32WB_UART_BRR(64000000, 115200), + .timeout = &g_whalTimeout, + }, +}; +``` + +The `.driver: direct API mapping` comment indicates this peripheral is using +the optimization path described above — calls to `whal_Uart_*` link directly +to the chip-specific implementation, no vtable dispatch. + +See the platform-specific headers in `wolfHAL//` for the full set +of configuration options for each driver, and the example boards in `boards/` +for full instantiations. + +## Initialization + +The board is responsible for initializing peripherals in dependency order. +Drivers do not enable their own clocks or power supplies — the board must +handle these prerequisites explicitly before calling a driver's Init. + +A typical initialization sequence: + +1. Do any pre-clock-controller initialization (e.g., flash wait states, + power supplies) +2. Bring up the clock tree (oscillators, optional PLL, sysclk source) +3. Enable peripheral clocks +4. Initialize peripheral drivers +5. Start timers + +The chip's clock driver exposes `Enable*`/`Disable*`/`Set*` helpers that +the board calls in order. There is no generic `whal_Clock_Init` walker — +clock-tree shape varies too much across vendors to abstract. + +```c +static const whal_Myplatform_Clock_PeriphClk g_periphClks[] = { + {MY_PLATFORM_GPIOB_CLOCK}, + {MY_PLATFORM_UART1_CLOCK}, +}; +#define PERIPH_CLK_COUNT \ + (sizeof(g_periphClks) / sizeof(g_periphClks[0])) + +whal_Error Board_Init(void) +{ + whal_Error err; + + /* Bring up clocks (chip-specific helpers, called in order) */ + err = whal_Myplatform_Clock_EnableOsc(&g_whalClock, + &(whal_Myplatform_Clock_OscCfg){WHAL_MYPLATFORM_CLOCK_OSC0_CFG}); + if (err) + return err; + err = whal_Myplatform_Clock_SetSysClock(&g_whalClock, + WHAL_MYPLATFORM_CLOCK_SYSCLK_SRC_OSC0); + if (err) + return err; + + /* Enable peripheral clocks */ + for (size_t i = 0; i < PERIPH_CLK_COUNT; i++) { + err = whal_Myplatform_Clock_EnablePeriphClk(&g_whalClock, + &g_periphClks[i]); + if (err) + return err; + } + + /* Initialize peripherals */ + err = whal_Gpio_Init(&g_whalGpio); + if (err) + return err; + + err = whal_Uart_Init(&g_whalUart); + if (err) + return err; + + err = whal_Timer_Init(&g_whalTimer); + if (err) + return err; + + err = whal_Timer_Start(&g_whalTimer); + if (err) + return err; + + return WHAL_SUCCESS; +} +``` + +See the board examples in `boards/` for complete initialization sequences +including platform-specific steps. + +## Using the API + +After initialization, use the wolfHAL API to interact with peripherals: + +```c +#include +#include "board.h" + +void main(void) +{ + if (Board_Init() != WHAL_SUCCESS) + while (1); + + while (1) { + whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 1); + whal_Uart_Send(&g_whalUart, "Hello!\r\n", 8); + Board_WaitMs(1000); + + whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 0); + Board_WaitMs(1000); + } +} +``` + +All API functions return `whal_Error`. Check for `WHAL_SUCCESS` to confirm the +operation completed. The error codes are: + +| Code | Meaning | +|------|---------| +| `WHAL_SUCCESS` | Operation completed successfully | +| `WHAL_EINVAL` | Invalid argument (null device pointer, null data pointer) | +| `WHAL_ENOTREADY` | Resource is busy or not yet available | +| `WHAL_EHARDWARE` | Hardware error (e.g., RNG entropy failure) | +| `WHAL_ETIMEOUT` | Operation timed out waiting for hardware | +| `WHAL_ENOTSUP` | Operation or parameter not supported by this driver/hardware | + +## Optimizing for Size + +wolfHAL gives you several ways to reduce code size depending on how much +control you want. + +### Direct API Mapping + +Each driver source (platform or peripheral) provides an `#ifdef` block that +renames its driver functions to the top-level API names. When the +corresponding `WHAL_CFG__API_MAPPING_` flag is defined, the +driver file itself provides the definition of the top-level API — no +wrapper, no vtable indirection, no runtime null-check. + +For example, `-DWHAL_CFG_UART_API_MAPPING_STM32WB` causes +`src/uart/stm32wb_uart.c` to emit external symbols named `whal_Uart_Init`, +`whal_Uart_Deinit`, `whal_Uart_Send`, and `whal_Uart_Recv`, each bound to +the polled STM32WB UART driver body. Application code calling +`whal_Uart_Send(&dev, buf, sz)` links directly to the driver. + +The same pattern works for peripheral drivers: enabling +`WHAL_CFG_SDHC_SPI_BLOCK_DIRECT_API_MAPPING`, for instance, makes the +external SD-card-over-SPI driver provide the top-level `whal_Block_*` API +symbols directly. + +Direct API mapping is only safe when a single driver of that type is +present in the build. Boards that combine multiple drivers of the same type +(e.g., on-chip flash + external SPI NOR flash) cannot enable mapping for +that type — they keep the vtable dispatch so both drivers can be linked +simultaneously. + +**The dispatch source `src//.c` must not be compiled when the +corresponding mapping flag is active.** Both the dispatch source and the +mapped driver source provide the same top-level symbols, which would cause +a multiple-definition link error. Exclude the dispatch source from the +board's source list. + +Only one mapping flag may be active per device type per build. + +### Custom Vtables + +The platform drivers provide a pre-built vtable with all operations populated. +If you only use a subset of a driver's functionality, you can define your own +vtable that only includes the functions you need: + +```c +static const whal_GpioDriver myGpioDriver = { + .Init = whal_Stm32wb_Gpio_Init, + .Deinit = whal_Stm32wb_Gpio_Deinit, + .Set = whal_Stm32wb_Gpio_Set, + /* Get left as NULL — calls return WHAL_ENOTSUP, saves pulling in that code */ +}; + +whal_Gpio g_whalGpio = { + .regmap = { .base = 0x48000000, .size = 0x2000 }, + .driver = &myGpioDriver, + .cfg = &gpioConfig, +}; +``` + +With link-time optimization (`-flto`) or garbage collection (`-ffunction-sections` ++ `-Wl,--gc-sections`), any driver functions not referenced through the vtable +will be stripped from the final binary. + +### Calling Driver Functions Directly + +For maximum control, you can skip the vtable entirely and call the underlying +platform driver functions directly: + +```c +#include + +whal_Stm32wb_Gpio_Init(&g_whalGpio); +whal_Stm32wb_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 1); +``` + +This eliminates the vtable indirection and lets the compiler inline or optimize +the calls more aggressively. + +Register-level drivers do not call other drivers internally, so this works +without any caveats. Peripheral drivers (e.g., SPI flash) still call their +bus driver through the vtable. + +## Next Steps + +- See `boards/` for complete board configuration examples +- See [Writing a Driver](writing_a_driver.md) for how to add support for a new + platform +- See [Adding a Board](adding_a_board.md) for how to create a board + configuration for your hardware diff --git a/docs/writing_a_driver.md b/docs/writing_a_driver.md new file mode 100644 index 0000000..38b48dd --- /dev/null +++ b/docs/writing_a_driver.md @@ -0,0 +1,1299 @@ +# Writing a Driver + +This guide covers how to implement wolfHAL drivers. + +## Driver Categories + +wolfHAL has three categories of drivers: + +**Platform drivers** operate directly on SoC hardware registers at fixed memory +addresses. They are tied to a specific microcontroller family and use the +`whal_Reg_*` helpers to read and write peripheral registers. Examples: +`stm32wb_uart`, `stm32wb_gpio`, `stm32wb_dma`. + +**Peripheral drivers** communicate with external chips over a bus (SPI, I2C, +MDIO). They call platform drivers (e.g., `whal_Spi_SendRecv`) to reach the +hardware and are portable across any SoC that provides the required bus. +Examples: `spi_nor_flash` (SPI flash), `sdhc_spi_block` (SD card over SPI), +`bmi270_sensor` (IMU over I2C), `lan8742a_eth_phy` (Ethernet PHY over MDIO). + +**Board-level drivers** only expose a chip-specific interface — no vtable, no +generic `whal__*` API. Their parameters and operations are chip-specific +enough that abstracting them serves no portable consumer; every caller lives +in `board.c`. Applications reach board-level behavior through +`Board_()` wrapper functions in `board.c` when needed. A board-level +driver can be either platform (e.g., the on-MCU clock controller) or +peripheral (e.g., an external clock generator IC over I2C, or a PMIC). Examples: +clock, power. + +Platform and peripheral drivers share the same vtable interface — the +application calls `whal_Flash_Read()` whether the flash is on-chip (platform +driver) or external SPI NOR (peripheral driver). Board-level drivers are the +exception: there is no generic API for applications to call. + +## Common Pattern + +Platform and peripheral drivers follow the same structure: + +1. A **driver vtable** — a struct of function pointers that the driver must + populate. +2. A **device struct** — contains a register map, a pointer to the driver + vtable, and a pointer to driver-specific configuration. +3. A **generic dispatch layer** — validates inputs and calls through the vtable. + +To write a driver for a device type, you implement the functions defined in that +type's vtable and expose them as a const driver instance. + +Board-level drivers follow a different pattern: no vtable, no generic +`whal__*` API, no `DIRECT_API_MAPPING`. They expose chip-specific +helper functions (e.g., `whal___EnableOsc`) that boards call +directly. The "Clock" section below is the canonical example. The rest of +this "Common Pattern" section applies to platform and peripheral drivers +only. + +### File Layout + +For a platform driver implementing device type `foo` on platform `myplatform`: + +- `wolfHAL/foo/myplatform_foo.h` — configuration types and driver extern +- `src/foo/myplatform_foo.c` — driver implementation and vtable definition + +For a peripheral driver implementing device type `foo` for chip `mychip`: + +- `wolfHAL/foo/mychip_foo.h` — configuration types and driver extern +- `src/foo/mychip_foo.c` — driver implementation and vtable definition + +For a board-level driver implementing device type `foo` on platform `myplatform`: + +- `wolfHAL/foo/myplatform_foo.h` — types, enums, descriptor macros, and + chip-specific helper declarations (no driver extern, no `_DRIVER` macro) +- `src/foo/myplatform_foo.c` — chip-specific helper implementations (no + vtable, no `DIRECT_API_MAPPING` block) + +### Driver Vtable + +Every vtable includes `Init` and `Deinit`. The remaining functions are specific +to the device type. All functions receive a pointer to the device instance as +their first argument and return `whal_Error`. + +```c +const whal_FooDriver whal_Myplatform_Foo_Driver = { + .Init = whal_Myplatform_Foo_Init, + .Deinit = whal_Myplatform_Foo_Deinit, + /* device-specific operations */ +}; +``` + +### Direct API Mapping + +Every driver should include a rename-at-definition block that lets the build +system map chip-specific function names to the top-level API. Place this +`#ifdef` block after your register defines and before the first function +definition: + +```c +#ifdef WHAL_CFG_MYPLATFORM_FOO_DIRECT_API_MAPPING +#define whal_Myplatform_Foo_Init whal_Foo_Init +#define whal_Myplatform_Foo_Deinit whal_Foo_Deinit +/* ... one #define per mapped function ... */ +#endif +``` + +When the flag is defined, the preprocessor renames each function to its +generic API name at the definition site. The driver source uses chip-specific +names everywhere — the macros handle the rest. The flag name is +`WHAL_CFG__DIRECT_API_MAPPING` where `` is the driver's full +prefix in upper case, e.g., `WHAL_CFG_STM32WB_UART_DIRECT_API_MAPPING` for a +platform driver or `WHAL_CFG_BMI270_SENSOR_DIRECT_API_MAPPING` for a +peripheral driver. + +Wrap the vtable in `#ifndef` since it is unused when mapping is active: + +```c +#ifndef WHAL_CFG_MYPLATFORM_FOO_DIRECT_API_MAPPING +const whal_FooDriver whal_Myplatform_Foo_Driver = { + .Init = whal_Myplatform_Foo_Init, + .Deinit = whal_Myplatform_Foo_Deinit, +}; +#endif +``` + +In the chip header, wrap the mapped function prototypes and extern vtable +declaration in the same `#ifndef` guard. Types and configuration structs +stay unconditional. Extension functions (`Ext_*`) that have no generic +equivalent are never mapped and stay unconditional. + +### Configuration + +The device struct's `cfg` field points to your platform-specific configuration. +Cast it in your driver functions: + +```c +static whal_Error whal_Myplatform_Foo_Init(whal_Foo *fooDev) +{ + whal_Myplatform_Foo_Cfg *cfg = (whal_Myplatform_Foo_Cfg *)fooDev->cfg; + /* ... */ +} +``` + +### No Cross-Driver Calls + +Platform drivers should only touch their own registers. The board handles all +cross-peripheral setup — clock enables, power-rail sequencing, flash wait +states — before calling Init. + +There are two exceptions: + +- **Peripheral drivers** call their underlying bus driver (SPI, I2C, MDIO) to + communicate with the external chip. +- **DMA-backed platform drivers** (e.g., `stm32wb_uart_dma`) call the DMA + driver to set up and start transfers. The DMA device is passed through the + driver's configuration struct. + +### Register Access + +wolfHAL provides register access helpers in `wolfHAL/regmap.h`: + +- `whal_Reg_Write()` — write a full register value +- `whal_Reg_Read()` — read a full register value +- `whal_Reg_Update()` — read-modify-write a specific field within a register +- `whal_Reg_Get()` — read and extract a masked field from a register + +Use `Write` and `Read` for whole-register access. Use `Update` and `Get` when +you need to modify or read individual fields within a register without +disturbing other bits. + +### Timeouts + +Most drivers poll hardware status registers in busy-wait loops (e.g., waiting +for a DMA transfer to complete, a flash erase to finish, or an AES computation +to produce a result). wolfHAL provides an optional timeout mechanism to bound +these waits and prevent infinite hangs if hardware misbehaves. + +#### Timeout Struct + +The board creates a `whal_Timeout` instance with a tick source and a timeout +duration. Drivers receive a pointer to it through their configuration struct: + +```c +typedef struct { + uint32_t timeoutTicks; /* max ticks before timeout */ + uint32_t startTick; /* snapshot, set by START */ + uint32_t (*GetTick)(void); /* board-provided tick source */ +} whal_Timeout; +``` + +The tick units are determined by the board's `GetTick` implementation. A 1 kHz +SysTick gives millisecond ticks; a 1 MHz timer gives microsecond ticks. Drivers +do not need to know the tick rate. + +#### whal_Reg_ReadPoll + +For the common case of polling a register bit, use `whal_Reg_ReadPoll` from +`wolfHAL/regmap.h`: + +```c +whal_Error whal_Reg_ReadPoll(size_t base, size_t offset, + size_t mask, size_t value, + whal_Timeout *timeout); +``` + +This polls until `(reg & mask) == value` or the timeout expires. Pass the mask +as the value to wait for bits to be set, or `0` to wait for bits to be clear: + +```c +/* Wait for TXE flag to be set */ +err = whal_Reg_ReadPoll(base, SPI_SR_REG, SPI_SR_TXE_Msk, + SPI_SR_TXE_Msk, cfg->timeout); + +/* Wait for BSY flag to be clear */ +err = whal_Reg_ReadPoll(base, SPI_SR_REG, SPI_SR_BSY_Msk, + 0, cfg->timeout); +``` + +A NULL timeout pointer means unbounded wait (poll forever). + +#### Driver-Specific Helpers + +When a driver has many polling sites that also need post-poll cleanup (e.g., +clearing a flag), wrap the pattern in a local helper to avoid code duplication: + +```c +static whal_Error WaitForCCF(size_t base, whal_Timeout *timeout) +{ + whal_Error err; + err = whal_Reg_ReadPoll(base, AES_SR_REG, AES_SR_CCF_Msk, + AES_SR_CCF_Msk, timeout); + if (err) + return err; + whal_Reg_Update(base, AES_CR_REG, AES_CR_CCFC_Msk, AES_CR_CCFC_Msk); + return WHAL_SUCCESS; +} +``` + +This keeps code size small — one function body shared across all call sites +instead of inlined polling loops at each location. + +#### Cleanup on Timeout + +When a timeout occurs during an operation that has enabled a hardware mode +(e.g., flash programming mode, AES enable), the driver must still clean up +before returning. Use a `goto cleanup` pattern: + +```c +whal_Error err = WHAL_SUCCESS; + +whal_Reg_Update(base, CR_REG, PG_Msk, 1); /* enable programming mode */ + +for (...) { + err = whal_Reg_ReadPoll(base, SR_REG, BSY_Msk, 0, cfg->timeout); + if (err) + goto cleanup; +} + +cleanup: + whal_Reg_Update(base, CR_REG, PG_Msk, 0); /* always disable */ + return err; +``` + +Never return directly from inside a polling loop if the peripheral is in a +mode that requires cleanup. + +#### Compile-Time Disable + +Define `WHAL_CFG_NO_TIMEOUT` to remove all timeout logic from the binary. +When defined, `WHAL_TIMEOUT_START` becomes a no-op and `WHAL_TIMEOUT_EXPIRED` +always evaluates to `0`, so polling loops run until the hardware condition is +met with no overhead. + +#### Adding Timeout to a Config Struct + +Driver config structs that use polling should include an optional timeout +pointer: + +```c +typedef struct { + /* ... other config fields ... */ + whal_Timeout *timeout; +} whal_Myplatform_Foo_Cfg; +``` + +The timeout is optional — if the board does not set it (NULL), all waits are +unbounded. + +### Reusing a Driver Across Platforms + +Many MCU families share identical peripheral IP blocks. For example, the +STM32WB and STM32H5 have register-compatible GPIO and UART peripherals. +Rather than duplicating driver code, create thin alias files for the new +platform that re-export the existing driver under platform-specific names. + +#### Header + +The alias header `typedef`s the config structs and `#define`s the driver +instance, functions, and any enum constants. The driver and function aliases +are wrapped in `#ifndef` so they are omitted when direct API mapping is active +for the alias platform: + +```c +#ifndef WHAL_STM32H5_GPIO_H +#define WHAL_STM32H5_GPIO_H + +#include + +typedef whal_Stm32wb_Gpio_Cfg whal_Stm32h5_Gpio_Cfg; +typedef whal_Stm32wb_Gpio_PinCfg whal_Stm32h5_Gpio_PinCfg; + +#ifndef WHAL_CFG_GPIO_API_MAPPING_STM32H5 +#define whal_Stm32h5_Gpio_Driver whal_Stm32wb_Gpio_Driver +#define whal_Stm32h5_Gpio_Init whal_Stm32wb_Gpio_Init +#define whal_Stm32h5_Gpio_Deinit whal_Stm32wb_Gpio_Deinit +#define whal_Stm32h5_Gpio_Get whal_Stm32wb_Gpio_Get +#define whal_Stm32h5_Gpio_Set whal_Stm32wb_Gpio_Set +#endif + +/* Re-export enum constants under the new platform name */ +#define WHAL_STM32H5_GPIO_MODE_OUT WHAL_STM32WB_GPIO_MODE_OUT +/* ... */ + +#endif +``` + +Use `typedef` for types (gives proper type-checking and debugger visibility) +and `#define` for the driver instance and functions (which are values, not +types). + +#### Source + +The source file includes the original implementation directly: + +```c +#include "stm32wb_gpio.c" +``` + +This works because `#include` is textual insertion — the compiler does not +distinguish `.h` from `.c`. The new platform's `board.mk` compiles this +file and does **not** compile the original. The original platform's +`board.mk` still compiles its own file directly. Both must never appear in +the same build. + +#### When to alias vs. write a new driver + +Alias when the register layout is identical — same offsets, same bit positions, +same behavior. If even one register differs (different bit position, extra +field, different reset value that affects behavior), write a new driver. A +partial alias that papers over register differences with workarounds is worse +than a clean separate implementation. + +### Platform Device Macros + +Add regmap and driver macros to your platform header +(`wolfHAL/platform//.h`) so that board configs can instantiate +devices without knowing the register addresses or driver symbols: + +```c +#define WHAL_MYPLATFORM_FOO_REGMAP \ + .base = 0x40000000, \ + .size = 0x400 +#define WHAL_MYPLATFORM_FOO_DRIVER &whal_Myplatform_Foo_Driver +``` + +The board uses these in device struct initializers: + +```c +whal_Foo g_whalFoo = { + .regmap = { WHAL_MYPLATFORM_FOO_REGMAP }, + .driver = WHAL_MYPLATFORM_FOO_DRIVER, + .cfg = &fooCfg, +}; +``` + +When direct API mapping is active for a device type, the board omits the +`.driver` field since the vtable is unused. It's a good idea to note that +you are doing so with the following comment. + +```c +whal_Foo g_whalFoo = { + .regmap = { WHAL_MYPLATFORM_FOO_REGMAP }, + /* .driver: direct API mapping */ + .cfg = &fooCfg, +}; +``` + +--- + +## Clock + +Header: `wolfHAL/clock/clock.h` + +Clock is a **board-level driver** (see Driver Categories). The generic +`clock.h` declares only the typed handle `whal_Clock { regmap }` — no +`whal_Clock_Init`/`Deinit`/`Enable`/`Disable` API, no `whal_ClockDriver` +vtable. Each chip clock driver exposes imperative chip-specific helpers +that boards call directly from `Board_Init` in the right order. + +### API contract + +The chip clock driver header may declare ONLY functions whose names match +one of these prefix patterns: + +- `whal___Enable(...)` — turn a clock node on (and wait + ready, if applicable) +- `whal___Disable(...)` — turn a clock node off +- `whal___Set(...)` — change a clock node's + selection/value (e.g. `SetSysClock`, peripheral-mux selects, divider/range + selects) + +Where `` is the chip's clock-controller name (e.g. `Rcc` on STM32, +`Clock` on PIC32CZ). NOT allowed in the public header: `Init`, `Deinit`, +`Configure`, `BringUp`, `GetFreq`, or any other operation. Internal helpers go +in the `.c` file as `static`. + +Configuration and enabling are bundled into a single `Enable*` call where +the order is fixed (e.g. `EnablePll` writes the dividers/source then turns +the PLL on and waits for ready). Boards do not call separate `Configure` +operations. + +The chip header may declare types, enums, and `_CFG` field-initializer +macros freely — those are not constrained. + +### Typical helpers + +A chip with a fairly common clock tree exposes: + +```c +whal_Error whal___EnableOsc(whal_Clock *, + const whal___OscCfg *); +whal_Error whal___DisableOsc(whal_Clock *, + const whal___OscCfg *); +whal_Error whal___EnablePll(whal_Clock *, + const whal___PllCfg *); +whal_Error whal___DisablePll(whal_Clock *); +whal_Error whal___SetSysClock(whal_Clock *, + whal___SysClockSrc); +whal_Error whal___EnablePeriphClk(whal_Clock *, + const whal___PeriphClk *); +whal_Error whal___DisablePeriphClk(whal_Clock *, + const whal___PeriphClk *); +``` + +Adapt the set to what the chip actually has. A chip without a PLL drops +`EnablePll`/`DisablePll`. A chip with extra muxes adds `Set*` helpers. A +chip with a different topology (e.g., generic-clock generators routing to +peripheral channels) adds operations like `EnableGclkGen` and keeps to the same +naming convention. + +### Reference implementation + +`wolfHAL/clock/stm32wb_rcc.h` and `src/clock/stm32wb_rcc.c` are the +canonical reference. New chip clock drivers should be modeled on its +shape. `wolfHAL/clock/pic32cz_clock.h` shows how the same convention +covers a fundamentally different topology (oscillators + GCLK generators ++ peripheral channels). + +### Board responsibilities + +The board calls helpers in the right order in `Board_Init` — typically +oscillator(s) on, PLL configure-and-enable, sysclk source switch, +peripheral clock enables. The board also handles flash wait states and +voltage scaling around the sysclk transition. There is no walker; the +ordering is explicit at the call site. + +--- + +## GPIO + +Header: `wolfHAL/gpio/gpio.h` + +The GPIO driver configures and controls general-purpose I/O pins. The +configuration describes a table of pins, and the API operates on pins by their +index in that table (not raw hardware pin/port numbers). + +### Init + +Configure all pins described in the device's configuration. For each pin this +typically involves: + +- Setting the pin mode (input, output, alternate function, analog) +- Configuring output type (push-pull or open-drain), speed, and pull + resistors as applicable +- Setting the alternate function mux if the pin is in alternate function mode + (e.g., for UART TX/RX or SPI signals) + +The board must enable GPIO port clocks before calling Init. + +If any pin configuration fails, Init should stop and return an error. + +### Deinit + +Reset GPIO pin configurations as needed. The board is responsible for +disabling GPIO port clocks after Deinit. + +### Get + +Read the current state of a pin. The `pin` parameter is an index into the +configured pin table, not a raw hardware pin number. The driver should look up +the actual port and pin from the configuration. + +For output pins, read the output data register (the value being driven). For +input pins, read the input data register (the value being sampled from the +pad). Store the result (0 or 1) in the output pointer. + +### Set + +Drive a pin to the given value (0 or 1). The `pin` parameter is an index into +the configured pin table. The driver writes to the output data register for the +corresponding port and pin. + +--- + +## UART + +Header: `wolfHAL/uart/uart.h` + +The UART driver provides basic serial transmit and receive. All operations are +blocking — Send does not return until all bytes have been transmitted, and Recv +does not return until all requested bytes have been received. + +### Init + +Configure and enable the UART peripheral: + +- Write the baud rate register value from the configuration. The board + pre-computes this value from the clock frequency and desired baud rate + (e.g., `BRR = clockFreq / baud`) +- Configure word length, stop bits, and parity as needed +- Enable the transmitter and receiver +- Enable the UART peripheral + +The board must enable the peripheral clock before calling Init. + +On platforms with synchronization requirements (e.g., Microchip SERCOM), the +driver must poll synchronization busy flags after writing to certain registers +before proceeding. + +### Deinit + +Disable the UART peripheral: + +- Disable the transmitter and receiver +- Clear the baud rate register + +### Send + +Transmit `dataSz` bytes from the provided buffer. For each byte: + +1. Poll the transmit-ready flag (TX empty / data register empty) until the + hardware is ready to accept a byte +2. Write the byte to the transmit data register + +After sending all bytes, poll the transmission-complete flag to ensure the last +byte has fully shifted out before returning. + +### Recv + +Receive `dataSz` bytes into the provided buffer. For each byte: + +1. Poll the receive-ready flag (RX not empty / receive complete) until a byte + is available +2. Read the byte from the receive data register and store it in the buffer + +### SendAsync + +Start a non-blocking transmit. Returns immediately after initiating the +transfer. The buffer must remain valid until the transfer completes. The +driver signals completion through a platform-specific mechanism. + +Drivers that do not support async should set SendAsync to NULL in the vtable. +The dispatch layer returns WHAL_ENOTSUP when the caller tries to use any +NULL vtable entry (or when the driver pointer itself is NULL). When direct +API mapping is active, polled drivers provide stub implementations that +return WHAL_ENOTSUP directly. + +### RecvAsync + +Start a non-blocking receive. Returns immediately after initiating the +transfer. The buffer must remain valid until the transfer completes. + +The async variants are optional — a driver vtable only needs to populate +them if the platform supports non-blocking transfers. Polled-only drivers +leave these NULL (the dispatch layer returns WHAL_ENOTSUP) or provide +stubs returning WHAL_ENOTSUP (direct API mapping). + +--- + +## IRQ + +Header: `wolfHAL/irq/irq.h` + +The IRQ driver controls an interrupt controller. It provides a +platform-independent way to enable and disable individual interrupt lines. + +### Init + +Initialize the interrupt controller. + +### Deinit + +Shut down the interrupt controller. + +### Enable + +Enable an interrupt line. The `irqCfg` parameter is platform-specific and +can contain settings such as priority. Pass NULL for defaults. + +### Disable + +Disable an interrupt line. + +--- + +## SPI + +Header: `wolfHAL/spi/spi.h` + +The SPI driver provides serial peripheral interface communication. A +communication session is bracketed by `StartCom` / `EndCom`, which configure +the peripheral for a specific mode and speed. All transfers within a session +use the same settings, allowing the bus to be shared between devices with +different configurations by starting a new session for each device. + +### Init + +Perform one-time SPI peripheral configuration that remains fixed for the +lifetime of the device. Do not configure mode, baud rate, or data size +here — these are applied per-session via `StartCom`. + +The board must enable the peripheral clock before calling Init. The +configuration struct should include the peripheral clock frequency so the +driver can compute baud rate prescalers. + +### Deinit + +Disable the SPI peripheral. + +### StartCom + +Begin a communication session. Configures the peripheral from the +platform-independent `whal_Spi_ComCfg` struct: + +- `freq` — bus frequency in Hz +- `mode` — SPI mode (CPOL/CPHA) +- `wordSz` — word size in bits (e.g. 8) +- `dataLines` — number of data lines (1 for standard SPI) + +The driver should disable the peripheral, apply the new settings (mode, baud +rate prescaler, data size, FIFO threshold), and re-enable it. Return +`WHAL_EINVAL` if a requested setting is not supported by the hardware. + +### EndCom + +End the current communication session by disabling the peripheral. + +### SendRecv + +Perform a full-duplex SPI transfer. This is the only transfer function — there +are no separate Send or Recv operations. + +The driver clocks `max(txLen, rxLen)` bytes: + +- When `tx` is NULL or exhausted, send `0xFF` for remaining clocks +- When `rx` is NULL or exhausted, discard received bytes +- Every TX byte produces an RX byte; always drain the RX FIFO to prevent + overflow + +After the loop, wait for the bus to go idle before returning. + +--- + +## I2C + +Header: `wolfHAL/i2c/i2c.h` + +The I2C driver provides inter-integrated circuit bus communication. A +communication session is bracketed by `StartCom` / `EndCom`, which configure +the peripheral for a specific target address and bus frequency. The message-based +`Transfer` API gives full control over bus conditions (START, STOP, direction). + +Convenience helpers `whal_I2c_WriteReg` and `whal_I2c_ReadReg` are provided +as inline functions for the common register read/write patterns. + +### Init + +Perform one-time I2C peripheral configuration (noise filters, enable). Do not +configure timing or address here — these are applied per-session via `StartCom`. + +The board must enable the peripheral clock before calling Init. + +### Deinit + +Disable the I2C peripheral. + +### StartCom + +Begin a communication session. Configures the peripheral from the +platform-independent `whal_I2c_ComCfg` struct: + +- `freq` — bus frequency in Hz (100000, 400000, 1000000) +- `addr` — target device address (7-bit or 10-bit) +- `addrSz` — address size in bits (7 or 10) + +The driver should compute timing parameters from `freq` and the peripheral +clock, then write the target address into the appropriate hardware register. + +### EndCom + +End the current communication session by clearing the target address. + +### Transfer + +Execute a sequence of I2C messages. Each message has a data buffer, size, and +flags that control bus conditions: + +- `WHAL_I2C_MSG_WRITE` (0) — master write +- `WHAL_I2C_MSG_READ` — master read +- `WHAL_I2C_MSG_START` — generate START (or repeated START) +- `WHAL_I2C_MSG_STOP` — generate STOP after this message + +The driver processes messages sequentially. When the current message has no +STOP and the next message has no START, use hardware RELOAD to continue the +same transfer without re-addressing (for multi-part writes). When the next +message has START, use TC (transfer complete) so the hardware can generate a +repeated START for direction changes. + +For messages larger than 255 bytes, the driver must split into chunks using +the hardware RELOAD mechanism internally. + +--- + +## Sensor + +Header: `wolfHAL/sensor/sensor.h` + +The sensor driver provides a bus-agnostic API for reading sensor data. Each +sensor driver implements the vtable and uses the appropriate bus (I2C, SPI, +etc.) internally. The `Read` function fills a driver-defined data struct +passed as a void pointer. + +### Init + +Initialize the sensor hardware. This may involve loading configuration data, +verifying device identity, and configuring operating parameters. The driver +should call `StartCom` / `EndCom` on its bus device to bracket I2C/SPI access. + +### Deinit + +Shut down the sensor (e.g., soft reset). Should bracket bus access with +`StartCom` / `EndCom`. + +### Read + +Read sensor data into a driver-defined struct. The driver fetches a new sample +from the hardware and fills the struct. The struct type is defined by each +sensor driver (e.g., `whal_Bmi270_Data` with `accelX/Y/Z`, `gyroX/Y/Z`). + +Each `Read` call should bracket its bus access with `StartCom` / `EndCom` so +the bus is released between reads and other devices can use it. + +--- + +## Flash + +Header: `wolfHAL/flash/flash.h` + +The flash driver provides access to on-chip or external flash memory. Flash +has specific constraints around alignment, erase-before-write, and region +protection that the driver must handle. + +### Init + +Initialize the flash controller. This may involve clearing error flags or +releasing hardware mutex locks. The board must enable the flash interface clock +before calling Init. + +### Deinit + +Release flash controller resources. + +### Lock + +Write-protect a flash region to prevent modification. On some platforms this is +a global lock (the `addr` and `len` parameters are ignored and the entire flash +is locked). On others it may protect specific regions. After locking, Write and +Erase operations on the protected region should fail until Unlock is called. + +The implementation varies significantly by platform — some use an unlock key +sequence (where Lock simply sets a lock bit), while others have dedicated +write-protect registers for individual regions. + +### Unlock + +Remove write protection to allow Write and Erase operations. On platforms that +use a key sequence, this typically involves writing two specific magic values to +a key register in the correct order. An incorrect sequence may trigger a bus +fault (this is a hardware security feature). + +### Read + +Read data from flash. On most platforms, flash is memory-mapped, so this is a +straightforward memory copy from the flash address into the provided buffer. No +special flash controller interaction is needed. + +Some platforms may require acquiring a mutex or performing cache maintenance +before reading. + +### Write + +Program data into flash starting at the given address. The caller must ensure +the region is unlocked and erased before writing (flash can only transition bits +from 1 to 0; erasing sets all bits to 1). + +Key constraints: +- **Alignment**: writes must be aligned to the platform's programming unit + (e.g., 8-byte double-word on STM32, 8 or 32 bytes on PIC32CZ). The driver + should validate alignment and return an error if misaligned. +- **Programming unit**: the driver writes data in hardware-defined chunks. Some + platforms support multiple write sizes (e.g., single double-word vs. quad + double-word) and the driver can optimize by using larger writes when + alignment permits. +- **Busy polling**: after each write, poll the flash controller's busy flag + until the operation completes. +- **Error flags**: clear any pending error flags before starting, and check for + new errors after each operation. + +### Erase + +Erase a flash region. Flash erase operates at sector/page granularity +(typically 4 KB). The driver should: + +- Calculate the page range covering the requested address and size +- Erase each page individually, polling for completion between pages +- Validate that the region is unlocked before erasing + +The `addr` does not need to be page-aligned — the driver should erase all pages +that overlap with the requested range. Peripheral flash drivers (e.g., SPI-NOR) +may enforce stricter alignment requirements where the underlying hardware +requires aligned erase addresses. + +--- + +## Block + +Header: `wolfHAL/block/block.h` + +The block driver provides access to block-addressable storage devices such as +SD cards and eMMC. Unlike flash, block devices are addressed by block number +rather than byte address, and all operations work in units of fixed-size blocks +(e.g. 512 bytes). + +Block drivers are peripheral drivers — they call their underlying bus driver +(SPI, SDIO) to communicate with the storage device. + +### Init + +Initialize the storage device. This includes any device-specific initialization +sequence required to bring the device into a usable state. The board must have +already initialized the bus driver and enabled the relevant clocks and GPIOs +before calling Init. + +### Deinit + +Release the storage device. + +### Read + +Read one or more blocks from the device into a buffer. The driver should handle +both single-block and multi-block reads based on the block count. + +### Write + +Write one or more blocks from a buffer to the device. The driver should handle +both single-block and multi-block writes, and wait for the device to finish +programming before returning. + +### Erase + +Erase a range of blocks on the device. + +--- + +## Timer + +Header: `wolfHAL/timer/timer.h` + +The timer driver provides periodic tick or counter functionality. The most +common implementation is the ARM Cortex-M SysTick timer, which provides a +simple periodic interrupt for system timekeeping. + +### Init + +Configure the timer hardware but do **not** start it. This includes: + +- Setting the reload value (determines the tick interval: + interval = reload / clock_frequency) +- Selecting the clock source (e.g., CPU clock vs. external reference) +- Enabling or disabling the tick interrupt + +The timer should not begin counting until Start is called. + +### Deinit + +Stop the timer and release resources. + +### Start + +Start the timer counting. The timer begins decrementing from the configured +reload value, generating interrupts (if enabled) each time it reaches zero and +reloading automatically. + +### Stop + +Stop the timer without resetting it. The counter holds its current value. + +### Reset + +Reset the timer counter back to its initialized state. + +--- + +## RNG + +Header: `wolfHAL/rng/rng.h` + +The RNG driver provides access to a hardware random number generator. The +hardware typically uses an analog entropy source to produce true random numbers. + +### Init + +Initialize the RNG hardware. The board must enable the peripheral clock (and +any additional clock sources the RNG's entropy source requires) before calling +Init. + +### Deinit + +Shut down the RNG hardware. + +### Generate + +Fill the provided buffer with random data. The driver should: + +1. Enable the RNG peripheral +2. Loop until the buffer is filled: + - Check for hardware errors (seed errors, clock errors). If the entropy + source has failed, disable the RNG and return `WHAL_EHARDWARE` + - Poll the data-ready flag until a new random word is available + - Read the random word (typically 32 bits) and extract as many bytes as + needed into the output buffer +3. Disable the RNG peripheral before returning + +The RNG is enabled only for the duration of the Generate call to minimize power +consumption and avoid unnecessary entropy source wear. + +--- + +## Crypto + +Header: `wolfHAL/crypto/crypto.h` + +The crypto driver provides access to hardware cryptographic accelerators +(ciphers, hashes, MACs, and public key operations). The driver vtable uses a +unified **StartOp / Process / EndOp** pattern that supports both one-shot and +streaming use cases. + +### Device Struct + +```c +struct whal_Crypto { + const whal_Regmap regmap; + const whal_CryptoDriver *driver; + const void *cfg; +}; +``` + +### Driver Vtable + +```c +typedef struct { + whal_Error (*Init)(whal_Crypto *cryptoDev); + whal_Error (*Deinit)(whal_Crypto *cryptoDev); + whal_Error (*StartOp)(whal_Crypto *cryptoDev, size_t opId, void *opArgs); + whal_Error (*Process)(whal_Crypto *cryptoDev, size_t opId, void *opArgs); + whal_Error (*EndOp)(whal_Crypto *cryptoDev, size_t opId, void *opArgs); +} whal_CryptoDriver; +``` + +The `opId` parameter is a framework-defined enum value (e.g. `WHAL_CRYPTO_AES_GCM`, +`WHAL_CRYPTO_SHA256`). The `opArgs` parameter is a pointer to an +algorithm-specific argument struct (e.g., `whal_Crypto_AesGcmArgs`, +`whal_Crypto_HashArgs`). The driver casts it to the correct type based on +`opId`. See `wolfHAL/crypto/crypto.h` for the full set of argument structs and +operation IDs. + +### Init / Deinit + +The board must enable the peripheral clock before calling Init. Deinit should +disable the crypto accelerator peripheral. + +### Operations + +Each crypto operation is split into three phases: + +- **StartOp** — Configure hardware, load key/IV, process AAD for AEAD modes. +- **Process** — Feed data through the hardware. May be called multiple times + for streaming. Optional for single-shot operations (e.g. GMAC has no + payload). +- **EndOp** — Finalize the operation, read output (tag, digest), release + hardware. On StartOp failure, the driver cleans up internally and EndOp + should not be called. + +Unsupported `opId` values return `WHAL_ENOTSUP`. Unsupported parameter +combinations (e.g. AES-192 on hardware that only supports 128/256) also +return `WHAL_ENOTSUP`. + +### Convenience Wrappers + +`crypto.h` provides typed inline wrappers for each algorithm. One-shot +wrappers call all three phases in sequence: + +```c +whal_Crypto_AesGcmArgs args = { + .dir = WHAL_CRYPTO_ENCRYPT, .key = key, .keySz = 32, + .iv = iv, .ivSz = 12, + .in = plaintext, .out = ct, .sz = sizeof(plaintext), + .aad = aad, .aadSz = sizeof(aad), + .tag = tag, .tagSz = 16, +}; +whal_Crypto_AesGcm(&g_whalCrypto, &args); +``` + +Streaming wrappers expose each phase individually with typed parameters: + +```c +whal_Crypto_Sha256_Start(&g_whalHash); +whal_Crypto_Sha256_Update(&g_whalHash, chunk1, chunk1Sz); +whal_Crypto_Sha256_Update(&g_whalHash, chunk2, chunk2Sz); +whal_Crypto_Sha256_Finalize(&g_whalHash, digest, 32); +``` + +Both wrapper styles are guarded by `WHAL_CFG_CRYPTO_` defines (e.g. +`WHAL_CFG_CRYPTO_AES_GCM`, `WHAL_CFG_CRYPTO_SHA256`). + +### Board Integration + +The board enables supported algorithms via `-D` flags in `board.mk` and +instantiates the crypto device: + +```c +whal_Crypto g_whalCrypto = { + .regmap = { WHAL_STM32WB55_AES1_REGMAP }, + .cfg = &(whal_Stm32wb_Aes_Cfg) { .timeout = &g_whalTimeout }, +}; +``` + +When API mapping is active (e.g. `-DWHAL_CFG_CRYPTO_API_MAPPING_STM32WB_AES`), +the driver functions are mapped directly to the top-level API, eliminating the +vtable indirection. + +--- + +## Power + +Header: `wolfHAL/power/power.h` + +Power is a **board-level driver** (see Driver Categories). The generic +`power.h` declares only the typed handle `whal_Power { regmap }` — no +`whal_Power_Init`/`Deinit`/`Enable`/`Disable` API, no `whal_PowerDriver` +vtable. Each chip power driver exposes imperative chip-specific helpers +that boards call directly from `Board_Init` (typically before clock setup, +to bring up regulators that downstream peripherals depend on). + +### API contract + +The chip power driver header declares chip-specific helpers named +`whal___(...)`, where `` is the chip's +power-controller name (e.g. `Pwr` on STM32L1, `Supc` on PIC32CZ). The set +of operations is whatever the chip actually exposes — there is no fixed +list. Examples: + +- `whal_Stm32l1_Pwr_SetVosRange(whal_Power *, range, timeout)` — voltage + scaling range select with ready-bit poll +- `whal_Pic32cz_Supc_EnableSupply(whal_Power *, const whal_Pic32cz_Supc_Supply *)` + / `DisableSupply(...)` — toggle a regulator output identified by a + descriptor (register mask + position) + +NOT allowed in the public header: `Init`, `Deinit`, or any abstracted +"generic power" operation. Internal helpers go in the `.c` file as +`static`. + +The chip header may declare types, enums, and descriptor-initializer +macros freely. + +### Reference implementations + +`wolfHAL/power/stm32l1_pwr.h` (single voltage-scaling helper) and +`wolfHAL/power/pic32cz_supc.h` (regulator enable/disable by descriptor) +are the canonical examples for the two common shapes. + +### Board responsibilities + +The board calls power helpers from `Board_Init` in the right order — +typically before clock configuration, since some clock nodes (e.g., a +PLL's analog regulator) require their supply to be enabled first. There +is no separate Init/Deinit step; helpers are imperative and only do what +they're called to do. + +--- + +## Ethernet + +Header: `wolfHAL/eth/eth.h` + +The Ethernet driver controls a MAC (Media Access Controller) with an integrated +DMA engine. It manages descriptor rings in RAM for transmit and receive, handles +MDIO bus access for PHY communication, and configures the MAC for the negotiated +link speed and duplex. + +### Init + +Initialize the MAC, DMA, and MTL. Set up TX and RX descriptor rings in RAM, +program the MAC address, and configure DMA bus mode and burst lengths. Does NOT +enable TX/RX — call Start for that. The descriptor and buffer memory must be +pre-allocated by the board and passed via the config struct. Validate all config +fields (descriptor counts > 0, buffer pointers non-NULL, buffer sizes > 0). + +### Deinit + +Perform a DMA software reset to clear all state. + +### Start + +Configure the MAC speed and duplex to match the PHY, enable MAC TX/RX, start +the DMA TX and RX engines, and kick the RX DMA by writing the tail pointer. +Speed and duplex are passed as parameters — the board reads these from the PHY +driver before calling Start. + +### Stop + +Stop DMA TX and RX engines, then disable MAC TX and RX. + +### Send + +Transmit a single Ethernet frame. Find the next available TX descriptor (OWN=0), +copy the frame into the pre-allocated TX buffer, fill in the descriptor fields +(buffer address, length, OWN=1, FD, LD), and write the DMA tail pointer to kick +transmission. Return WHAL_ENOTREADY if no descriptor is available. Validate that +the frame length does not exceed the TX buffer size. + +### Recv + +Receive a single Ethernet frame by polling. Check the next RX descriptor — if +OWN=0, DMA has written a frame. Read the packet length from the descriptor, copy +the frame data to the caller's buffer, re-arm the descriptor (set OWN=1), and +update the tail pointer. Return WHAL_ENOTREADY if no frame is available, or +WHAL_EHARDWARE if the error summary bit is set. + +### MdioRead + +Read a 16-bit PHY register via the MDIO management bus. Write the PHY address, +register address, and read command to the MDIO address register, poll for +completion, then read the data register. Use a timeout to avoid infinite hang +if the PHY is not responding. + +### MdioWrite + +Write a 16-bit value to a PHY register via MDIO. Write the data first, then +issue the write command and poll for completion with a timeout. + +--- + +## EthPhy + +Header: `wolfHAL/eth_phy/eth_phy.h` + +The Ethernet PHY driver handles link negotiation and status for an external PHY +chip connected to a MAC via the MDIO bus. The PHY device struct holds a pointer +to its parent MAC (for MDIO access) and the PHY address on the bus. Different +PHY chips (e.g., LAN8742A, DP83848) have different vendor-specific registers +but share the same API. + +### Init + +Reset the PHY via software reset (BCR bit 15), wait for the reset bit to +self-clear, then enable autonegotiation. Does not block waiting for link — the +board or application polls GetLinkState separately. + +### Deinit + +Power down the PHY or release resources. May be a no-op on simple PHYs. + +### GetLinkState + +Read the current link status, negotiated speed, and duplex mode. The IEEE 802.3 +BSR register (reg 1) link bit is latching-low — read it twice and use the second +result for current status. Speed and duplex are read from a vendor-specific +status register (e.g., register 0x1F on LAN8742A). Return speed as 10 or 100, +and duplex as WHAL_ETH_DUPLEX_HALF or WHAL_ETH_DUPLEX_FULL. + +--- + +## DMA + +Header: `wolfHAL/dma/dma.h` + +The DMA driver controls a DMA controller. A single device instance represents +one controller, and individual channels are identified by index. Channel +configuration is platform-specific and passed as an opaque pointer. + +DMA is a service peripheral — peripheral drivers (UART, SPI) consume it +internally. The application never calls the DMA API directly. Peripheral +drivers receive a `whal_Dma` pointer and channel number through their +configuration struct and use them to set up transfers. + +### Init + +Initialize the DMA controller. Clear any pending interrupt flags and reset +controller state. The board must enable the DMA controller clock before calling +Init. + +### Deinit + +Shut down the DMA controller. + +### Configure + +Configure a DMA channel for transfers. The `chCfg` parameter is a +platform-specific struct containing: + +- Transfer direction (memory-to-peripheral, peripheral-to-memory, etc.) +- Source and destination addresses +- Transfer width (8, 16, 32 bit) +- Buffer address and length +- Burst size (if supported) +- Peripheral request mapping (e.g., DMAMUX request ID) + +The DMA driver does not store callbacks. Instead, the board defines ISR +entries in the vector table and calls the driver's IRQ handler (e.g., +`whal_Stm32wb_Dma_IRQHandler()`), passing a callback and context pointer. +The IRQ handler checks and clears the interrupt flags, then invokes the +callback. Peripheral drivers expose their completion callbacks for the +board to wire up (e.g., `whal_Stm32wb_UartDma_TxCallback`). + +Configure sets up all channel registers but does not start the transfer. +Call Start to begin. A channel can be reconfigured between transfers (e.g., +to change the buffer address and length) by calling Configure again. + +### Start + +Start a previously configured DMA channel. This enables the channel, +beginning the transfer. The channel must have been configured via Configure +before calling Start. + +### Stop + +Stop a DMA channel. This aborts any in-progress transfer and disables the +channel. The peripheral driver should call Stop in its cleanup path or when +a transfer needs to be cancelled. + +--- + +## Watchdog + +Header: `wolfHAL/watchdog/watchdog.h` + +The watchdog driver provides access to hardware watchdog timers that reset the +system if not periodically refreshed. On most hardware, the watchdog cannot be +stopped once started — only a system reset clears it. + +### Init + +Configure and start the watchdog. This typically involves: + +- Setting the prescaler and reload/counter values from the configuration +- Enabling the watchdog peripheral + +On some hardware (e.g., STM32 IWDG), the watchdog must be started before its +configuration registers can be written. The driver should handle this ordering +internally. + +The board must enable any required clocks before calling Init. For example, the +STM32 WWDG requires an APB clock, while the IWDG requires the LSI oscillator. + +The watchdog is NOT initialized in `Board_Init` — the test or application +controls when it starts, since once started it cannot be stopped. + +### Deinit + +Shut down the watchdog. On hardware where the watchdog cannot be stopped (e.g., +STM32 IWDG), this function is a no-op. + +### Refresh + +Reload the watchdog counter to prevent a reset. Must be called periodically +within the configured timeout window. The exact mechanism is hardware-specific +(e.g., writing a reload key to IWDG_KR, or writing the counter value back to +WWDG_CR). + +For window watchdogs, the refresh must occur within the valid window — refreshing +too early or too late triggers a reset. The driver does not enforce window timing; +it writes the reload value unconditionally and relies on the hardware to enforce +the window. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..46d5d48 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,27 @@ +# wolfHAL Examples + +## Building + +From an example directory: + +``` +cd blinky +make BOARD= +``` + +The output binary is placed in `build//`. + +## Example Structure + +Each example contains its application source and a Makefile that references a +board from the top-level `boards/` directory: + +``` +/ + main.c + Makefile +``` + +Board support (device instances, linker scripts, etc.) lives in the top-level +`boards//` directory. See [boards/README.md](../boards/README.md) for +details. diff --git a/examples/blinky/Makefile b/examples/blinky/Makefile new file mode 100644 index 0000000..e31db81 --- /dev/null +++ b/examples/blinky/Makefile @@ -0,0 +1,37 @@ +WHAL_DIR = $(CURDIR)/../.. + +.DEFAULT_GOAL := all + +BOARD ?= stm32wb55xx_nucleo +BOARD_DIR = $(WHAL_DIR)/boards/$(BOARD) +BUILD_DIR = build/$(BOARD) + +INCLUDE = -I$(WHAL_DIR) + +include $(BOARD_DIR)/board.mk + +SOURCE = main.c +SOURCE += $(BOARD_SOURCE) + +OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(SOURCE)) +DEPENDS = $(OBJECTS:.o=.d) + +all: $(BUILD_DIR)/$(notdir $(CURDIR)).bin + +$(BUILD_DIR)/%.o: %.c Makefile + @mkdir -p $(dir $@) + $(GCC) $(CFLAGS) -c -o $@ $< + +.SECONDARY: +$(BUILD_DIR)/%.elf: $(OBJECTS) $(LINKER_SCRIPT) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS) -T $(LINKER_SCRIPT) -o $@ $(OBJECTS) + +$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf + $(OBJCOPY) $^ -O binary $@ + +.PHONY: clean +clean: + rm -rf build + +-include $(DEPENDS) diff --git a/examples/blinky/main.c b/examples/blinky/main.c new file mode 100644 index 0000000..89760d6 --- /dev/null +++ b/examples/blinky/main.c @@ -0,0 +1,21 @@ +#include +#include +#include "board.h" + +void main(void) +{ + if (Board_Init() != WHAL_SUCCESS) + goto loop; + + while (1) { + whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 1); + whal_Uart_Send(&g_whalUart, "Blink!\r\n", 8); + Board_WaitMs(1000); + whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 0); + whal_Uart_Send(&g_whalUart, "Blink!\r\n", 8); + Board_WaitMs(1000); + } + +loop: + while (1); +} diff --git a/src/clock/stm32l1_rcc.c b/src/clock/stm32l1_rcc.c new file mode 100644 index 0000000..35f650c --- /dev/null +++ b/src/clock/stm32l1_rcc.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include + +#define RCC_CR_REG 0x00 +#define RCC_CR_PLLON_Msk (1UL << 24) +#define RCC_CR_PLLRDY_Msk (1UL << 25) +#define RCC_CR_PLLRDY_Pos 25 + +#define RCC_CFGR_REG 0x08 +#define RCC_CFGR_SW_Pos 0 +#define RCC_CFGR_SW_Msk (WHAL_BITMASK(2) << RCC_CFGR_SW_Pos) +#define RCC_CFGR_SWS_Pos 2 +#define RCC_CFGR_SWS_Msk (WHAL_BITMASK(2) << RCC_CFGR_SWS_Pos) +#define RCC_CFGR_PLLSRC_Pos 16 +#define RCC_CFGR_PLLSRC_Msk (1UL << RCC_CFGR_PLLSRC_Pos) +#define RCC_CFGR_PLLMUL_Pos 18 +#define RCC_CFGR_PLLMUL_Msk (WHAL_BITMASK(4) << RCC_CFGR_PLLMUL_Pos) +#define RCC_CFGR_PLLDIV_Pos 22 +#define RCC_CFGR_PLLDIV_Msk (WHAL_BITMASK(2) << RCC_CFGR_PLLDIV_Pos) + +whal_Error whal_Stm32l1_Rcc_EnableOsc(whal_Clock *clkDev, + const whal_Stm32l1_Rcc_OscCfg *cfg) +{ + size_t rdy; + + if (!clkDev || !cfg) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, cfg->onReg, cfg->onMsk, cfg->onMsk); + do { + whal_Reg_Get(clkDev->regmap.base, cfg->rdyReg, cfg->rdyMsk, + cfg->rdyPos, &rdy); + } while (!rdy); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Rcc_DisableOsc(whal_Clock *clkDev, + const whal_Stm32l1_Rcc_OscCfg *cfg) +{ + if (!clkDev || !cfg) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, cfg->onReg, cfg->onMsk, 0); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Rcc_EnablePll(whal_Clock *clkDev, + const whal_Stm32l1_Rcc_PllCfg *cfg) +{ + size_t rdy; + uint32_t pllsrc; + + if (!clkDev || !cfg) + return WHAL_EINVAL; + + /* Disable PLL before reconfiguring; wait until it's actually off. */ + whal_Reg_Update(clkDev->regmap.base, RCC_CR_REG, RCC_CR_PLLON_Msk, 0); + do { + whal_Reg_Get(clkDev->regmap.base, RCC_CR_REG, RCC_CR_PLLRDY_Msk, + RCC_CR_PLLRDY_Pos, &rdy); + } while (rdy); + + pllsrc = (cfg->clkSrc == WHAL_STM32L1_RCC_PLLSRC_HSE) ? 1 : 0; + whal_Reg_Update(clkDev->regmap.base, RCC_CFGR_REG, RCC_CFGR_PLLSRC_Msk, + whal_SetBits(RCC_CFGR_PLLSRC_Msk, RCC_CFGR_PLLSRC_Pos, + pllsrc)); + whal_Reg_Update(clkDev->regmap.base, RCC_CFGR_REG, RCC_CFGR_PLLMUL_Msk, + whal_SetBits(RCC_CFGR_PLLMUL_Msk, RCC_CFGR_PLLMUL_Pos, + cfg->pllmul)); + whal_Reg_Update(clkDev->regmap.base, RCC_CFGR_REG, RCC_CFGR_PLLDIV_Msk, + whal_SetBits(RCC_CFGR_PLLDIV_Msk, RCC_CFGR_PLLDIV_Pos, + cfg->plldiv)); + + whal_Reg_Update(clkDev->regmap.base, RCC_CR_REG, RCC_CR_PLLON_Msk, + RCC_CR_PLLON_Msk); + do { + whal_Reg_Get(clkDev->regmap.base, RCC_CR_REG, RCC_CR_PLLRDY_Msk, + RCC_CR_PLLRDY_Pos, &rdy); + } while (!rdy); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Rcc_DisablePll(whal_Clock *clkDev) +{ + if (!clkDev) + return WHAL_EINVAL; + whal_Reg_Update(clkDev->regmap.base, RCC_CR_REG, RCC_CR_PLLON_Msk, 0); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Rcc_SetSysClock(whal_Clock *clkDev, + whal_Stm32l1_Rcc_SysClockSrc src) +{ + size_t sws; + + if (!clkDev) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, RCC_CFGR_REG, RCC_CFGR_SW_Msk, + whal_SetBits(RCC_CFGR_SW_Msk, RCC_CFGR_SW_Pos, src)); + do { + whal_Reg_Get(clkDev->regmap.base, RCC_CFGR_REG, RCC_CFGR_SWS_Msk, + RCC_CFGR_SWS_Pos, &sws); + } while (sws != (size_t)src); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Rcc_EnablePeriphClk(whal_Clock *clkDev, + const whal_Stm32l1_Rcc_PeriphClk *clk) +{ + if (!clkDev || !clk) + return WHAL_EINVAL; + whal_Reg_Update(clkDev->regmap.base, clk->regOffset, clk->enableMask, + whal_SetBits(clk->enableMask, clk->enablePos, 1)); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Rcc_DisablePeriphClk(whal_Clock *clkDev, + const whal_Stm32l1_Rcc_PeriphClk *clk) +{ + if (!clkDev || !clk) + return WHAL_EINVAL; + whal_Reg_Update(clkDev->regmap.base, clk->regOffset, clk->enableMask, + whal_SetBits(clk->enableMask, clk->enablePos, 0)); + return WHAL_SUCCESS; +} diff --git a/src/clock/stm32wb_rcc.c b/src/clock/stm32wb_rcc.c new file mode 100644 index 0000000..11e6cbd --- /dev/null +++ b/src/clock/stm32wb_rcc.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include + +/* + * STM32WB RCC Register Definitions + */ + +/* Clock Control Register */ +#define RCC_CR_REG 0x000 +#define RCC_CR_MSION_Msk (1UL << 0) +#define RCC_CR_MSIRDY_Msk (1UL << 1) +#define RCC_CR_MSIRDY_Pos 1 +#define RCC_CR_MSIRANGE_Pos 4 +#define RCC_CR_MSIRANGE_Msk (WHAL_BITMASK(4) << RCC_CR_MSIRANGE_Pos) +#define RCC_CR_PLLON_Msk (1UL << 24) +#define RCC_CR_PLLRDY_Msk (1UL << 25) +#define RCC_CR_PLLRDY_Pos 25 + +/* Clock Configuration Register */ +#define RCC_CFGR_REG 0x008 +#define RCC_CFGR_SW_Pos 0 +#define RCC_CFGR_SW_Msk (WHAL_BITMASK(2) << RCC_CFGR_SW_Pos) +#define RCC_CFGR_SWS_Pos 2 +#define RCC_CFGR_SWS_Msk (WHAL_BITMASK(2) << RCC_CFGR_SWS_Pos) + +/* PLL Configuration Register */ +#define RCC_PLLCFGR_REG 0x00C +#define RCC_PLLCFGR_PLLSRC_Pos 0 +#define RCC_PLLCFGR_PLLSRC_Msk (WHAL_BITMASK(2) << RCC_PLLCFGR_PLLSRC_Pos) +#define RCC_PLLCFGR_PLLM_Pos 4 +#define RCC_PLLCFGR_PLLM_Msk (WHAL_BITMASK(3) << RCC_PLLCFGR_PLLM_Pos) +#define RCC_PLLCFGR_PLLN_Pos 8 +#define RCC_PLLCFGR_PLLN_Msk (WHAL_BITMASK(7) << RCC_PLLCFGR_PLLN_Pos) +#define RCC_PLLCFGR_PLLP_Pos 17 +#define RCC_PLLCFGR_PLLP_Msk (WHAL_BITMASK(5) << RCC_PLLCFGR_PLLP_Pos) +#define RCC_PLLCFGR_PLLQ_Pos 25 +#define RCC_PLLCFGR_PLLQ_Msk (WHAL_BITMASK(3) << RCC_PLLCFGR_PLLQ_Pos) +#define RCC_PLLCFGR_PLLREN_Pos 28 +#define RCC_PLLCFGR_PLLREN_Msk (1UL << RCC_PLLCFGR_PLLREN_Pos) +#define RCC_PLLCFGR_PLLR_Pos 29 +#define RCC_PLLCFGR_PLLR_Msk (WHAL_BITMASK(3) << RCC_PLLCFGR_PLLR_Pos) +#define RCC_PLLCFGR_Msk \ + (RCC_PLLCFGR_PLLSRC_Msk | RCC_PLLCFGR_PLLM_Msk | RCC_PLLCFGR_PLLN_Msk | \ + RCC_PLLCFGR_PLLP_Msk | RCC_PLLCFGR_PLLQ_Msk | RCC_PLLCFGR_PLLREN_Msk | \ + RCC_PLLCFGR_PLLR_Msk) + +whal_Error whal_Stm32wb_Rcc_EnableOsc(whal_Clock *clkDev, + const whal_Stm32wb_Rcc_OscCfg *cfg) +{ + size_t rdy; + + if (!clkDev || !cfg) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, cfg->onReg, cfg->onMsk, cfg->onMsk); + do { + whal_Reg_Get(clkDev->regmap.base, cfg->rdyReg, cfg->rdyMsk, + cfg->rdyPos, &rdy); + } while (!rdy); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Rcc_DisableOsc(whal_Clock *clkDev, + const whal_Stm32wb_Rcc_OscCfg *cfg) +{ + if (!clkDev || !cfg) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, cfg->onReg, cfg->onMsk, 0); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Rcc_EnableMsi(whal_Clock *clkDev, + whal_Stm32wb_Rcc_MsiRange range) +{ + size_t rdy; + + if (!clkDev) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, RCC_CR_REG, RCC_CR_MSION_Msk, + RCC_CR_MSION_Msk); + do { + whal_Reg_Get(clkDev->regmap.base, RCC_CR_REG, RCC_CR_MSIRDY_Msk, + RCC_CR_MSIRDY_Pos, &rdy); + } while (!rdy); + whal_Reg_Update(clkDev->regmap.base, RCC_CR_REG, RCC_CR_MSIRANGE_Msk, + whal_SetBits(RCC_CR_MSIRANGE_Msk, RCC_CR_MSIRANGE_Pos, + range)); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Rcc_EnablePll(whal_Clock *clkDev, + const whal_Stm32wb_Rcc_PllCfg *cfg) +{ + size_t rdy; + + if (!clkDev || !cfg) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, RCC_PLLCFGR_REG, RCC_PLLCFGR_Msk, + whal_SetBits(RCC_PLLCFGR_PLLSRC_Msk, RCC_PLLCFGR_PLLSRC_Pos, cfg->clkSrc) | + whal_SetBits(RCC_PLLCFGR_PLLM_Msk, RCC_PLLCFGR_PLLM_Pos, cfg->m) | + whal_SetBits(RCC_PLLCFGR_PLLN_Msk, RCC_PLLCFGR_PLLN_Pos, cfg->n) | + whal_SetBits(RCC_PLLCFGR_PLLP_Msk, RCC_PLLCFGR_PLLP_Pos, cfg->p) | + whal_SetBits(RCC_PLLCFGR_PLLQ_Msk, RCC_PLLCFGR_PLLQ_Pos, cfg->q) | + whal_SetBits(RCC_PLLCFGR_PLLREN_Msk, RCC_PLLCFGR_PLLREN_Pos, 1) | + whal_SetBits(RCC_PLLCFGR_PLLR_Msk, RCC_PLLCFGR_PLLR_Pos, cfg->r)); + whal_Reg_Update(clkDev->regmap.base, RCC_CR_REG, RCC_CR_PLLON_Msk, + RCC_CR_PLLON_Msk); + do { + whal_Reg_Get(clkDev->regmap.base, RCC_CR_REG, RCC_CR_PLLRDY_Msk, + RCC_CR_PLLRDY_Pos, &rdy); + } while (!rdy); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Rcc_DisablePll(whal_Clock *clkDev) +{ + if (!clkDev) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, RCC_CR_REG, RCC_CR_PLLON_Msk, 0); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Rcc_SetSysClock(whal_Clock *clkDev, + whal_Stm32wb_Rcc_SysClockSrc src) +{ + size_t sws; + + if (!clkDev) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, RCC_CFGR_REG, RCC_CFGR_SW_Msk, + whal_SetBits(RCC_CFGR_SW_Msk, RCC_CFGR_SW_Pos, src)); + do { + whal_Reg_Get(clkDev->regmap.base, RCC_CFGR_REG, RCC_CFGR_SWS_Msk, + RCC_CFGR_SWS_Pos, &sws); + } while (sws != (size_t)src); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Rcc_EnablePeriphClk(whal_Clock *clkDev, + const whal_Stm32wb_Rcc_PeriphClk *clk) +{ + if (!clkDev || !clk) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, clk->regOffset, clk->enableMask, + whal_SetBits(clk->enableMask, clk->enablePos, 1)); + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Rcc_DisablePeriphClk(whal_Clock *clkDev, + const whal_Stm32wb_Rcc_PeriphClk *clk) +{ + if (!clkDev || !clk) + return WHAL_EINVAL; + + whal_Reg_Update(clkDev->regmap.base, clk->regOffset, clk->enableMask, + whal_SetBits(clk->enableMask, clk->enablePos, 0)); + return WHAL_SUCCESS; +} diff --git a/src/flash/flash.c b/src/flash/flash.c new file mode 100644 index 0000000..ca71d82 --- /dev/null +++ b/src/flash/flash.c @@ -0,0 +1,77 @@ +#include +#include +#include + +whal_Error whal_Flash_Init(whal_Flash *flashDev) +{ + if (!flashDev) + return WHAL_EINVAL; + if (!flashDev->driver || !flashDev->driver->Init) + return WHAL_ENOTSUP; + + return flashDev->driver->Init(flashDev); +} + +whal_Error whal_Flash_Deinit(whal_Flash *flashDev) +{ + if (!flashDev) + return WHAL_EINVAL; + if (!flashDev->driver || !flashDev->driver->Deinit) + return WHAL_ENOTSUP; + + return flashDev->driver->Deinit(flashDev); +} + +whal_Error whal_Flash_Lock(whal_Flash *flashDev, size_t addr, size_t len) +{ + if (!flashDev) + return WHAL_EINVAL; + if (!flashDev->driver || !flashDev->driver->Lock) + return WHAL_ENOTSUP; + + return flashDev->driver->Lock(flashDev, addr, len); +} + +whal_Error whal_Flash_Unlock(whal_Flash *flashDev, size_t addr, size_t len) +{ + if (!flashDev) + return WHAL_EINVAL; + if (!flashDev->driver || !flashDev->driver->Unlock) + return WHAL_ENOTSUP; + + return flashDev->driver->Unlock(flashDev, addr, len); +} + +whal_Error whal_Flash_Read(whal_Flash *flashDev, size_t addr, void *data, + size_t dataSz) +{ + if (!flashDev || !data) + return WHAL_EINVAL; + if (!flashDev->driver || !flashDev->driver->Read) + return WHAL_ENOTSUP; + + return flashDev->driver->Read(flashDev, addr, data, dataSz); +} + +whal_Error whal_Flash_Write(whal_Flash *flashDev, size_t addr, const void *data, + size_t dataSz) +{ + if (!flashDev || !data) + return WHAL_EINVAL; + if (!flashDev->driver || !flashDev->driver->Write) + return WHAL_ENOTSUP; + + return flashDev->driver->Write(flashDev, addr, data, dataSz); +} + +whal_Error whal_Flash_Erase(whal_Flash *flashDev, size_t addr, + size_t dataSz) +{ + if (!flashDev) + return WHAL_EINVAL; + if (!flashDev->driver || !flashDev->driver->Erase) + return WHAL_ENOTSUP; + + return flashDev->driver->Erase(flashDev, addr, dataSz); +} + diff --git a/src/flash/stm32l1_flash.c b/src/flash/stm32l1_flash.c new file mode 100644 index 0000000..6fda4d4 --- /dev/null +++ b/src/flash/stm32l1_flash.c @@ -0,0 +1,343 @@ +#include +#include +#include +#include +#include +#include +#include + +/* + * STM32L1 Flash Register Definitions (RM0038 Section 3.9) + * + * The STM32L1 flash uses a PECR-based access model that differs from + * other STM32 families. All program/erase operations are controlled + * through FLASH_PECR bits, and unlocking requires a two-stage key + * sequence (PEKEYR then PRGKEYR). + */ + +#define FLASH_ACR_REG 0x00 +#define FLASH_ACR_LATENCY_Pos 0 +#define FLASH_ACR_LATENCY_Msk (1UL << FLASH_ACR_LATENCY_Pos) +#define FLASH_ACR_PRFTEN_Pos 1 +#define FLASH_ACR_PRFTEN_Msk (1UL << FLASH_ACR_PRFTEN_Pos) +#define FLASH_ACR_ACC64_Pos 2 +#define FLASH_ACR_ACC64_Msk (1UL << FLASH_ACR_ACC64_Pos) + +#define FLASH_BASE_ADDR 0x40023C00 + +#define FLASH_PECR_REG 0x04 +#define FLASH_PECR_PELOCK_Pos 0 +#define FLASH_PECR_PELOCK_Msk (1UL << FLASH_PECR_PELOCK_Pos) +#define FLASH_PECR_PRGLOCK_Pos 1 +#define FLASH_PECR_PRGLOCK_Msk (1UL << FLASH_PECR_PRGLOCK_Pos) +#define FLASH_PECR_PROG_Pos 3 +#define FLASH_PECR_PROG_Msk (1UL << FLASH_PECR_PROG_Pos) +#define FLASH_PECR_ERASE_Pos 9 +#define FLASH_PECR_ERASE_Msk (1UL << FLASH_PECR_ERASE_Pos) + +#define FLASH_PEKEYR_REG 0x0C +#define FLASH_PRGKEYR_REG 0x10 + +#define FLASH_SR_REG 0x18 +#define FLASH_SR_BSY_Pos 0 +#define FLASH_SR_BSY_Msk (1UL << FLASH_SR_BSY_Pos) +#define FLASH_SR_EOP_Pos 1 +#define FLASH_SR_EOP_Msk (1UL << FLASH_SR_EOP_Pos) +#define FLASH_SR_WRPERR_Pos 8 +#define FLASH_SR_WRPERR_Msk (1UL << FLASH_SR_WRPERR_Pos) +#define FLASH_SR_PGAERR_Pos 9 +#define FLASH_SR_PGAERR_Msk (1UL << FLASH_SR_PGAERR_Pos) +#define FLASH_SR_SIZERR_Pos 10 +#define FLASH_SR_SIZERR_Msk (1UL << FLASH_SR_SIZERR_Pos) + +#define FLASH_SR_ALL_ERR (FLASH_SR_WRPERR_Msk | FLASH_SR_PGAERR_Msk | \ + FLASH_SR_SIZERR_Msk) + +/* Unlock keys */ +#define PEKEY1 0x89ABCDEF +#define PEKEY2 0x02030405 +#define PRGKEY1 0x8C9DAEBF +#define PRGKEY2 0x13141516 + +/* Page size: 256 bytes */ +#define PAGE_SIZE 256 +#define PAGE_SHIFT 8 + +#ifdef WHAL_CFG_STM32L1_FLASH_DIRECT_API_MAPPING +#define whal_Stm32l1_Flash_Init whal_Flash_Init +#define whal_Stm32l1_Flash_Deinit whal_Flash_Deinit +#define whal_Stm32l1_Flash_Lock whal_Flash_Lock +#define whal_Stm32l1_Flash_Unlock whal_Flash_Unlock +#define whal_Stm32l1_Flash_Read whal_Flash_Read +#define whal_Stm32l1_Flash_Write whal_Flash_Write +#define whal_Stm32l1_Flash_Erase whal_Flash_Erase +#endif + +/* + * Unlock FLASH_PECR by writing the PEKEY sequence to FLASH_PEKEYR. + * This clears PELOCK if the keys are correct. + */ +static void UnlockPecr(size_t base) +{ + size_t pelock; + whal_Reg_Get(base, FLASH_PECR_REG, FLASH_PECR_PELOCK_Msk, + FLASH_PECR_PELOCK_Pos, &pelock); + if (!pelock) + return; + + whal_Reg_Write(base, FLASH_PEKEYR_REG, PEKEY1); + whal_Reg_Write(base, FLASH_PEKEYR_REG, PEKEY2); +} + +/* + * Unlock program memory by writing the PRGKEY sequence to FLASH_PRGKEYR. + * PECR must be unlocked first. + */ +static void UnlockProgram(size_t base) +{ + size_t prglock; + whal_Reg_Get(base, FLASH_PECR_REG, FLASH_PECR_PRGLOCK_Msk, + FLASH_PECR_PRGLOCK_Pos, &prglock); + if (!prglock) + return; + + whal_Reg_Write(base, FLASH_PRGKEYR_REG, PRGKEY1); + whal_Reg_Write(base, FLASH_PRGKEYR_REG, PRGKEY2); +} + +whal_Error whal_Stm32l1_Flash_Init(whal_Flash *flashDev) +{ + if (!flashDev || !flashDev->cfg) + return WHAL_EINVAL; + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Flash_Deinit(whal_Flash *flashDev) +{ + if (!flashDev) + return WHAL_EINVAL; + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Flash_Lock(whal_Flash *flashDev, size_t addr, size_t len) +{ + (void)addr; + (void)len; + + if (!flashDev) + return WHAL_EINVAL; + + /* Set PELOCK to lock everything */ + whal_Reg_Update(flashDev->regmap.base, FLASH_PECR_REG, + FLASH_PECR_PELOCK_Msk, + whal_SetBits(FLASH_PECR_PELOCK_Msk, + FLASH_PECR_PELOCK_Pos, 1)); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Flash_Unlock(whal_Flash *flashDev, size_t addr, size_t len) +{ + size_t base, pelock, prglock; + + (void)addr; + (void)len; + + if (!flashDev) + return WHAL_EINVAL; + + base = flashDev->regmap.base; + + /* Unlock PECR then program memory */ + UnlockPecr(base); + UnlockProgram(base); + + /* Verify both lock bits cleared. If either is still set, the key + * sequence was rejected — typically because of a prior bad write. */ + whal_Reg_Get(base, FLASH_PECR_REG, FLASH_PECR_PELOCK_Msk, + FLASH_PECR_PELOCK_Pos, &pelock); + whal_Reg_Get(base, FLASH_PECR_REG, FLASH_PECR_PRGLOCK_Msk, + FLASH_PECR_PRGLOCK_Pos, &prglock); + if (pelock || prglock) + return WHAL_EHARDWARE; + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Flash_Read(whal_Flash *flashDev, size_t addr, void *data, + size_t dataSz) +{ + whal_Stm32l1_Flash_Cfg *cfg; + uint8_t *dataBuf = (uint8_t *)data; + + if (!flashDev || !flashDev->cfg || !data) + return WHAL_EINVAL; + + if (dataSz == 0) + return WHAL_SUCCESS; + + cfg = flashDev->cfg; + + if (addr < cfg->startAddr || addr + dataSz > cfg->startAddr + cfg->size) + return WHAL_EINVAL; + + /* Flash is memory-mapped, direct read */ + uint8_t *flashAddr = (uint8_t *)addr; + for (size_t i = 0; i < dataSz; ++i) + dataBuf[i] = flashAddr[i]; + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Flash_Write(whal_Flash *flashDev, size_t addr, + const void *data, size_t dataSz) +{ + whal_Stm32l1_Flash_Cfg *cfg; + const uint8_t *buf = (const uint8_t *)data; + size_t base; + whal_Error err = WHAL_SUCCESS; + + if (!flashDev || !flashDev->cfg || !data) + return WHAL_EINVAL; + + if (dataSz == 0) + return WHAL_SUCCESS; + + /* Word-aligned writes only */ + if ((addr & 0x3) || (dataSz & 0x3)) + return WHAL_EINVAL; + + cfg = flashDev->cfg; + base = flashDev->regmap.base; + + if (addr < cfg->startAddr || addr + dataSz > cfg->startAddr + cfg->size) + return WHAL_EINVAL; + + /* Clear error flags */ + whal_Reg_Write(base, FLASH_SR_REG, FLASH_SR_ALL_ERR); + + /* + * Fast Word Write: PECR and program memory must already be unlocked. + * Simply write a 32-bit word to the flash address; the hardware + * performs the programming automatically. + */ + for (size_t i = 0; i < dataSz; i += 4) { + volatile uint32_t *flashAddr = (volatile uint32_t *)(addr + i); + uint32_t word = (uint32_t)buf[i] + | ((uint32_t)buf[i + 1] << 8) + | ((uint32_t)buf[i + 2] << 16) + | ((uint32_t)buf[i + 3] << 24); + + *flashAddr = word; + + err = whal_Reg_ReadPoll(base, FLASH_SR_REG, FLASH_SR_BSY_Msk, 0, + cfg->timeout); + if (err) + return err; + + if (whal_Reg_Read(base, FLASH_SR_REG) & FLASH_SR_ALL_ERR) + return WHAL_EHARDWARE; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32l1_Flash_Erase(whal_Flash *flashDev, size_t addr, + size_t dataSz) +{ + whal_Stm32l1_Flash_Cfg *cfg; + size_t base; + whal_Error err = WHAL_SUCCESS; + + if (!flashDev || !flashDev->cfg) + return WHAL_EINVAL; + + if (dataSz == 0) + return WHAL_SUCCESS; + + cfg = flashDev->cfg; + base = flashDev->regmap.base; + + if (addr < cfg->startAddr || addr + dataSz > cfg->startAddr + cfg->size) + return WHAL_EINVAL; + + err = whal_Reg_ReadPoll(base, FLASH_SR_REG, FLASH_SR_BSY_Msk, 0, + cfg->timeout); + if (err) + return err; + + whal_Reg_Write(base, FLASH_SR_REG, FLASH_SR_ALL_ERR); + + size_t startPage = (addr - cfg->startAddr) >> PAGE_SHIFT; + size_t endPage = ((addr - cfg->startAddr) + dataSz - 1) >> PAGE_SHIFT; + + whal_Reg_Update(base, FLASH_PECR_REG, FLASH_PECR_ERASE_Msk, + whal_SetBits(FLASH_PECR_ERASE_Msk, + FLASH_PECR_ERASE_Pos, 1)); + whal_Reg_Update(base, FLASH_PECR_REG, FLASH_PECR_PROG_Msk, + whal_SetBits(FLASH_PECR_PROG_Msk, + FLASH_PECR_PROG_Pos, 1)); + + for (size_t page = startPage; page <= endPage; ++page) { + volatile uint32_t *pageAddr = + (volatile uint32_t *)(cfg->startAddr + (page << PAGE_SHIFT)); + *pageAddr = 0x00000000; + + err = whal_Reg_ReadPoll(base, FLASH_SR_REG, FLASH_SR_BSY_Msk, 0, + cfg->timeout); + if (err) + goto cleanup; + + if (whal_Reg_Read(base, FLASH_SR_REG) & FLASH_SR_ALL_ERR) { + err = WHAL_EHARDWARE; + goto cleanup; + } + } + +cleanup: + whal_Reg_Update(base, FLASH_PECR_REG, + FLASH_PECR_ERASE_Msk | FLASH_PECR_PROG_Msk, 0); + + return err; +} + +whal_Error whal_Stm32l1_Flash_Ext_SetLatency(whal_Stm32l1_Flash_Latency latency) +{ + size_t val; + + whal_Reg_Update(FLASH_BASE_ADDR, FLASH_ACR_REG, FLASH_ACR_ACC64_Msk, + whal_SetBits(FLASH_ACR_ACC64_Msk, + FLASH_ACR_ACC64_Pos, 1)); + do { + whal_Reg_Get(FLASH_BASE_ADDR, FLASH_ACR_REG, + FLASH_ACR_ACC64_Msk, FLASH_ACR_ACC64_Pos, &val); + } while (!val); + + whal_Reg_Update(FLASH_BASE_ADDR, FLASH_ACR_REG, FLASH_ACR_PRFTEN_Msk, + whal_SetBits(FLASH_ACR_PRFTEN_Msk, + FLASH_ACR_PRFTEN_Pos, 1)); + + whal_Reg_Update(FLASH_BASE_ADDR, FLASH_ACR_REG, FLASH_ACR_LATENCY_Msk, + whal_SetBits(FLASH_ACR_LATENCY_Msk, + FLASH_ACR_LATENCY_Pos, latency)); + do { + whal_Reg_Get(FLASH_BASE_ADDR, FLASH_ACR_REG, + FLASH_ACR_LATENCY_Msk, FLASH_ACR_LATENCY_Pos, &val); + } while (val != (size_t)latency); + + return WHAL_SUCCESS; +} + +#ifndef WHAL_CFG_STM32L1_FLASH_DIRECT_API_MAPPING +const whal_FlashDriver whal_Stm32l1_Flash_Driver = { + .Init = whal_Stm32l1_Flash_Init, + .Deinit = whal_Stm32l1_Flash_Deinit, + .Lock = whal_Stm32l1_Flash_Lock, + .Unlock = whal_Stm32l1_Flash_Unlock, + .Read = whal_Stm32l1_Flash_Read, + .Write = whal_Stm32l1_Flash_Write, + .Erase = whal_Stm32l1_Flash_Erase, +}; +#endif /* !WHAL_CFG_STM32L1_FLASH_DIRECT_API_MAPPING */ diff --git a/src/flash/stm32wb_flash.c b/src/flash/stm32wb_flash.c new file mode 100644 index 0000000..d5edaf1 --- /dev/null +++ b/src/flash/stm32wb_flash.c @@ -0,0 +1,322 @@ +#include +#include +#include +#include +#include +#include + +/* + * STM32WB Flash Register Definitions + * + * The flash controller manages embedded flash memory operations including + * programming, erasing, and access control. Flash is organized in 4 KB pages. + */ + +/* Access Control Register - configures latency and caches */ +#define FLASH_ACR_REG 0x00 +#define FLASH_ACR_LATENCY_Pos 0 /* Wait states (0-3) */ +#define FLASH_ACR_LATENCY_Msk (WHAL_BITMASK(3) << FLASH_ACR_LATENCY_Pos) + +/* Key Register - unlock sequence for write operations */ +#define FLASH_KEYR_REG 0x08 +#define FLASH_KEYR_KEY_Msk (~0UL) + +/* Status Register - operation status and error flags */ +#define FLASH_SR_REG 0x10 +#define FLASH_SR_EOP_Pos 0 /* End of operation */ +#define FLASH_SR_EOP_Msk (1UL << FLASH_SR_EOP_Pos) + +#define FLASH_SR_OP_ERR_Pos 1 /* Operation error */ +#define FLASH_SR_OP_ERR_Msk (1UL << FLASH_SR_OP_ERR_Pos) + +#define FLASH_SR_PROG_ERR_Pos 3 /* Programming error */ +#define FLASH_SR_PROG_ERR_Msk (1UL << FLASH_SR_PROG_ERR_Pos) + +#define FLASH_SR_WRP_ERR_Pos 4 /* Write protection error */ +#define FLASH_SR_WRP_ERR_Msk (1UL << FLASH_SR_WRP_ERR_Pos) + +#define FLASH_SR_PGA_ERR_Pos 5 /* Programming alignment error */ +#define FLASH_SR_PGA_ERR_Msk (1UL << FLASH_SR_PGA_ERR_Pos) + +#define FLASH_SR_SIZ_ERR_Pos 6 /* Size error */ +#define FLASH_SR_SIZ_ERR_Msk (1UL << FLASH_SR_SIZ_ERR_Pos) + +#define FLASH_SR_PGS_ERR_Pos 7 /* Programming sequence error */ +#define FLASH_SR_PGS_ERR_Msk (1UL << FLASH_SR_PGS_ERR_Pos) + +#define FLASH_SR_MISS_ERR_Pos 8 /* Fast programming miss error */ +#define FLASH_SR_MISS_ERR_Msk (1UL << FLASH_SR_MISS_ERR_Pos) + +#define FLASH_SR_FAST_ERR_Pos 9 /* Fast programming error */ +#define FLASH_SR_FAST_ERR_Msk (1UL << FLASH_SR_FAST_ERR_Pos) + +#define FLASH_SR_RD_ERR_Pos 14 /* Read protection error */ +#define FLASH_SR_RD_ERR_Msk (1UL << FLASH_SR_RD_ERR_Pos) + +#define FLASH_SR_OPTV_ERR_Pos 15 /* Option validity error */ +#define FLASH_SR_OPTV_ERR_Msk (1UL << FLASH_SR_OPTV_ERR_Pos) + +#define FLASH_SR_BSY_Pos 16 /* Busy flag */ +#define FLASH_SR_BSY_Msk (1UL << FLASH_SR_BSY_Pos) + +#define FLASH_SR_CFGBSY_Pos 18 /* Configuration busy */ +#define FLASH_SR_CFGBSY_Msk (1UL << FLASH_SR_CFGBSY_Pos) + +#define FLASH_SR_PESD_Pos 19 /* Programming/erase suspended */ +#define FLASH_SR_PESD_Msk (1UL << FLASH_SR_PESD_Pos) + +/* Combined mask for all error flags */ +#define FLASH_SR_ALL_ERR (FLASH_SR_OP_ERR_Msk | FLASH_SR_PROG_ERR_Msk | FLASH_SR_WRP_ERR_Msk | \ + FLASH_SR_PGA_ERR_Msk | FLASH_SR_SIZ_ERR_Msk | FLASH_SR_PGS_ERR_Msk | \ + FLASH_SR_MISS_ERR_Msk | FLASH_SR_FAST_ERR_Msk | FLASH_SR_RD_ERR_Msk | \ + FLASH_SR_OPTV_ERR_Msk) + +/* Control Register - enables operations and selects pages */ +#define FLASH_CR_REG 0x14 +#define FLASH_CR_PG_Pos 0 /* Programming enable */ +#define FLASH_CR_PG_Msk (1UL << FLASH_CR_PG_Pos) + +#define FLASH_CR_PER_Pos 1 /* Page erase enable */ +#define FLASH_CR_PER_Msk (1UL << FLASH_CR_PER_Pos) + +#define FLASH_CR_PNB_Pos 3 /* Page number for erase */ +#define FLASH_CR_PNB_Msk (WHAL_BITMASK(8) << FLASH_CR_PNB_Pos) + +#define FLASH_CR_STRT_Pos 16 /* Start erase operation */ +#define FLASH_CR_STRT_Msk (1UL << FLASH_CR_STRT_Pos) + +#define FLASH_CR_LOCK_Pos 31 /* Lock flash control */ +#define FLASH_CR_LOCK_Msk (1UL << FLASH_CR_LOCK_Pos) + +#ifdef WHAL_CFG_STM32WB_FLASH_DIRECT_API_MAPPING +#define whal_Stm32wb_Flash_Init whal_Flash_Init +#define whal_Stm32wb_Flash_Deinit whal_Flash_Deinit +#define whal_Stm32wb_Flash_Lock whal_Flash_Lock +#define whal_Stm32wb_Flash_Unlock whal_Flash_Unlock +#define whal_Stm32wb_Flash_Read whal_Flash_Read +#define whal_Stm32wb_Flash_Write whal_Flash_Write +#define whal_Stm32wb_Flash_Erase whal_Flash_Erase +#endif /* WHAL_CFG_STM32WB_FLASH_DIRECT_API_MAPPING */ + +whal_Error whal_Stm32wb_Flash_Init(whal_Flash *flashDev) +{ + if (!flashDev) { + return WHAL_EINVAL; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Flash_Deinit(whal_Flash *flashDev) +{ + if (!flashDev) { + return WHAL_EINVAL; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Flash_Lock(whal_Flash *flashDev, size_t addr, size_t len) +{ + const whal_Regmap *regmap; + + (void)addr; + (void)len; + + if (!flashDev) { + return WHAL_EINVAL; + } + + regmap = &flashDev->regmap; + + /* Setting LOCK bit prevents further flash modifications until next unlock */ + whal_Reg_Update(regmap->base, FLASH_CR_REG, FLASH_CR_LOCK_Msk, + whal_SetBits(FLASH_CR_LOCK_Msk, FLASH_CR_LOCK_Pos, 1)); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Flash_Unlock(whal_Flash *flashDev, size_t addr, size_t len) +{ + const whal_Regmap *regmap; + + (void)addr; + (void)len; + + if (!flashDev) { + return WHAL_EINVAL; + } + + regmap = &flashDev->regmap; + + /* + * Unlock sequence: write KEY1 then KEY2 to KEYR register. + * Incorrect sequence or order will trigger a bus error. + */ + whal_Reg_Update(regmap->base, FLASH_KEYR_REG, FLASH_KEYR_KEY_Msk, 0x45670123); + whal_Reg_Update(regmap->base, FLASH_KEYR_REG, FLASH_KEYR_KEY_Msk, 0xCDEF89AB); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Flash_Read(whal_Flash *flashDev, size_t addr, void *data, + size_t dataSz) +{ + whal_Stm32wb_Flash_Cfg *cfg; + uint8_t *dataBuf = (uint8_t *)data; + + if (!flashDev || !flashDev->cfg || !data) + return WHAL_EINVAL; + + if (dataSz == 0) + return WHAL_SUCCESS; + + cfg = flashDev->cfg; + + if (addr < cfg->startAddr || addr + dataSz > cfg->startAddr + cfg->size) + return WHAL_EINVAL; + + /* Flash is memory-mapped, so reading is a simple memory copy */ + uint8_t *flashAddr = (uint8_t *)addr; + for (size_t i = 0; i < dataSz; ++i) { + dataBuf[i] = flashAddr[i]; + } + return WHAL_SUCCESS; +} + +/* + * Internal helper for write and erase operations. + * + * For write (write=1): Programs data in 64-bit (8 byte) chunks. + * For erase (write=0): Erases 4 KB pages covering the address range. + */ +static whal_Error whal_Stm32wb_Flash_WriteOrErase(whal_Flash *flashDev, size_t addr, const uint8_t *data, + size_t dataSz, uint8_t write) +{ + whal_Stm32wb_Flash_Cfg *cfg; + const whal_Regmap *regmap; + size_t bsy; + size_t pesd; + + if (!flashDev || !flashDev->cfg) { + return WHAL_EINVAL; + } + + cfg = flashDev->cfg; + regmap = &flashDev->regmap; + + if (dataSz == 0) + return WHAL_SUCCESS; + + /* Validate address alignment and bounds */ + if (addr & 0x7 || addr < cfg->startAddr || addr + dataSz > cfg->startAddr + cfg->size || + (write && (data == NULL || (dataSz & 0x7)))) { + return WHAL_EINVAL; + } + + /* Check if flash is busy or suspended */ + whal_Reg_Get(regmap->base, FLASH_SR_REG, FLASH_SR_BSY_Msk, FLASH_SR_BSY_Pos, &bsy); + whal_Reg_Get(regmap->base, FLASH_SR_REG, FLASH_SR_PESD_Msk, FLASH_SR_PESD_Pos, &pesd); + + if (bsy || pesd) { + return WHAL_ENOTREADY; + } + + /* Clear all error flags by writing 1 to each */ + whal_Reg_Update(regmap->base, FLASH_SR_REG, FLASH_SR_ALL_ERR, 0xffffffff); + + whal_Error err = WHAL_SUCCESS; + + if (write) { + /* Enable flash programming mode */ + whal_Reg_Update(regmap->base, FLASH_CR_REG, FLASH_CR_PG_Msk, 1); + + /* Program data in 64-bit (8 byte) double-word chunks */ + for (size_t i = 0; i < dataSz; i += 8) { + uint32_t *flashAddr = (uint32_t *)(addr + i); + uint32_t *dataAddr = (uint32_t *)(data + i); + + /* Write both 32-bit words to trigger the 64-bit programming */ + flashAddr[0] = dataAddr[0]; + flashAddr[1] = dataAddr[1]; + + /* Wait for programming to complete */ + err = whal_Reg_ReadPoll(regmap->base, FLASH_SR_REG, + FLASH_SR_CFGBSY_Msk, 0, cfg->timeout); + if (err) + goto cleanup; + } + } + else { + /* Calculate page range to erase (4 KB per page) */ + size_t startPage, endPage; + startPage = (addr - cfg->startAddr) >> 12; + endPage = ((addr - cfg->startAddr) + dataSz - 1) >> 12; + + /* Enable page erase mode */ + whal_Reg_Update(regmap->base, FLASH_CR_REG, FLASH_CR_PER_Msk, + whal_SetBits(FLASH_CR_PER_Msk, FLASH_CR_PER_Pos, 1)); + + /* Erase each page in the range */ + for (size_t page = startPage; page <= endPage; ++page) { + /* Select page number */ + whal_Reg_Update(regmap->base, FLASH_CR_REG, FLASH_CR_PNB_Msk, + whal_SetBits(FLASH_CR_PNB_Msk, FLASH_CR_PNB_Pos, page)); + + /* Start erase operation */ + whal_Reg_Update(regmap->base, FLASH_CR_REG, FLASH_CR_STRT_Msk, + whal_SetBits(FLASH_CR_STRT_Msk, FLASH_CR_STRT_Pos, 1)); + + /* Wait for erase to complete */ + err = whal_Reg_ReadPoll(regmap->base, FLASH_SR_REG, + FLASH_SR_CFGBSY_Msk, 0, cfg->timeout); + if (err) + goto cleanup; + } + + /* Disable page erase mode */ + whal_Reg_Update(regmap->base, FLASH_CR_REG, FLASH_CR_PER_Msk, + whal_SetBits(FLASH_CR_PER_Msk, FLASH_CR_PER_Pos, 0)); + } + +cleanup: + /* Disable flash programming mode */ + whal_Reg_Update(regmap->base, FLASH_CR_REG, FLASH_CR_PG_Msk, 0); + + return err; +} + +whal_Error whal_Stm32wb_Flash_Write(whal_Flash *flashDev, size_t addr, const void *data, + size_t dataSz) +{ + return whal_Stm32wb_Flash_WriteOrErase(flashDev, addr, (const uint8_t *)data, dataSz, 1); +} + +whal_Error whal_Stm32wb_Flash_Erase(whal_Flash *flashDev, size_t addr, + size_t dataSz) +{ + return whal_Stm32wb_Flash_WriteOrErase(flashDev, addr, NULL, dataSz, 0); +} + +whal_Error whal_Stm32wb_Flash_Ext_SetLatency(whal_Flash *flashDev, enum whal_Stm32wb_Flash_Latency latency) +{ + if (!flashDev) { + return WHAL_EINVAL; + } + + const whal_Regmap *reg = &flashDev->regmap; + whal_Reg_Update(reg->base, FLASH_ACR_REG, FLASH_ACR_LATENCY_Msk, latency); + return WHAL_SUCCESS; +} + +#ifndef WHAL_CFG_STM32WB_FLASH_DIRECT_API_MAPPING +const whal_FlashDriver whal_Stm32wb_Flash_Driver = { + .Init = whal_Stm32wb_Flash_Init, + .Deinit = whal_Stm32wb_Flash_Deinit, + .Lock = whal_Stm32wb_Flash_Lock, + .Unlock = whal_Stm32wb_Flash_Unlock, + .Read = whal_Stm32wb_Flash_Read, + .Write = whal_Stm32wb_Flash_Write, + .Erase = whal_Stm32wb_Flash_Erase, +}; +#endif /* !WHAL_CFG_STM32WB_FLASH_DIRECT_API_MAPPING */ diff --git a/src/gpio/gpio.c b/src/gpio/gpio.c new file mode 100644 index 0000000..527b5cb --- /dev/null +++ b/src/gpio/gpio.c @@ -0,0 +1,43 @@ +#include +#include + +whal_Error whal_Gpio_Init(whal_Gpio *gpioDev) +{ + if (!gpioDev) + return WHAL_EINVAL; + if (!gpioDev->driver || !gpioDev->driver->Init) + return WHAL_ENOTSUP; + + return gpioDev->driver->Init(gpioDev); +} + +whal_Error whal_Gpio_Deinit(whal_Gpio *gpioDev) +{ + if (!gpioDev) + return WHAL_EINVAL; + if (!gpioDev->driver || !gpioDev->driver->Deinit) + return WHAL_ENOTSUP; + + return gpioDev->driver->Deinit(gpioDev); +} + +whal_Error whal_Gpio_Get(whal_Gpio *gpioDev, size_t pin, size_t *value) +{ + if (!gpioDev || !value) + return WHAL_EINVAL; + if (!gpioDev->driver || !gpioDev->driver->Get) + return WHAL_ENOTSUP; + + return gpioDev->driver->Get(gpioDev, pin, value); +} + +whal_Error whal_Gpio_Set(whal_Gpio *gpioDev, size_t pin, size_t value) +{ + if (!gpioDev) + return WHAL_EINVAL; + if (!gpioDev->driver || !gpioDev->driver->Set) + return WHAL_ENOTSUP; + + return gpioDev->driver->Set(gpioDev, pin, value); +} + diff --git a/src/gpio/stm32l1_gpio.c b/src/gpio/stm32l1_gpio.c new file mode 100644 index 0000000..7ddf812 --- /dev/null +++ b/src/gpio/stm32l1_gpio.c @@ -0,0 +1 @@ +#include "stm32wb_gpio.c" diff --git a/src/gpio/stm32wb_gpio.c b/src/gpio/stm32wb_gpio.c new file mode 100644 index 0000000..9861c83 --- /dev/null +++ b/src/gpio/stm32wb_gpio.c @@ -0,0 +1,199 @@ +#include +#include +#include +#include +#include +#include + +/* + * STM32WB GPIO Register Definitions + * + * Each GPIO port has a 0x400 byte register block. Register offsets + * are relative to the port base address. + */ + +/* Size of each GPIO port register block */ +#define GPIO_PORT_SIZE 0x400 + +/* Mode register - 2 bits per pin, selects input/output/altfn/analog */ +#define GPIO_MODE_REG 0x00 +/* Output type register - 1 bit per pin, push-pull or open-drain */ +#define GPIO_OUTTYPE_REG 0x04 +/* Output speed register - 2 bits per pin */ +#define GPIO_SPEED_REG 0x08 +/* Pull-up/pull-down register - 2 bits per pin */ +#define GPIO_PULL_REG 0x0C +/* Input data register - read-only, 1 bit per pin */ +#define GPIO_IDR_REG 0x10 +/* Output data register - 1 bit per pin */ +#define GPIO_ODR_REG 0x14 +/* Alternate function low register - 4 bits per pin for pins 0-7 */ +#define GPIO_ALTFNL_REG 0x20 +/* Alternate function high register - 4 bits per pin for pins 8-15 */ +#define GPIO_ALTFNH_REG 0x24 + +#if defined(WHAL_CFG_STM32WB_GPIO_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32F4_GPIO_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32H5_GPIO_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32C0_GPIO_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32F0_GPIO_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32F3_GPIO_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32L1_GPIO_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32N6_GPIO_DIRECT_API_MAPPING) +#define whal_Stm32wb_Gpio_Init whal_Gpio_Init +#define whal_Stm32wb_Gpio_Deinit whal_Gpio_Deinit +#define whal_Stm32wb_Gpio_Get whal_Gpio_Get +#define whal_Stm32wb_Gpio_Set whal_Gpio_Set +#endif /* WHAL_CFG_GPIO_API_MAPPING */ + +/* + * Configure alternate function for a pin. + * + * The AFRL (pins 0-7) and AFRH (pins 8-15) registers are combined into + * a 64-bit value for easier manipulation. Each pin uses 4 bits to select + * one of 16 alternate functions (AF0-AF15). + */ +/* + * Initialize a single GPIO pin with the specified configuration. + */ +static inline whal_Error whal_Stm32wb_Gpio_InitPin(whal_Gpio *gpioDev, + whal_Stm32wb_Gpio_PinCfg cfg) +{ + uint8_t pin = WHAL_STM32WB_GPIO_GET_PIN(cfg); + size_t portBase; + uint8_t pos2; + size_t mask2, mask1; + + if (pin > 15) + return WHAL_EINVAL; + + portBase = gpioDev->regmap.base + + (WHAL_STM32WB_GPIO_GET_PORT(cfg) * GPIO_PORT_SIZE); + pos2 = pin << 1; + mask2 = WHAL_BITMASK(2) << pos2; + mask1 = 1UL << pin; + + whal_Reg_Update(portBase, GPIO_MODE_REG, mask2, + WHAL_STM32WB_GPIO_GET_MODE(cfg) << pos2); + + whal_Reg_Update(portBase, GPIO_SPEED_REG, mask2, + WHAL_STM32WB_GPIO_GET_SPEED(cfg) << pos2); + + whal_Reg_Update(portBase, GPIO_PULL_REG, mask2, + WHAL_STM32WB_GPIO_GET_PULL(cfg) << pos2); + + whal_Reg_Update(portBase, GPIO_OUTTYPE_REG, mask1, + WHAL_STM32WB_GPIO_GET_OUTTYPE(cfg) << pin); + + if (WHAL_STM32WB_GPIO_GET_MODE(cfg) == WHAL_STM32WB_GPIO_MODE_ALTFN) { + uint8_t afPos = (pin & 7) << 2; + + whal_Reg_Update(portBase, + (pin < 8) ? GPIO_ALTFNL_REG : GPIO_ALTFNH_REG, + WHAL_BITMASK(4) << afPos, + WHAL_STM32WB_GPIO_GET_ALTFN(cfg) << afPos); + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Gpio_Init(whal_Gpio *gpioDev) +{ + whal_Error err; + whal_Stm32wb_Gpio_Cfg *cfg; + whal_Stm32wb_Gpio_PinCfg *pinCfg; + + if (!gpioDev || !gpioDev->cfg) { + return WHAL_EINVAL; + } + + cfg = (whal_Stm32wb_Gpio_Cfg *)gpioDev->cfg; + pinCfg = cfg->pinCfg; + + /* Initialize each pin in the configuration array */ + for (size_t pin = 0; pin < cfg->pinCount; ++pin) { + err = whal_Stm32wb_Gpio_InitPin(gpioDev, pinCfg[pin]); + if (err) { + return err; + } + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Gpio_Deinit(whal_Gpio *gpioDev) +{ + if (!gpioDev) { + return WHAL_EINVAL; + } + + return WHAL_SUCCESS; +} + +/* + * Shared helper for reading or writing GPIO pin values. + * + * For set operations (set=1): writes value to ODR register + * For get operations (set=0): reads value from IDR register + */ +static whal_Error whal_Stm32wb_Gpio_SetOrGet(whal_Gpio *gpioDev, size_t idx, + size_t *value, uint8_t set) +{ + whal_Stm32wb_Gpio_Cfg *cfg; + whal_Stm32wb_Gpio_PinCfg pinCfg; + uint8_t port, pin; + size_t portBase, mask; + + if (!gpioDev || !gpioDev->cfg || !value) { + return WHAL_EINVAL; + } + + cfg = (whal_Stm32wb_Gpio_Cfg *)gpioDev->cfg; + if (idx >= cfg->pinCount) { + return WHAL_EINVAL; + } + pinCfg = cfg->pinCfg[idx]; + port = WHAL_STM32WB_GPIO_GET_PORT(pinCfg); + pin = WHAL_STM32WB_GPIO_GET_PIN(pinCfg); + + if (pin > 15) { + return WHAL_EINVAL; + } + + portBase = gpioDev->regmap.base + (port * GPIO_PORT_SIZE); + mask = 1UL << pin; + + if (set) { + whal_Reg_Update(portBase, GPIO_ODR_REG, mask, + whal_SetBits(mask, pin, *value)); + } else { + whal_Reg_Get(portBase, GPIO_IDR_REG, mask, pin, value); + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Gpio_Get(whal_Gpio *gpioDev, size_t pin, size_t *value) +{ + return whal_Stm32wb_Gpio_SetOrGet(gpioDev, pin, value, 0); +} + +whal_Error whal_Stm32wb_Gpio_Set(whal_Gpio *gpioDev, size_t pin, size_t value) +{ + return whal_Stm32wb_Gpio_SetOrGet(gpioDev, pin, &value, 1); +} + +#if !defined(WHAL_CFG_STM32WB_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32F4_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32H5_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32C0_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32F0_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32F3_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32L1_GPIO_DIRECT_API_MAPPING) +const whal_GpioDriver whal_Stm32wb_Gpio_Driver = { + .Init = whal_Stm32wb_Gpio_Init, + .Deinit = whal_Stm32wb_Gpio_Deinit, + .Get = whal_Stm32wb_Gpio_Get, + .Set = whal_Stm32wb_Gpio_Set, +}; +#endif /* !WHAL_CFG_GPIO_API_MAPPING */ diff --git a/src/irq/cortex_m_nvic.c b/src/irq/cortex_m_nvic.c new file mode 100644 index 0000000..84813af --- /dev/null +++ b/src/irq/cortex_m_nvic.c @@ -0,0 +1,86 @@ +#include +#include +#include + +/* + * ARM Cortex-M NVIC register offsets (relative to 0xE000E100). + * + * ISER: Interrupt Set-Enable Registers (0x000-0x01C, 32 IRQs per register) + * ICER: Interrupt Clear-Enable Registers (0x080-0x09C) + * IPR: Interrupt Priority Registers (0x300-0x37F, 4 IRQs per register) + */ +#define NVIC_ISER_REG(irq) (0x000 + (((irq) >> 5) << 2)) +#define NVIC_ICER_REG(irq) (0x080 + (((irq) >> 5) << 2)) +#define NVIC_IPR_REG(irq) (0x300 + (((irq) >> 2) << 2)) +#define NVIC_IPR_SHIFT(irq) (((irq) & 0x3) << 3) + +#ifdef WHAL_CFG_NVIC_IRQ_DIRECT_API_MAPPING +#define whal_Nvic_Init whal_Irq_Init +#define whal_Nvic_Deinit whal_Irq_Deinit +#define whal_Nvic_Enable whal_Irq_Enable +#define whal_Nvic_Disable whal_Irq_Disable +#endif /* WHAL_CFG_NVIC_IRQ_DIRECT_API_MAPPING */ + +whal_Error whal_Nvic_Init(whal_Irq *irqDev) +{ + if (!irqDev) { + return WHAL_EINVAL; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Nvic_Deinit(whal_Irq *irqDev) +{ + if (!irqDev) { + return WHAL_EINVAL; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Nvic_Enable(whal_Irq *irqDev, size_t irq, + const void *irqCfg) +{ + if (!irqDev) { + return WHAL_EINVAL; + } + + size_t base = irqDev->regmap.base; + + /* Set priority if config provided */ + if (irqCfg) { + const whal_Nvic_Cfg *cfg = (const whal_Nvic_Cfg *)irqCfg; + size_t shift = NVIC_IPR_SHIFT(irq); + size_t mask = (0xFFUL << shift); + whal_Reg_Update(base, NVIC_IPR_REG(irq), mask, + (size_t)(cfg->priority << 4) << shift); + } + + /* Enable the interrupt */ + whal_Reg_Write(base, NVIC_ISER_REG(irq), (1UL << (irq & 0x1F))); + + return WHAL_SUCCESS; +} + +whal_Error whal_Nvic_Disable(whal_Irq *irqDev, size_t irq) +{ + if (!irqDev) { + return WHAL_EINVAL; + } + + size_t base = irqDev->regmap.base; + + whal_Reg_Write(base, NVIC_ICER_REG(irq), (1UL << (irq & 0x1F))); + + return WHAL_SUCCESS; +} + +#ifndef WHAL_CFG_NVIC_IRQ_DIRECT_API_MAPPING +const whal_IrqDriver whal_Nvic_Driver = { + .Init = whal_Nvic_Init, + .Deinit = whal_Nvic_Deinit, + .Enable = whal_Nvic_Enable, + .Disable = whal_Nvic_Disable, +}; +#endif /* !WHAL_CFG_NVIC_IRQ_DIRECT_API_MAPPING */ diff --git a/src/power/stm32l1_pwr.c b/src/power/stm32l1_pwr.c new file mode 100644 index 0000000..56befb1 --- /dev/null +++ b/src/power/stm32l1_pwr.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +#define PWR_CR_REG 0x00 +#define PWR_CR_VOS_Pos 11 +#define PWR_CR_VOS_Msk (WHAL_BITMASK(2) << PWR_CR_VOS_Pos) +#define PWR_CSR_REG 0x04 +#define PWR_CSR_VOSF_Pos 4 +#define PWR_CSR_VOSF_Msk (1UL << PWR_CSR_VOSF_Pos) + +whal_Error whal_Stm32l1_Pwr_SetVosRange(whal_Power *powerDev, + whal_Stm32l1_Pwr_VosRange range, + whal_Timeout *timeout) +{ + size_t base; + whal_Error err; + + if (!powerDev) + return WHAL_EINVAL; + + base = powerDev->regmap.base; + + err = whal_Reg_ReadPoll(base, PWR_CSR_REG, PWR_CSR_VOSF_Msk, 0, timeout); + if (err) + return err; + + whal_Reg_Update(base, PWR_CR_REG, PWR_CR_VOS_Msk, + whal_SetBits(PWR_CR_VOS_Msk, PWR_CR_VOS_Pos, range)); + + return whal_Reg_ReadPoll(base, PWR_CSR_REG, PWR_CSR_VOSF_Msk, 0, timeout); +} diff --git a/src/timer/systick.c b/src/timer/systick.c new file mode 100644 index 0000000..03cbd36 --- /dev/null +++ b/src/timer/systick.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +#define SYSTICK_CSR_REG 0x00 +#define SYSTICK_CSR_ENABLE_Pos 0 +#define SYSTICK_CSR_ENABLE_Msk (1UL << SYSTICK_CSR_ENABLE_Pos) + +#define SYSTICK_CSR_TICKINT_Pos 1 +#define SYSTICK_CSR_TICKINT_Msk (1UL << SYSTICK_CSR_TICKINT_Pos) + +#define SYSTICK_CSR_CLKSOURCE_Pos 2 +#define SYSTICK_CSR_CLKSOURCE_Msk (1UL << SYSTICK_CSR_CLKSOURCE_Pos) + +#define SYSTICK_CSR_COUNTFLAG_Pos 16 +#define SYSTICK_CSR_COUNTFLAG_Msk (1UL << SYSTICK_CSR_COUNTFLAG_Pos) + +#define SYSTICK_RVR_REG 0x04 +#define SYSTICK_RVR_RELOAD_Pos 0 +#define SYSTICK_RVR_RELOAD_Msk (WHAL_BITMASK(24) << SYSTICK_RVR_RELOAD_Pos) + +#ifdef WHAL_CFG_SYSTICK_TIMER_DIRECT_API_MAPPING +#define whal_SysTick_Init whal_Timer_Init +#define whal_SysTick_Deinit whal_Timer_Deinit +#define whal_SysTick_Start whal_Timer_Start +#define whal_SysTick_Stop whal_Timer_Stop +#define whal_SysTick_Reset whal_Timer_Reset +#endif /* WHAL_CFG_SYSTICK_TIMER_DIRECT_API_MAPPING */ + +whal_Error whal_SysTick_Init(whal_Timer *timerDev) +{ + whal_SysTick_Cfg *cfg; + const whal_Regmap *reg; + + if (!timerDev || !timerDev->cfg) { + return WHAL_EINVAL; + } + + reg = &timerDev->regmap; + + cfg = (whal_SysTick_Cfg *)timerDev->cfg; + + whal_Reg_Update(reg->base, SYSTICK_CSR_REG, + SYSTICK_CSR_CLKSOURCE_Msk | SYSTICK_CSR_TICKINT_Msk, + whal_SetBits(SYSTICK_CSR_CLKSOURCE_Msk, SYSTICK_CSR_CLKSOURCE_Pos, cfg->clkSrc) | + whal_SetBits(SYSTICK_CSR_TICKINT_Msk, SYSTICK_CSR_TICKINT_Pos, cfg->tickInt)); + + whal_Reg_Update(reg->base, SYSTICK_RVR_REG, + SYSTICK_RVR_RELOAD_Msk, + whal_SetBits(SYSTICK_RVR_RELOAD_Msk, SYSTICK_RVR_RELOAD_Pos, cfg->cyclesPerTick)); + + return WHAL_SUCCESS; +} + +whal_Error whal_SysTick_Deinit(whal_Timer *timerDev) +{ + if (!timerDev) { + return WHAL_EINVAL; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_SysTick_Start(whal_Timer *timerDev) +{ + const whal_Regmap *reg; + + if (!timerDev || !timerDev->cfg) { + return WHAL_EINVAL; + } + + reg = &timerDev->regmap; + + whal_Reg_Update(reg->base, SYSTICK_CSR_REG, SYSTICK_CSR_ENABLE_Msk, + whal_SetBits(SYSTICK_CSR_ENABLE_Msk, SYSTICK_CSR_ENABLE_Pos, 1)); + + return WHAL_SUCCESS; +} + +whal_Error whal_SysTick_Stop(whal_Timer *timerDev) +{ + const whal_Regmap *reg; + + if (!timerDev || !timerDev->cfg) { + return WHAL_EINVAL; + } + + reg = &timerDev->regmap; + + whal_Reg_Update(reg->base, SYSTICK_CSR_REG, SYSTICK_CSR_ENABLE_Msk, + whal_SetBits(SYSTICK_CSR_ENABLE_Msk, SYSTICK_CSR_ENABLE_Pos, 0)); + + return WHAL_SUCCESS; +} + +whal_Error whal_SysTick_Reset(whal_Timer *timerDev) +{ + if (!timerDev) { + return WHAL_EINVAL; + } + + return WHAL_SUCCESS; +} + +#ifndef WHAL_CFG_SYSTICK_TIMER_DIRECT_API_MAPPING +const whal_TimerDriver whal_SysTick_Driver = { + .Init = whal_SysTick_Init, + .Deinit = whal_SysTick_Deinit, + .Start = whal_SysTick_Start, + .Stop = whal_SysTick_Stop, + .Reset = whal_SysTick_Reset, +}; +#endif /* !WHAL_CFG_SYSTICK_TIMER_DIRECT_API_MAPPING */ diff --git a/src/timer/timer.c b/src/timer/timer.c new file mode 100644 index 0000000..cafec48 --- /dev/null +++ b/src/timer/timer.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +whal_Error whal_Timer_Init(whal_Timer *timerDev) +{ + if (!timerDev) + return WHAL_EINVAL; + if (!timerDev->driver || !timerDev->driver->Init) + return WHAL_ENOTSUP; + + return timerDev->driver->Init(timerDev); +} + +whal_Error whal_Timer_Deinit(whal_Timer *timerDev) +{ + if (!timerDev) + return WHAL_EINVAL; + if (!timerDev->driver || !timerDev->driver->Deinit) + return WHAL_ENOTSUP; + + return timerDev->driver->Deinit(timerDev); +} + +whal_Error whal_Timer_Start(whal_Timer *timerDev) +{ + if (!timerDev) + return WHAL_EINVAL; + if (!timerDev->driver || !timerDev->driver->Start) + return WHAL_ENOTSUP; + + return timerDev->driver->Start(timerDev); +} + +whal_Error whal_Timer_Stop(whal_Timer *timerDev) +{ + if (!timerDev) + return WHAL_EINVAL; + if (!timerDev->driver || !timerDev->driver->Stop) + return WHAL_ENOTSUP; + + return timerDev->driver->Stop(timerDev); +} + +whal_Error whal_Timer_Reset(whal_Timer *timerDev) +{ + if (!timerDev) + return WHAL_EINVAL; + if (!timerDev->driver || !timerDev->driver->Reset) + return WHAL_ENOTSUP; + + return timerDev->driver->Reset(timerDev); +} diff --git a/src/uart/stm32f4_uart.c b/src/uart/stm32f4_uart.c new file mode 100644 index 0000000..e59373b --- /dev/null +++ b/src/uart/stm32f4_uart.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include +#include + +/* + * STM32F4 USART Register Definitions (USARTv1) + * + * The STM32F4 USART uses SR/DR style registers which differ from the + * ISR/TDR/RDR layout on newer STM32 families. + */ + +/* Status Register */ +#define UART_SR_REG 0x00 +#define UART_SR_RXNE_Pos 5 /* Receive data register not empty */ +#define UART_SR_RXNE_Msk (1UL << UART_SR_RXNE_Pos) + +#define UART_SR_TC_Pos 6 /* Transmission complete */ +#define UART_SR_TC_Msk (1UL << UART_SR_TC_Pos) + +#define UART_SR_TXE_Pos 7 /* Transmit data register empty */ +#define UART_SR_TXE_Msk (1UL << UART_SR_TXE_Pos) + +/* Data Register - shared TX/RX */ +#define UART_DR_REG 0x04 +#define UART_DR_Pos 0 +#define UART_DR_Msk (WHAL_BITMASK(9) << UART_DR_Pos) + +/* Baud Rate Register */ +#define UART_BRR_REG 0x08 +#define UART_BRR_Pos 0 +#define UART_BRR_Msk (WHAL_BITMASK(16) << UART_BRR_Pos) + +/* Control Register 1 */ +#define UART_CR1_REG 0x0C +#define UART_CR1_RE_Pos 2 /* Receiver enable */ +#define UART_CR1_RE_Msk (1UL << UART_CR1_RE_Pos) + +#define UART_CR1_TE_Pos 3 /* Transmitter enable */ +#define UART_CR1_TE_Msk (1UL << UART_CR1_TE_Pos) + +#define UART_CR1_UE_Pos 13 /* USART enable */ +#define UART_CR1_UE_Msk (1UL << UART_CR1_UE_Pos) + +#if defined(WHAL_CFG_STM32F4_UART_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32L1_UART_DIRECT_API_MAPPING) +#define whal_Stm32f4_Uart_Init whal_Uart_Init +#define whal_Stm32f4_Uart_Deinit whal_Uart_Deinit +#define whal_Stm32f4_Uart_Send whal_Uart_Send +#define whal_Stm32f4_Uart_Recv whal_Uart_Recv +#define whal_Stm32f4_Uart_SendAsync whal_Uart_SendAsync +#define whal_Stm32f4_Uart_RecvAsync whal_Uart_RecvAsync +#endif /* WHAL_CFG_STM32F4_UART_DIRECT_API_MAPPING */ + +whal_Error whal_Stm32f4_Uart_Init(whal_Uart *uartDev) +{ + whal_Stm32f4_Uart_Cfg *cfg; + const whal_Regmap *reg; + uint32_t brr; + + if (!uartDev || !uartDev->cfg) + return WHAL_EINVAL; + + reg = &uartDev->regmap; + cfg = (whal_Stm32f4_Uart_Cfg *)uartDev->cfg; + + brr = cfg->brr; + + /* Set baud rate */ + whal_Reg_Update(reg->base, UART_BRR_REG, + UART_BRR_Msk, + whal_SetBits(UART_BRR_Msk, UART_BRR_Pos, brr)); + + /* Enable USART, transmitter, and receiver */ + whal_Reg_Update(reg->base, UART_CR1_REG, + UART_CR1_UE_Msk | UART_CR1_RE_Msk | UART_CR1_TE_Msk, + whal_SetBits(UART_CR1_UE_Msk, UART_CR1_UE_Pos, 1) | + whal_SetBits(UART_CR1_RE_Msk, UART_CR1_RE_Pos, 1) | + whal_SetBits(UART_CR1_TE_Msk, UART_CR1_TE_Pos, 1)); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32f4_Uart_Deinit(whal_Uart *uartDev) +{ + const whal_Regmap *reg; + + if (!uartDev) + return WHAL_EINVAL; + + reg = &uartDev->regmap; + + /* Disable USART, transmitter, and receiver */ + whal_Reg_Update(reg->base, UART_CR1_REG, + UART_CR1_UE_Msk | UART_CR1_RE_Msk | UART_CR1_TE_Msk, + whal_SetBits(UART_CR1_UE_Msk, UART_CR1_UE_Pos, 0) | + whal_SetBits(UART_CR1_RE_Msk, UART_CR1_RE_Pos, 0) | + whal_SetBits(UART_CR1_TE_Msk, UART_CR1_TE_Pos, 0)); + + /* Clear baud rate */ + whal_Reg_Update(reg->base, UART_BRR_REG, + UART_BRR_Msk, + whal_SetBits(UART_BRR_Msk, UART_BRR_Pos, 0)); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32f4_Uart_Send(whal_Uart *uartDev, const void *data, size_t dataSz) +{ + const whal_Regmap *reg; + whal_Stm32f4_Uart_Cfg *cfg; + const uint8_t *buf = data; + + if (!uartDev || !uartDev->cfg || !data) + return WHAL_EINVAL; + + reg = &uartDev->regmap; + cfg = (whal_Stm32f4_Uart_Cfg *)uartDev->cfg; + + for (size_t i = 0; i < dataSz; ++i) { + whal_Error err; + + /* Wait for transmit data register empty */ + err = whal_Reg_ReadPoll(reg->base, UART_SR_REG, UART_SR_TXE_Msk, + UART_SR_TXE_Msk, cfg->timeout); + if (err) + return err; + + /* Write byte to data register (must not read DR — use pure write) */ + whal_Reg_Write(reg->base, UART_DR_REG, buf[i]); + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32f4_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) +{ + const whal_Regmap *reg; + whal_Stm32f4_Uart_Cfg *cfg; + uint8_t *buf = data; + + if (!uartDev || !uartDev->cfg || !data) + return WHAL_EINVAL; + + reg = &uartDev->regmap; + cfg = (whal_Stm32f4_Uart_Cfg *)uartDev->cfg; + size_t d; + + for (size_t i = 0; i < dataSz; ++i) { + /* Wait for receive data register not empty */ + whal_Error err = whal_Reg_ReadPoll(reg->base, UART_SR_REG, + UART_SR_RXNE_Msk, + UART_SR_RXNE_Msk, cfg->timeout); + if (err) + return err; + + /* Read received byte */ + whal_Reg_Get(reg->base, UART_DR_REG, + UART_DR_Msk, UART_DR_Pos, &d); + + buf[i] = d; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32f4_Uart_SendAsync(whal_Uart *uartDev, const void *data, size_t dataSz) +{ + (void)dataSz; + if (!uartDev || !data) + return WHAL_EINVAL; + return WHAL_ENOTSUP; +} + +whal_Error whal_Stm32f4_Uart_RecvAsync(whal_Uart *uartDev, void *data, size_t dataSz) +{ + (void)dataSz; + if (!uartDev || !data) + return WHAL_EINVAL; + return WHAL_ENOTSUP; +} + +#if !defined(WHAL_CFG_STM32F4_UART_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32L1_UART_DIRECT_API_MAPPING) +const whal_UartDriver whal_Stm32f4_Uart_Driver = { + .Init = whal_Stm32f4_Uart_Init, + .Deinit = whal_Stm32f4_Uart_Deinit, + .Send = whal_Stm32f4_Uart_Send, + .SendAsync = whal_Stm32f4_Uart_SendAsync, + .RecvAsync = whal_Stm32f4_Uart_RecvAsync, + .Recv = whal_Stm32f4_Uart_Recv, +}; +#endif /* !WHAL_CFG_UART_API_MAPPING */ diff --git a/src/uart/stm32l1_uart.c b/src/uart/stm32l1_uart.c new file mode 100644 index 0000000..545287d --- /dev/null +++ b/src/uart/stm32l1_uart.c @@ -0,0 +1 @@ +#include "stm32f4_uart.c" diff --git a/src/uart/stm32wb_uart.c b/src/uart/stm32wb_uart.c new file mode 100644 index 0000000..2072c8c --- /dev/null +++ b/src/uart/stm32wb_uart.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include + +#define UART_CR1_REG 0x00 +#define UART_CR1_UE_Pos 0 +#define UART_CR1_UE_Msk (1UL << UART_CR1_UE_Pos) + +#define UART_CR1_RE_Pos 2 +#define UART_CR1_RE_Msk (1UL << UART_CR1_RE_Pos) + +#define UART_CR1_TE_Pos 3 +#define UART_CR1_TE_Msk (1UL << UART_CR1_TE_Pos) + +#define UART_CR1_FIFOEN_Pos 29 +#define UART_CR1_FIFOEN_Msk (1UL << UART_CR1_FIFOEN_Pos) + +#define UART_BRR_REG 0x0C +#define UART_BRR_Pos 0 +#define UART_BRR_Msk (WHAL_BITMASK(20) << UART_BRR_Pos) + +#define UART_ISR_REG 0x1C +#define UART_ISR_RXFNE_Pos 5 +#define UART_ISR_RXFNE_Msk (1UL << UART_ISR_RXFNE_Pos) + +#define UART_ISR_TC_Pos 6 +#define UART_ISR_TC_Msk (1UL << UART_ISR_TC_Pos) + +#define UART_ISR_TXE_Pos 7 +#define UART_ISR_TXE_Msk (1UL << UART_ISR_TXE_Pos) + +#define UART_RDR_REG 0x24 +#define UART_RDR_Pos 0 +#define UART_RDR_Msk (WHAL_BITMASK(9) << UART_RDR_Pos) + +#define UART_TDR_REG 0x28 +#define UART_TDR_Pos 0 +#define UART_TDR_Msk (WHAL_BITMASK(9) << UART_TDR_Pos) + +#if defined(WHAL_CFG_STM32WB_UART_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32H5_UART_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32C0_UART_DIRECT_API_MAPPING) || \ + defined(WHAL_CFG_STM32N6_UART_DIRECT_API_MAPPING) +#define whal_Stm32wb_Uart_Init whal_Uart_Init +#define whal_Stm32wb_Uart_Deinit whal_Uart_Deinit +#define whal_Stm32wb_Uart_Send whal_Uart_Send +#define whal_Stm32wb_Uart_Recv whal_Uart_Recv +#define whal_Stm32wb_Uart_SendAsync whal_Uart_SendAsync +#define whal_Stm32wb_Uart_RecvAsync whal_Uart_RecvAsync +#endif /* WHAL_CFG_UART_API_MAPPING */ + +#ifdef WHAL_CFG_STM32WB_UART_DMA_DIRECT_API_MAPPING +#define whal_Stm32wb_Uart_Init whal_Uart_Init +#define whal_Stm32wb_Uart_Deinit whal_Uart_Deinit +#endif /* WHAL_CFG_STM32WB_UART_DMA_DIRECT_API_MAPPING */ + +whal_Error whal_Stm32wb_Uart_Init(whal_Uart *uartDev) +{ + whal_Stm32wb_Uart_Cfg *cfg; + const whal_Regmap *reg; + uint32_t brr; + + if (!uartDev || !uartDev->cfg) { + return WHAL_EINVAL; + } + + reg = &uartDev->regmap; + cfg = (whal_Stm32wb_Uart_Cfg *)uartDev->cfg; + + brr = cfg->brr; + + whal_Reg_Update(reg->base, UART_BRR_REG, + UART_BRR_Msk, + whal_SetBits(UART_BRR_Msk, UART_BRR_Pos, brr)); + whal_Reg_Update(reg->base, UART_CR1_REG, + UART_CR1_UE_Msk | UART_CR1_RE_Msk | UART_CR1_TE_Msk | + UART_CR1_FIFOEN_Msk, + whal_SetBits(UART_CR1_UE_Msk, UART_CR1_UE_Pos, 1) | + whal_SetBits(UART_CR1_RE_Msk, UART_CR1_RE_Pos, 1) | + whal_SetBits(UART_CR1_TE_Msk, UART_CR1_TE_Pos, 1) | + whal_SetBits(UART_CR1_FIFOEN_Msk, UART_CR1_FIFOEN_Pos, 1)); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Uart_Deinit(whal_Uart *uartDev) +{ + const whal_Regmap *reg; + + if (!uartDev) { + return WHAL_EINVAL; + } + + reg = &uartDev->regmap; + + whal_Reg_Update(reg->base, UART_CR1_REG, + UART_CR1_UE_Msk | UART_CR1_RE_Msk | UART_CR1_TE_Msk, + whal_SetBits(UART_CR1_UE_Msk, UART_CR1_UE_Pos, 0) | + whal_SetBits(UART_CR1_RE_Msk, UART_CR1_RE_Pos, 0) | + whal_SetBits(UART_CR1_TE_Msk, UART_CR1_TE_Pos, 0)); + + whal_Reg_Update(reg->base, UART_BRR_REG, + UART_BRR_Msk, + whal_SetBits(UART_BRR_Msk, UART_BRR_Pos, 0)); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Uart_Send(whal_Uart *uartDev, const void *data, size_t dataSz) +{ + const whal_Regmap *reg; + whal_Stm32wb_Uart_Cfg *cfg; + const uint8_t *buf = data; + + if (!uartDev || !uartDev->cfg || !data) { + return WHAL_EINVAL; + } + + reg = &uartDev->regmap; + cfg = (whal_Stm32wb_Uart_Cfg *)uartDev->cfg; + + for (size_t i = 0; i < dataSz; ++i) { + whal_Error err; + whal_Reg_Update(reg->base, UART_TDR_REG, UART_TDR_Msk, + whal_SetBits(UART_TDR_Msk, UART_TDR_Pos, buf[i])); + + err = whal_Reg_ReadPoll(reg->base, UART_ISR_REG, UART_ISR_TC_Msk, + UART_ISR_TC_Msk, cfg->timeout); + if (err) + return err; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) +{ + const whal_Regmap *reg; + whal_Stm32wb_Uart_Cfg *cfg; + uint8_t *buf = data; + + if (!uartDev || !uartDev->cfg || !data) { + return WHAL_EINVAL; + } + + reg = &uartDev->regmap; + cfg = (whal_Stm32wb_Uart_Cfg *)uartDev->cfg; + size_t d; + + for (size_t i = 0; i < dataSz; ++i) { + whal_Error err = whal_Reg_ReadPoll(reg->base, UART_ISR_REG, + UART_ISR_RXFNE_Msk, + UART_ISR_RXFNE_Msk, cfg->timeout); + if (err) + return err; + + whal_Reg_Get(reg->base, UART_RDR_REG, + UART_RDR_Msk, UART_RDR_Pos, &d); + + buf[i] = d; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wb_Uart_SendAsync(whal_Uart *uartDev, const void *data, size_t dataSz) +{ + (void)dataSz; + if (!uartDev || !data) + return WHAL_EINVAL; + return WHAL_ENOTSUP; +} + +whal_Error whal_Stm32wb_Uart_RecvAsync(whal_Uart *uartDev, void *data, size_t dataSz) +{ + (void)dataSz; + if (!uartDev || !data) + return WHAL_EINVAL; + return WHAL_ENOTSUP; +} + +#if !defined(WHAL_CFG_STM32WB_UART_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32WB_UART_DMA_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32H5_UART_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32C0_UART_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32N6_UART_DIRECT_API_MAPPING) +const whal_UartDriver whal_Stm32wb_Uart_Driver = { + .Init = whal_Stm32wb_Uart_Init, + .Deinit = whal_Stm32wb_Uart_Deinit, + .Send = whal_Stm32wb_Uart_Send, + .Recv = whal_Stm32wb_Uart_Recv, + .SendAsync = whal_Stm32wb_Uart_SendAsync, + .RecvAsync = whal_Stm32wb_Uart_RecvAsync, +}; +#endif /* !WHAL_CFG_UART_API_MAPPING */ + diff --git a/src/uart/uart.c b/src/uart/uart.c new file mode 100644 index 0000000..1d06dda --- /dev/null +++ b/src/uart/uart.c @@ -0,0 +1,64 @@ +#include +#include +#include + +whal_Error whal_Uart_Init(whal_Uart *uartDev) +{ + if (!uartDev) + return WHAL_EINVAL; + if (!uartDev->driver || !uartDev->driver->Init) + return WHAL_ENOTSUP; + + return uartDev->driver->Init(uartDev); +} + +whal_Error whal_Uart_Deinit(whal_Uart *uartDev) +{ + if (!uartDev) + return WHAL_EINVAL; + if (!uartDev->driver || !uartDev->driver->Deinit) + return WHAL_ENOTSUP; + + return uartDev->driver->Deinit(uartDev); +} + +whal_Error whal_Uart_Send(whal_Uart *uartDev, const void *data, size_t dataSz) +{ + if (!uartDev || !data) + return WHAL_EINVAL; + if (!uartDev->driver || !uartDev->driver->Send) + return WHAL_ENOTSUP; + + return uartDev->driver->Send(uartDev, data, dataSz); +} + +whal_Error whal_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) +{ + if (!uartDev || !data) + return WHAL_EINVAL; + if (!uartDev->driver || !uartDev->driver->Recv) + return WHAL_ENOTSUP; + + return uartDev->driver->Recv(uartDev, data, dataSz); +} + +whal_Error whal_Uart_SendAsync(whal_Uart *uartDev, const void *data, size_t dataSz) +{ + if (!uartDev || !data) + return WHAL_EINVAL; + if (!uartDev->driver || !uartDev->driver->SendAsync) + return WHAL_ENOTSUP; + + return uartDev->driver->SendAsync(uartDev, data, dataSz); +} + +whal_Error whal_Uart_RecvAsync(whal_Uart *uartDev, void *data, size_t dataSz) +{ + if (!uartDev || !data) + return WHAL_EINVAL; + if (!uartDev->driver || !uartDev->driver->RecvAsync) + return WHAL_ENOTSUP; + + return uartDev->driver->RecvAsync(uartDev, data, dataSz); +} + diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..04eefdd --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,43 @@ +WHAL_DIR = $(CURDIR)/.. + +.DEFAULT_GOAL := all + +BOARD ?= stm32wb55xx_nucleo +BOARD_DIR = $(WHAL_DIR)/boards/$(BOARD) +BUILD_DIR = build/$(BOARD) +PERIPHERAL_DIR = $(WHAL_DIR)/boards/peripheral +INCLUDE = -I$(WHAL_DIR) -I. -I$(PERIPHERAL_DIR) + +include $(BOARD_DIR)/board.mk + +uc = $(shell echo $(1) | tr a-z A-Z) +CFLAGS += $(foreach t,$(TESTS),-DWHAL_TEST_ENABLE_$(call uc,$(t))) +CFLAGS += $(foreach t,$(TESTS),$(if $(shell find . -name 'test_$(PLATFORM)_$(t).c'),-DWHAL_TEST_ENABLE_$(call uc,$(t))_PLATFORM)) + +SOURCE = main.c +SOURCE += $(BOARD_SOURCE) +SOURCE += $(foreach t,$(TESTS),$(shell find . -name 'test_$(t).c')) +SOURCE += $(foreach t,$(TESTS),$(shell find . -name 'test_$(PLATFORM)_$(t).c')) + +OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(SOURCE)) +DEPENDS = $(OBJECTS:.o=.d) + +all: $(BUILD_DIR)/test_hw.bin + +$(BUILD_DIR)/%.o: %.c Makefile + @mkdir -p $(dir $@) + $(GCC) $(CFLAGS) -c -o $@ $< + +.SECONDARY: +$(BUILD_DIR)/%.elf: $(OBJECTS) $(LINKER_SCRIPT) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS) -T $(LINKER_SCRIPT) -o $@ $(OBJECTS) + +$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf + $(OBJCOPY) $^ -O binary $@ + +.PHONY: clean +clean: + rm -rf build + +-include $(DEPENDS) diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..df66151 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,71 @@ +# wolfHAL Test Suite + +## Hardware Tests + +### Building + +``` +make BOARD= +``` + +Each board defines its supported tests in `boards//board.mk`. The output binary +is placed in `build//`. + +### Selecting Tests + +By default, all supported tests for the specified board will be built. Override +the `TESTS` variable to build a subset: + +``` +make BOARD=stm32wb55xx_nucleo TESTS=gpio +make BOARD=stm32wb55xx_nucleo TESTS="gpio clock" +``` + +### Test Structure + +Tests are organized by device type: + +``` +tests/ + main.c # Test runner entry point + test.h # Test macros (WHAL_ASSERT_EQ, WHAL_TEST, etc.) + Makefile + gpio/ + test_gpio.c # Generic API tests + test__gpio.c # Platform-specific tests + clock/ + ... +``` + +Each device directory contains: + +- `test_.c` — Generic tests that exercise the wolfHAL API. +- `test__.c` — Platform-specific tests. These are + automatically detected and compiled when building for the matching board. + +Board support (device instances, linker scripts, etc.) lives in the top-level +`boards/` directory. See [boards/README.md](../boards/README.md) for details. + +### Peripheral Devices + +External peripheral drivers (SPI-NOR flash, SD cards, IMUs, etc.) are opt-in. +Enable them using the `PERIPHERALS` variable when building: + +``` +make BOARD=stm32wb55xx_nucleo PERIPHERALS="spi_nor_w25q64" +make BOARD=stm32wb55xx_nucleo PERIPHERALS="bmi270" TESTS="bmi270" +make BOARD=stm32wb55xx_nucleo PERIPHERALS="spi_nor_w25q64 bmi270" +``` + +Peripheral devices are automatically tested by their matching test suite (e.g., +`flash` tests iterate all entries in `g_peripheralFlash[]`, `bmi270` tests +use `g_peripheralSensor[]`). + +## Core Tests + +Host-side unit tests (bitops, dispatch, endian) live in `core/` and build with +the native compiler: + +``` +cd core && make && make run +``` diff --git a/tests/clock/test_clock.c b/tests/clock/test_clock.c new file mode 100644 index 0000000..b57b4cf --- /dev/null +++ b/tests/clock/test_clock.c @@ -0,0 +1,9 @@ +#include +#include "board.h" +#include "test.h" + +void whal_Test_Clock(void) +{ + /* No generic clock API to test; chip-specific tests live in + * test__clock.c. */ +} diff --git a/tests/clock/test_stm32wb_clock.c b/tests/clock/test_stm32wb_clock.c new file mode 100644 index 0000000..77e2690 --- /dev/null +++ b/tests/clock/test_stm32wb_clock.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include "board.h" +#include "test.h" + +static void Test_Clock_EnableDisable(void) +{ + whal_Stm32wb_Rcc_PeriphClk testClk = { WHAL_STM32WB55_GPIOA_GATE }; + + /* Save original state */ + size_t origVal = 0; + whal_Reg_Get(g_whalClock.regmap.base, 0x4C, (1 << 0), 0, &origVal); + + /* Enable and verify */ + WHAL_ASSERT_EQ(whal_Stm32wb_Rcc_EnablePeriphClk(&g_whalClock, &testClk), WHAL_SUCCESS); + + size_t val = 0; + whal_Reg_Get(g_whalClock.regmap.base, 0x4C, (1 << 0), 0, &val); + WHAL_ASSERT_EQ(val, 1); + + /* Disable and verify */ + WHAL_ASSERT_EQ(whal_Stm32wb_Rcc_DisablePeriphClk(&g_whalClock, &testClk), WHAL_SUCCESS); + + whal_Reg_Get(g_whalClock.regmap.base, 0x4C, (1 << 0), 0, &val); + WHAL_ASSERT_EQ(val, 0); + + /* Restore original state */ + if (origVal) + whal_Stm32wb_Rcc_EnablePeriphClk(&g_whalClock, &testClk); +} + +void whal_Test_Clock_Platform(void) +{ + WHAL_TEST(Test_Clock_EnableDisable); +} diff --git a/tests/core/Makefile b/tests/core/Makefile new file mode 100644 index 0000000..3eb04c4 --- /dev/null +++ b/tests/core/Makefile @@ -0,0 +1,32 @@ +WHAL_DIR = $(CURDIR)/../.. + +INCLUDE = -I$(WHAL_DIR) +CFLAGS += -Wall -Werror $(INCLUDE) -g3 + +TEST_SRC = main.c test_bitops.c test_dispatch.c test_endian.c test_timeout.c + +WHAL_SRC = $(WHAL_DIR)/src/gpio/gpio.c \ + $(WHAL_DIR)/src/uart/uart.c \ + $(WHAL_DIR)/src/flash/flash.c \ + $(WHAL_DIR)/src/timer/timer.c + +SOURCE = $(TEST_SRC) $(WHAL_SRC) +OBJECTS = $(patsubst %.c,%.o,$(SOURCE)) + +TARGET = test_core + +all: $(TARGET) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(TARGET): $(OBJECTS) + $(CC) $(CFLAGS) -o $@ $(OBJECTS) + +.PHONY: run clean + +run: $(TARGET) + ./$(TARGET) + +clean: + rm -f $(OBJECTS) $(TARGET) diff --git a/tests/core/main.c b/tests/core/main.c new file mode 100644 index 0000000..d067871 --- /dev/null +++ b/tests/core/main.c @@ -0,0 +1,38 @@ +#include +#include "../test.h" + +int g_whalTestPassed; +int g_whalTestFailed; +int g_whalTestSkipped; +int g_whalTestCurFailed; +int g_whalTestCurSkipped; + +void whal_Test_Puts(const char *s) +{ + fputs(s, stdout); +} + +void whal_Test_Bitops(void); +void whal_Test_Dispatch(void); +void whal_Test_Endian(void); +#ifndef WHAL_CFG_NO_TIMEOUT +void whal_Test_Timeout(void); +#endif + +int main(void) +{ + g_whalTestPassed = 0; + g_whalTestFailed = 0; + g_whalTestSkipped = 0; + + whal_Test_Bitops(); + whal_Test_Dispatch(); + whal_Test_Endian(); +#ifndef WHAL_CFG_NO_TIMEOUT + whal_Test_Timeout(); +#endif + + WHAL_TEST_SUMMARY(); + + return g_whalTestFailed ? 1 : 0; +} diff --git a/tests/core/test_bitops.c b/tests/core/test_bitops.c new file mode 100644 index 0000000..5df9440 --- /dev/null +++ b/tests/core/test_bitops.c @@ -0,0 +1,77 @@ +#include +#include "../test.h" + +static void Test_Bitops_Bitmask4(void) +{ + WHAL_ASSERT_EQ(WHAL_BITMASK(4), 0xFul); +} + +static void Test_Bitops_Bitmask8(void) +{ + WHAL_ASSERT_EQ(WHAL_BITMASK(8), 0xFFul); +} + +static void Test_Bitops_Bitmask1(void) +{ + WHAL_ASSERT_EQ(WHAL_BITMASK(1), 1ul); +} + +static void Test_Bitops_SetbitsLow(void) +{ + size_t msk = WHAL_BITMASK(4); + size_t pos = 0; + WHAL_ASSERT_EQ(whal_SetBits(msk, pos, 0xA), 0xAul); +} + +static void Test_Bitops_SetbitsShifted(void) +{ + size_t msk = (WHAL_BITMASK(4) << 4); + size_t pos = 4; + WHAL_ASSERT_EQ(whal_SetBits(msk, pos, 0x5), 0x50ul); +} + +static void Test_Bitops_GetbitsLow(void) +{ + size_t msk = WHAL_BITMASK(4); + size_t pos = 0; + WHAL_ASSERT_EQ(whal_GetBits(msk, pos, 0xABul), 0xBul); +} + +static void Test_Bitops_GetbitsShifted(void) +{ + size_t msk = (WHAL_BITMASK(4) << 4); + size_t pos = 4; + WHAL_ASSERT_EQ(whal_GetBits(msk, pos, 0xABul), 0xAul); +} + +static void Test_Bitops_SetbitsGetbitsRoundtrip(void) +{ + size_t msk = (WHAL_BITMASK(7) << 8); + size_t pos = 8; + size_t val = 42; + size_t encoded = whal_SetBits(msk, pos, val); + WHAL_ASSERT_EQ(whal_GetBits(msk, pos, encoded), val); +} + +static void Test_Bitops_SetbitsSingleBit(void) +{ + size_t msk = (1UL << 24); + size_t pos = 24; + WHAL_ASSERT_EQ(whal_SetBits(msk, pos, 1), (1UL << 24)); + WHAL_ASSERT_EQ(whal_SetBits(msk, pos, 0), 0ul); +} + +void whal_Test_Bitops(void) +{ + WHAL_TEST_SUITE_START("bitops"); + WHAL_TEST(Test_Bitops_Bitmask4); + WHAL_TEST(Test_Bitops_Bitmask8); + WHAL_TEST(Test_Bitops_Bitmask1); + WHAL_TEST(Test_Bitops_SetbitsLow); + WHAL_TEST(Test_Bitops_SetbitsShifted); + WHAL_TEST(Test_Bitops_GetbitsLow); + WHAL_TEST(Test_Bitops_GetbitsShifted); + WHAL_TEST(Test_Bitops_SetbitsGetbitsRoundtrip); + WHAL_TEST(Test_Bitops_SetbitsSingleBit); + WHAL_TEST_SUITE_END(); +} diff --git a/tests/core/test_dispatch.c b/tests/core/test_dispatch.c new file mode 100644 index 0000000..7fd808c --- /dev/null +++ b/tests/core/test_dispatch.c @@ -0,0 +1,354 @@ +#include +#include +#include +#include +#include "../test.h" + +/* + * Mock drivers that return SUCCESS for all operations. + * Used to verify the generic dispatch layer. + * + * Clock and power are board-level drivers — they have no generic + * whal__* API or vtable, so they are not exercised here. + */ + +static whal_Error MockGpioInit(whal_Gpio *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockGpioDeinit(whal_Gpio *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockGpioGet(whal_Gpio *d, size_t p, size_t *v) { (void)d; (void)p; *v = 1; return WHAL_SUCCESS; } +static whal_Error MockGpioSet(whal_Gpio *d, size_t p, size_t v) { (void)d; (void)p; (void)v; return WHAL_SUCCESS; } + +static const whal_GpioDriver mockGpioDriver = { + .Init = MockGpioInit, + .Deinit = MockGpioDeinit, + .Get = MockGpioGet, + .Set = MockGpioSet, +}; + +static whal_Error MockUartInit(whal_Uart *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockUartDeinit(whal_Uart *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockUartSend(whal_Uart *d, const void *data, size_t sz) { (void)d; (void)data; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockUartRecv(whal_Uart *d, void *data, size_t sz) { (void)d; (void)data; (void)sz; return WHAL_SUCCESS; } + +static whal_Error MockUartSendAsync(whal_Uart *d, const void *data, size_t sz) { (void)d; (void)data; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockUartRecvAsync(whal_Uart *d, void *data, size_t sz) { (void)d; (void)data; (void)sz; return WHAL_SUCCESS; } + +static const whal_UartDriver mockUartDriver = { + .Init = MockUartInit, + .Deinit = MockUartDeinit, + .Send = MockUartSend, + .Recv = MockUartRecv, +}; + +static const whal_UartDriver mockUartAsyncDriver = { + .Init = MockUartInit, + .Deinit = MockUartDeinit, + .Send = MockUartSend, + .Recv = MockUartRecv, + .SendAsync = MockUartSendAsync, + .RecvAsync = MockUartRecvAsync, +}; + +static whal_Error MockFlashInit(whal_Flash *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockFlashDeinit(whal_Flash *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockFlashLock(whal_Flash *d, size_t a, size_t l) { (void)d; (void)a; (void)l; return WHAL_SUCCESS; } +static whal_Error MockFlashUnlock(whal_Flash *d, size_t a, size_t l) { (void)d; (void)a; (void)l; return WHAL_SUCCESS; } +static whal_Error MockFlashRead(whal_Flash *d, size_t a, void *data, size_t sz) { (void)d; (void)a; (void)data; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockFlashWrite(whal_Flash *d, size_t a, const void *data, size_t sz) { (void)d; (void)a; (void)data; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockFlashErase(whal_Flash *d, size_t a, size_t sz) { (void)d; (void)a; (void)sz; return WHAL_SUCCESS; } + +static const whal_FlashDriver mockFlashDriver = { + .Init = MockFlashInit, + .Deinit = MockFlashDeinit, + .Lock = MockFlashLock, + .Unlock = MockFlashUnlock, + .Read = MockFlashRead, + .Write = MockFlashWrite, + .Erase = MockFlashErase, +}; + +static whal_Error MockTimerInit(whal_Timer *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockTimerDeinit(whal_Timer *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockTimerStart(whal_Timer *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockTimerStop(whal_Timer *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockTimerReset(whal_Timer *d) { (void)d; return WHAL_SUCCESS; } + +static const whal_TimerDriver mockTimerDriver = { + .Init = MockTimerInit, + .Deinit = MockTimerDeinit, + .Start = MockTimerStart, + .Stop = MockTimerStop, + .Reset = MockTimerReset, +}; + +static whal_Error MockRngInit(whal_Rng *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockRngDeinit(whal_Rng *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockRngGenerate(whal_Rng *d, void *data, size_t sz) { (void)d; (void)data; (void)sz; return WHAL_SUCCESS; } + +static const whal_RngDriver mockRngDriver = { + .Init = MockRngInit, + .Deinit = MockRngDeinit, + .Generate = MockRngGenerate, +}; + + +/* --- GPIO dispatch tests --- */ + +static void Test_Gpio_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_Gpio_Init(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Gpio_Set(NULL, 0, 0), WHAL_EINVAL); + size_t val; + WHAL_ASSERT_EQ(whal_Gpio_Get(NULL, 0, &val), WHAL_EINVAL); +} + +static void Test_Gpio_NullDriver(void) +{ + whal_Gpio dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Gpio_Init(&dev), WHAL_ENOTSUP); +} + +static void Test_Gpio_ValidDispatch(void) +{ + whal_Gpio dev = { .driver = &mockGpioDriver }; + WHAL_ASSERT_EQ(whal_Gpio_Init(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Gpio_Set(&dev, 0, 1), WHAL_SUCCESS); + size_t val; + WHAL_ASSERT_EQ(whal_Gpio_Get(&dev, 0, &val), WHAL_SUCCESS); + WHAL_ASSERT_EQ(val, 1); +} + +/* --- UART dispatch tests --- */ + +static void Test_Uart_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_Uart_Init(NULL), WHAL_EINVAL); + uint8_t buf[1]; + WHAL_ASSERT_EQ(whal_Uart_Send(NULL, buf, 1), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_Recv(NULL, buf, 1), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_SendAsync(NULL, buf, 1), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_RecvAsync(NULL, buf, 1), WHAL_EINVAL); +} + +static void Test_Uart_NullDriver(void) +{ + whal_Uart dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Uart_Init(&dev), WHAL_ENOTSUP); +} + +static void Test_Uart_NullAsyncVtable(void) +{ + whal_Uart dev = { .driver = &mockUartDriver }; + uint8_t buf[1] = {0}; + WHAL_ASSERT_EQ(whal_Uart_SendAsync(&dev, buf, 1), WHAL_ENOTSUP); + WHAL_ASSERT_EQ(whal_Uart_RecvAsync(&dev, buf, 1), WHAL_ENOTSUP); +} + +static void Test_Uart_ValidDispatch(void) +{ + whal_Uart dev = { .driver = &mockUartDriver }; + WHAL_ASSERT_EQ(whal_Uart_Init(&dev), WHAL_SUCCESS); + uint8_t buf[4] = {0}; + WHAL_ASSERT_EQ(whal_Uart_Send(&dev, buf, sizeof(buf)), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Uart_Recv(&dev, buf, sizeof(buf)), WHAL_SUCCESS); + + whal_Uart asyncDev = { .driver = &mockUartAsyncDriver }; + WHAL_ASSERT_EQ(whal_Uart_SendAsync(&asyncDev, buf, sizeof(buf)), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Uart_RecvAsync(&asyncDev, buf, sizeof(buf)), WHAL_SUCCESS); +} + +/* --- Flash dispatch tests --- */ + +static void Test_Flash_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_Flash_Init(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Flash_Lock(NULL, 0, 0), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Flash_Unlock(NULL, 0, 0), WHAL_EINVAL); + uint8_t buf[1]; + WHAL_ASSERT_EQ(whal_Flash_Read(NULL, 0, buf, 1), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Flash_Write(NULL, 0, buf, 1), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Flash_Erase(NULL, 0, 1), WHAL_EINVAL); +} + +static void Test_Flash_NullDriver(void) +{ + whal_Flash dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Flash_Init(&dev), WHAL_ENOTSUP); +} + +static void Test_Flash_ValidDispatch(void) +{ + whal_Flash dev = { .driver = &mockFlashDriver }; + WHAL_ASSERT_EQ(whal_Flash_Init(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Lock(&dev, 0, 0), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Unlock(&dev, 0, 0), WHAL_SUCCESS); + uint8_t buf[4] = {0}; + WHAL_ASSERT_EQ(whal_Flash_Read(&dev, 0, buf, sizeof(buf)), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Write(&dev, 0, buf, sizeof(buf)), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Flash_Erase(&dev, 0, sizeof(buf)), WHAL_SUCCESS); +} + +/* --- Timer dispatch tests --- */ + +static void Test_Timer_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_Timer_Init(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Timer_Start(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Timer_Stop(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Timer_Reset(NULL), WHAL_EINVAL); +} + +static void Test_Timer_NullDriver(void) +{ + whal_Timer dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Timer_Init(&dev), WHAL_ENOTSUP); +} + +static void Test_Timer_ValidDispatch(void) +{ + whal_Timer dev = { .driver = &mockTimerDriver }; + WHAL_ASSERT_EQ(whal_Timer_Init(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Timer_Start(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Timer_Stop(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Timer_Reset(&dev), WHAL_SUCCESS); +} + +/* --- RNG dispatch tests --- */ + +static void Test_Rng_NullDev(void) +{ + uint8_t buf[1]; + WHAL_ASSERT_EQ(whal_Rng_Init(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Rng_Deinit(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Rng_Generate(NULL, buf, 1), WHAL_EINVAL); +} + +static void Test_Rng_NullDriver(void) +{ + whal_Rng dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Rng_Init(&dev), WHAL_ENOTSUP); +} + +static void Test_Rng_ValidDispatch(void) +{ + whal_Rng dev = { .driver = &mockRngDriver }; + WHAL_ASSERT_EQ(whal_Rng_Init(&dev), WHAL_SUCCESS); + uint8_t buf[4] = {0}; + WHAL_ASSERT_EQ(whal_Rng_Generate(&dev, buf, sizeof(buf)), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Rng_Deinit(&dev), WHAL_SUCCESS); +} + +/* --- SPI dispatch tests --- */ + +static whal_Error MockSpiInit(whal_Spi *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockSpiDeinit(whal_Spi *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockSpiStartCom(whal_Spi *d, whal_Spi_ComCfg *c) { (void)d; (void)c; return WHAL_SUCCESS; } +static whal_Error MockSpiEndCom(whal_Spi *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockSpiSendRecv(whal_Spi *d, const void *tx, size_t txLen, void *rx, size_t rxLen) { (void)d; (void)tx; (void)txLen; (void)rx; (void)rxLen; return WHAL_SUCCESS; } + +static const whal_SpiDriver mockSpiDriver = { + .Init = MockSpiInit, + .Deinit = MockSpiDeinit, + .StartCom = MockSpiStartCom, + .EndCom = MockSpiEndCom, + .SendRecv = MockSpiSendRecv, +}; + +static void Test_Spi_NullDev(void) +{ + whal_Spi_ComCfg comCfg = {0}; + uint8_t buf[1]; + WHAL_ASSERT_EQ(whal_Spi_Init(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Spi_Deinit(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Spi_StartCom(NULL, &comCfg), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Spi_EndCom(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Spi_SendRecv(NULL, buf, 1, buf, 1), WHAL_EINVAL); +} + +static void Test_Spi_NullDriver(void) +{ + whal_Spi dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Spi_Init(&dev), WHAL_ENOTSUP); +} + +static void Test_Spi_ValidDispatch(void) +{ + whal_Spi dev = { .driver = &mockSpiDriver }; + whal_Spi_ComCfg comCfg = {0}; + uint8_t buf[4] = {0}; + WHAL_ASSERT_EQ(whal_Spi_Init(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Spi_StartCom(&dev, &comCfg), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Spi_SendRecv(&dev, buf, sizeof(buf), buf, sizeof(buf)), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Spi_EndCom(&dev), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Spi_Deinit(&dev), WHAL_SUCCESS); +} + +/* --- Block dispatch tests --- */ + +static whal_Error MockBlockInit(whal_Block *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockBlockDeinit(whal_Block *d) { (void)d; return WHAL_SUCCESS; } +static whal_Error MockBlockRead(whal_Block *d, uint32_t b, void *data, uint32_t c) { (void)d; (void)b; (void)data; (void)c; return WHAL_SUCCESS; } +static whal_Error MockBlockWrite(whal_Block *d, uint32_t b, const void *data, uint32_t c) { (void)d; (void)b; (void)data; (void)c; return WHAL_SUCCESS; } +static whal_Error MockBlockErase(whal_Block *d, uint32_t b, uint32_t c) { (void)d; (void)b; (void)c; return WHAL_SUCCESS; } + +static const whal_BlockDriver mockBlockDriver = { + .Init = MockBlockInit, + .Deinit = MockBlockDeinit, + .Read = MockBlockRead, + .Write = MockBlockWrite, + .Erase = MockBlockErase, +}; + +static void Test_Block_NullDev(void) +{ + WHAL_ASSERT_EQ(whal_Block_Init(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Block_Deinit(NULL), WHAL_EINVAL); + uint8_t buf[1]; + WHAL_ASSERT_EQ(whal_Block_Read(NULL, 0, buf, 1), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Block_Write(NULL, 0, buf, 1), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Block_Erase(NULL, 0, 1), WHAL_EINVAL); +} + +static void Test_Block_NullDriver(void) +{ + whal_Block dev = { .driver = NULL }; + WHAL_ASSERT_EQ(whal_Block_Init(&dev), WHAL_ENOTSUP); +} + +static void Test_Block_ValidDispatch(void) +{ + whal_Block dev = { .driver = &mockBlockDriver }; + WHAL_ASSERT_EQ(whal_Block_Init(&dev), WHAL_SUCCESS); + uint8_t buf[4] = {0}; + WHAL_ASSERT_EQ(whal_Block_Read(&dev, 0, buf, 1), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Block_Write(&dev, 0, buf, 1), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Block_Erase(&dev, 0, 1), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Block_Deinit(&dev), WHAL_SUCCESS); +} + +void whal_Test_Dispatch(void) +{ + WHAL_TEST_SUITE_START("dispatch"); + WHAL_TEST(Test_Gpio_NullDev); + WHAL_TEST(Test_Gpio_NullDriver); + WHAL_TEST(Test_Gpio_ValidDispatch); + WHAL_TEST(Test_Uart_NullDev); + WHAL_TEST(Test_Uart_NullDriver); + WHAL_TEST(Test_Uart_NullAsyncVtable); + WHAL_TEST(Test_Uart_ValidDispatch); + WHAL_TEST(Test_Flash_NullDev); + WHAL_TEST(Test_Flash_NullDriver); + WHAL_TEST(Test_Flash_ValidDispatch); + WHAL_TEST(Test_Timer_NullDev); + WHAL_TEST(Test_Timer_NullDriver); + WHAL_TEST(Test_Timer_ValidDispatch); + WHAL_TEST(Test_Rng_NullDev); + WHAL_TEST(Test_Rng_NullDriver); + WHAL_TEST(Test_Rng_ValidDispatch); + WHAL_TEST(Test_Spi_NullDev); + WHAL_TEST(Test_Spi_NullDriver); + WHAL_TEST(Test_Spi_ValidDispatch); + WHAL_TEST(Test_Block_NullDev); + WHAL_TEST(Test_Block_NullDriver); + WHAL_TEST(Test_Block_ValidDispatch); + WHAL_TEST_SUITE_END(); +} diff --git a/tests/core/test_endian.c b/tests/core/test_endian.c new file mode 100644 index 0000000..44d5706 --- /dev/null +++ b/tests/core/test_endian.c @@ -0,0 +1,84 @@ +#include +#include "../test.h" + +static void Test_Endian_LoadBe32(void) +{ + const uint8_t buf[] = { 0xDE, 0xAD, 0xBE, 0xEF }; + WHAL_ASSERT_EQ(whal_LoadBe32(buf), 0xDEADBEEFul); +} + +static void Test_Endian_LoadBe32_Zero(void) +{ + const uint8_t buf[] = { 0x00, 0x00, 0x00, 0x00 }; + WHAL_ASSERT_EQ(whal_LoadBe32(buf), 0x00000000ul); +} + +static void Test_Endian_LoadBe32_AllOnes(void) +{ + const uint8_t buf[] = { 0xFF, 0xFF, 0xFF, 0xFF }; + WHAL_ASSERT_EQ(whal_LoadBe32(buf), 0xFFFFFFFFul); +} + +static void Test_Endian_LoadBe32_MsbOnly(void) +{ + const uint8_t buf[] = { 0x80, 0x00, 0x00, 0x00 }; + WHAL_ASSERT_EQ(whal_LoadBe32(buf), 0x80000000ul); +} + +static void Test_Endian_LoadBe32_LsbOnly(void) +{ + const uint8_t buf[] = { 0x00, 0x00, 0x00, 0x01 }; + WHAL_ASSERT_EQ(whal_LoadBe32(buf), 0x00000001ul); +} + +static void Test_Endian_StoreBe32(void) +{ + uint8_t buf[4] = {0}; + whal_StoreBe32(buf, 0xDEADBEEF); + WHAL_ASSERT_EQ(buf[0], 0xDE); + WHAL_ASSERT_EQ(buf[1], 0xAD); + WHAL_ASSERT_EQ(buf[2], 0xBE); + WHAL_ASSERT_EQ(buf[3], 0xEF); +} + +static void Test_Endian_StoreBe32_Zero(void) +{ + uint8_t buf[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + whal_StoreBe32(buf, 0x00000000); + WHAL_ASSERT_EQ(buf[0], 0x00); + WHAL_ASSERT_EQ(buf[1], 0x00); + WHAL_ASSERT_EQ(buf[2], 0x00); + WHAL_ASSERT_EQ(buf[3], 0x00); +} + +static void Test_Endian_StoreBe32_MsbOnly(void) +{ + uint8_t buf[4] = {0}; + whal_StoreBe32(buf, 0x80000000); + WHAL_ASSERT_EQ(buf[0], 0x80); + WHAL_ASSERT_EQ(buf[1], 0x00); + WHAL_ASSERT_EQ(buf[2], 0x00); + WHAL_ASSERT_EQ(buf[3], 0x00); +} + +static void Test_Endian_Roundtrip(void) +{ + uint8_t buf[4] = {0}; + whal_StoreBe32(buf, 0xCAFEBABE); + WHAL_ASSERT_EQ(whal_LoadBe32(buf), 0xCAFEBABEul); +} + +void whal_Test_Endian(void) +{ + WHAL_TEST_SUITE_START("endian"); + WHAL_TEST(Test_Endian_LoadBe32); + WHAL_TEST(Test_Endian_LoadBe32_Zero); + WHAL_TEST(Test_Endian_LoadBe32_AllOnes); + WHAL_TEST(Test_Endian_LoadBe32_MsbOnly); + WHAL_TEST(Test_Endian_LoadBe32_LsbOnly); + WHAL_TEST(Test_Endian_StoreBe32); + WHAL_TEST(Test_Endian_StoreBe32_Zero); + WHAL_TEST(Test_Endian_StoreBe32_MsbOnly); + WHAL_TEST(Test_Endian_Roundtrip); + WHAL_TEST_SUITE_END(); +} diff --git a/tests/core/test_timeout.c b/tests/core/test_timeout.c new file mode 100644 index 0000000..8275abf --- /dev/null +++ b/tests/core/test_timeout.c @@ -0,0 +1,141 @@ +#include +#include "../test.h" + +#ifndef WHAL_CFG_NO_TIMEOUT + +static uint32_t g_fakeTick; + +static uint32_t FakeTick(void) +{ + return g_fakeTick; +} + +static whal_Timeout g_timeout = { + .timeoutTicks = 10, + .GetTick = FakeTick, +}; + +/* Helper: get a pointer to g_timeout (avoids -Waddress on &global) */ +static whal_Timeout *timeout(void) +{ + return &g_timeout; +} + +/* WHAL_TIMEOUT_EXPIRED returns 0 when NULL */ +static void Test_Timeout_NullNotExpired(void) +{ + whal_Timeout *t = NULL; + int expired = WHAL_TIMEOUT_EXPIRED(t); + WHAL_ASSERT_EQ(expired, 0); +} + +/* START snapshots current tick */ +static void Test_Timeout_StartSnapshotsTick(void) +{ + g_fakeTick = 42; + WHAL_TIMEOUT_START(timeout()); + WHAL_ASSERT_EQ(g_timeout.startTick, 42); +} + +/* Not expired immediately after start */ +static void Test_Timeout_NotExpiredImmediately(void) +{ + g_fakeTick = 0; + WHAL_TIMEOUT_START(timeout()); + int expired = WHAL_TIMEOUT_EXPIRED(timeout()); + WHAL_ASSERT_EQ(expired, 0); +} + +/* Not expired one tick before deadline */ +static void Test_Timeout_NotExpiredBeforeDeadline(void) +{ + g_fakeTick = 0; + WHAL_TIMEOUT_START(timeout()); + g_fakeTick = 9; + int expired = WHAL_TIMEOUT_EXPIRED(timeout()); + WHAL_ASSERT_EQ(expired, 0); +} + +/* Expired exactly at deadline */ +static void Test_Timeout_ExpiredAtDeadline(void) +{ + g_fakeTick = 0; + WHAL_TIMEOUT_START(timeout()); + g_fakeTick = 10; + int expired = WHAL_TIMEOUT_EXPIRED(timeout()); + WHAL_ASSERT_NEQ(expired, 0); +} + +/* Expired well past deadline */ +static void Test_Timeout_ExpiredPastDeadline(void) +{ + g_fakeTick = 0; + WHAL_TIMEOUT_START(timeout()); + g_fakeTick = 100; + int expired = WHAL_TIMEOUT_EXPIRED(timeout()); + WHAL_ASSERT_NEQ(expired, 0); +} + +/* START resets the window */ +static void Test_Timeout_StartResetsWindow(void) +{ + int expired; + + g_fakeTick = 0; + WHAL_TIMEOUT_START(timeout()); + g_fakeTick = 10; + expired = WHAL_TIMEOUT_EXPIRED(timeout()); + WHAL_ASSERT_NEQ(expired, 0); + + /* Restart — should no longer be expired */ + WHAL_TIMEOUT_START(timeout()); + expired = WHAL_TIMEOUT_EXPIRED(timeout()); + WHAL_ASSERT_EQ(expired, 0); +} + +/* Non-zero start tick works correctly */ +static void Test_Timeout_NonZeroStart(void) +{ + int expired; + + g_fakeTick = 1000; + WHAL_TIMEOUT_START(timeout()); + g_fakeTick = 1005; + expired = WHAL_TIMEOUT_EXPIRED(timeout()); + WHAL_ASSERT_EQ(expired, 0); + g_fakeTick = 1010; + expired = WHAL_TIMEOUT_EXPIRED(timeout()); + WHAL_ASSERT_NEQ(expired, 0); +} + +/* Tick counter wrapping (unsigned subtraction handles this) */ +static void Test_Timeout_TickWrap(void) +{ + int expired; + + g_fakeTick = UINT32_MAX - 3; + WHAL_TIMEOUT_START(timeout()); + g_fakeTick = UINT32_MAX; + expired = WHAL_TIMEOUT_EXPIRED(timeout()); + WHAL_ASSERT_EQ(expired, 0); + g_fakeTick = UINT32_MAX - 3 + 10; + expired = WHAL_TIMEOUT_EXPIRED(timeout()); + WHAL_ASSERT_NEQ(expired, 0); +} + +void whal_Test_Timeout(void) +{ + WHAL_TEST_SUITE_START("timeout"); + WHAL_TEST(Test_Timeout_NullNotExpired); + WHAL_TEST(Test_Timeout_StartSnapshotsTick); + WHAL_TEST(Test_Timeout_NotExpiredImmediately); + WHAL_TEST(Test_Timeout_NotExpiredBeforeDeadline); + WHAL_TEST(Test_Timeout_ExpiredAtDeadline); + WHAL_TEST(Test_Timeout_ExpiredPastDeadline); + WHAL_TEST(Test_Timeout_StartResetsWindow); + WHAL_TEST(Test_Timeout_NonZeroStart); + WHAL_TEST(Test_Timeout_TickWrap); + WHAL_TEST_SUITE_END(); +} + +#endif /* !WHAL_CFG_NO_TIMEOUT */ diff --git a/tests/gpio/test_gpio.c b/tests/gpio/test_gpio.c new file mode 100644 index 0000000..839f497 --- /dev/null +++ b/tests/gpio/test_gpio.c @@ -0,0 +1,37 @@ +#include +#include "board.h" +#include "test.h" + +static void Test_Gpio_Api(void) +{ + size_t val; + + WHAL_ASSERT_EQ(whal_Gpio_Init(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Gpio_Deinit(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Gpio_Get(NULL, 0, &val), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Gpio_Set(NULL, 0, 0), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Gpio_Get(&g_whalGpio, 0, NULL), WHAL_EINVAL); +} + +static void Test_Gpio_SetGetHighLow(void) +{ + size_t val = 0; + + /* Set high and verify */ + WHAL_ASSERT_EQ(whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 1), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Gpio_Get(&g_whalGpio, BOARD_LED_PIN, &val), WHAL_SUCCESS); + WHAL_ASSERT_EQ(val, 1); + + /* Set low and verify */ + WHAL_ASSERT_EQ(whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 0), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Gpio_Get(&g_whalGpio, BOARD_LED_PIN, &val), WHAL_SUCCESS); + WHAL_ASSERT_EQ(val, 0); +} + +void whal_Test_Gpio(void) +{ + WHAL_TEST_SUITE_START("gpio"); + WHAL_TEST(Test_Gpio_Api); + WHAL_TEST(Test_Gpio_SetGetHighLow); + WHAL_TEST_SUITE_END(); +} diff --git a/tests/gpio/test_stm32l1_gpio.c b/tests/gpio/test_stm32l1_gpio.c new file mode 100644 index 0000000..c287cb9 --- /dev/null +++ b/tests/gpio/test_stm32l1_gpio.c @@ -0,0 +1 @@ +#include "test_stm32wb_gpio.c" diff --git a/tests/gpio/test_stm32wb_gpio.c b/tests/gpio/test_stm32wb_gpio.c new file mode 100644 index 0000000..3596e4b --- /dev/null +++ b/tests/gpio/test_stm32wb_gpio.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include "board.h" +#include "test.h" + +/* + * GPIO register offsets. Port and pin are derived from the LED entry in + * g_whalGpio.cfg->pinCfg[BOARD_LED_PIN]. Port stride is 0x400 on all + * supported STM32 families. + */ +#define GPIOx_MODE_REG 0x00 +#define GPIOx_ODR_REG 0x14 +#define GPIOx_STRIDE 0x400 + +static inline size_t Board_LedPortBase(void) +{ + whal_Stm32wb_Gpio_Cfg *cfg = (whal_Stm32wb_Gpio_Cfg *)g_whalGpio.cfg; + whal_Stm32wb_Gpio_PinCfg led = cfg->pinCfg[BOARD_LED_PIN]; + return g_whalGpio.regmap.base + WHAL_STM32WB_GPIO_GET_PORT(led) * GPIOx_STRIDE; +} + +static inline size_t Board_LedPinNum(void) +{ + whal_Stm32wb_Gpio_Cfg *cfg = (whal_Stm32wb_Gpio_Cfg *)g_whalGpio.cfg; + whal_Stm32wb_Gpio_PinCfg led = cfg->pinCfg[BOARD_LED_PIN]; + return WHAL_STM32WB_GPIO_GET_PIN(led); +} + +static void Test_Gpio_PinCfgRoundTrip(void) +{ + whal_Stm32wb_Gpio_PinCfg cfg = WHAL_STM32WB_GPIO_PIN( + WHAL_STM32WB_GPIO_PORT_C, 13, WHAL_STM32WB_GPIO_MODE_ALTFN, + WHAL_STM32WB_GPIO_OUTTYPE_OPENDRAIN, WHAL_STM32WB_GPIO_SPEED_HIGH, + WHAL_STM32WB_GPIO_PULL_DOWN, 9); + + WHAL_ASSERT_EQ(WHAL_STM32WB_GPIO_GET_PORT(cfg), WHAL_STM32WB_GPIO_PORT_C); + WHAL_ASSERT_EQ(WHAL_STM32WB_GPIO_GET_PIN(cfg), 13); + WHAL_ASSERT_EQ(WHAL_STM32WB_GPIO_GET_MODE(cfg), WHAL_STM32WB_GPIO_MODE_ALTFN); + WHAL_ASSERT_EQ(WHAL_STM32WB_GPIO_GET_OUTTYPE(cfg), WHAL_STM32WB_GPIO_OUTTYPE_OPENDRAIN); + WHAL_ASSERT_EQ(WHAL_STM32WB_GPIO_GET_SPEED(cfg), WHAL_STM32WB_GPIO_SPEED_HIGH); + WHAL_ASSERT_EQ(WHAL_STM32WB_GPIO_GET_PULL(cfg), WHAL_STM32WB_GPIO_PULL_DOWN); + WHAL_ASSERT_EQ(WHAL_STM32WB_GPIO_GET_ALTFN(cfg), 9); +} + +static void Test_Gpio_NoDuplicatePins(void) +{ + whal_Stm32wb_Gpio_Cfg *cfg = (whal_Stm32wb_Gpio_Cfg *)g_whalGpio.cfg; + whal_Stm32wb_Gpio_PinCfg *pins = cfg->pinCfg; + + for (size_t i = 0; i < cfg->pinCount; i++) { + for (size_t j = i + 1; j < cfg->pinCount; j++) { + if (WHAL_STM32WB_GPIO_GET_PORT(pins[i]) == WHAL_STM32WB_GPIO_GET_PORT(pins[j]) && + WHAL_STM32WB_GPIO_GET_PIN(pins[i]) == WHAL_STM32WB_GPIO_GET_PIN(pins[j])) { + WHAL_ASSERT_NEQ(WHAL_STM32WB_GPIO_GET_PORT(pins[i]), + WHAL_STM32WB_GPIO_GET_PORT(pins[j])); + } + } + } +} + +static void Test_Gpio_ModeRegister(void) +{ + size_t pinNum = Board_LedPinNum(); + size_t bitPos = pinNum << 1; + size_t mask = (WHAL_BITMASK(2) << bitPos); + size_t val = 0; + + whal_Reg_Get(Board_LedPortBase(), GPIOx_MODE_REG, mask, bitPos, &val); + WHAL_ASSERT_EQ(val, WHAL_STM32WB_GPIO_MODE_OUT); +} + +static void Test_Gpio_SetHighReg(void) +{ + WHAL_ASSERT_EQ(whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 1), WHAL_SUCCESS); + + size_t pinNum = Board_LedPinNum(); + size_t val = 0; + whal_Reg_Get(Board_LedPortBase(), GPIOx_ODR_REG, (1UL << pinNum), + pinNum, &val); + WHAL_ASSERT_EQ(val, 1); +} + +static void Test_Gpio_SetLowReg(void) +{ + WHAL_ASSERT_EQ(whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 0), WHAL_SUCCESS); + + size_t pinNum = Board_LedPinNum(); + size_t val = 0; + whal_Reg_Get(Board_LedPortBase(), GPIOx_ODR_REG, (1UL << pinNum), + pinNum, &val); + WHAL_ASSERT_EQ(val, 0); +} + +void whal_Test_Gpio_Platform(void) +{ + WHAL_TEST(Test_Gpio_PinCfgRoundTrip); + WHAL_TEST(Test_Gpio_NoDuplicatePins); + WHAL_TEST(Test_Gpio_ModeRegister); + WHAL_TEST(Test_Gpio_SetHighReg); + WHAL_TEST(Test_Gpio_SetLowReg); +} diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..c463c30 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,277 @@ +#include +#include +#include "board.h" +#include "test.h" + +#ifdef WHAL_TEST_ENABLE_CLOCK +void whal_Test_Clock(void); +#ifdef WHAL_TEST_ENABLE_CLOCK_PLATFORM +void whal_Test_Clock_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_GPIO +void whal_Test_Gpio(void); +#ifdef WHAL_TEST_ENABLE_GPIO_PLATFORM +void whal_Test_Gpio_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_FLASH +void whal_Test_Flash(void); +#ifdef WHAL_TEST_ENABLE_FLASH_PLATFORM +void whal_Test_Flash_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_TIMER +void whal_Test_Timer(void); +#ifdef WHAL_TEST_ENABLE_TIMER_PLATFORM +void whal_Test_Timer_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_RNG +void whal_Test_Rng(void); +#ifdef WHAL_TEST_ENABLE_RNG_PLATFORM +void whal_Test_Rng_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_IPC +void whal_Test_Ipc(void); +#ifdef WHAL_TEST_ENABLE_IPC_PLATFORM +void whal_Test_Ipc_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_SPI_LOOPBACK +void whal_Test_Spi_Loopback(void); +#ifdef WHAL_TEST_ENABLE_SPI_LOOPBACK_PLATFORM +void whal_Test_Spi_Loopback_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_CRYPTO +void whal_Test_Crypto(void); +#ifdef WHAL_TEST_ENABLE_CRYPTO_PLATFORM +void whal_Test_Crypto_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_BLOCK +void whal_Test_Block(void); +#endif + +#ifdef WHAL_TEST_ENABLE_ETH +void whal_Test_Eth(void); +#ifdef WHAL_TEST_ENABLE_ETH_PLATFORM +void whal_Test_Eth_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_BMI270_SENSOR +void whal_Test_Bmi270_Sensor(void); +#endif + +#ifdef WHAL_TEST_ENABLE_WATCHDOG +void whal_Test_Watchdog(void); +#ifdef WHAL_TEST_ENABLE_WATCHDOG_PLATFORM +void whal_Test_Watchdog_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_UART +void whal_Test_Uart(void); +#ifdef WHAL_TEST_ENABLE_UART_PLATFORM +void whal_Test_Uart_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_I2C +void whal_Test_I2c(void); +#ifdef WHAL_TEST_ENABLE_I2C_PLATFORM +void whal_Test_I2c_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_DMA +void whal_Test_Dma(void); +#ifdef WHAL_TEST_ENABLE_DMA_PLATFORM +void whal_Test_Dma_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_IRQ +void whal_Test_Irq(void); +#ifdef WHAL_TEST_ENABLE_IRQ_PLATFORM +void whal_Test_Irq_Platform(void); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_SENSOR +void whal_Test_Sensor(void); +#endif + +int g_whalTestPassed; +int g_whalTestFailed; +int g_whalTestSkipped; +int g_whalTestCurFailed; +int g_whalTestCurSkipped; + +void whal_Test_Puts(const char *s) +{ + size_t len = 0; + while (s[len]) + len++; + + if (len == 0) + return; + + if (s[len - 1] == '\n') { + if (len > 1) + whal_Uart_Send(&g_whalUart, s, len - 1); + whal_Uart_Send(&g_whalUart, "\r\n", 2); + } else { + whal_Uart_Send(&g_whalUart, s, len); + } +} + +void main(void) +{ + g_whalTestPassed = 0; + g_whalTestFailed = 0; + g_whalTestSkipped = 0; + + if (Board_Init() != WHAL_SUCCESS) + while (1); + + whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 1); + + whal_Test_Printf("wolfHAL HW Test Suite\n"); + whal_Test_Printf("=====================\n"); + +#ifdef WHAL_TEST_ENABLE_CLOCK + whal_Test_Clock(); +#ifdef WHAL_TEST_ENABLE_CLOCK_PLATFORM + whal_Test_Clock_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_GPIO + whal_Test_Gpio(); +#ifdef WHAL_TEST_ENABLE_GPIO_PLATFORM + whal_Test_Gpio_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_FLASH + whal_Test_Flash(); +#ifdef WHAL_TEST_ENABLE_FLASH_PLATFORM + whal_Test_Flash_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_TIMER + whal_Test_Timer(); +#ifdef WHAL_TEST_ENABLE_TIMER_PLATFORM + whal_Test_Timer_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_RNG + whal_Test_Rng(); +#ifdef WHAL_TEST_ENABLE_RNG_PLATFORM + whal_Test_Rng_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_IPC + whal_Test_Ipc(); +#ifdef WHAL_TEST_ENABLE_IPC_PLATFORM + whal_Test_Ipc_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_SPI_LOOPBACK + whal_Test_Spi_Loopback(); +#ifdef WHAL_TEST_ENABLE_SPI_LOOPBACK_PLATFORM + whal_Test_Spi_Loopback_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_CRYPTO + whal_Test_Crypto(); +#ifdef WHAL_TEST_ENABLE_CRYPTO_PLATFORM + whal_Test_Crypto_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_BLOCK + whal_Test_Block(); +#endif + +#ifdef WHAL_TEST_ENABLE_ETH + whal_Test_Eth(); +#ifdef WHAL_TEST_ENABLE_ETH_PLATFORM + whal_Test_Eth_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_BMI270_SENSOR + whal_Test_Bmi270_Sensor(); +#endif + +#ifdef WHAL_TEST_ENABLE_WATCHDOG + whal_Test_Watchdog(); +#ifdef WHAL_TEST_ENABLE_WATCHDOG_PLATFORM + whal_Test_Watchdog_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_UART + whal_Test_Uart(); +#ifdef WHAL_TEST_ENABLE_UART_PLATFORM + whal_Test_Uart_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_I2C + whal_Test_I2c(); +#ifdef WHAL_TEST_ENABLE_I2C_PLATFORM + whal_Test_I2c_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_DMA + whal_Test_Dma(); +#ifdef WHAL_TEST_ENABLE_DMA_PLATFORM + whal_Test_Dma_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_IRQ + whal_Test_Irq(); +#ifdef WHAL_TEST_ENABLE_IRQ_PLATFORM + whal_Test_Irq_Platform(); +#endif +#endif + +#ifdef WHAL_TEST_ENABLE_SENSOR + whal_Test_Sensor(); +#endif + + WHAL_TEST_SUMMARY(); + + if (g_whalTestFailed == 0) { + whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 1); + while (1); + } + + while (1) { + whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 1); + Board_WaitMs(100); + whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 0); + Board_WaitMs(100); + } +} diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 0000000..fe9baa3 --- /dev/null +++ b/tests/test.h @@ -0,0 +1,182 @@ +#ifndef WHAL_TEST_H +#define WHAL_TEST_H + +#include +#include + +/* + * Minimal test framework for wolfHAL. + * + * Provides test runner macros, assertions, and pass/fail tracking. + * The print backend (whal_Test_Puts) is implemented per-harness: + * - core: libc puts + * - hw: UART send + */ + +/* Provided by each harness */ +void whal_Test_Puts(const char *s); + +/* Convert an integer to decimal in a static buffer (no libc dependency) */ +static inline const char *whal_Test_Itoa(int val) +{ + static char buf[16]; + char *p = buf + sizeof(buf) - 1; + int neg = 0; + + *p = '\0'; + if (val < 0) { + neg = 1; + val = -val; + } + + do { + *--p = '0' + (val % 10); + val /= 10; + } while (val); + + if (neg) + *--p = '-'; + + return p; +} + +/* Minimal printf: supports %d, %s, and %%. Calls whal_Test_Puts once. */ +static inline void whal_Test_Printf(const char *fmt, ...) +{ + static char buf[128]; + char *out = buf; + char *end = buf + sizeof(buf) - 1; + va_list ap; + + va_start(ap, fmt); + while (*fmt && out < end) { + if (*fmt != '%') { + *out++ = *fmt++; + continue; + } + fmt++; + if (*fmt == 'd') { + const char *s = whal_Test_Itoa(va_arg(ap, int)); + while (*s && out < end) + *out++ = *s++; + } + else if (*fmt == 's') { + const char *s = va_arg(ap, const char *); + while (*s && out < end) + *out++ = *s++; + } + else if (*fmt == '%') { + *out++ = '%'; + } + fmt++; + } + va_end(ap); + + *out = '\0'; + whal_Test_Puts(buf); +} + +extern int g_whalTestPassed; +extern int g_whalTestFailed; +extern int g_whalTestSkipped; +extern int g_whalTestCurFailed; +extern int g_whalTestCurSkipped; + +#define WHAL_TEST_SUITE_START(name) \ + do { \ + whal_Test_Printf("\n=== " name " ===\n"); \ + } while (0) + +#define WHAL_TEST_SUITE_END() \ + do { } while (0) + +#define WHAL_SKIP() \ + do { \ + g_whalTestCurSkipped = 1; \ + return; \ + } while (0) + +#define WHAL_TEST(fn) \ + do { \ + g_whalTestCurFailed = 0; \ + g_whalTestCurSkipped = 0; \ + fn(); \ + if (g_whalTestCurSkipped) { \ + whal_Test_Printf(#fn ": SKIP\n"); \ + g_whalTestSkipped++; \ + } \ + else if (g_whalTestCurFailed) { \ + whal_Test_Printf(#fn ": FAIL\n"); \ + g_whalTestFailed++; \ + } \ + else { \ + whal_Test_Printf(#fn ": PASS\n"); \ + g_whalTestPassed++; \ + } \ + } while (0) + +#define WHAL_ASSERT_EQ(a, b) \ + do { \ + int _a = (int)(a); \ + int _b = (int)(b); \ + if (_a != _b) { \ + whal_Test_Printf(" ASSERT_EQ failed at %s:%d\n", \ + __FILE__, __LINE__); \ + whal_Test_Printf(" got: %d, expected: %d\n", \ + _a, _b); \ + g_whalTestCurFailed = 1; \ + return; \ + } \ + } while (0) + +#define WHAL_ASSERT_NEQ(a, b) \ + do { \ + if ((a) == (b)) { \ + whal_Test_Printf(" ASSERT_NEQ failed at %s:%d\n", \ + __FILE__, __LINE__); \ + g_whalTestCurFailed = 1; \ + return; \ + } \ + } while (0) + +#define WHAL_ASSERT_MEM_EQ(a, b, len) \ + do { \ + const unsigned char *_a = (const unsigned char *)(a); \ + const unsigned char *_b = (const unsigned char *)(b); \ + for (size_t _i = 0; _i < (len); _i++) { \ + if (_a[_i] != _b[_i]) { \ + whal_Test_Printf(" ASSERT_MEM_EQ failed at " \ + "%s:%d, byte offset: %d\n", \ + __FILE__, __LINE__, (int)_i); \ + g_whalTestCurFailed = 1; \ + return; \ + } \ + } \ + } while (0) + +#define WHAL_ASSERT_MEM_NEQ(a, b, len) \ + do { \ + const unsigned char *_a = (const unsigned char *)(a); \ + const unsigned char *_b = (const unsigned char *)(b); \ + int _differ = 0; \ + for (size_t _i = 0; _i < (len); _i++) { \ + if (_a[_i] != _b[_i]) { _differ = 1; break; } \ + } \ + if (!_differ) { \ + whal_Test_Printf(" ASSERT_MEM_NEQ failed at %s:%d\n", \ + __FILE__, __LINE__); \ + g_whalTestCurFailed = 1; \ + return; \ + } \ + } while (0) + +#define WHAL_TEST_SUMMARY() \ + do { \ + whal_Test_Printf("\n"); \ + whal_Test_Printf("--- Results ---\n"); \ + whal_Test_Printf("Skipped: %d\n", g_whalTestSkipped); \ + whal_Test_Printf("Passed: %d\n", g_whalTestPassed); \ + whal_Test_Printf("Failed: %d\n", g_whalTestFailed); \ + } while (0) + +#endif /* WHAL_TEST_H */ diff --git a/tests/uart/test_uart.c b/tests/uart/test_uart.c new file mode 100644 index 0000000..2cd2eb7 --- /dev/null +++ b/tests/uart/test_uart.c @@ -0,0 +1,24 @@ +#include +#include "board.h" +#include "test.h" + +static void Test_Uart_Api(void) +{ + uint8_t buf[8]; + + WHAL_ASSERT_EQ(whal_Uart_Init(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_Deinit(NULL), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_Send(NULL, buf, sizeof(buf)), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_Recv(NULL, buf, sizeof(buf)), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_SendAsync(NULL, buf, sizeof(buf)), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_RecvAsync(NULL, buf, sizeof(buf)), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_Send(&g_whalUart, NULL, 8), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_Recv(&g_whalUart, NULL, 8), WHAL_EINVAL); +} + +void whal_Test_Uart(void) +{ + WHAL_TEST_SUITE_START("uart"); + WHAL_TEST(Test_Uart_Api); + WHAL_TEST_SUITE_END(); +} diff --git a/wolfHAL/bitops.h b/wolfHAL/bitops.h new file mode 100644 index 0000000..240d31e --- /dev/null +++ b/wolfHAL/bitops.h @@ -0,0 +1,31 @@ +#ifndef WHAL_BITOPS_H +#define WHAL_BITOPS_H + +/* + * @file bitops.h + * @brief Bit manipulation helpers for register fields. + */ + +#include + +/* + * @brief Create a bitmask of @p width bits starting at bit 0. + * + * Example: WHAL_BITMASK(4) = 0xF + */ +#define WHAL_BITMASK(width) \ + ((1UL << (width)) - 1) + +/* + * @brief Encode a value into a bit field described by @p msk and @p pos. + */ +#define whal_SetBits(msk, pos, val) \ + (((val) << (pos)) & (msk)) + +/* + * @brief Extract a bit field value from a register using @p msk and @p pos. + */ +#define whal_GetBits(msk, pos, reg) \ + (((reg) & (msk)) >> (pos)) + +#endif /* WHAL_BITOPS_H */ diff --git a/wolfHAL/clock/clock.h b/wolfHAL/clock/clock.h new file mode 100644 index 0000000..4b28dfe --- /dev/null +++ b/wolfHAL/clock/clock.h @@ -0,0 +1,20 @@ +#ifndef WHAL_CLOCK_H +#define WHAL_CLOCK_H + +#include + +/* + * @file clock.h + * @brief Clock device handle. + * + * wolfHAL doesn't provide a generic clock API — clock-tree shape and + * peripheral-gate semantics vary too much across vendors. Each chip's + * clock driver header (e.g. ) defines its + * own imperative bring-up and gate-toggle helpers. This file just + * provides a typed handle so those helpers have something to take. + */ +typedef struct { + const whal_Regmap regmap; +} whal_Clock; + +#endif /* WHAL_CLOCK_H */ diff --git a/wolfHAL/clock/stm32l1_rcc.h b/wolfHAL/clock/stm32l1_rcc.h new file mode 100644 index 0000000..f43d69b --- /dev/null +++ b/wolfHAL/clock/stm32l1_rcc.h @@ -0,0 +1,138 @@ +#ifndef WHAL_STM32L1_RCC_H +#define WHAL_STM32L1_RCC_H + +#include +#include +#include + +/* + * @file stm32l1_rcc.h + * @brief STM32L1 RCC (Reset and Clock Control) driver. + * + * Boards bring up the clock tree imperatively from Board_Init. + * + * Clock sources: + * MSI = multispeed internal (default after reset) + * HSI = 16 MHz internal RC + * HSE = 1-24 MHz external + * PLL = HSI or HSE * PLLMUL / PLLDIV (max 32 MHz) + */ + +/* + * @brief System clock source selection (RCC_CFGR.SW). + */ +typedef enum { + WHAL_STM32L1_RCC_SYSCLK_SRC_MSI, + WHAL_STM32L1_RCC_SYSCLK_SRC_HSI, + WHAL_STM32L1_RCC_SYSCLK_SRC_HSE, + WHAL_STM32L1_RCC_SYSCLK_SRC_PLL, +} whal_Stm32l1_Rcc_SysClockSrc; + +/* + * @brief PLL input selection. + */ +typedef enum { + WHAL_STM32L1_RCC_PLLSRC_HSI, + WHAL_STM32L1_RCC_PLLSRC_HSE, +} whal_Stm32l1_Rcc_PllClockSrc; + +/* + * @brief PLL multiplication factor (RCC_CFGR.PLLMUL). + */ +typedef enum { + WHAL_STM32L1_RCC_PLLMUL_3 = 0, + WHAL_STM32L1_RCC_PLLMUL_4 = 1, + WHAL_STM32L1_RCC_PLLMUL_6 = 2, + WHAL_STM32L1_RCC_PLLMUL_8 = 3, + WHAL_STM32L1_RCC_PLLMUL_12 = 4, + WHAL_STM32L1_RCC_PLLMUL_16 = 5, + WHAL_STM32L1_RCC_PLLMUL_24 = 6, + WHAL_STM32L1_RCC_PLLMUL_32 = 7, + WHAL_STM32L1_RCC_PLLMUL_48 = 8, +} whal_Stm32l1_Rcc_PllMul; + +/* + * @brief PLL output division factor (RCC_CFGR.PLLDIV). + */ +typedef enum { + WHAL_STM32L1_RCC_PLLDIV_2 = 1, + WHAL_STM32L1_RCC_PLLDIV_3 = 2, + WHAL_STM32L1_RCC_PLLDIV_4 = 3, +} whal_Stm32l1_Rcc_PllDiv; + +/* + * @brief PLL configuration parameters. + */ +typedef struct { + whal_Stm32l1_Rcc_PllClockSrc clkSrc; + whal_Stm32l1_Rcc_PllMul pllmul; + whal_Stm32l1_Rcc_PllDiv plldiv; +} whal_Stm32l1_Rcc_PllCfg; + +/* + * @brief Peripheral clock descriptor. + */ +typedef struct { + size_t regOffset; + size_t enableMask; + size_t enablePos; +} whal_Stm32l1_Rcc_PeriphClk; + +/* + * @brief Cfg for EnableOsc/DisableOsc — on bit + ready bit. + */ +typedef struct { + size_t onReg; + size_t onMsk; + size_t rdyReg; + size_t rdyMsk; + size_t rdyPos; +} whal_Stm32l1_Rcc_OscCfg; + +#define WHAL_STM32L1_RCC_HSI_CFG \ + .onReg = 0x000, .onMsk = (1UL << 0), \ + .rdyReg = 0x000, .rdyMsk = (1UL << 1), .rdyPos = 1 +#define WHAL_STM32L1_RCC_HSE_CFG \ + .onReg = 0x000, .onMsk = (1UL << 16), \ + .rdyReg = 0x000, .rdyMsk = (1UL << 17), .rdyPos = 17 + +/* + * @brief Enable an oscillator (HSI/HSE). Blocks until ready. + */ +whal_Error whal_Stm32l1_Rcc_EnableOsc(whal_Clock *clkDev, + const whal_Stm32l1_Rcc_OscCfg *cfg); +/* + * @brief Disable an oscillator. + */ +whal_Error whal_Stm32l1_Rcc_DisableOsc(whal_Clock *clkDev, + const whal_Stm32l1_Rcc_OscCfg *cfg); + +/* + * @brief Configure and enable the PLL. Caller must have the PLL source + * oscillator already enabled. Blocks until PLL is ready. + */ +whal_Error whal_Stm32l1_Rcc_EnablePll(whal_Clock *clkDev, + const whal_Stm32l1_Rcc_PllCfg *cfg); +/* + * @brief Disable the PLL. + */ +whal_Error whal_Stm32l1_Rcc_DisablePll(whal_Clock *clkDev); + +/* + * @brief Switch SYSCLK to the given source. Blocks until SWS confirms. + */ +whal_Error whal_Stm32l1_Rcc_SetSysClock(whal_Clock *clkDev, + whal_Stm32l1_Rcc_SysClockSrc src); + +/* + * @brief Enable a peripheral clock. + */ +whal_Error whal_Stm32l1_Rcc_EnablePeriphClk(whal_Clock *clkDev, + const whal_Stm32l1_Rcc_PeriphClk *clk); +/* + * @brief Disable a peripheral clock. + */ +whal_Error whal_Stm32l1_Rcc_DisablePeriphClk(whal_Clock *clkDev, + const whal_Stm32l1_Rcc_PeriphClk *clk); + +#endif /* WHAL_STM32L1_RCC_H */ diff --git a/wolfHAL/clock/stm32wb_rcc.h b/wolfHAL/clock/stm32wb_rcc.h new file mode 100644 index 0000000..7a597af --- /dev/null +++ b/wolfHAL/clock/stm32wb_rcc.h @@ -0,0 +1,174 @@ +#ifndef WHAL_STM32WB_RCC_H +#define WHAL_STM32WB_RCC_H + +#include +#include +#include + +/* + * @file stm32wb_rcc.h + * @brief STM32WB RCC (Reset and Clock Control) driver. + * + * Boards bring up the clock tree imperatively from Board_Init by calling + * the helpers below in order: + * + * whal_Stm32wb_Rcc_EnableMsi(...); + * whal_Stm32wb_Rcc_EnablePll(..., &pllCfg); + * whal_Stm32wb_Rcc_EnableOsc(..., &hsi48Cfg); + * whal_Stm32wb_Rcc_SetSysClock(..., WHAL_STM32WB_RCC_SYSCLK_SRC_PLL); + * for each peripheral clock: whal_Stm32wb_Rcc_EnablePeriphClk(...); + * + * The driver does not provide a declarative tree or an Init/Deinit + * walker — boards know their own bring-up order. + */ + +/* + * @brief System clock source selection (RCC_CFGR.SW). + */ +typedef enum { + WHAL_STM32WB_RCC_SYSCLK_SRC_MSI, + WHAL_STM32WB_RCC_SYSCLK_SRC_HSI16, + WHAL_STM32WB_RCC_SYSCLK_SRC_HSE, + WHAL_STM32WB_RCC_SYSCLK_SRC_PLL, +} whal_Stm32wb_Rcc_SysClockSrc; + +/* + * @brief PLL input clock source selection (RCC_PLLCFGR.PLLSRC). + */ +typedef enum { + WHAL_STM32WB_RCC_PLLCLK_SRC_NONE, + WHAL_STM32WB_RCC_PLLCLK_SRC_MSI, + WHAL_STM32WB_RCC_PLLCLK_SRC_HSI16, + WHAL_STM32WB_RCC_PLLCLK_SRC_HSE, +} whal_Stm32wb_Rcc_PllClockSrc; + +/* + * @brief MSI oscillator frequency range (RCC_CR.MSIRANGE). + */ +typedef enum { + WHAL_STM32WB_RCC_MSIRANGE_100kHz, + WHAL_STM32WB_RCC_MSIRANGE_200kHz, + WHAL_STM32WB_RCC_MSIRANGE_400kHz, + WHAL_STM32WB_RCC_MSIRANGE_800kHz, + WHAL_STM32WB_RCC_MSIRANGE_1MHz, + WHAL_STM32WB_RCC_MSIRANGE_2MHz, + WHAL_STM32WB_RCC_MSIRANGE_4MHz, + WHAL_STM32WB_RCC_MSIRANGE_8MHz, + WHAL_STM32WB_RCC_MSIRANGE_16MHz, + WHAL_STM32WB_RCC_MSIRANGE_24MHz, + WHAL_STM32WB_RCC_MSIRANGE_32MHz, + WHAL_STM32WB_RCC_MSIRANGE_48MHz, +} whal_Stm32wb_Rcc_MsiRange; + +/* + * @brief Peripheral clock descriptor (RCC *ENR enable bit). + */ +typedef struct { + size_t regOffset; + size_t enableMask; + size_t enablePos; +} whal_Stm32wb_Rcc_PeriphClk; + +/* + * @brief Cfg for EnableOsc/DisableOsc — on bit + ready bit. Boards + * construct one with the WHAL_STM32WB_RCC_*_CFG macros below. + * NOTE: LSE assumes PWR_CR1.DBP has been set by the caller. + */ +typedef struct { + size_t onReg; + size_t onMsk; + size_t rdyReg; + size_t rdyMsk; + size_t rdyPos; +} whal_Stm32wb_Rcc_OscCfg; + +#define WHAL_STM32WB_RCC_HSI_CFG \ + .onReg = 0x000, .onMsk = (1UL << 8), \ + .rdyReg = 0x000, .rdyMsk = (1UL << 10), .rdyPos = 10 +#define WHAL_STM32WB_RCC_HSE_CFG \ + .onReg = 0x000, .onMsk = (1UL << 16), \ + .rdyReg = 0x000, .rdyMsk = (1UL << 17), .rdyPos = 17 +#define WHAL_STM32WB_RCC_HSI48_CFG \ + .onReg = 0x098, .onMsk = (1UL << 0), \ + .rdyReg = 0x098, .rdyMsk = (1UL << 1), .rdyPos = 1 +#define WHAL_STM32WB_RCC_LSI_CFG \ + .onReg = 0x094, .onMsk = (1UL << 0), \ + .rdyReg = 0x094, .rdyMsk = (1UL << 1), .rdyPos = 1 +#define WHAL_STM32WB_RCC_LSE_CFG \ + .onReg = 0x090, .onMsk = (1UL << 0), \ + .rdyReg = 0x090, .rdyMsk = (1UL << 1), .rdyPos = 1 + +/* + * @brief PLL configuration parameters. + * + * The PLL output frequency is calculated as: + * f_vco = (f_input / m) * n + * f_pllr = f_vco / r (main PLL output, used for SYSCLK) + * f_pllq = f_vco / q (used for USB, RNG, etc.) + * f_pllp = f_vco / p (used for SAI, etc.) + * + * Constraints: + * - VCO frequency must be 96-344 MHz + * - PLL input (f_input / m) must be 2.66-16 MHz + * - n: 8-127 + * - m: 1-8 (register value 0-7) + * - r, q: 2, 4, 6, 8 (register value 0-3 maps to div by 2/4/6/8) + * - p: 2-32 (register value 1-31, 0 reserved) + */ +typedef struct { + whal_Stm32wb_Rcc_PllClockSrc clkSrc; + uint8_t r; + uint8_t q; + uint8_t p; + uint8_t n; + uint8_t m; +} whal_Stm32wb_Rcc_PllCfg; + +/* + * @brief Enable an oscillator (HSI/HSE/HSI48/LSI/LSE) and block until + * the ready bit is set. + */ +whal_Error whal_Stm32wb_Rcc_EnableOsc(whal_Clock *clkDev, + const whal_Stm32wb_Rcc_OscCfg *cfg); +/* + * @brief Disable an oscillator. + */ +whal_Error whal_Stm32wb_Rcc_DisableOsc(whal_Clock *clkDev, + const whal_Stm32wb_Rcc_OscCfg *cfg); + +/* + * @brief Enable the MSI oscillator at the given range. Blocks until ready. + */ +whal_Error whal_Stm32wb_Rcc_EnableMsi(whal_Clock *clkDev, + whal_Stm32wb_Rcc_MsiRange range); + +/* + * @brief Configure the PLL dividers/source and enable it. Blocks until + * the PLL ready bit is set. + */ +whal_Error whal_Stm32wb_Rcc_EnablePll(whal_Clock *clkDev, + const whal_Stm32wb_Rcc_PllCfg *cfg); +/* + * @brief Disable the PLL. + */ +whal_Error whal_Stm32wb_Rcc_DisablePll(whal_Clock *clkDev); + +/* + * @brief Switch SYSCLK to the given source. Blocks until RCC_CFGR.SWS + * reflects the new source. + */ +whal_Error whal_Stm32wb_Rcc_SetSysClock(whal_Clock *clkDev, + whal_Stm32wb_Rcc_SysClockSrc src); + +/* + * @brief Enable a peripheral clock. + */ +whal_Error whal_Stm32wb_Rcc_EnablePeriphClk(whal_Clock *clkDev, + const whal_Stm32wb_Rcc_PeriphClk *clk); +/* + * @brief Disable a peripheral clock. + */ +whal_Error whal_Stm32wb_Rcc_DisablePeriphClk(whal_Clock *clkDev, + const whal_Stm32wb_Rcc_PeriphClk *clk); + +#endif /* WHAL_STM32WB_RCC_H */ diff --git a/wolfHAL/endian.h b/wolfHAL/endian.h new file mode 100644 index 0000000..2b06de3 --- /dev/null +++ b/wolfHAL/endian.h @@ -0,0 +1,67 @@ +#ifndef WHAL_ENDIAN_H +#define WHAL_ENDIAN_H + +/* + * @file endian.h + * @brief Byte-order conversion helpers. + */ + +#include +#include + +/* + * @brief Load a 32-bit value from a big-endian byte array. + */ +static inline uint32_t whal_LoadBe32(const uint8_t *p) +{ + return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | + ((uint32_t)p[2] << 8) | p[3]; +} + +/* + * @brief Store a 32-bit value into a big-endian byte array. + */ +static inline void whal_StoreBe32(uint8_t *p, uint32_t v) +{ + p[0] = (uint8_t)(v >> 24); + p[1] = (uint8_t)(v >> 16); + p[2] = (uint8_t)(v >> 8); + p[3] = (uint8_t)v; +} + +/* + * @brief Load n bytes from a byte array into a 32-bit big-endian value. + * + * The first byte becomes the MSB and the remaining (4-n) bytes are zero. + * Equivalent to whal_LoadBe32 when n == 4. + * + * @param p Source byte array. + * @param n Number of bytes to load (must be 0..4). + */ +static inline uint32_t whal_LoadBe32Partial(const uint8_t *p, size_t n) +{ + uint32_t v = 0; + size_t i; + for (i = 0; i < n; i++) + v |= (uint32_t)p[i] << (24 - i * 8); + return v; +} + +/* + * @brief Load n bytes from a byte array into a 32-bit little-endian value. + * + * The first byte becomes the LSB and the remaining (4-n) bytes are zero. + * + * @param p Source byte array. + * @param n Number of bytes to load (must be 0..4). + */ +static inline uint32_t whal_LoadLe32Partial(const uint8_t *p, size_t n) +{ + uint32_t v = 0; + size_t i; + for (i = 0; i < n; i++) + v |= (uint32_t)p[i] << (i * 8); + return v; +} + +#endif /* WHAL_ENDIAN_H */ diff --git a/wolfHAL/error.h b/wolfHAL/error.h new file mode 100644 index 0000000..49acb67 --- /dev/null +++ b/wolfHAL/error.h @@ -0,0 +1,32 @@ +#ifndef WHAL_ERROR_H +#define WHAL_ERROR_H + + +/* + * @file error.h + * @brief Shared error codes for wolfHAL APIs. + */ + +/* Signed status code type used by wolfHAL. */ +typedef int whal_Error; + +enum { + /* Operation completed successfully. */ + WHAL_SUCCESS = 0, + /* Invalid argument (null pointer, bad configuration). */ + WHAL_EINVAL = -4000, + /* Resource not ready or busy. */ + WHAL_ENOTREADY = -4001, + /* Hardware device error. */ + WHAL_EHARDWARE = -4002, + /* Operation timed out. */ + WHAL_ETIMEOUT = -4003, + /* Operation or argument not supported by the selected driver/hardware + * (use for requests that are valid in general but this implementation + * cannot fulfill — e.g., hardware lacks the feature or the specific + * parameter combination isn't supported). For universally invalid + * arguments (null pointer, out-of-range enum) return WHAL_EINVAL. */ + WHAL_ENOTSUP = -4004, +}; + +#endif /* WHAL_ERROR_H */ diff --git a/wolfHAL/flash/flash.h b/wolfHAL/flash/flash.h new file mode 100644 index 0000000..42c4a32 --- /dev/null +++ b/wolfHAL/flash/flash.h @@ -0,0 +1,128 @@ +#ifndef WHAL_FLASH_H +#define WHAL_FLASH_H + +#include +#include +#include +#include + +/* + * @file flash.h + * @brief Generic flash abstraction and driver interface. + */ + +typedef struct whal_Flash whal_Flash; + +/* + * @brief Driver vtable for flash devices. + */ +typedef struct { + /* Bring the flash peripheral into a usable state. */ + whal_Error (*Init)(whal_Flash *flashDev); + /* Release any resources owned by the flash driver. */ + whal_Error (*Deinit)(whal_Flash *flashDev); + /* Lock a flash region to prevent modification. */ + whal_Error (*Lock)(whal_Flash *flashDev, size_t addr, size_t len); + /* Unlock a flash region to allow modification. */ + whal_Error (*Unlock)(whal_Flash *flashDev, size_t addr, size_t len); + /* Read data from flash into a buffer. */ + whal_Error (*Read)(whal_Flash *flashDev, size_t addr, void *data, size_t dataSz); + /* Program a region of flash starting at @p addr. */ + whal_Error (*Write)(whal_Flash *flashDev, size_t addr, const void *data, size_t dataSz); + /* Erase a flash range starting at @p addr. */ + whal_Error (*Erase)(whal_Flash *flashDev, size_t addr, size_t dataSz); +} whal_FlashDriver; + +/* + * @brief Flash device instance tying configuration to a driver implementation. + */ +struct whal_Flash { + const whal_Regmap regmap; + const whal_FlashDriver *driver; + void *cfg; +}; + +/* + * @brief Initialize a flash device and its driver. + * + * @param flashDev Flash instance to initialize. + * + * @retval WHAL_SUCCESS Driver-specific init completed. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Flash_Init(whal_Flash *flashDev); +/* + * @brief Deinitialize a flash device. + * + * @param flashDev Flash instance to deinitialize. + * + * @retval WHAL_SUCCESS Driver-specific deinit completed. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Flash_Deinit(whal_Flash *flashDev); +/* + * @brief Lock a region of flash to prevent modification. + * + * @param flashDev Flash instance to lock. + * @param addr Byte address in flash to lock. + * @param len Number of bytes to lock. + * + * @retval WHAL_SUCCESS Lock applied. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Flash_Lock(whal_Flash *flashDev, size_t addr, size_t len); +/* + * @brief Unlock a region of flash to allow modification. + * + * @param flashDev Flash instance to unlock. + * @param addr Byte address in flash to unlock. + * @param len Number of bytes to unlock. + * + * @retval WHAL_SUCCESS Unlock applied. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Flash_Unlock(whal_Flash *flashDev, size_t addr, size_t len); +/* + * @brief Read data from flash into a buffer. + * + * @param flashDev Flash instance to read from. + * @param addr Byte address in flash to read. + * @param data Destination buffer. + * @param dataSz Number of bytes to read. + * + * @retval WHAL_SUCCESS Read completed. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Flash_Read(whal_Flash *flashDev, size_t addr, void *data, size_t dataSz); +/* + * @brief Write data into flash. + * + * @param flashDev Flash instance to program. + * @param addr Byte address in flash to start writing. + * @param data Buffer containing data to write. + * @param dataSz Number of bytes to write. + * + * @retval WHAL_SUCCESS Write accepted or completed. + * @retval WHAL_EINVAL Null pointer or bad arguments. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Flash_Write(whal_Flash *flashDev, size_t addr, const void *data, size_t dataSz); +/* + * @brief Erase a region of flash. + * + * @param flashDev Flash instance to erase. + * @param addr Byte address in flash where erasure starts. + * @param dataSz Number of bytes (or sector-aligned size) to erase. + * + * @retval WHAL_SUCCESS Erase accepted or completed. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Flash_Erase(whal_Flash *flashDev, size_t addr, size_t dataSz); + +#endif /* WHAL_FLASH_H */ diff --git a/wolfHAL/flash/stm32l1_flash.h b/wolfHAL/flash/stm32l1_flash.h new file mode 100644 index 0000000..4486c45 --- /dev/null +++ b/wolfHAL/flash/stm32l1_flash.h @@ -0,0 +1,167 @@ +#ifndef WHAL_STM32L1_FLASH_H +#define WHAL_STM32L1_FLASH_H + +#include +#include +#include +#include + +/* + * @file stm32l1_flash.h + * @brief STM32L1 flash driver configuration. + * + * The STM32L1 flash uses a PECR-based unlock model: + * 1. Unlock FLASH_PECR via FLASH_PEKEYR (PEKEY1=0x89ABCDEF, PEKEY2=0x02030405) + * 2. Unlock program memory via FLASH_PRGKEYR (PRGKEY1=0x8C9DAEBF, PRGKEY2=0x13141516) + * + * Register layout: + * FLASH_ACR = 0x00 (LATENCY, ACC64, PRFTEN, RUN_PD, SLEEP_PD) + * FLASH_PECR = 0x04 (PELOCK, PRGLOCK, OPTLOCK, PROG, ERASE, FPRG, DATA, FTDW) + * FLASH_PDKEYR = 0x08 + * FLASH_PEKEYR = 0x0C + * FLASH_PRGKEYR = 0x10 + * FLASH_OPTKEYR = 0x14 + * FLASH_SR = 0x18 (BSY, EOP, ENDHV, READY, WRPERR, PGAERR, SIZERR, OPTVERR) + * FLASH_OBR = 0x1C + * FLASH_WRPR1 = 0x20 + * + * Programming: + * - Write alignment: 4 bytes (word) + * - Erase granularity: 256 bytes (page) + * - Fast word write: unlock PECR + PRGKEYR, write 32-bit word to flash address + * - Page erase: set ERASE+PROG in PECR, write 0x00000000 to first word of page + */ + +/* + * @brief STM32L1 flash driver configuration. + */ +typedef struct whal_Stm32l1_Flash_Cfg { + size_t startAddr; /* Flash region start address */ + size_t size; /* Flash region size in bytes */ + whal_Timeout *timeout; /* Optional timeout for poll loops */ +} whal_Stm32l1_Flash_Cfg; + +/* + * @brief Flash access latency values for STM32L1. + * + * 0 WS: HCLK <= 16 MHz (voltage range 1 / 2), HCLK <= 8 MHz (range 3) + * 1 WS: 16 < HCLK <= 32 MHz (range 1), 8 < HCLK <= 16 MHz (range 2/3) + */ +typedef enum { + WHAL_STM32L1_FLASH_LATENCY_0 = 0, + WHAL_STM32L1_FLASH_LATENCY_1 = 1, +} whal_Stm32l1_Flash_Latency; + +#ifndef WHAL_CFG_STM32L1_FLASH_DIRECT_API_MAPPING +/* + * @brief Driver instance for the STM32L1 embedded flash controller. + */ +extern const whal_FlashDriver whal_Stm32l1_Flash_Driver; + +/* + * @brief Initialize the STM32L1 flash driver. Validates cfg. + * + * @param flashDev Flash device instance. + * + * @retval WHAL_SUCCESS Driver is ready. + * @retval WHAL_EINVAL Null pointer or missing cfg. + */ +whal_Error whal_Stm32l1_Flash_Init(whal_Flash *flashDev); + +/* + * @brief Deinitialize the STM32L1 flash driver. + * + * @param flashDev Flash device instance. + * + * @retval WHAL_SUCCESS Driver is deinitialized. + * @retval WHAL_EINVAL Null pointer. + */ +whal_Error whal_Stm32l1_Flash_Deinit(whal_Flash *flashDev); + +/* + * @brief Re-lock program memory (sets PRGLOCK in FLASH_PECR). + * + * @param flashDev Flash device instance. + * @param addr Range start (ignored — flash is globally locked). + * @param len Range length (ignored). + * + * @retval WHAL_SUCCESS Flash is locked. + * @retval WHAL_EINVAL Null pointer. + */ +whal_Error whal_Stm32l1_Flash_Lock(whal_Flash *flashDev, size_t addr, size_t len); + +/* + * @brief Unlock program memory via PEKEYR + PRGKEYR sequences. + * + * @param flashDev Flash device instance. + * @param addr Range start (ignored — flash is globally unlocked). + * @param len Range length (ignored). + * + * @retval WHAL_SUCCESS Flash is unlocked. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_EHARDWARE Key sequence rejected. + */ +whal_Error whal_Stm32l1_Flash_Unlock(whal_Flash *flashDev, size_t addr, size_t len); + +/* + * @brief Read `dataSz` bytes from flash at `addr` into `data`. + * + * @param flashDev Flash device instance. + * @param addr Source address within the flash region. + * @param data Destination buffer. + * @param dataSz Number of bytes to read. + * + * @retval WHAL_SUCCESS Read completed. + * @retval WHAL_EINVAL Null pointer or address out of region. + */ +whal_Error whal_Stm32l1_Flash_Read(whal_Flash *flashDev, size_t addr, void *data, + size_t dataSz); + +/* + * @brief Program `dataSz` bytes from `data` to flash at `addr`. Address and + * size must be 4-byte (word) aligned. + * + * @param flashDev Flash device instance. + * @param addr Destination address within the flash region. + * @param data Source buffer. + * @param dataSz Number of bytes to write. + * + * @retval WHAL_SUCCESS Write completed. + * @retval WHAL_EINVAL Null pointer, misaligned address/size, or address out of region. + * @retval WHAL_ETIMEOUT BSY did not clear within the configured timeout. + * @retval WHAL_EHARDWARE Programming error reported in FLASH_SR. + */ +whal_Error whal_Stm32l1_Flash_Write(whal_Flash *flashDev, size_t addr, + const void *data, size_t dataSz); + +/* + * @brief Erase one or more 256-byte pages covering [addr, addr+dataSz). + * + * @param flashDev Flash device instance. + * @param addr First page address. + * @param dataSz Number of bytes to erase (rounded up to whole pages). + * + * @retval WHAL_SUCCESS Erase completed. + * @retval WHAL_EINVAL Null pointer or address out of region. + * @retval WHAL_ETIMEOUT BSY did not clear within the configured timeout. + * @retval WHAL_EHARDWARE Erase error reported in FLASH_SR. + */ +whal_Error whal_Stm32l1_Flash_Erase(whal_Flash *flashDev, size_t addr, + size_t dataSz); +#endif /* !WHAL_CFG_STM32L1_FLASH_DIRECT_API_MAPPING */ + +/* + * @brief Set flash access latency (FLASH_ACR.LATENCY). + * + * Enables 64-bit access (ACC64) and prefetch (PRFTEN) as required by RM0038 + * §3.9.1 before programming the LATENCY bit, and polls until LATENCY matches + * the requested value. Must be called by the board before increasing SYSCLK + * beyond 16 MHz. + * + * @param latency Desired latency (LATENCY_0 or LATENCY_1). + * + * @retval WHAL_SUCCESS Latency updated. + */ +whal_Error whal_Stm32l1_Flash_Ext_SetLatency(whal_Stm32l1_Flash_Latency latency); + +#endif /* WHAL_STM32L1_FLASH_H */ diff --git a/wolfHAL/flash/stm32wb_flash.h b/wolfHAL/flash/stm32wb_flash.h new file mode 100644 index 0000000..a018afe --- /dev/null +++ b/wolfHAL/flash/stm32wb_flash.h @@ -0,0 +1,146 @@ +#ifndef WHAL_STM32WB_FLASH_H +#define WHAL_STM32WB_FLASH_H + +#include +#include + +/* + * @file stm32wb_flash.h + * @brief STM32WB flash driver configuration and helpers. + * + * The STM32WB embedded flash provides: + * - Up to 1 MB of flash memory organized in 4 KB pages + * - Double-word (64-bit) programming + * - Page erase and mass erase operations + * - Read-while-write capability on different banks + * - Configurable wait states based on CPU frequency + * + * Flash must be unlocked before write/erase operations and locked + * afterward for protection. Wait states must be configured appropriately + * for the system clock frequency. + */ + +/* + * @brief Flash device configuration. + */ +typedef struct whal_Stm32wb_Flash_Cfg { + size_t startAddr; /* Flash base address (typically 0x08000000) */ + size_t size; /* Flash size in bytes */ + whal_Timeout *timeout; +} whal_Stm32wb_Flash_Cfg; + +/* + * @brief Flash access latency (wait states). + * + * The number of wait states must be configured based on the CPU frequency + * and supply voltage. Insufficient wait states will cause flash read errors. + * + * Typical settings at VOS1 (1.2V): + * - 0 WS: up to 18 MHz + * - 1 WS: up to 36 MHz + * - 2 WS: up to 54 MHz + * - 3 WS: up to 64 MHz + */ +typedef enum whal_Stm32wb_Flash_Latency { + WHAL_STM32WB_FLASH_LATENCY_0, /* 0 wait states */ + WHAL_STM32WB_FLASH_LATENCY_1, /* 1 wait state */ + WHAL_STM32WB_FLASH_LATENCY_2, /* 2 wait states */ + WHAL_STM32WB_FLASH_LATENCY_3, /* 3 wait states */ +} whal_Stm32wb_Flash_Latency; + +#ifndef WHAL_CFG_STM32WB_FLASH_DIRECT_API_MAPPING +/* + * @brief Driver instance for STM32 flash. + */ +extern const whal_FlashDriver whal_Stm32wb_Flash_Driver; + +/* + * @brief Initialize the STM32 flash interface. + * + * @param flashDev Flash device instance to initialize. + * + * @retval WHAL_SUCCESS Initialization completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Flash_Init(whal_Flash *flashDev); +/* + * @brief Deinitialize the STM32 flash interface. + * + * @param flashDev Flash device instance to deinitialize. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Flash_Deinit(whal_Flash *flashDev); +/* + * @brief Lock a flash range. + * + * @param flashDev Flash device instance. + * @param addr Flash address to lock. + * @param len Number of bytes to lock. + * + * @retval WHAL_SUCCESS Lock applied. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Flash_Lock(whal_Flash *flashDev, size_t addr, size_t len); +/* + * @brief Unlock a flash range. + * + * @param flashDev Flash device instance. + * @param addr Flash address to unlock. + * @param len Number of bytes to unlock. + * + * @retval WHAL_SUCCESS Unlock applied. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Flash_Unlock(whal_Flash *flashDev, size_t addr, size_t len); +/* + * @brief Read data from flash into a buffer. + * + * @param flashDev Flash device instance. + * @param addr Flash address to read. + * @param data Destination buffer. + * @param dataSz Number of bytes to read. + * + * @retval WHAL_SUCCESS Read completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Flash_Read(whal_Flash *flashDev, size_t addr, void *data, + size_t dataSz); +/* + * @brief Write a block of data to flash. + * + * @param flashDev Flash device instance. + * @param addr Flash address to program. + * @param data Buffer to program. + * @param dataSz Number of bytes to program. + * + * @retval WHAL_SUCCESS Program completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Flash_Write(whal_Flash *flashDev, size_t addr, const void *data, + size_t dataSz); +/* + * @brief Erase a flash range. + * + * @param flashDev Flash device instance. + * @param addr Flash address to start erasing. + * @param dataSz Number of bytes to erase. + * + * @retval WHAL_SUCCESS Erase completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Flash_Erase(whal_Flash *flashDev, size_t addr, size_t dataSz); +#endif /* !WHAL_CFG_STM32WB_FLASH_DIRECT_API_MAPPING */ +/* + * @brief Update flash latency wait states. + * + * @param flashDev Flash device instance. + * @param latency Latency setting to apply. + * + * @retval WHAL_SUCCESS Latency updated. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Flash_Ext_SetLatency(whal_Flash *flashDev, enum whal_Stm32wb_Flash_Latency latency); + +#endif /* WHAL_STM32WB_FLASH_H */ diff --git a/wolfHAL/gpio/gpio.h b/wolfHAL/gpio/gpio.h new file mode 100644 index 0000000..94d789c --- /dev/null +++ b/wolfHAL/gpio/gpio.h @@ -0,0 +1,83 @@ +#ifndef WHAL_GPIO_H +#define WHAL_GPIO_H + +#include +#include +#include + +/* + * @file gpio.h + * @brief Generic GPIO abstraction and driver interface. + */ + +typedef struct whal_Gpio whal_Gpio; + +/* + * @brief Driver vtable for GPIO devices. + */ +typedef struct { + /* Initialize GPIO hardware and configured pins. */ + whal_Error (*Init)(whal_Gpio *gpioDev); + /* Deinitialize GPIO hardware. */ + whal_Error (*Deinit)(whal_Gpio *gpioDev); + /* Read a pin value. */ + whal_Error (*Get)(whal_Gpio *gpioDev, size_t pin, size_t *value); + /* Write a pin value. */ + whal_Error (*Set)(whal_Gpio *gpioDev, size_t pin, size_t value); +} whal_GpioDriver; + +/* + * @brief GPIO device instance containing driver and configuration data. + */ +struct whal_Gpio { + const whal_Regmap regmap; + const whal_GpioDriver *driver; + const void *cfg; +}; + +/* + * @brief Initialize a GPIO device and its pins. + * + * @param gpioDev GPIO instance to initialize. + * + * @retval WHAL_SUCCESS Driver-specific init completed. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Gpio_Init(whal_Gpio *gpioDev); +/* + * @brief Deinitialize a GPIO device. + * + * @param gpioDev GPIO instance to deinitialize. + * + * @retval WHAL_SUCCESS Driver-specific deinit completed. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Gpio_Deinit(whal_Gpio *gpioDev); +/* + * @brief Read the state of a pin. + * + * @param gpioDev GPIO instance containing the pin. + * @param pin Pin index into the configured pin table. + * @param value Storage for the sampled pin value. + * + * @retval WHAL_SUCCESS Pin value stored in @p value. + * @retval WHAL_EINVAL Null pointer or bad pin. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Gpio_Get(whal_Gpio *gpioDev, size_t pin, size_t *value); +/* + * @brief Set the state of a pin. + * + * @param gpioDev GPIO instance containing the pin. + * @param pin Pin index into the configured pin table. + * @param value Output value to drive (typically 0 or 1). + * + * @retval WHAL_SUCCESS Pin updated. + * @retval WHAL_EINVAL Null pointer or bad pin. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Gpio_Set(whal_Gpio *gpioDev, size_t pin, size_t value); + +#endif /* WHAL_GPIO_H */ diff --git a/wolfHAL/gpio/stm32l1_gpio.h b/wolfHAL/gpio/stm32l1_gpio.h new file mode 100644 index 0000000..5462a0d --- /dev/null +++ b/wolfHAL/gpio/stm32l1_gpio.h @@ -0,0 +1,54 @@ +#ifndef WHAL_STM32L1_GPIO_H +#define WHAL_STM32L1_GPIO_H + +/* + * @file stm32l1_gpio.h + * @brief STM32L1 GPIO driver (alias for STM32WB GPIO). + * + * The STM32L1 GPIO peripheral is register-compatible with the STM32WB GPIO. + * This header re-exports the STM32WB GPIO driver types and symbols under + * STM32L1-specific names. The underlying implementation is shared. + */ + +#include + +typedef whal_Stm32wb_Gpio_Cfg whal_Stm32l1_Gpio_Cfg; +typedef whal_Stm32wb_Gpio_PinCfg whal_Stm32l1_Gpio_PinCfg; + +#ifndef WHAL_CFG_STM32L1_GPIO_DIRECT_API_MAPPING +#define whal_Stm32l1_Gpio_Driver whal_Stm32wb_Gpio_Driver +#define whal_Stm32l1_Gpio_Init whal_Stm32wb_Gpio_Init +#define whal_Stm32l1_Gpio_Deinit whal_Stm32wb_Gpio_Deinit +#define whal_Stm32l1_Gpio_Get whal_Stm32wb_Gpio_Get +#define whal_Stm32l1_Gpio_Set whal_Stm32wb_Gpio_Set +#endif /* !WHAL_CFG_STM32L1_GPIO_DIRECT_API_MAPPING */ + +#define WHAL_STM32L1_GPIO_MODE_IN WHAL_STM32WB_GPIO_MODE_IN +#define WHAL_STM32L1_GPIO_MODE_OUT WHAL_STM32WB_GPIO_MODE_OUT +#define WHAL_STM32L1_GPIO_MODE_ALTFN WHAL_STM32WB_GPIO_MODE_ALTFN +#define WHAL_STM32L1_GPIO_MODE_ANALOG WHAL_STM32WB_GPIO_MODE_ANALOG + +#define WHAL_STM32L1_GPIO_OUTTYPE_PUSHPULL WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL +#define WHAL_STM32L1_GPIO_OUTTYPE_OPENDRAIN WHAL_STM32WB_GPIO_OUTTYPE_OPENDRAIN + +#define WHAL_STM32L1_GPIO_SPEED_LOW WHAL_STM32WB_GPIO_SPEED_LOW +#define WHAL_STM32L1_GPIO_SPEED_MEDIUM WHAL_STM32WB_GPIO_SPEED_MEDIUM +#define WHAL_STM32L1_GPIO_SPEED_FAST WHAL_STM32WB_GPIO_SPEED_FAST +#define WHAL_STM32L1_GPIO_SPEED_HIGH WHAL_STM32WB_GPIO_SPEED_HIGH + +#define WHAL_STM32L1_GPIO_PULL_NONE WHAL_STM32WB_GPIO_PULL_NONE +#define WHAL_STM32L1_GPIO_PULL_UP WHAL_STM32WB_GPIO_PULL_UP +#define WHAL_STM32L1_GPIO_PULL_DOWN WHAL_STM32WB_GPIO_PULL_DOWN + +#define WHAL_STM32L1_GPIO_PORT_A WHAL_STM32WB_GPIO_PORT_A +#define WHAL_STM32L1_GPIO_PORT_B WHAL_STM32WB_GPIO_PORT_B +#define WHAL_STM32L1_GPIO_PORT_C WHAL_STM32WB_GPIO_PORT_C +#define WHAL_STM32L1_GPIO_PORT_D WHAL_STM32WB_GPIO_PORT_D +#define WHAL_STM32L1_GPIO_PORT_E WHAL_STM32WB_GPIO_PORT_E +#define WHAL_STM32L1_GPIO_PORT_F WHAL_STM32WB_GPIO_PORT_F +#define WHAL_STM32L1_GPIO_PORT_G WHAL_STM32WB_GPIO_PORT_G +#define WHAL_STM32L1_GPIO_PORT_H WHAL_STM32WB_GPIO_PORT_H + +#define WHAL_STM32L1_GPIO_PIN WHAL_STM32WB_GPIO_PIN + +#endif /* WHAL_STM32L1_GPIO_H */ diff --git a/wolfHAL/gpio/stm32wb_gpio.h b/wolfHAL/gpio/stm32wb_gpio.h new file mode 100644 index 0000000..628af48 --- /dev/null +++ b/wolfHAL/gpio/stm32wb_gpio.h @@ -0,0 +1,165 @@ +#ifndef WHAL_STM32WB_GPIO_H +#define WHAL_STM32WB_GPIO_H + +#include +#include +#include + +/* + * @file stm32wb_gpio.h + * @brief STM32WB GPIO driver configuration types. + * + * The STM32WB GPIO peripheral provides: + * - Up to 8 GPIO ports (A-H), each with up to 16 pins + * - Configurable pin modes: input, output, alternate function, analog + * - Output types: push-pull or open-drain + * - Configurable output speed for EMI/power tradeoff + * - Internal pull-up and pull-down resistors + * - Alternate function mapping for peripheral connections (UART, SPI, etc.) + * + * Each port occupies 0x400 bytes in the memory map starting from GPIOA base. + */ + +/* + * @brief Packed per-pin GPIO configuration (uint32_t). + * + * Bit layout: + * [3:0] port — GPIO port A-I (4 bits) + * [7:4] pin — Pin number 0-15 (4 bits) + * [9:8] mode — Pin mode (2 bits) + * [10] outType — Output type (1 bit) + * [12:11] speed — Output speed (2 bits) + * [14:13] pull — Pull resistor (2 bits) + * [18:15] altFn — Alternate function 0-15 (4 bits) + * + * Use WHAL_STM32WB_GPIO_PIN() to build values and + * WHAL_STM32WB_GPIO_GET_*() to extract fields. + */ +typedef uint32_t whal_Stm32wb_Gpio_PinCfg; + +/* Field positions */ +#define WHAL_STM32WB_GPIO_PORT_Pos 0 +#define WHAL_STM32WB_GPIO_PIN_Pos 4 +#define WHAL_STM32WB_GPIO_MODE_Pos 8 +#define WHAL_STM32WB_GPIO_OUTTYPE_Pos 10 +#define WHAL_STM32WB_GPIO_SPEED_Pos 11 +#define WHAL_STM32WB_GPIO_PULL_Pos 13 +#define WHAL_STM32WB_GPIO_ALTFN_Pos 15 + +/* Port values */ +#define WHAL_STM32WB_GPIO_PORT_A 0 +#define WHAL_STM32WB_GPIO_PORT_B 1 +#define WHAL_STM32WB_GPIO_PORT_C 2 +#define WHAL_STM32WB_GPIO_PORT_D 3 +#define WHAL_STM32WB_GPIO_PORT_E 4 +#define WHAL_STM32WB_GPIO_PORT_F 5 +#define WHAL_STM32WB_GPIO_PORT_G 6 +#define WHAL_STM32WB_GPIO_PORT_H 7 +#define WHAL_STM32WB_GPIO_PORT_I 8 + +/* Mode values */ +#define WHAL_STM32WB_GPIO_MODE_IN 0 +#define WHAL_STM32WB_GPIO_MODE_OUT 1 +#define WHAL_STM32WB_GPIO_MODE_ALTFN 2 +#define WHAL_STM32WB_GPIO_MODE_ANALOG 3 + +/* Output type values */ +#define WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL 0 +#define WHAL_STM32WB_GPIO_OUTTYPE_OPENDRAIN 1 + +/* Speed values */ +#define WHAL_STM32WB_GPIO_SPEED_LOW 0 +#define WHAL_STM32WB_GPIO_SPEED_MEDIUM 1 +#define WHAL_STM32WB_GPIO_SPEED_FAST 2 +#define WHAL_STM32WB_GPIO_SPEED_HIGH 3 + +/* Pull values */ +#define WHAL_STM32WB_GPIO_PULL_NONE 0 +#define WHAL_STM32WB_GPIO_PULL_UP 1 +#define WHAL_STM32WB_GPIO_PULL_DOWN 2 + +/* Pack a pin configuration into a uint32_t */ +#define WHAL_STM32WB_GPIO_PIN(port, pin, mode, outType, speed, pull, altFn) \ + ((((uint32_t)(port) & 0xFu) << WHAL_STM32WB_GPIO_PORT_Pos) | \ + (((uint32_t)(pin) & 0xFu) << WHAL_STM32WB_GPIO_PIN_Pos) | \ + (((uint32_t)(mode) & 0x3u) << WHAL_STM32WB_GPIO_MODE_Pos) | \ + (((uint32_t)(outType) & 0x1u) << WHAL_STM32WB_GPIO_OUTTYPE_Pos) | \ + (((uint32_t)(speed) & 0x3u) << WHAL_STM32WB_GPIO_SPEED_Pos) | \ + (((uint32_t)(pull) & 0x3u) << WHAL_STM32WB_GPIO_PULL_Pos) | \ + (((uint32_t)(altFn) & 0xFu) << WHAL_STM32WB_GPIO_ALTFN_Pos)) + +/* Extract individual fields */ +#define WHAL_STM32WB_GPIO_GET_PORT(cfg) (((cfg) >> WHAL_STM32WB_GPIO_PORT_Pos) & 0xF) +#define WHAL_STM32WB_GPIO_GET_PIN(cfg) (((cfg) >> WHAL_STM32WB_GPIO_PIN_Pos) & 0xF) +#define WHAL_STM32WB_GPIO_GET_MODE(cfg) (((cfg) >> WHAL_STM32WB_GPIO_MODE_Pos) & 0x3) +#define WHAL_STM32WB_GPIO_GET_OUTTYPE(cfg) (((cfg) >> WHAL_STM32WB_GPIO_OUTTYPE_Pos) & 0x1) +#define WHAL_STM32WB_GPIO_GET_SPEED(cfg) (((cfg) >> WHAL_STM32WB_GPIO_SPEED_Pos) & 0x3) +#define WHAL_STM32WB_GPIO_GET_PULL(cfg) (((cfg) >> WHAL_STM32WB_GPIO_PULL_Pos) & 0x3) +#define WHAL_STM32WB_GPIO_GET_ALTFN(cfg) (((cfg) >> WHAL_STM32WB_GPIO_ALTFN_Pos) & 0xF) + +/* + * @brief GPIO device configuration. + * + * Contains clock control references and an array of pin configurations. + */ +typedef struct { + whal_Stm32wb_Gpio_PinCfg *pinCfg; /* Array of pin configurations */ + size_t pinCount; /* Number of pins to configure */ +} whal_Stm32wb_Gpio_Cfg; + +#if !defined(WHAL_CFG_STM32WB_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32F4_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32H5_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32C0_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32F0_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32F3_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32L1_GPIO_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32N6_GPIO_DIRECT_API_MAPPING) +/* + * @brief Driver instance for STM32 GPIO. + */ +extern const whal_GpioDriver whal_Stm32wb_Gpio_Driver; + +/* + * @brief Initialize the STM32 GPIO peripheral and configured pins. + * + * @param gpioDev GPIO device instance. + * + * @retval WHAL_SUCCESS Initialization completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Gpio_Init(whal_Gpio *gpioDev); +/* + * @brief Deinitialize the STM32 GPIO peripheral. + * + * @param gpioDev GPIO device instance. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Gpio_Deinit(whal_Gpio *gpioDev); +/* + * @brief Read a GPIO pin value. + * + * @param gpioDev GPIO device instance. + * @param pin Pin index in the configured pin table. + * @param value Output for the sampled pin value. + * + * @retval WHAL_SUCCESS Pin value read. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Gpio_Get(whal_Gpio *gpioDev, size_t pin, size_t *value); +/* + * @brief Set a GPIO pin value. + * + * @param gpioDev GPIO device instance. + * @param pin Pin index in the configured pin table. + * @param value Value to drive. + * + * @retval WHAL_SUCCESS Pin updated. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Gpio_Set(whal_Gpio *gpioDev, size_t pin, size_t value); +#endif /* !WHAL_CFG_GPIO_API_MAPPING */ + +#endif /* WHAL_STM32WB_GPIO_H */ diff --git a/wolfHAL/irq/cortex_m_nvic.h b/wolfHAL/irq/cortex_m_nvic.h new file mode 100644 index 0000000..2bf9388 --- /dev/null +++ b/wolfHAL/irq/cortex_m_nvic.h @@ -0,0 +1,75 @@ +#ifndef WHAL_CORTEX_M_NVIC_H +#define WHAL_CORTEX_M_NVIC_H + +#include +#include + +/* + * @file cortex_m_nvic.h + * @brief ARM Cortex-M NVIC driver (shared across M3/M4/M7/etc.). + * + * Uses a 4-bit priority field in bits[7:4] of each IPR byte. + * The regmap base should be 0xE000E100 (NVIC_ISER0). + */ + +/* + * @brief Optional per-interrupt configuration. + * + * Pass to whal_Irq_Enable() to set priority, or pass NULL for default (0). + */ +typedef struct { + uint8_t priority; +} whal_Nvic_Cfg; + +#ifndef WHAL_CFG_NVIC_IRQ_DIRECT_API_MAPPING +/* + * @brief Driver instance for Cortex-M4 NVIC. + */ +extern const whal_IrqDriver whal_Nvic_Driver; + +/* + * @brief Initialize the Cortex-M4 NVIC driver. + * + * @param irqDev IRQ device instance. + * + * @retval WHAL_SUCCESS NVIC is ready. + * @retval WHAL_EINVAL Null pointer. + */ +whal_Error whal_Nvic_Init(whal_Irq *irqDev); + +/* + * @brief Deinitialize the NVIC driver. + * + * @param irqDev IRQ device instance. + * + * @retval WHAL_SUCCESS Driver deinitialized. + * @retval WHAL_EINVAL Null pointer. + */ +whal_Error whal_Nvic_Deinit(whal_Irq *irqDev); + +/* + * @brief Enable an IRQ in the NVIC and optionally set its priority. + * + * @param irqDev IRQ device instance. + * @param irq Interrupt number. + * @param irqCfg Optional pointer to a `whal_Nvic_Cfg` for priority. Pass + * NULL to leave the priority at its current value. + * + * @retval WHAL_SUCCESS Interrupt enabled. + * @retval WHAL_EINVAL Null pointer. + */ +whal_Error whal_Nvic_Enable(whal_Irq *irqDev, size_t irq, const void *irqCfg); + +/* + * @brief Disable an IRQ in the NVIC. + * + * @param irqDev IRQ device instance. + * @param irq Interrupt number. + * + * @retval WHAL_SUCCESS Interrupt disabled. + * @retval WHAL_EINVAL Null pointer. + */ +whal_Error whal_Nvic_Disable(whal_Irq *irqDev, size_t irq); +#endif /* !WHAL_CFG_NVIC_IRQ_DIRECT_API_MAPPING */ + +#endif /* WHAL_CORTEX_M_NVIC_H */ diff --git a/wolfHAL/irq/irq.h b/wolfHAL/irq/irq.h new file mode 100644 index 0000000..dd80745 --- /dev/null +++ b/wolfHAL/irq/irq.h @@ -0,0 +1,83 @@ +#ifndef WHAL_IRQ_H +#define WHAL_IRQ_H + +#include +#include +#include + +/* + * @file irq.h + * @brief Generic interrupt controller abstraction and driver interface. + * + * A whal_Irq device represents an interrupt controller (e.g., ARM Cortex-M + * NVIC). The API allows enabling and disabling individual interrupt lines, + * with optional platform-specific configuration (e.g., priority) passed + * through the Enable call. + */ + +typedef struct whal_Irq whal_Irq; + +/* + * @brief Driver vtable for interrupt controllers. + */ +typedef struct { + /* Initialize the interrupt controller. */ + whal_Error (*Init)(whal_Irq *irqDev); + /* Deinitialize the interrupt controller. */ + whal_Error (*Deinit)(whal_Irq *irqDev); + /* Enable an interrupt line. irqCfg is platform-specific (or NULL). */ + whal_Error (*Enable)(whal_Irq *irqDev, size_t irq, const void *irqCfg); + /* Disable an interrupt line. */ + whal_Error (*Disable)(whal_Irq *irqDev, size_t irq); +} whal_IrqDriver; + +/* + * @brief Interrupt controller device instance. + */ +struct whal_Irq { + const whal_Regmap regmap; + const whal_IrqDriver *driver; + const void *cfg; +}; + +/* + * @brief Initialize the interrupt controller. + * + * @param irqDev Interrupt controller instance. + * + * @retval WHAL_SUCCESS Init completed. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Irq_Init(whal_Irq *irqDev); +/* + * @brief Deinitialize the interrupt controller. + * + * @param irqDev Interrupt controller instance. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Irq_Deinit(whal_Irq *irqDev); +/* + * @brief Enable an interrupt line. + * + * @param irqDev Interrupt controller instance. + * @param irq Interrupt number. + * @param irqCfg Platform-specific config (e.g., priority), or NULL for defaults. + * + * @retval WHAL_SUCCESS Interrupt enabled. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Irq_Enable(whal_Irq *irqDev, size_t irq, const void *irqCfg); +/* + * @brief Disable an interrupt line. + * + * @param irqDev Interrupt controller instance. + * @param irq Interrupt number. + * + * @retval WHAL_SUCCESS Interrupt disabled. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Irq_Disable(whal_Irq *irqDev, size_t irq); + +#endif /* WHAL_IRQ_H */ diff --git a/wolfHAL/platform/arm/cortex_m3.h b/wolfHAL/platform/arm/cortex_m3.h new file mode 100644 index 0000000..806a704 --- /dev/null +++ b/wolfHAL/platform/arm/cortex_m3.h @@ -0,0 +1,17 @@ +#ifndef WHAL_CORTEX_M3_H +#define WHAL_CORTEX_M3_H + +#include +#include + +#define WHAL_CORTEX_M3_SYSTICK_REGMAP \ + .base = 0xE000E010, \ + .size = 0x400 +#define WHAL_CORTEX_M3_SYSTICK_DRIVER &whal_SysTick_Driver + +#define WHAL_CORTEX_M3_NVIC_REGMAP \ + .base = 0xE000E100, \ + .size = 0x400 +#define WHAL_CORTEX_M3_NVIC_DRIVER &whal_Nvic_Driver + +#endif /* WHAL_CORTEX_M3_H */ diff --git a/wolfHAL/platform/arm/cortex_m4.h b/wolfHAL/platform/arm/cortex_m4.h new file mode 100644 index 0000000..b07ad8b --- /dev/null +++ b/wolfHAL/platform/arm/cortex_m4.h @@ -0,0 +1,17 @@ +#ifndef WHAL_CORTEX_M4_H +#define WHAL_CORTEX_M4_H + +#include +#include + +#define WHAL_CORTEX_M4_SYSTICK_REGMAP \ + .base = 0xE000E010, \ + .size = 0x400 +#define WHAL_CORTEX_M4_SYSTICK_DRIVER &whal_SysTick_Driver + +#define WHAL_CORTEX_M4_NVIC_REGMAP \ + .base = 0xE000E100, \ + .size = 0x400 +#define WHAL_CORTEX_M4_NVIC_DRIVER &whal_Nvic_Driver + +#endif /* WHAL_CORTEX_M4_H */ diff --git a/wolfHAL/platform/st/stm32l152re.h b/wolfHAL/platform/st/stm32l152re.h new file mode 100644 index 0000000..c683fb6 --- /dev/null +++ b/wolfHAL/platform/st/stm32l152re.h @@ -0,0 +1,59 @@ +#ifndef WHAL_STM32L152RE_H +#define WHAL_STM32L152RE_H + +#include + +#include +#include +#include +#include +#include + +/* + * @file stm32l152re.h + * @brief Convenience initializers for STM32L152RE device instances. + */ + +#define WHAL_STM32L152_RCC_REGMAP \ + .base = 0x40023800, \ + .size = 0x400 + +#define WHAL_STM32L152_GPIO_REGMAP \ + .base = 0x40020000, \ + .size = 0x2000 +#define WHAL_STM32L152_GPIO_DRIVER &whal_Stm32l1_Gpio_Driver + +#define WHAL_STM32L152_USART2_REGMAP \ + .base = 0x40004400, \ + .size = 0x400 +#define WHAL_STM32L152_USART2_DRIVER &whal_Stm32l1_Uart_Driver + +#define WHAL_STM32L152_FLASH_REGMAP \ + .base = 0x40023C00, \ + .size = 0x400 +#define WHAL_STM32L152_FLASH_DRIVER &whal_Stm32l1_Flash_Driver + +#define WHAL_STM32L152_PWR_REGMAP \ + .base = 0x40007000, \ + .size = 0x400 + +/* RCC_AHBENR (offset 0x1C) */ + +#define WHAL_STM32L152_GPIOA_CLOCK \ + .regOffset = 0x1C, \ + .enableMask = (1UL << 0), \ + .enablePos = 0 + +/* RCC_APB1ENR (offset 0x24) */ + +#define WHAL_STM32L152_USART2_CLOCK \ + .regOffset = 0x24, \ + .enableMask = (1UL << 17), \ + .enablePos = 17 + +#define WHAL_STM32L152_PWR_CLOCK \ + .regOffset = 0x24, \ + .enableMask = (1UL << 28), \ + .enablePos = 28 + +#endif /* WHAL_STM32L152RE_H */ diff --git a/wolfHAL/platform/st/stm32wb55xx.h b/wolfHAL/platform/st/stm32wb55xx.h new file mode 100644 index 0000000..7cab33f --- /dev/null +++ b/wolfHAL/platform/st/stm32wb55xx.h @@ -0,0 +1,50 @@ +#ifndef WHAL_STM32WB55XX_H +#define WHAL_STM32WB55XX_H + +#include + +#include +#include +#include +#include + +/* + * @file stm32wb55xx.h + * @brief Convenience initializers for STM32WB55xx device instances. + */ + +#define WHAL_STM32WB55_RCC_REGMAP \ + .base = 0x58000000, \ + .size = 0x400 + +#define WHAL_STM32WB55_GPIO_REGMAP \ + .base = 0x48000000, \ + .size = 0x400 +#define WHAL_STM32WB55_GPIO_DRIVER &whal_Stm32wb_Gpio_Driver + +#define WHAL_STM32WB55_UART1_REGMAP \ + .base = 0x40013800, \ + .size = 0x400 +#define WHAL_STM32WB55_UART1_DRIVER &whal_Stm32wb_Uart_Driver + +#define WHAL_STM32WB55_FLASH_REGMAP \ + .base = 0x58004000, \ + .size = 0x400 +#define WHAL_STM32WB55_FLASH_DRIVER &whal_Stm32wb_Flash_Driver + +#define WHAL_STM32WB55_GPIOA_GATE \ + .regOffset = 0x4C, \ + .enableMask = (1UL << 0), \ + .enablePos = 0 + +#define WHAL_STM32WB55_GPIOB_GATE \ + .regOffset = 0x4C, \ + .enableMask = (1UL << 1), \ + .enablePos = 1 + +#define WHAL_STM32WB55_UART1_GATE \ + .regOffset = 0x60, \ + .enableMask = (1UL << 14), \ + .enablePos = 14 + +#endif /* WHAL_STM32WB55XX_H */ diff --git a/wolfHAL/power/power.h b/wolfHAL/power/power.h new file mode 100644 index 0000000..137c04b --- /dev/null +++ b/wolfHAL/power/power.h @@ -0,0 +1,23 @@ +#ifndef WHAL_POWER_H +#define WHAL_POWER_H + +#include + +/* + * @file power.h + * @brief Power-controller device handle. + * + * wolfHAL doesn't provide a generic power API — the operations chips + * expose around their power controllers (voltage scaling, low-power mode + * entry, regulator on/off) are too chip-specific to abstract usefully. + * Each chip's power driver header (e.g. , + * ) defines its own imperative helpers. + * Power is a board-level driver — boards call those helpers directly + * from Board_Init, and applications reach board-level behavior through + * Board_() wrappers. + */ +typedef struct { + const whal_Regmap regmap; +} whal_Power; + +#endif /* WHAL_POWER_H */ diff --git a/wolfHAL/power/stm32l1_pwr.h b/wolfHAL/power/stm32l1_pwr.h new file mode 100644 index 0000000..4169023 --- /dev/null +++ b/wolfHAL/power/stm32l1_pwr.h @@ -0,0 +1,52 @@ +#ifndef WHAL_STM32L1_PWR_H +#define WHAL_STM32L1_PWR_H + +#include +#include +#include + +/* + * @file stm32l1_pwr.h + * @brief STM32L1 PWR (power control) driver. + * + * Boards bring up the regulator imperatively from Board_Init by calling + * the helpers below. Power is a board-level driver — there is no generic + * whal_Power_* API or vtable. + * + * The internal voltage regulator output range (PWR_CR.VOS) bounds the + * maximum permitted SYSCLK and PLL VCO frequencies. Must be configured + * before bringing the PLL above the reset-default limits. + * + * Range 1 (1.8 V): SYSCLK <= 32 MHz, PLL VCO <= 96 MHz. + * Range 2 (1.5 V): SYSCLK <= 16 MHz, PLL VCO <= 48 MHz. Reset default. + * Range 3 (1.2 V): SYSCLK <= 4 MHz, PLL disabled. + */ + +/* + * @brief Internal voltage regulator output ranges (PWR_CR.VOS). + */ +typedef enum { + WHAL_STM32L1_PWR_VOS_RANGE_1 = 1, + WHAL_STM32L1_PWR_VOS_RANGE_2 = 2, + WHAL_STM32L1_PWR_VOS_RANGE_3 = 3, +} whal_Stm32l1_Pwr_VosRange; + +/* + * @brief Set the voltage regulator output range. Polls PWR_CSR.VOSF + * until voltage scaling completes. The PWR APB1 clock must already + * be enabled (via whal_Stm32l1_Rcc_EnablePeriphClk with + * WHAL_STM32L152_PWR_CLOCK) before calling. + * + * @param powerDev Power device instance. + * @param range Desired VOS range (RANGE_1, RANGE_2, or RANGE_3). + * @param timeout Optional timeout for the VOSF poll loop. + * + * @retval WHAL_SUCCESS Voltage scaling settled at the requested range. + * @retval WHAL_EINVAL Null powerDev. + * @retval WHAL_ETIMEOUT VOSF did not clear within the configured timeout. + */ +whal_Error whal_Stm32l1_Pwr_SetVosRange(whal_Power *powerDev, + whal_Stm32l1_Pwr_VosRange range, + whal_Timeout *timeout); + +#endif /* WHAL_STM32L1_PWR_H */ diff --git a/wolfHAL/regmap.h b/wolfHAL/regmap.h new file mode 100644 index 0000000..aeccf58 --- /dev/null +++ b/wolfHAL/regmap.h @@ -0,0 +1,110 @@ +#ifndef WHAL_REGMAP_H +#define WHAL_REGMAP_H + +#include +#include +#include +#include + +/* + * @file regmap.h + * @brief Helpers for accessing and manipulating memory-mapped registers. + */ + +/* + * @brief Description of a contiguous register map. + */ +typedef struct whal_Regmap { + size_t base; + size_t size; +} whal_Regmap; + +/* + * @brief Update a masked field within a memory-mapped register. + * + * The function applies a read-modify-write using @p mask to only touch the + * desired bit field. + * + * @param base Base address of the register block. + * @param offset Byte offset from @p base to the register. + * @param mask Bit mask selecting the field to update. + * @param value Value to write, already positioned within @p mask. + * Only bits selected by @p mask are applied (no shifting). + * + * @note No return value. Callers are responsible for passing valid inputs. + */ +static inline void whal_Reg_Update(size_t base, size_t offset, size_t mask, + size_t value) +{ + volatile size_t *reg = (size_t *)(base + offset); + *reg = (*reg & ~mask) | (value & mask); +} + +/* + * @brief Read a masked field from a memory-mapped register. + * + * @param base Base address of the register block. + * @param offset Byte offset from @p base to the register. + * @param msk Bit mask selecting the field to extract. + * @param pos Bit position of the field's LSB. + * @param value Output storage for the decoded field. + * + * @note No return value. Callers are responsible for passing valid inputs. + */ +static inline void whal_Reg_Get(size_t base, size_t offset, size_t msk, + size_t pos, size_t *value) +{ + size_t val = *(volatile size_t *)(base + offset); + *value = whal_GetBits(msk, pos, val); +} + +/* + * @brief Write a 32-bit value to a memory-mapped register. + * + * @param base Base address of the register block. + * @param offset Byte offset from @p base to the register. + * @param value Value to write. + */ +static inline void whal_Reg_Write(size_t base, size_t offset, size_t value) +{ + *(volatile size_t *)(base + offset) = value; +} + +/* + * @brief Read a 32-bit value from a memory-mapped register. + * + * @param base Base address of the register block. + * @param offset Byte offset from @p base to the register. + * + * @return The register value. + */ +static inline size_t whal_Reg_Read(size_t base, size_t offset) +{ + return *(volatile size_t *)(base + offset); +} + +/* + * @brief Poll a register until (reg & mask) == value, or timeout. + * + * @param base Register block base address. + * @param offset Register offset. + * @param mask Bit mask to extract the field. + * @param value Expected value of the masked field. + * @param timeout Timeout instance (NULL for unbounded wait). + */ +static inline whal_Error whal_Reg_ReadPoll(size_t base, size_t offset, + size_t mask, size_t value, + whal_Timeout *timeout) +{ +#ifdef WHAL_CFG_NO_TIMEOUT + (void)(timeout); +#endif + WHAL_TIMEOUT_START(timeout); + while ((whal_Reg_Read(base, offset) & mask) != value) { + if (WHAL_TIMEOUT_EXPIRED(timeout)) + return WHAL_ETIMEOUT; + } + return WHAL_SUCCESS; +} + +#endif /* WHAL_REGMAP_H */ diff --git a/wolfHAL/timeout.h b/wolfHAL/timeout.h new file mode 100644 index 0000000..7c03c4a --- /dev/null +++ b/wolfHAL/timeout.h @@ -0,0 +1,53 @@ +#ifndef WHAL_TIMEOUT_H +#define WHAL_TIMEOUT_H + +#include +#include +#include + +/* + * @file timeout.h + * @brief Timeout abstraction for bounded polling and delays. + * + * The board sets timeoutTicks and a GetTick callback directly on a + * whal_Timeout instance. Drivers use WHAL_TIMEOUT_START / WHAL_TIMEOUT_EXPIRED + * macros for zero-overhead polling guards. When WHAL_CFG_NO_TIMEOUT is + * defined, all timeout operations compile away completely. + */ + +typedef struct { + uint32_t timeoutTicks; + uint32_t startTick; + uint32_t (*GetTick)(void); +} whal_Timeout; + +#ifdef WHAL_CFG_NO_TIMEOUT + +#define WHAL_TIMEOUT_START(t) ((void)(0)) +#define WHAL_TIMEOUT_EXPIRED(t) (0) + +#else /* !WHAL_CFG_NO_TIMEOUT */ + +/* + * @brief Snapshot the current tick. + * + * If @p t is NULL, this is a no-op so drivers can leave the timeout + * pointer unset for unbounded polling. + */ +#define WHAL_TIMEOUT_START(t) do { \ + if (t) { \ + (t)->startTick = (t)->GetTick(); \ + } \ +} while (0) + +/* + * @brief Evaluate to nonzero if the timeout has expired. + * + * Safe to call with a NULL pointer — returns 0 (not expired). + */ +#define WHAL_TIMEOUT_EXPIRED(t) \ + ((t) && ((uint32_t)((t)->GetTick() - (t)->startTick) >= (t)->timeoutTicks)) + +#endif /* WHAL_CFG_NO_TIMEOUT */ + +#endif /* WHAL_TIMEOUT_H */ diff --git a/wolfHAL/timer/systick.h b/wolfHAL/timer/systick.h new file mode 100644 index 0000000..ee34732 --- /dev/null +++ b/wolfHAL/timer/systick.h @@ -0,0 +1,90 @@ +#ifndef WHAL_SYSTICK_H +#define WHAL_SYSTICK_H + +#include +#include + +/* + * @file systick.h + * @brief Configuration for the Cortex-M SysTick timer driver. + */ + +/* + * @brief Available SysTick clock sources. + */ +typedef enum { + WHAL_SYSTICK_CLKSRC_EXT, + WHAL_SYSTICK_CLKSRC_SYSCLK, +} whal_SysTick_ClkSrc; + +/* + * @brief Enable or disable the SysTick interrupt generation. + */ +typedef enum { + WHAL_SYSTICK_TICKINT_DISABLED, + WHAL_SYSTICK_TICKINT_ENABLED, +} whal_SysTick_TickInt; + +/* + * @brief SysTick configuration parameters. + */ +typedef struct { + size_t cyclesPerTick; + whal_SysTick_ClkSrc clkSrc; + whal_SysTick_TickInt tickInt; +} whal_SysTick_Cfg; + +#ifndef WHAL_CFG_SYSTICK_TIMER_DIRECT_API_MAPPING +/* + * @brief Driver instance for the Cortex-M SysTick timer. + */ +extern const whal_TimerDriver whal_SysTick_Driver; + +/* + * @brief Initialize the SysTick timer with the configured clock and reload values. + * + * @param timerDev Timer device instance to initialize. + * + * @retval WHAL_SUCCESS Initialization completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_SysTick_Init(whal_Timer *timerDev); +/* + * @brief Deinitialize the SysTick timer. + * + * @param timerDev Timer device instance to deinitialize. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_SysTick_Deinit(whal_Timer *timerDev); +/* + * @brief Start the SysTick counter. + * + * @param timerDev Timer device instance. + * + * @retval WHAL_SUCCESS Timer started. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_SysTick_Start(whal_Timer *timerDev); +/* + * @brief Stop the SysTick counter. + * + * @param timerDev Timer device instance. + * + * @retval WHAL_SUCCESS Timer stopped. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_SysTick_Stop(whal_Timer *timerDev); +/* + * @brief Reset the SysTick counter state. + * + * @param timerDev Timer device instance. + * + * @retval WHAL_SUCCESS Timer reset. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_SysTick_Reset(whal_Timer *timerDev); +#endif /* !WHAL_CFG_SYSTICK_TIMER_DIRECT_API_MAPPING */ + +#endif /* WHAL_SYSTICK_H */ diff --git a/wolfHAL/timer/timer.h b/wolfHAL/timer/timer.h new file mode 100644 index 0000000..09297c9 --- /dev/null +++ b/wolfHAL/timer/timer.h @@ -0,0 +1,91 @@ +#ifndef WHAL_TIMER_H +#define WHAL_TIMER_H + +#include +#include +#include + +/* + * @file timer.h + * @brief Generic timer abstraction for periodic events or counters. + */ + +typedef struct whal_Timer whal_Timer; + +/* + * @brief Driver vtable for timer devices. + */ +typedef struct { + /* Initialize the timer hardware. */ + whal_Error (*Init)(whal_Timer *timerDev); + /* Deinitialize the timer hardware. */ + whal_Error (*Deinit)(whal_Timer *timerDev); + /* Start the timer running. */ + whal_Error (*Start)(whal_Timer *timerDev); + /* Stop the timer. */ + whal_Error (*Stop)(whal_Timer *timerDev); + /* Reset the timer counter and configuration as needed. */ + whal_Error (*Reset)(whal_Timer *timerDev); +} whal_TimerDriver; + +/* + * @brief Timer device instance pairing configuration with a driver. + */ +struct whal_Timer{ + const whal_Regmap regmap; + const whal_TimerDriver *driver; + void *cfg; +}; + +/* + * @brief Initialize a timer device. + * + * @param timerDev Timer instance to initialize. + * + * @retval WHAL_SUCCESS Driver-specific init completed. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Timer_Init(whal_Timer *timerDev); +/* + * @brief Deinitialize a timer device. + * + * @param timerDev Timer instance to deinitialize. + * + * @retval WHAL_SUCCESS Driver-specific deinit completed. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Timer_Deinit(whal_Timer *timerDev); +/* + * @brief Start a timer. + * + * @param timerDev Timer instance to start. + * + * @retval WHAL_SUCCESS Timer started. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Timer_Start(whal_Timer *timerDev); +/* + * @brief Stop a timer. + * + * @param timerDev Timer instance to stop. + * + * @retval WHAL_SUCCESS Timer stopped. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Timer_Stop(whal_Timer *timerDev); +/* + * @brief Reset a timer. + * + * @param timerDev Timer instance to reset. + * + * @retval WHAL_SUCCESS Timer reset. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Timer_Reset(whal_Timer *timerDev); + +#endif /* WHAL_TIMER_H */ diff --git a/wolfHAL/uart/stm32f4_uart.h b/wolfHAL/uart/stm32f4_uart.h new file mode 100644 index 0000000..e2d9a3d --- /dev/null +++ b/wolfHAL/uart/stm32f4_uart.h @@ -0,0 +1,122 @@ +#ifndef WHAL_STM32F4_UART_H +#define WHAL_STM32F4_UART_H + +#include +#include +#include +#include + +/* + * @file stm32f4_uart.h + * @brief STM32F4 UART driver configuration. + * + * The STM32F4 USART uses the older USARTv1 register layout with + * SR (Status Register) and DR (Data Register) at offsets 0x00 and 0x04, + * which differs from the USARTv2 ISR/TDR/RDR layout used by STM32WB/H5. + * + * Register layout: + * SR at 0x00 - Status register (TXE, RXNE, TC flags) + * DR at 0x04 - Data register (shared TX/RX) + * BRR at 0x08 - Baud rate register + * CR1 at 0x0C - Control register 1 (UE, TE, RE) + * CR2 at 0x10 - Control register 2 + * CR3 at 0x14 - Control register 3 + */ + +/* + * @brief Compute UART BRR register value for STM32F4. + * + * For oversampling by 16 (OVER8=0): + * BRR = fPCLK / baudrate + * + * @param clk Peripheral clock frequency in Hz. + * @param baud Desired baud rate. + */ +#define WHAL_STM32F4_UART_BRR(clk, baud) ((clk) / (baud)) + +/* + * @brief STM32F4 UART configuration parameters. + */ +typedef struct whal_Stm32f4_Uart_Cfg { + uint32_t brr; + whal_Timeout *timeout; +} whal_Stm32f4_Uart_Cfg; + +#ifndef WHAL_CFG_STM32F4_UART_DIRECT_API_MAPPING +/* + * @brief Driver instance for STM32F4 UART peripheral. + */ +extern const whal_UartDriver whal_Stm32f4_Uart_Driver; + +/* + * @brief Initialize the STM32F4 UART peripheral. + * + * @param uartDev UART device instance to initialize. + * + * @retval WHAL_SUCCESS Initialization completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32f4_Uart_Init(whal_Uart *uartDev); + +/* + * @brief Deinitialize the STM32F4 UART peripheral. + * + * @param uartDev UART device instance to deinitialize. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32f4_Uart_Deinit(whal_Uart *uartDev); + +/* + * @brief Transmit a buffer over UART. + * + * @param uartDev UART device instance. + * @param data Buffer to transmit. + * @param dataSz Number of bytes to transmit. + * + * @retval WHAL_SUCCESS Transfer completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32f4_Uart_Send(whal_Uart *uartDev, const void *data, size_t dataSz); + +/* + * @brief Receive a buffer over UART. + * + * @param uartDev UART device instance. + * @param data Receive buffer. + * @param dataSz Number of bytes to receive. + * + * @retval WHAL_SUCCESS Transfer completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32f4_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz); + +/* + * @brief Start an asynchronous UART transmit. + * + * @param uartDev UART device instance. + * @param data Buffer to transmit. + * @param dataSz Number of bytes to transmit. + * + * @retval WHAL_ENOTSUP Polled driver does not implement async; provided so + * stm32l1_uart can alias the symbol. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32f4_Uart_SendAsync(whal_Uart *uartDev, const void *data, size_t dataSz); + +/* + * @brief Start an asynchronous UART receive. + * + * @param uartDev UART device instance. + * @param data Receive buffer. + * @param dataSz Number of bytes to receive. + * + * @retval WHAL_ENOTSUP Polled driver does not implement async; provided so + * stm32l1_uart can alias the symbol. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32f4_Uart_RecvAsync(whal_Uart *uartDev, void *data, size_t dataSz); +#endif /* !WHAL_CFG_STM32F4_UART_DIRECT_API_MAPPING */ + +#endif /* WHAL_STM32F4_UART_H */ diff --git a/wolfHAL/uart/stm32l1_uart.h b/wolfHAL/uart/stm32l1_uart.h new file mode 100644 index 0000000..514d3b2 --- /dev/null +++ b/wolfHAL/uart/stm32l1_uart.h @@ -0,0 +1,28 @@ +#ifndef WHAL_STM32L1_UART_H +#define WHAL_STM32L1_UART_H + +/* + * @file stm32l1_uart.h + * @brief STM32L1 UART driver (alias for STM32F4 UART). + * + * The STM32L1 USART uses the same USARTv1 register layout as the STM32F4 + * (SR/DR at 0x00/0x04, BRR at 0x08, CR1 at 0x0C). + */ + +#include + +typedef whal_Stm32f4_Uart_Cfg whal_Stm32l1_Uart_Cfg; + +#define WHAL_STM32L1_UART_BRR WHAL_STM32F4_UART_BRR + +#ifndef WHAL_CFG_STM32L1_UART_DIRECT_API_MAPPING +#define whal_Stm32l1_Uart_Driver whal_Stm32f4_Uart_Driver +#define whal_Stm32l1_Uart_Init whal_Stm32f4_Uart_Init +#define whal_Stm32l1_Uart_Deinit whal_Stm32f4_Uart_Deinit +#define whal_Stm32l1_Uart_Send whal_Stm32f4_Uart_Send +#define whal_Stm32l1_Uart_Recv whal_Stm32f4_Uart_Recv +#define whal_Stm32l1_Uart_SendAsync whal_Stm32f4_Uart_SendAsync +#define whal_Stm32l1_Uart_RecvAsync whal_Stm32f4_Uart_RecvAsync +#endif /* !WHAL_CFG_STM32L1_UART_DIRECT_API_MAPPING */ + +#endif /* WHAL_STM32L1_UART_H */ diff --git a/wolfHAL/uart/stm32wb_uart.h b/wolfHAL/uart/stm32wb_uart.h new file mode 100644 index 0000000..9649bb1 --- /dev/null +++ b/wolfHAL/uart/stm32wb_uart.h @@ -0,0 +1,88 @@ +#ifndef WHAL_STM32WB_UART_H +#define WHAL_STM32WB_UART_H + +#include +#include +#include +#include +/* + * @file stm32wb_uart.h + * @brief STM32WB UART driver — polled variant. + */ + +/* + * @brief Compute UART BRR register value. + */ +#define WHAL_STM32WB_UART_BRR(clk, baud) ((clk) / (baud)) + +/* + * @brief Compute LPUART BRR register value. + */ +#define WHAL_STM32WB_LPUART_BRR(clk, baud) ((uint32_t)(((uint64_t)(clk) * 256) / (baud))) + +/* Polled UART */ + +/* + * @brief Polled UART configuration. + */ +typedef struct whal_Stm32wb_Uart_Cfg { + uint32_t brr; + whal_Timeout *timeout; +} whal_Stm32wb_Uart_Cfg; + +#if !defined(WHAL_CFG_STM32WB_UART_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32WB_UART_DMA_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32H5_UART_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32C0_UART_DIRECT_API_MAPPING) && \ + !defined(WHAL_CFG_STM32N6_UART_DIRECT_API_MAPPING) +/* + * @brief Polled UART driver. Implements Init, Deinit, Send, Recv. + */ +extern const whal_UartDriver whal_Stm32wb_Uart_Driver; + +/* + * @brief Initialize the STM32WB UART peripheral. + * + * @param uartDev UART device instance. + * + * @retval WHAL_SUCCESS Initialization completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Uart_Init(whal_Uart *uartDev); + +/* + * @brief Deinitialize the STM32WB UART peripheral. + * + * @param uartDev UART device instance. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Uart_Deinit(whal_Uart *uartDev); + +/* + * @brief Transmit a buffer over UART (blocking, polled). + * + * @param uartDev UART device instance. + * @param data Buffer to transmit. + * @param dataSz Number of bytes to transmit. + * + * @retval WHAL_SUCCESS All bytes transmitted. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Uart_Send(whal_Uart *uartDev, const void *data, size_t dataSz); + +/* + * @brief Receive a buffer over UART (blocking, polled). + * + * @param uartDev UART device instance. + * @param data Receive buffer. + * @param dataSz Number of bytes to receive. + * + * @retval WHAL_SUCCESS All bytes received. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wb_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz); +#endif /* !WHAL_CFG_UART_API_MAPPING */ + +#endif /* WHAL_STM32WB_UART_H */ diff --git a/wolfHAL/uart/uart.h b/wolfHAL/uart/uart.h new file mode 100644 index 0000000..985fc78 --- /dev/null +++ b/wolfHAL/uart/uart.h @@ -0,0 +1,125 @@ +#ifndef WHAL_UART_H +#define WHAL_UART_H + +#include +#include +#include +#include + +/* + * @file uart.h + * @brief Generic UART abstraction and driver interface. + */ + +typedef struct whal_Uart whal_Uart; + +/* + * @brief Driver vtable for UART devices. + */ +typedef struct { + /* Initialize the UART hardware. */ + whal_Error (*Init)(whal_Uart *uartDev); + /* Deinitialize the UART hardware. */ + whal_Error (*Deinit)(whal_Uart *uartDev); + /* Transmit a buffer. */ + whal_Error (*Send)(whal_Uart *uartDev, const void *data, size_t dataSz); + /* Receive into a buffer. */ + whal_Error (*Recv)(whal_Uart *uartDev, void *data, size_t dataSz); + /* Start an asynchronous transmit. NULL if not supported. */ + whal_Error (*SendAsync)(whal_Uart *uartDev, const void *data, size_t dataSz); + /* Start an asynchronous receive. NULL if not supported. */ + whal_Error (*RecvAsync)(whal_Uart *uartDev, void *data, size_t dataSz); +} whal_UartDriver; + +/* + * @brief UART device instance tying a register map and driver. + */ +struct whal_Uart { + const whal_Regmap regmap; + const whal_UartDriver *driver; + void *cfg; +}; + +/* + * @brief Initializes a UART device and its driver. + * + * @param uartDev Pointer to the UART instance to initialize. + * + * @retval WHAL_SUCCESS Driver-specific init completed. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Uart_Init(whal_Uart *uartDev); + +/* + * @brief Deinitializes a UART device and releases resources. + * + * @param uartDev Pointer to the UART instance to deinitialize. + * + * @retval WHAL_SUCCESS Driver-specific deinit completed. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Uart_Deinit(whal_Uart *uartDev); + +/* + * @brief Sends a buffer over the UART. + * + * @param uartDev Pointer to the UART instance. + * @param data Buffer to transmit. + * @param dataSz Number of bytes to send. + * + * @retval WHAL_SUCCESS Buffer was queued or transmitted. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Uart_Send(whal_Uart *uartDev, const void *data, size_t dataSz); + +/* + * @brief Receives data from the UART into a buffer. + * + * @param uartDev Pointer to the UART instance. + * @param data Destination buffer. + * @param dataSz Maximum number of bytes to read. + * + * @retval WHAL_SUCCESS Buffer was filled or receive started. + * @retval WHAL_EINVAL Null pointer. + * @retval WHAL_ENOTSUP Operation not implemented by this driver. + */ +whal_Error whal_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz); + +/* + * @brief Start an asynchronous transmit. + * + * Returns immediately. The buffer must remain valid until the transfer + * completes. Completion signaling is driver-specific (e.g., a status + * field in the driver config). + * + * @param uartDev Pointer to the UART instance. + * @param data Buffer to transmit. Must remain valid until completion. + * @param dataSz Number of bytes to send. + * + * @retval WHAL_SUCCESS Transfer started. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ENOTSUP Async not supported by this driver. + */ +whal_Error whal_Uart_SendAsync(whal_Uart *uartDev, const void *data, size_t dataSz); + +/* + * @brief Start an asynchronous receive. + * + * Returns immediately. The buffer must remain valid until the transfer + * completes. Completion signaling is driver-specific (e.g., a status + * field in the driver config). + * + * @param uartDev Pointer to the UART instance. + * @param data Receive buffer. Must remain valid until completion. + * @param dataSz Number of bytes to receive. + * + * @retval WHAL_SUCCESS Receive started. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ENOTSUP Async not supported by this driver. + */ +whal_Error whal_Uart_RecvAsync(whal_Uart *uartDev, void *data, size_t dataSz); + +#endif /* WHAL_UART_H */ diff --git a/wolfHAL/wolfHAL.h b/wolfHAL/wolfHAL.h new file mode 100644 index 0000000..641b268 --- /dev/null +++ b/wolfHAL/wolfHAL.h @@ -0,0 +1,21 @@ +#ifndef WOLFHAL_H +#define WOLFHAL_H + +/* + * @file wolfHAL.h + * @brief Convenience umbrella header that pulls in all core wolfHAL modules. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#endif /* WOLFHAL_H */