├── .cargo └── config.toml ├── .github └── workflows │ └── test-macro.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── asm.s ├── assemble.sh ├── bin └── msp430-none-elf.a ├── build.rs ├── link.x.in ├── macros ├── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── compiletest.rs │ └── ui │ ├── entry_bad_arg.rs │ ├── entry_bad_arg.stderr │ ├── entry_bad_param.rs │ ├── entry_bad_param.stderr │ ├── entry_bad_ret.rs │ ├── entry_bad_ret.stderr │ ├── entry_extern_c.rs │ ├── entry_extern_c.stderr │ ├── entry_preinterrupt_mismatch.rs │ ├── entry_preinterrupt_mismatch.stderr │ ├── entry_preinterrupt_no_arg.rs │ ├── entry_preinterrupt_no_arg.stderr │ ├── entry_preinterrupt_return_cs.rs │ ├── entry_preinterrupt_return_cs.stderr │ ├── entry_preinterrupt_return_cs2.rs │ ├── entry_preinterrupt_return_cs2.stderr │ ├── entry_preinterrupt_return_cs3.rs │ ├── entry_preinterrupt_return_cs3.stderr │ ├── entry_preinterrupt_return_cs_cloned.rs │ ├── entry_preinterrupt_return_cs_cloned.stderr │ ├── entry_preinterrupt_return_cs_cloned_static.rs │ ├── entry_preinterrupt_return_cs_cloned_static.stderr │ ├── entry_preinterrupt_return_cs_static.rs │ ├── entry_preinterrupt_return_cs_static.stderr │ ├── interrupt_args.rs │ ├── interrupt_args.stderr │ ├── interrupt_bad_cs_import.rs │ ├── interrupt_bad_cs_import.stderr │ ├── interrupt_bad_param.rs │ ├── interrupt_bad_param.stderr │ ├── interrupt_bad_ret.rs │ ├── interrupt_bad_ret.stderr │ ├── interrupt_extern_c.rs │ ├── interrupt_extern_c.stderr │ ├── interrupt_wrong_import.rs │ ├── interrupt_wrong_import.stderr │ ├── preinit_arg.rs │ ├── preinit_arg.stderr │ ├── preinit_bad_param.rs │ ├── preinit_bad_param.stderr │ ├── preinit_bad_ret.rs │ ├── preinit_bad_ret.stderr │ ├── preinit_safe.rs │ ├── preinit_safe.stderr │ ├── recursion.rs │ ├── recursion.stderr │ ├── recursion2.rs │ └── recursion2.stderr ├── rust-toolchain.toml └── src └── lib.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # Only one target to support for all chips! 3 | target = "msp430-none-elf" 4 | 5 | [unstable] 6 | # MSP430 doesn't come with libcore compiled already. But when it does, this 7 | # key can be removed. 8 | build-std = ["core"] 9 | -------------------------------------------------------------------------------- /.github/workflows/test-macro.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Run Macro Tests 4 | 5 | jobs: 6 | testing: 7 | name: testing 8 | runs-on: ubuntu-18.04 9 | 10 | steps: 11 | - name: Checkout sources 12 | uses: actions/checkout@v2 13 | 14 | - name: Install toolchain 15 | uses: actions-rs/toolchain@v1 16 | with: 17 | profile: minimal 18 | toolchain: nightly 19 | override: true 20 | 21 | - name: Test macro library 22 | uses: actions-rs/cargo@v1 23 | with: 24 | command: test 25 | args: -p msp430-rt-macros --features device 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .#* 3 | Cargo.lock 4 | target/ 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ## [v0.4.0]- 2022-09-11 11 | 12 | ### Changed 13 | - Update [`msp430`] crate from `0.3.0` to `0.4.0`. This crate remains 14 | API-compatible with [v0.3.1]. Updating [`msp430`] is _transitively_ a 15 | breaking change due to previously always-enabled functionality now being 16 | gated behind the default-disabled `critical-section-single-core` feature. 17 | 18 | ### Fixed 19 | - `trybuild` tests with outdated messages were updated or removed as 20 | appropriate. 21 | 22 | ## [v0.3.1]- 2022-02-19 23 | 24 | ### Added 25 | - The `entry` attribute macro for main can now take an `interrupt_enable` 26 | argument. The `interrupt_enable` argument will enable interrupts before 27 | calling the function with the `entry` attribute, possibly calling an an 28 | optional setup function before enabling interrupts. 29 | - Not using the `interrupt_enable` argument is backwards-compatible with [v0.3.0]. 30 | - Use [`trybuild`](https://github.com/dtolnay/trybuild) to test error messages 31 | from the `pre-init`, `entry`, and `interrupt` macros. 32 | - Use [GHA](https://github.com/rust-embedded/msp430-rt/actions) for these UI 33 | tests. 34 | 35 | ### Changed 36 | - Nested functions generated by the `entry` and `interrupt` macros have the 37 | `inline(always)` attribute applied to them. This in fact saves space and 38 | execution time by removing a call/jump into the nested function body. 39 | 40 | ### Fixed 41 | - [docs.rs](https://docs.rs) [metadata](https://docs.rs/about/metadata) added 42 | to `Cargo.toml` to fix documentation building [failure](https://github.com/rust-embedded/msp430-rt/issues/16). 43 | - Change instances of `msp430_macros` in docs to correct `msp430_rt_macros` 44 | import. 45 | 46 | ## [v0.3.0]- 2022-01-25 47 | 48 | ### Changed 49 | - [breaking-change] In preparation for bumping the version of `bare-metal` in 50 | `msp430` to `v1.0`, the `CriticalSection` parameters to the `main` and ISR 51 | functions now have their lifetimes constrained to the body of their 52 | functions. Due to `no_mangle`, PACs using v0.2.x will not work properly 53 | with `v0.3.0` of this crate. 54 | - [breaking-change] Bump `msp430` to `v0.3.0`, which uses the new 55 | `CriticalSection<'a>` parameters generated for the `main` and ISR functions 56 | (via `interrupt::free` and `interrupt::Mutex`) 57 | - Remaining dependencies were bumped to the most recent versions available on 58 | Cargo at the time. 59 | - The RNG used to generate idents was changed from [`Pcg64Mcg`](https://docs.rs/rand/0.6.0/rand/rngs/struct.SmallRng.html) 60 | (64-bit) and `Pcg32` (32-bit) to [`Xoshiro128PlusPlus`](https://docs.rs/rand/0.8.4/rand/rngs/struct.SmallRng.html) 61 | (32/64-bit) to avoid the need for the `SmallRng` feature (and consistency 62 | between 32/64-bit platforms). 63 | - No other functional changes intended by bumping dependencies. 64 | 65 | ## [v0.2.5]- 2021-10-27 66 | 67 | To replace [`r0`], startup code is now written in assembly, optimized for 68 | size. The startup code provides the non-mangled function called `Reset` which: 69 | - Initializes the stack pointer. 70 | - Calls `PreInit`. 71 | - Initializes static data in the `.bss` and `.data` sections. 72 | - Calls `main`. 73 | 74 | Because `Reset` is no longer written in Rust, a user's `main` function will no 75 | longer be inlined into `Reset`. Additionally, `ResetTrampoline` was removed, 76 | since its sole purpose was to set the stack pointer; `Reset` does this now. 77 | 78 | ### Removed 79 | - Remove [`r0`] dependency in light of potential [unsoundness](https://github.com/rust-embedded/cortex-m-rt/issues/300). 80 | This internal change should not affect user-facing applications. 81 | 82 | ## [v0.2.4]- 2020-03-04 83 | 84 | ### Fixed 85 | - `msp430-rt` now depends on the correct version of `msp430-rt-macros`. 86 | 87 | ## [v0.2.3]- 2020-03-04 (YANKED) 88 | 89 | This release was yanked because `msp430-rt-macros` was not updated accordingly 90 | before release. 91 | 92 | ### Added 93 | - Modify `#[entry]` and `#[interrupt]` macros so that `main()` and interrupt 94 | handlers optionally accept a `CriticalSection` argument. 95 | 96 | ### Changed 97 | - Proactively update [`r0`] to 1.0.0, the first crate of 98 | [WG Issue #383](https://github.com/rust-embedded/wg/issues/383). 99 | 100 | ## [v0.2.2]- 2020-01-07 101 | 102 | ### Fixed 103 | - Fix entry point in linker script to be `ResetTrampoline` instead of `Reset`. 104 | This caused subtle breakage during debugging where invoking and exiting `gdb` 105 | without forcing a `monitor reset` (_which you should be doing anyway_). 106 | Specifically, `gdb` would reset the program counter to skip the stack 107 | initialization code in `ResetTrampoline`, which can leak stack memory. 108 | 109 | ## [v0.2.1]- 2020-01-07 110 | 111 | ### Fixed 112 | - Correct build.rs script so that msp430-rt is only recompiled if `link.x.in` 113 | changed, rather than `link.x`. The latter triggers unconditional rebuilds. 114 | 115 | ## [v0.2.0]- 2020-01-01 116 | 117 | - [breaking-change] Interrupts are now implemented using an attribute macro 118 | called `#[interrupt]`, provided by the [`msp430-rt-macros`](macros) package. 119 | - [breaking-change] Old compilers using the `Termination` trait will no longer 120 | compile this crate. 121 | - [breaking-change] The `INTERRUPTS` array is now called `__INTERRUPTS` for 122 | parity with [`cortex-m-rt`], 123 | and the linker script has been updated to accomodate. `^0.1.0` PACs will not 124 | work properly with this crate. 125 | - [breaking-change] If the `device` feature is enabled, the linker script 126 | expects a PAC, such as [`msp430g2553`](https://github.com/pftbest/msp430g2553), 127 | to provide interrupt vector addresses via the `device.x` file. This should be 128 | transparent to the user due to `build.rs`. 129 | - [breaking-change] The `default_handler` macro was removed; a default 130 | interrupt handler is defined defining an function called `DefaultHandler` 131 | with the `#[interrupt]` attribute. 132 | - [breaking-change] An application's entry point is now defined using the 133 | `#[entry]` attribute macro, with function signature `fn() -> !` 134 | - Add `#[pre_init]` attribute macro, for parity with [`cortex-m-rt`]. 135 | - Removed instances of `asm` macros. Use a separate assembly file called 136 | `asm.s` and `libmsp430.a` for stable assembly when absolutely necessary (at 137 | the cost of some inlining). This should be transparent to the user thanks 138 | to `build.rs`. 139 | - Reset handler name changed from `reset_handler` to `Reset`; this is invisible 140 | to users. 141 | - All but one required feature have either stabilized (`used`) or are no longer 142 | used in the crate (`asm`, `lang_items`, `linkage`, `naked_functions`). The 143 | only remaining unstable feature is [`abi_msp430_interrupt`](https://github.com/rust-lang/rust/issues/38487). 144 | 145 | ## [v0.1.4] - 2019-11-01 146 | 147 | - Removed panic_implementation 148 | 149 | ## [v0.1.3] - 2018-06-18 150 | 151 | - Upgrade to panic_implementation 152 | 153 | ## [v0.1.2] - 2018-04-08 154 | 155 | - Fix version tags 156 | 157 | - Fix build with recent nightly-2018-04-08. 158 | 159 | ## [v0.1.1] - 2018-02-02 160 | 161 | - Import `Termination` trait code from [`cortex-m-rt`] to permit compiling with 162 | recent nightlies. 163 | 164 | ## v0.1.0 - 2017-07-22 165 | 166 | Initial release 167 | 168 | [`r0`]: https://github.com/rust-embedded/r0 169 | [`cortex-m-rt`]: https://github.com/japaric/cortex-m-rt 170 | [`msp430`]: https://github.com/rust-embedded/msp430 171 | 172 | [Unreleased]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.4.0...HEAD 173 | [v0.4.0]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.3.1...msp_v0.4.0 174 | [v0.3.1]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.3.0...msp_v0.3.1 175 | [v0.3.0]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.2.5...msp_v0.3.0 176 | [v0.2.5]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.2.4...msp_v0.2.5 177 | [v0.2.4]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.2.3...msp_v0.2.4 178 | [v0.2.3]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.2.2...msp_v0.2.3 179 | [v0.2.2]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.2.1...msp_v0.2.2 180 | [v0.2.1]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.2.0...msp_v0.2.1 181 | [v0.2.0]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.1.4...msp_v0.2.0 182 | [v0.1.4]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.1.3...msp_v0.1.4 183 | [v0.1.3]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.1.2...msp_v0.1.3 184 | [v0.1.2]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.1.1...msp_v0.1.2 185 | [v0.1.1]: https://github.com/rust-embedded/msp430-rt/compare/msp_v0.1.0...msp_v0.1.1 186 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "Jorge Aparicio ", 4 | "Vadzim Dambrouski ", 5 | "William D. Jones " 6 | ] 7 | categories = ["embedded", "no-std"] 8 | description = "Minimal runtime / startup for MSP430 microcontrollers" 9 | documentation = "https://docs.rs/msp430-rt" 10 | edition = "2018" 11 | keywords = ["msp430", "runtime", "startup"] 12 | license = "MIT OR Apache-2.0" 13 | name = "msp430-rt" 14 | repository = "https://github.com/rust-embedded/msp430-rt" 15 | version = "0.4.0" 16 | 17 | [dependencies] 18 | msp430 = "0.4.0" 19 | 20 | [dependencies.msp430-rt-macros] 21 | version = "=0.4.0" 22 | path = "macros" 23 | 24 | [features] 25 | device = ["msp430-rt-macros/device"] 26 | 27 | [package.metadata.docs.rs] 28 | features = ["device"] 29 | targets = ["msp430-none-elf"] 30 | cargo-args = ["-Z", "build-std=core"] 31 | 32 | [workspace] 33 | members = ["macros"] 34 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Jorge Aparicio 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/v/msp430-rt.svg)](https://crates.io/crates/msp430-rt) 2 | [![crates.io](https://img.shields.io/crates/d/msp430-rt.svg)](https://crates.io/crates/msp430-rt) 3 | 4 | # `msp430-rt` 5 | 6 | > Minimal runtime / startup for MSP430 microcontrollers 7 | 8 | This crate is based on [cortex-m-rt](https://docs.rs/cortex-m-rt) crate by Jorge Aparicio (@japaric). 9 | 10 | # [Documentation](https://docs.rs/msp430-rt) 11 | 12 | # License 13 | 14 | Licensed under either of 15 | 16 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 17 | http://www.apache.org/licenses/LICENSE-2.0) 18 | 19 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 20 | 21 | at your option. 22 | 23 | ## Contribution 24 | 25 | Unless you explicitly state otherwise, any contribution intentionally submitted 26 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 27 | dual licensed as above, without any additional terms or conditions. 28 | -------------------------------------------------------------------------------- /asm.s: -------------------------------------------------------------------------------- 1 | .section .Reset, "ax" 2 | .global Reset 3 | .type Reset,%function 4 | Reset: 5 | mov #_stack_start, r1 6 | call #PreInit 7 | 8 | ; .bss init 9 | clr r4 10 | .more_bss: 11 | cmp bss_size, r4 12 | jhs .done_bss ; r4 >= bss_size 13 | clr.b _sbss(r4) ; Zero out RAM. 14 | inc r4 15 | jmp .more_bss 16 | 17 | ; .data init 18 | .done_bss: 19 | clr r4 20 | .more_data: 21 | cmp data_size, r4 22 | jhs .done_data ; r4 >= data_size 23 | mov.b _sidata(r4), _sdata(r4) ; Copy from ROM to RAM. 24 | inc r4 25 | jmp .more_data 26 | 27 | .done_data: 28 | br #main 29 | 30 | .section .rodata, "a" 31 | .align 2 ; MSP430 will not behave properly w/ unaligned reads, from my testing. 32 | bss_size: .word _ebss - _sbss 33 | data_size: .word _edata - _sdata 34 | -------------------------------------------------------------------------------- /assemble.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euxo pipefail 4 | 5 | # cflags taken from cc 1.0.22 6 | 7 | crate=msp430-rt 8 | 9 | # remove existing blobs because otherwise this will append object files to the old blobs 10 | rm -f bin/*.a 11 | 12 | msp430-elf-as -mcpu=msp430 asm.s -o bin/$crate.o 13 | ar crs bin/msp430-none-elf.a bin/$crate.o 14 | 15 | rm bin/$crate.o 16 | -------------------------------------------------------------------------------- /bin/msp430-none-elf.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-embedded/msp430-rt/261000f6d7f37086eb36fd86ddeefeffbc1fc9f5/bin/msp430-none-elf.a -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs, fs::File, io::Write, path::PathBuf}; 2 | 3 | fn main() { 4 | let target = env::var("TARGET").unwrap(); 5 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 6 | 7 | if target == "msp430-none-elf" { 8 | fs::copy(format!("bin/{}.a", target), out_dir.join("libmsp430-rt.a")).unwrap(); 9 | println!("cargo:rustc-link-lib=static=msp430-rt"); 10 | } 11 | 12 | // Put the linker script somewhere the linker can find it 13 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 14 | let link_x = include_bytes!("link.x.in"); 15 | if env::var_os("CARGO_FEATURE_DEVICE").is_some() { 16 | let mut f = File::create(out.join("link.x")).unwrap(); 17 | 18 | f.write_all(link_x).unwrap(); 19 | 20 | // *IMPORTANT*: The weak aliases (i.e. `PROVIDED`) must come *after* `EXTERN(__INTERRUPTS)`. 21 | // Otherwise the linker will ignore user defined interrupts and always populate the table 22 | // with the weak aliases. 23 | writeln!( 24 | f, 25 | r#" 26 | /* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */ 27 | /* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */ 28 | INCLUDE device.x"# 29 | ) 30 | .unwrap(); 31 | } else { 32 | let mut f = File::create(out.join("link.x")).unwrap(); 33 | f.write_all(link_x).unwrap(); 34 | }; 35 | 36 | println!("cargo:rustc-link-search={}", out_dir.display()); 37 | 38 | println!("cargo:rerun-if-changed=build.rs"); 39 | println!("cargo:rerun-if-changed=link.x.in"); 40 | } 41 | -------------------------------------------------------------------------------- /link.x.in: -------------------------------------------------------------------------------- 1 | INCLUDE memory.x 2 | 3 | /* Entry point */ 4 | ENTRY(Reset); 5 | EXTERN(__RESET_VECTOR); 6 | 7 | /* Create an undefined reference to the INTERRUPTS symbol. This is required to 8 | force the linker to *not* drop the INTERRUPTS symbol if it comes from an 9 | object file that's passed to the linker *before* this crate */ 10 | EXTERN(__INTERRUPTS); 11 | 12 | /* Provide a default for __VECTORS_END_ADDR. Can be overriden in the user memory.x file */ 13 | PROVIDE(__VECTORS_END_ADDR = 0x10000); 14 | 15 | /* # Pre-initialization function */ 16 | /* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function, 17 | then the function this points to will be called before the RAM is initialized. */ 18 | PROVIDE(PreInit = PreInit_); 19 | 20 | /* # Default interrupt handler */ 21 | EXTERN(DefaultHandler); /* If this line is not here, all unused interrupt 22 | handlers will be zeroed out instead of doing 23 | to the DefaultHandler! */ 24 | PROVIDE(DefaultHandler = DefaultHandler_); 25 | 26 | /* XXX Are there use cases for making this user overridable? */ 27 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); 28 | 29 | SECTIONS 30 | { 31 | .vector_table ORIGIN(VECTORS) : ALIGN(2) 32 | { 33 | KEEP(*(.vector_table.interrupts)); 34 | KEEP(*(.__RESET_VECTOR)); 35 | } > VECTORS 36 | 37 | .text ORIGIN(ROM) : 38 | { 39 | /* Put the reset handler at the beginning of the .text section */ 40 | KEEP(*(.Reset)); 41 | 42 | *(.text .text.*); 43 | } > ROM 44 | 45 | .rodata : ALIGN(2) 46 | { 47 | *(.rodata .rodata.*); 48 | . = ALIGN(2); 49 | } > ROM 50 | 51 | .bss : ALIGN(2) 52 | { 53 | _sbss = .; 54 | *(.bss .bss.*); 55 | . = ALIGN(2); 56 | _ebss = .; 57 | } > RAM 58 | 59 | .data : ALIGN(2) 60 | { 61 | _sidata = LOADADDR(.data); 62 | _sdata = .; 63 | *(.data .data.*); 64 | . = ALIGN(2); 65 | _edata = .; 66 | } > RAM AT > ROM 67 | 68 | /* fake output .got section */ 69 | /* Dynamic relocations are unsupported. This section is only used to detect 70 | relocatable code in the input files and raise an error if relocatable code 71 | is found */ 72 | .got : 73 | { 74 | _sgot = .; 75 | KEEP(*(.got .got.*)); 76 | _egot = .; 77 | } > RAM AT > ROM 78 | 79 | /* The heap starts right after the .bss + .data section ends */ 80 | _sheap = _edata; 81 | } 82 | 83 | /* Do not exceed this mark in the error messages below | */ 84 | ASSERT(ORIGIN(VECTORS) + LENGTH(VECTORS) == __VECTORS_END_ADDR, " 85 | ERROR(msp430-rt): The VECTORS memory region must end at address 0x10000. Check memory.x"); 86 | 87 | ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) == __VECTORS_END_ADDR, " 88 | ERROR(msp430-rt): .vector_table is shorter than expected. 89 | Possible solutions, from most likely to less likely: 90 | - Link to a svd2rust generated pac crate, if you are not 91 | - Fix _sinterrupts in memory.x; it doesn't match the number of interrupts provided by the 92 | pac crate 93 | - Disable the 'device' feature of msp430-rt to build a generic application; a dependency 94 | may be enabling it 95 | - Override __VECTORS_END_ADDR if your vector table is not placed at the regular vector location 96 | "); 97 | 98 | ASSERT(_sgot == _egot, " 99 | ERROR(msp430-rt): .got section detected in the input object files 100 | Dynamic relocations are not supported. If you are linking to C code compiled using 101 | the 'cc' crate then modify your build script to compile the C code _without_ 102 | the -fPIC flag. See the documentation of the `cc::Build.pic` method for details."); 103 | /* Do not exceed this mark in the error messages above | */ 104 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Jorge Aparicio "] 3 | categories = ["embedded", "no-std"] 4 | description = "Attributes re-exported in `msp430-rt`" 5 | documentation = "https://docs.rs/msp430-rt" 6 | keywords = ["msp430", "runtime", "startup"] 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/rust-embedded/msp430-rt" 9 | name = "msp430-rt-macros" 10 | version = "0.4.0" 11 | edition = "2018" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | quote = "1.0.14" 18 | proc-macro2 = "1.0.36" 19 | rand_xoshiro = "0.6.0" 20 | 21 | [dependencies.rand] 22 | default-features = false 23 | version = "0.8.4" 24 | 25 | [dependencies.syn] 26 | features = ["extra-traits", "full"] 27 | version = "1.0.85" 28 | 29 | [dev-dependencies] 30 | msp430 = "0.4.0" 31 | 32 | [target.'cfg(not(target_os = "none"))'.dev-dependencies] 33 | trybuild = "1" 34 | 35 | [features] 36 | device = [] 37 | 38 | [[test]] 39 | name = "compiletest" 40 | required-features = ["device"] 41 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | extern crate proc_macro2; 3 | extern crate quote; 4 | extern crate rand; 5 | extern crate rand_xoshiro; 6 | extern crate syn; 7 | 8 | use proc_macro::TokenStream; 9 | use std::{ 10 | collections::HashSet, 11 | sync::atomic::{AtomicUsize, Ordering}, 12 | time::{SystemTime, UNIX_EPOCH}, 13 | }; 14 | 15 | use proc_macro2::Span; 16 | use quote::{quote, quote_spanned}; 17 | use rand::Rng; 18 | use rand_xoshiro::rand_core::SeedableRng; 19 | use syn::{ 20 | parenthesized, 21 | parse::{self, Parse}, 22 | parse_macro_input, 23 | punctuated::Punctuated, 24 | spanned::Spanned, 25 | FnArg, Ident, Item, ItemFn, ItemStatic, Pat, PatIdent, PathArguments, PathSegment, ReturnType, 26 | Stmt, Token, Type, TypePath, Visibility, 27 | }; 28 | 29 | /// Attribute to declare the entry point of the program 30 | /// 31 | /// The specified function will be called by the reset handler *after* RAM has been initialized. 32 | /// 33 | /// The type of the specified function must be `[unsafe] fn([: CriticalSection]) -> !` (never 34 | /// ending function), where the `CriticalSection` argument is optional. 35 | /// 36 | /// # Properties 37 | /// 38 | /// The entry point will be called by the reset handler. The program can't reference to the entry 39 | /// point, much less invoke it. 40 | /// 41 | /// `static mut` variables declared within the entry point are safe to access. The compiler can't 42 | /// prove this is safe so the attribute will help by making a transformation to the source code: for 43 | /// this reason a variable like `static mut FOO: u32` will become `let FOO: &'static mut u32;`. Note 44 | /// that `&'static mut` references have move semantics. 45 | /// 46 | /// ## Examples 47 | /// 48 | /// - Simple entry point 49 | /// 50 | /// ``` no_run 51 | /// # #![no_main] 52 | /// # use msp430_rt_macros::entry; 53 | /// #[entry] 54 | /// fn main() -> ! { 55 | /// loop { 56 | /// /* .. */ 57 | /// } 58 | /// } 59 | /// ``` 60 | /// 61 | /// - `static mut` variables local to the entry point are safe to modify. 62 | /// 63 | /// ``` no_run 64 | /// # #![no_main] 65 | /// # use msp430_rt_macros::entry; 66 | /// #[entry] 67 | /// fn main(_cs: CriticalSection) -> ! { 68 | /// static mut FOO: u32 = 0; 69 | /// 70 | /// let foo: &'static mut u32 = FOO; 71 | /// assert_eq!(*foo, 0); 72 | /// *foo = 1; 73 | /// assert_eq!(*foo, 1); 74 | /// 75 | /// loop { 76 | /// /* .. */ 77 | /// } 78 | /// } 79 | /// ``` 80 | /// 81 | /// # Pre-entry Interrupt Enable 82 | /// 83 | /// If the argument `interrupt_enable` is passed into the macro, interrupts will be enabled 84 | /// globally before the entry function runs. If this is enabled then the entry function will no 85 | /// longer accept `CriticalSection` as a parameter, since that it will be unsound. 86 | /// 87 | /// The macro can also take arguments of the form `interrupt_enable(pre_interrupt = )`, where 88 | /// `init` is the name of a function with the signature `fn(cs: CriticalSection) -> `. The 89 | /// entry function can then optionally take a parameter of `Type`. This makes `init` run before 90 | /// interrupts are enabled and possibly pass its return value into the entry function, allowing 91 | /// pre-interrupt initialization to be done. 92 | /// 93 | /// Note that a function marked with the entry attribute is allowed to take no input parameters 94 | /// even if `init` returns a value, due to implementation details. To reduce code size, it is 95 | /// strongly recommended to put `#[inline(always)]` on `init` if it's used nowhere else. 96 | /// 97 | /// ## Examples 98 | /// 99 | /// - Enable interrupts before entry 100 | /// 101 | /// ``` no_run 102 | /// # #![no_main] 103 | /// # use msp430_rt_macros::entry; 104 | /// #[entry(interrupt_enable)] 105 | /// fn main() -> ! { 106 | /// /* interrupts now enabled */ 107 | /// loop {} 108 | /// } 109 | /// ``` 110 | /// 111 | /// - Pre-interrupt initialization 112 | /// 113 | /// ``` no_run 114 | /// # #![no_main] 115 | /// # use msp430_rt_macros::entry; 116 | /// use msp430::interrupt::CriticalSection; 117 | /// 118 | /// # struct Hal; 119 | /// #[inline(always)] 120 | /// fn init(cs: CriticalSection) -> Hal { 121 | /// /* initialize hardware */ 122 | /// # Hal 123 | /// } 124 | /// 125 | /// #[entry(interrupt_enable(pre_interrupt = init))] 126 | /// fn main(hal: Hal) -> ! { 127 | /// loop { 128 | /// /* do something with hal */ 129 | /// } 130 | /// } 131 | /// ``` 132 | /// 133 | /// - Pre-interrupt initialization with no return 134 | /// 135 | /// ``` no_run 136 | /// # #![no_main] 137 | /// # use msp430_rt_macros::entry; 138 | /// use msp430::interrupt::CriticalSection; 139 | /// 140 | /// #[inline(always)] 141 | /// fn arg(cs: CriticalSection) { 142 | /// /* initialize */ 143 | /// } 144 | /// 145 | /// #[entry(interrupt_enable(pre_interrupt = arg))] 146 | /// fn main() -> ! { 147 | /// loop {} 148 | /// } 149 | /// ``` 150 | /// 151 | /// ## Note 152 | /// 153 | /// The `CriticalSection`s passed into the entry and the pre-interrupt functions have their 154 | /// lifetimes restrained to their respective functions. Attempting to pass the `CriticalSection` 155 | /// outside its scope fails with a `borrowed value does not live long enough` error. 156 | #[proc_macro_attribute] 157 | pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { 158 | let interrupt_enable = if args.is_empty() { 159 | None 160 | } else { 161 | Some(parse_macro_input!(args as EntryInterruptEnable)) 162 | }; 163 | 164 | let f = parse_macro_input!(input as ItemFn); 165 | 166 | // check the function signature 167 | let valid_signature = f.sig.constness.is_none() 168 | && f.vis == Visibility::Inherited 169 | && f.sig.abi.is_none() 170 | && f.sig.generics.params.is_empty() 171 | && f.sig.generics.where_clause.is_none() 172 | && f.sig.variadic.is_none() 173 | && match f.sig.output { 174 | ReturnType::Default => false, 175 | ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)), 176 | }; 177 | 178 | let pair = match &interrupt_enable { 179 | Some(interrupt_enable) => interrupt_enable.extract_init_arg(&f.sig.inputs), 180 | None => extract_critical_section_arg(&f.sig.inputs), 181 | }; 182 | 183 | if let (true, Ok(ParamArgPair { fn_param, fn_arg })) = (valid_signature, pair) { 184 | // XXX should we blacklist other attributes? 185 | let attrs = f.attrs; 186 | let unsafety = f.sig.unsafety; 187 | let hash = random_ident(); 188 | let (statics, stmts) = match extract_static_muts(f.block.stmts) { 189 | Err(e) => return e.to_compile_error().into(), 190 | Ok(x) => x, 191 | }; 192 | 193 | let vars = statics 194 | .into_iter() 195 | .map(|var| { 196 | let attrs = var.attrs; 197 | let ident = var.ident; 198 | let ty = var.ty; 199 | let expr = var.expr; 200 | 201 | quote!( 202 | #[allow(non_snake_case)] 203 | let #ident: &'static mut #ty = unsafe { 204 | #(#attrs)* 205 | static mut #ident: #ty = #expr; 206 | 207 | &mut #ident 208 | }; 209 | ) 210 | }) 211 | .collect::>(); 212 | 213 | // Only generate the argument if fn_param exists, to handle the case where the argument 214 | // expression does exist but the entry function doesn't accept any args 215 | let arg_ident = fn_param 216 | .as_ref() 217 | .map(|_| Ident::new("arg", Span::mixed_site())); 218 | let arg_def = fn_arg 219 | .as_ref() 220 | .map(|arg| quote_spanned!(Span::mixed_site()=> let arg = #arg; )); 221 | 222 | quote!( 223 | #[export_name = "main"] 224 | #(#attrs)* 225 | pub #unsafety fn #hash() -> ! { 226 | #[inline(always)] 227 | #unsafety fn #hash<'a>(#fn_param) -> ! { 228 | #(#vars)* 229 | #(#stmts)* 230 | } 231 | #arg_def 232 | { #hash(#arg_ident) } 233 | } 234 | ) 235 | .into() 236 | } else { 237 | let err = match interrupt_enable { 238 | None => parse::Error::new( 239 | f.sig.span(), 240 | "`#[entry]` function must have signature `[unsafe] fn([ : CriticalSection]) -> !`", 241 | ), 242 | Some(EntryInterruptEnable { pre_interrupt: None }) => parse::Error::new( 243 | f.sig.span(), 244 | "`#[entry(interrupt_enable)]` function must have signature `[unsafe] fn() -> !`", 245 | ), 246 | Some(EntryInterruptEnable { pre_interrupt: Some(ident) }) => parse::Error::new( 247 | f.sig.span(), 248 | format!("`#[entry(interrupt_enable(pre_interrupt = {fname}))]` function must have signature `[unsafe] fn([ : ]) -> !`, where is the return value of {fname}", fname = ident) 249 | ), 250 | }; 251 | err.to_compile_error().into() 252 | } 253 | } 254 | 255 | #[derive(Default)] 256 | struct ParamArgPair { 257 | fn_param: Option, 258 | fn_arg: Option, 259 | } 260 | 261 | struct EntryInterruptEnable { 262 | pre_interrupt: Option, 263 | } 264 | 265 | impl Parse for EntryInterruptEnable { 266 | fn parse(input: parse::ParseStream) -> syn::Result { 267 | let interrupt_enable = input.parse::()?; 268 | if interrupt_enable != "interrupt_enable" { 269 | return Err(parse::Error::new( 270 | interrupt_enable.span(), 271 | "expected `interrupt_enable` or no arguments at all", 272 | )); 273 | } 274 | let pre_interrupt = if input.peek(syn::token::Paren) { 275 | let inner; 276 | parenthesized!(inner in input); 277 | let pre_interrupt = inner.parse::()?; 278 | if pre_interrupt != "pre_interrupt" { 279 | return Err(parse::Error::new( 280 | pre_interrupt.span(), 281 | "expected `pre_interrupt`", 282 | )); 283 | } 284 | inner.parse::()?; 285 | Some(inner.parse::()?) 286 | } else { 287 | None 288 | }; 289 | 290 | Ok(EntryInterruptEnable { pre_interrupt }) 291 | } 292 | } 293 | 294 | impl EntryInterruptEnable { 295 | fn extract_init_arg(&self, list: &Punctuated) -> Result { 296 | if let Some(fn_name) = &self.pre_interrupt { 297 | let hash = random_ident(); 298 | let fn_arg = Some(quote_spanned!(Span::mixed_site()=> { 299 | let cs = unsafe { msp430::interrupt::CriticalSection::new() }; 300 | 301 | // This struct forces the lifetime of the CriticalSection to match the lifetime of 302 | // the reference. Since the reference lifetime is restricted to this scope, the 303 | // compiler has to constrain the lifetime of the CriticalSection as well, 304 | // preventing the CriticalSection from being leaked as a return value. 305 | #[allow(non_camel_case_types)] 306 | struct #hash<'a>(&'a msp430::interrupt::CriticalSection<'a>); 307 | let arg = #fn_name(*#hash(&cs).0); 308 | 309 | unsafe { msp430::interrupt::enable() }; 310 | arg 311 | })); 312 | 313 | if let Some(first) = list.first() { 314 | if let FnArg::Typed(pat_type) = first { 315 | // Case where pre-init exists and entry takes a param 316 | return Ok(ParamArgPair { 317 | fn_param: Some(quote! { #pat_type }), 318 | fn_arg, 319 | }); 320 | } 321 | } else { 322 | // Case where pre-init exists but entry takes no params 323 | return Ok(ParamArgPair { 324 | fn_param: None, 325 | fn_arg, 326 | }); 327 | } 328 | } else if list.is_empty() { 329 | // Case where pre-init doesn't exist and entry takes no params 330 | return Ok(ParamArgPair { 331 | fn_param: None, 332 | fn_arg: Some(quote!({ 333 | unsafe { msp430::interrupt::enable() }; 334 | })), 335 | }); 336 | } 337 | Err(()) 338 | } 339 | } 340 | 341 | /// Attribute to declare an interrupt handler 342 | /// 343 | /// When the `device` feature is disabled this attribute can only be used to override the 344 | /// DefaultHandler. 345 | /// 346 | /// When the `device` feature is enabled this attribute can be used to override other interrupt 347 | /// handlers but only when imported from a PAC (Peripheral Access Crate) crate which re-exports it. 348 | /// Importing this attribute from the `msp430-rt` crate and using it on a function will result in a 349 | /// compiler error. 350 | /// 351 | /// # Syntax 352 | /// 353 | /// ``` ignore 354 | /// extern crate device; 355 | /// 356 | /// // the attribute comes from the device crate not from msp430-rt 357 | /// use device::interrupt; 358 | /// 359 | /// #[interrupt] 360 | /// // Pass in optional CriticalSection 361 | /// fn USART1(cs: CriticalSection) { 362 | /// // .. 363 | /// } 364 | /// ``` 365 | /// 366 | /// where the name of the function must be `DefaultHandler` or one of the device interrupts. 367 | /// 368 | /// # Usage 369 | /// 370 | /// `#[interrupt] fn Name(..` overrides the default handler for the interrupt with the given `Name`. 371 | /// These handlers must have signature `[unsafe] fn([: CriticalSection]) [-> !]`. It's 372 | /// possible to add state to these handlers by declaring `static mut` variables at the beginning of 373 | /// the body of the function. These variables will be safe to access from the function body. 374 | /// 375 | /// If the interrupt handler has not been overridden it will be dispatched by the default interrupt 376 | /// handler (`DefaultHandler`). 377 | /// 378 | /// `#[interrupt] fn DefaultHandler(..` can be used to override the default interrupt handler. When 379 | /// not overridden `DefaultHandler` defaults to an infinite loop. 380 | /// 381 | /// # Properties 382 | /// 383 | /// Interrupts handlers can only be called by the hardware. Other parts of the program can't refer 384 | /// to the interrupt handlers, much less invoke them as if they were functions. 385 | /// 386 | /// `static mut` variables declared within an interrupt handler are safe to access and can be used 387 | /// to preserve state across invocations of the handler. The compiler can't prove this is safe so 388 | /// the attribute will help by making a transformation to the source code: for this reason a 389 | /// variable like `static mut FOO: u32` will become `let FOO: &mut u32;`. 390 | /// 391 | /// ## Examples 392 | /// 393 | /// - Using state within an interrupt handler 394 | /// 395 | /// ``` ignore 396 | /// extern crate device; 397 | /// 398 | /// use device::interrupt; 399 | /// 400 | /// #[interrupt] 401 | /// fn TIM2() { 402 | /// static mut COUNT: i32 = 0; 403 | /// 404 | /// // `COUNT` is safe to access and has type `&mut i32` 405 | /// *COUNT += 1; 406 | /// 407 | /// println!("{}", COUNT); 408 | /// } 409 | /// ``` 410 | /// 411 | /// ## Note 412 | /// 413 | /// The `CriticalSection` passed into the interrupt function has its lifetime restrained to the 414 | /// function scope. Attempting to pass the `CriticalSection` outside its scope fails with a 415 | /// `borrowed value does not live long enough` error. 416 | #[proc_macro_attribute] 417 | pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { 418 | let f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); 419 | 420 | if !args.is_empty() { 421 | return parse::Error::new(Span::call_site(), "this attribute accepts no arguments") 422 | .to_compile_error() 423 | .into(); 424 | } 425 | 426 | let fspan = f.sig.span(); 427 | let ident = f.sig.ident; 428 | 429 | let check = if ident == "DefaultHandler" { 430 | None 431 | } else if cfg!(feature = "device") { 432 | Some(quote!(interrupt::#ident;)) 433 | } else { 434 | return parse::Error::new( 435 | ident.span(), 436 | "only the DefaultHandler can be overridden when the `device` feature is disabled", 437 | ) 438 | .to_compile_error() 439 | .into(); 440 | }; 441 | 442 | // XXX should we blacklist other attributes? 443 | let attrs = f.attrs; 444 | let block = f.block; 445 | let stmts = block.stmts; 446 | let unsafety = f.sig.unsafety; 447 | 448 | let valid_signature = f.sig.constness.is_none() 449 | && f.vis == Visibility::Inherited 450 | && f.sig.abi.is_none() 451 | && f.sig.generics.params.is_empty() 452 | && f.sig.generics.where_clause.is_none() 453 | && f.sig.variadic.is_none() 454 | && match f.sig.output { 455 | ReturnType::Default => true, 456 | ReturnType::Type(_, ref ty) => match **ty { 457 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), 458 | Type::Never(..) => true, 459 | _ => false, 460 | }, 461 | }; 462 | 463 | let pair = extract_critical_section_arg(&f.sig.inputs); 464 | 465 | if let (true, Ok(ParamArgPair { fn_arg, fn_param })) = (valid_signature, pair) { 466 | let (statics, stmts) = match extract_static_muts(stmts) { 467 | Err(e) => return e.to_compile_error().into(), 468 | Ok(x) => x, 469 | }; 470 | 471 | let vars = statics 472 | .into_iter() 473 | .map(|var| { 474 | let attrs = var.attrs; 475 | let ident = var.ident; 476 | let ty = var.ty; 477 | let expr = var.expr; 478 | 479 | quote!( 480 | #[allow(non_snake_case)] 481 | let #ident: &mut #ty = unsafe { 482 | #(#attrs)* 483 | static mut #ident: #ty = #expr; 484 | 485 | &mut #ident 486 | }; 487 | ) 488 | }) 489 | .collect::>(); 490 | 491 | let output = f.sig.output; 492 | let hash = random_ident(); 493 | let ident = ident.to_string(); 494 | quote!( 495 | #[export_name = #ident] 496 | #(#attrs)* 497 | #unsafety extern "msp430-interrupt" fn #hash() { 498 | #check 499 | 500 | #[inline(always)] 501 | #unsafety fn #hash<'a>(#fn_param) #output { 502 | #(#vars)* 503 | #(#stmts)* 504 | } 505 | { #hash(#fn_arg) } 506 | } 507 | ) 508 | .into() 509 | } else { 510 | parse::Error::new( 511 | fspan, 512 | "`#[interrupt]` handlers must have signature `[unsafe] fn([: CriticalSection]) [-> !]`", 513 | ) 514 | .to_compile_error() 515 | .into() 516 | } 517 | } 518 | 519 | /// Attribute to mark which function will be called at the beginning of the reset handler. 520 | /// 521 | /// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. 522 | /// 523 | /// The function must have the signature of `unsafe fn()`. 524 | /// 525 | /// The function passed will be called before static variables are initialized. Any access of static 526 | /// variables will result in undefined behavior. 527 | /// 528 | /// ## Examples 529 | /// 530 | /// ``` 531 | /// # use msp430_rt_macros::pre_init; 532 | /// #[pre_init] 533 | /// unsafe fn before_main() { 534 | /// // do something here 535 | /// } 536 | /// 537 | /// # fn main() {} 538 | /// ``` 539 | #[proc_macro_attribute] 540 | pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { 541 | let f = parse_macro_input!(input as ItemFn); 542 | 543 | // check the function signature 544 | let valid_signature = f.sig.constness.is_none() 545 | && f.vis == Visibility::Inherited 546 | && f.sig.unsafety.is_some() 547 | && f.sig.abi.is_none() 548 | && f.sig.inputs.is_empty() 549 | && f.sig.generics.params.is_empty() 550 | && f.sig.generics.where_clause.is_none() 551 | && f.sig.variadic.is_none() 552 | && match f.sig.output { 553 | ReturnType::Default => true, 554 | ReturnType::Type(_, ref ty) => match **ty { 555 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), 556 | _ => false, 557 | }, 558 | }; 559 | 560 | if !valid_signature { 561 | return parse::Error::new( 562 | f.sig.span(), 563 | "`#[pre_init]` function must have signature `unsafe fn()`", 564 | ) 565 | .to_compile_error() 566 | .into(); 567 | } 568 | 569 | if !args.is_empty() { 570 | return parse::Error::new(Span::call_site(), "this attribute accepts no arguments") 571 | .to_compile_error() 572 | .into(); 573 | } 574 | 575 | // XXX should we blacklist other attributes? 576 | let attrs = f.attrs; 577 | let ident = f.sig.ident; 578 | let block = f.block; 579 | 580 | quote!( 581 | #[export_name = "__pre_init"] 582 | #(#attrs)* 583 | pub unsafe fn #ident() #block 584 | ) 585 | .into() 586 | } 587 | 588 | // Parses an optional `: CriticalSection` from a list of function arguments. 589 | // Additional arguments are considered invalid 590 | fn extract_critical_section_arg(list: &Punctuated) -> Result { 591 | let num_args = list.len(); 592 | if num_args == 0 { 593 | return Ok(ParamArgPair::default()); 594 | } else if num_args == 1 { 595 | if let FnArg::Typed(pat_type) = list.first().unwrap() { 596 | if let ( 597 | Pat::Ident(PatIdent { 598 | ident: name, 599 | by_ref: None, 600 | mutability: None, 601 | subpat: None, 602 | attrs, 603 | }), 604 | Type::Path(TypePath { qself: None, path }), 605 | _, 606 | [], 607 | ) = ( 608 | &*pat_type.pat, 609 | &*pat_type.ty, 610 | pat_type.colon_token, 611 | &*pat_type.attrs, 612 | ) { 613 | if path.segments.len() == 1 && attrs.is_empty() { 614 | let seg = path.segments.first().unwrap(); 615 | if matches!( 616 | seg, 617 | PathSegment { 618 | ident, 619 | arguments: PathArguments::None, 620 | } if ident == "CriticalSection" 621 | ) { 622 | return Ok(ParamArgPair { 623 | fn_param: Some( 624 | quote! { #name: msp430::interrupt::CriticalSection<'a> }, 625 | ), 626 | fn_arg: Some( 627 | quote! { unsafe { msp430::interrupt::CriticalSection::new() } }, 628 | ), 629 | }); 630 | } 631 | } 632 | } 633 | } 634 | } 635 | Err(()) 636 | } 637 | 638 | // Creates a random identifier 639 | fn random_ident() -> Ident { 640 | static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); 641 | 642 | let secs = SystemTime::now() 643 | .duration_since(UNIX_EPOCH) 644 | .unwrap() 645 | .as_secs(); 646 | 647 | let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64; 648 | let mut seed: [u8; 16] = [0; 16]; 649 | 650 | for (i, v) in seed.iter_mut().take(8).enumerate() { 651 | *v = ((secs >> (i * 8)) & 0xFF) as u8 652 | } 653 | 654 | for (i, v) in seed.iter_mut().skip(8).enumerate() { 655 | *v = ((count >> (i * 8)) & 0xFF) as u8 656 | } 657 | 658 | let mut rng = rand_xoshiro::Xoshiro128PlusPlus::from_seed(seed); 659 | Ident::new( 660 | &(0..16) 661 | .map(|i| { 662 | if i == 0 || rng.gen() { 663 | (b'a' + rng.gen::() % 25) as char 664 | } else { 665 | (b'0' + rng.gen::() % 10) as char 666 | } 667 | }) 668 | .collect::(), 669 | Span::call_site(), 670 | ) 671 | } 672 | 673 | /// Extracts `static mut` vars from the beginning of the given statements 674 | fn extract_static_muts(stmts: Vec) -> Result<(Vec, Vec), parse::Error> { 675 | let mut istmts = stmts.into_iter(); 676 | 677 | let mut seen = HashSet::new(); 678 | let mut statics = vec![]; 679 | let mut stmts = vec![]; 680 | for stmt in istmts.by_ref() { 681 | match stmt { 682 | Stmt::Item(Item::Static(var)) => { 683 | if var.mutability.is_some() { 684 | if seen.contains(&var.ident) { 685 | return Err(parse::Error::new( 686 | var.ident.span(), 687 | format!("the name `{}` is defined multiple times", var.ident), 688 | )); 689 | } 690 | 691 | seen.insert(var.ident.clone()); 692 | statics.push(var); 693 | } else { 694 | stmts.push(Stmt::Item(Item::Static(var))); 695 | } 696 | } 697 | _ => { 698 | stmts.push(stmt); 699 | break; 700 | } 701 | } 702 | } 703 | 704 | stmts.extend(istmts); 705 | 706 | Ok((statics, stmts)) 707 | } 708 | -------------------------------------------------------------------------------- /macros/tests/compiletest.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn ui() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/ui/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_bad_arg.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::entry; 4 | 5 | #[entry(arg)] 6 | fn main() -> ! { 7 | loop {} 8 | } 9 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_bad_arg.stderr: -------------------------------------------------------------------------------- 1 | error: expected `interrupt_enable` or no arguments at all 2 | --> tests/ui/entry_bad_arg.rs:5:9 3 | | 4 | 5 | #[entry(arg)] 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_bad_param.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::entry; 4 | 5 | #[entry] 6 | fn main(i: u32) -> ! { 7 | loop {} 8 | } 9 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_bad_param.stderr: -------------------------------------------------------------------------------- 1 | error: `#[entry]` function must have signature `[unsafe] fn([ : CriticalSection]) -> !` 2 | --> tests/ui/entry_bad_param.rs:6:1 3 | | 4 | 6 | fn main(i: u32) -> ! { 5 | | ^^^^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_bad_ret.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::entry; 4 | 5 | #[entry] 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_bad_ret.stderr: -------------------------------------------------------------------------------- 1 | error: `#[entry]` function must have signature `[unsafe] fn([ : CriticalSection]) -> !` 2 | --> tests/ui/entry_bad_ret.rs:6:1 3 | | 4 | 6 | fn main() {} 5 | | ^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_extern_c.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::entry; 4 | 5 | #[entry] 6 | extern "C" fn main() -> ! { 7 | loop {} 8 | } 9 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_extern_c.stderr: -------------------------------------------------------------------------------- 1 | error: `#[entry]` function must have signature `[unsafe] fn([ : CriticalSection]) -> !` 2 | --> tests/ui/entry_extern_c.rs:6:1 3 | | 4 | 6 | extern "C" fn main() -> ! { 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_mismatch.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430::interrupt::CriticalSection; 4 | use msp430_rt_macros::entry; 5 | 6 | fn init(cs: CriticalSection) -> u32 { 7 | 32 8 | } 9 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 11 | fn main(_i: bool) -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_mismatch.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/ui/entry_preinterrupt_mismatch.rs:10:1 3 | | 4 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | | 7 | | expected `bool`, found `u32` 8 | | arguments to this function are incorrect 9 | | 10 | note: function defined here 11 | --> tests/ui/entry_preinterrupt_mismatch.rs:10:1 12 | | 13 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 14 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 15 | = note: this error originates in the attribute macro `entry` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_no_arg.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::entry; 4 | 5 | fn init() {} 6 | 7 | #[entry(interrupt_enable(pre_interrupt = init))] 8 | fn main() -> ! { 9 | loop {} 10 | } 11 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_no_arg.stderr: -------------------------------------------------------------------------------- 1 | error[E0061]: this function takes 0 arguments but 1 argument was supplied 2 | --> tests/ui/entry_preinterrupt_no_arg.rs:7:42 3 | | 4 | 7 | #[entry(interrupt_enable(pre_interrupt = init))] 5 | | -----------------------------------------^^^^--- argument of type `CriticalSection<'_>` unexpected 6 | | 7 | note: function defined here 8 | --> tests/ui/entry_preinterrupt_no_arg.rs:5:4 9 | | 10 | 5 | fn init() {} 11 | | ^^^^ 12 | help: remove the extra argument 13 | | 14 | 7 | init() 15 | | 16 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430::interrupt::CriticalSection; 4 | use msp430_rt_macros::entry; 5 | 6 | fn init<'a>(cs: CriticalSection<'a>) -> CriticalSection<'a> { 7 | cs 8 | } 9 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 11 | fn main(_cs: CriticalSection) -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `cs` does not live long enough 2 | --> tests/ui/entry_preinterrupt_return_cs.rs:10:1 3 | | 4 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- 6 | | | | 7 | | | `cs` dropped here while still borrowed 8 | | borrowed value does not live long enough 9 | | borrow later stored here 10 | | 11 | = note: this error originates in the attribute macro `entry` (in Nightly builds, run with -Z macro-backtrace for more info) 12 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs2.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430::interrupt::CriticalSection; 4 | use msp430_rt_macros::entry; 5 | 6 | fn init(cs: CriticalSection) -> CriticalSection { 7 | cs 8 | } 9 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 11 | fn main(_cs: CriticalSection) -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs2.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `cs` does not live long enough 2 | --> tests/ui/entry_preinterrupt_return_cs2.rs:10:1 3 | | 4 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- 6 | | | | 7 | | | `cs` dropped here while still borrowed 8 | | borrowed value does not live long enough 9 | | borrow later stored here 10 | | 11 | = note: this error originates in the attribute macro `entry` (in Nightly builds, run with -Z macro-backtrace for more info) 12 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs3.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430::interrupt::CriticalSection; 4 | use msp430_rt_macros::entry; 5 | 6 | fn init<'a>(cs: CriticalSection<'a>) -> CriticalSection<'a> { 7 | cs 8 | } 9 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 11 | fn main() -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs3.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `cs` does not live long enough 2 | --> tests/ui/entry_preinterrupt_return_cs3.rs:10:1 3 | | 4 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- 6 | | | | 7 | | | `cs` dropped here while still borrowed 8 | | borrowed value does not live long enough 9 | | borrow later stored here 10 | | 11 | = note: this error originates in the attribute macro `entry` (in Nightly builds, run with -Z macro-backtrace for more info) 12 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs_cloned.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430::interrupt::CriticalSection; 4 | use msp430_rt_macros::entry; 5 | 6 | fn init<'a>(cs: CriticalSection<'a>) -> CriticalSection<'a> { 7 | cs.clone() 8 | } 9 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 11 | fn main(_cs: CriticalSection) -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs_cloned.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `cs` does not live long enough 2 | --> tests/ui/entry_preinterrupt_return_cs_cloned.rs:10:1 3 | | 4 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- 6 | | | | 7 | | | `cs` dropped here while still borrowed 8 | | borrowed value does not live long enough 9 | | borrow later stored here 10 | | 11 | = note: this error originates in the attribute macro `entry` (in Nightly builds, run with -Z macro-backtrace for more info) 12 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs_cloned_static.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430::interrupt::CriticalSection; 4 | use msp430_rt_macros::entry; 5 | 6 | fn init(cs: CriticalSection<'static>) -> CriticalSection<'static> { 7 | cs.clone() 8 | } 9 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 11 | fn main(_cs: CriticalSection) -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs_cloned_static.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `cs` does not live long enough 2 | --> tests/ui/entry_preinterrupt_return_cs_cloned_static.rs:10:1 3 | | 4 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- 6 | | | | 7 | | | `cs` dropped here while still borrowed 8 | | borrowed value does not live long enough 9 | | argument requires that `cs` is borrowed for `'static` 10 | | 11 | = note: this error originates in the attribute macro `entry` (in Nightly builds, run with -Z macro-backtrace for more info) 12 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs_static.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430::interrupt::CriticalSection; 4 | use msp430_rt_macros::entry; 5 | 6 | fn init(cs: CriticalSection<'static>) -> CriticalSection<'static> { 7 | cs 8 | } 9 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 11 | fn main(_cs: CriticalSection) -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/entry_preinterrupt_return_cs_static.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `cs` does not live long enough 2 | --> tests/ui/entry_preinterrupt_return_cs_static.rs:10:1 3 | | 4 | 10 | #[entry(interrupt_enable(pre_interrupt = init))] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- 6 | | | | 7 | | | `cs` dropped here while still borrowed 8 | | borrowed value does not live long enough 9 | | argument requires that `cs` is borrowed for `'static` 10 | | 11 | = note: this error originates in the attribute macro `entry` (in Nightly builds, run with -Z macro-backtrace for more info) 12 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_args.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::{entry, interrupt}; 4 | 5 | #[entry] 6 | fn main() -> ! { 7 | loop {} 8 | } 9 | 10 | #[interrupt(arg)] 11 | fn TIM2() -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_args.stderr: -------------------------------------------------------------------------------- 1 | error: this attribute accepts no arguments 2 | --> tests/ui/interrupt_args.rs:10:1 3 | | 4 | 10 | #[interrupt(arg)] 5 | | ^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_bad_cs_import.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430::interrupt::CriticalSection as CritSec; 4 | use msp430_rt_macros::{entry, interrupt}; 5 | 6 | // If we use "msp430::interrupt::CriticalSection", we get unused import warning for CritSec, 7 | // which is correct but misleading b/c of interrupt sig requirements. 8 | fn arg(_cs: msp430::interrupt::CriticalSection) { 9 | /* initialize hardware */ 10 | } 11 | 12 | #[entry] 13 | fn main() -> ! { 14 | unimplemented!() 15 | } 16 | 17 | #[interrupt] 18 | fn TIM2(_cs: CritSec) { 19 | unimplemented!() 20 | } 21 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_bad_cs_import.stderr: -------------------------------------------------------------------------------- 1 | error: `#[interrupt]` handlers must have signature `[unsafe] fn([: CriticalSection]) [-> !]` 2 | --> tests/ui/interrupt_bad_cs_import.rs:18:1 3 | | 4 | 18 | fn TIM2(_cs: CritSec) { 5 | | ^^^^^^^^^^^^^^^^^^^^^ 6 | 7 | warning: unused import: `msp430::interrupt::CriticalSection as CritSec` 8 | --> tests/ui/interrupt_bad_cs_import.rs:3:5 9 | | 10 | 3 | use msp430::interrupt::CriticalSection as CritSec; 11 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 12 | | 13 | = note: `#[warn(unused_imports)]` on by default 14 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_bad_param.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::{entry, interrupt}; 4 | 5 | #[entry] 6 | fn main() -> ! { 7 | loop {} 8 | } 9 | 10 | #[interrupt] 11 | fn TIM2(i: u32) -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_bad_param.stderr: -------------------------------------------------------------------------------- 1 | error: `#[interrupt]` handlers must have signature `[unsafe] fn([: CriticalSection]) [-> !]` 2 | --> tests/ui/interrupt_bad_param.rs:11:1 3 | | 4 | 11 | fn TIM2(i: u32) -> ! { 5 | | ^^^^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_bad_ret.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::{entry, interrupt}; 4 | 5 | #[entry] 6 | fn main() -> ! { 7 | loop {} 8 | } 9 | 10 | #[interrupt] 11 | fn TIM2() -> bool { 12 | true 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_bad_ret.stderr: -------------------------------------------------------------------------------- 1 | error: `#[interrupt]` handlers must have signature `[unsafe] fn([: CriticalSection]) [-> !]` 2 | --> tests/ui/interrupt_bad_ret.rs:11:1 3 | | 4 | 11 | fn TIM2() -> bool { 5 | | ^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_extern_c.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::{entry, interrupt}; 4 | 5 | #[entry] 6 | fn main() -> ! { 7 | loop {} 8 | } 9 | 10 | #[interrupt] 11 | extern "C" fn TIM2() -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_extern_c.stderr: -------------------------------------------------------------------------------- 1 | error: `#[interrupt]` handlers must have signature `[unsafe] fn([: CriticalSection]) [-> !]` 2 | --> tests/ui/interrupt_extern_c.rs:11:1 3 | | 4 | 11 | extern "C" fn TIM2() -> ! { 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_wrong_import.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | // Incorrect w/ bad error message- use of undeclared crate or module `interrupt`. 4 | use msp430_rt_macros::{entry, interrupt}; 5 | 6 | #[entry] 7 | fn main() -> ! { 8 | unimplemented!() 9 | } 10 | 11 | #[interrupt] 12 | fn TIM2() { 13 | unimplemented!() 14 | } 15 | -------------------------------------------------------------------------------- /macros/tests/ui/interrupt_wrong_import.stderr: -------------------------------------------------------------------------------- 1 | error[E0433]: failed to resolve: use of undeclared crate or module `interrupt` 2 | --> tests/ui/interrupt_wrong_import.rs:11:1 3 | | 4 | 11 | #[interrupt] 5 | | ^^^^^^^^^^^^ use of undeclared crate or module `interrupt` 6 | | 7 | = note: this error originates in the attribute macro `interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error[E0658]: msp430-interrupt ABI is experimental and subject to change 10 | --> tests/ui/interrupt_wrong_import.rs:11:1 11 | | 12 | 11 | #[interrupt] 13 | | ^^^^^^^^^^^^ 14 | | 15 | = note: see issue #38487 for more information 16 | = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable 17 | = note: this error originates in the attribute macro `interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 18 | 19 | error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target 20 | --> tests/ui/interrupt_wrong_import.rs:11:1 21 | | 22 | 11 | #[interrupt] 23 | | ^^^^^^^^^^^^ 24 | | 25 | = note: this error originates in the attribute macro `interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 26 | -------------------------------------------------------------------------------- /macros/tests/ui/preinit_arg.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::{entry, pre_init}; 4 | 5 | #[entry] 6 | fn bar() -> ! { 7 | loop {} 8 | } 9 | 10 | #[pre_init(arg)] 11 | unsafe fn foo() {} 12 | -------------------------------------------------------------------------------- /macros/tests/ui/preinit_arg.stderr: -------------------------------------------------------------------------------- 1 | error: this attribute accepts no arguments 2 | --> tests/ui/preinit_arg.rs:10:1 3 | | 4 | 10 | #[pre_init(arg)] 5 | | ^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `pre_init` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /macros/tests/ui/preinit_bad_param.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::{entry, pre_init}; 4 | 5 | #[entry] 6 | fn bar() -> ! { 7 | loop {} 8 | } 9 | 10 | #[pre_init] 11 | unsafe fn foo(i: u32) {} 12 | -------------------------------------------------------------------------------- /macros/tests/ui/preinit_bad_param.stderr: -------------------------------------------------------------------------------- 1 | error: `#[pre_init]` function must have signature `unsafe fn()` 2 | --> tests/ui/preinit_bad_param.rs:11:1 3 | | 4 | 11 | unsafe fn foo(i: u32) {} 5 | | ^^^^^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /macros/tests/ui/preinit_bad_ret.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::{entry, pre_init}; 4 | 5 | #[entry] 6 | fn bar() -> ! { 7 | loop {} 8 | } 9 | 10 | #[pre_init] 11 | unsafe fn foo() -> u32 { 12 | 3 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/preinit_bad_ret.stderr: -------------------------------------------------------------------------------- 1 | error: `#[pre_init]` function must have signature `unsafe fn()` 2 | --> tests/ui/preinit_bad_ret.rs:11:1 3 | | 4 | 11 | unsafe fn foo() -> u32 { 5 | | ^^^^^^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /macros/tests/ui/preinit_safe.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::{entry, pre_init}; 4 | 5 | #[entry] 6 | fn bar() -> ! { 7 | loop {} 8 | } 9 | 10 | #[pre_init] 11 | fn foo() {} 12 | -------------------------------------------------------------------------------- /macros/tests/ui/preinit_safe.stderr: -------------------------------------------------------------------------------- 1 | error: `#[pre_init]` function must have signature `unsafe fn()` 2 | --> tests/ui/preinit_safe.rs:11:1 3 | | 4 | 11 | fn foo() {} 5 | | ^^^^^^^^ 6 | -------------------------------------------------------------------------------- /macros/tests/ui/recursion.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::{entry, interrupt}; 4 | 5 | #[entry] 6 | fn main(cs: CriticalSection) -> ! { 7 | main(cs) 8 | } 9 | 10 | #[interrupt] 11 | fn DefaultHandler(cs: CriticalSection) -> ! { 12 | DefaultHandler(cs) 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/recursion.stderr: -------------------------------------------------------------------------------- 1 | error[E0425]: cannot find function `main` in this scope 2 | --> tests/ui/recursion.rs:7:5 3 | | 4 | 7 | main(cs) 5 | | ^^^^ not found in this scope 6 | 7 | error[E0425]: cannot find function, tuple struct or tuple variant `DefaultHandler` in this scope 8 | --> tests/ui/recursion.rs:12:5 9 | | 10 | 12 | DefaultHandler(cs) 11 | | ^^^^^^^^^^^^^^ not found in this scope 12 | 13 | error[E0658]: msp430-interrupt ABI is experimental and subject to change 14 | --> tests/ui/recursion.rs:10:1 15 | | 16 | 10 | #[interrupt] 17 | | ^^^^^^^^^^^^ 18 | | 19 | = note: see issue #38487 for more information 20 | = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable 21 | = note: this error originates in the attribute macro `interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 22 | 23 | error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target 24 | --> tests/ui/recursion.rs:10:1 25 | | 26 | 10 | #[interrupt] 27 | | ^^^^^^^^^^^^ 28 | | 29 | = note: this error originates in the attribute macro `interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 30 | -------------------------------------------------------------------------------- /macros/tests/ui/recursion2.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use msp430_rt_macros::{entry, interrupt}; 4 | 5 | #[entry] 6 | fn main(cs: CriticalSection) -> ! { 7 | main() 8 | } 9 | 10 | #[interrupt] 11 | fn DefaultHandler(cs: CriticalSection) -> ! { 12 | DefaultHandler() 13 | } 14 | -------------------------------------------------------------------------------- /macros/tests/ui/recursion2.stderr: -------------------------------------------------------------------------------- 1 | error[E0425]: cannot find function `main` in this scope 2 | --> tests/ui/recursion2.rs:7:5 3 | | 4 | 7 | main() 5 | | ^^^^ not found in this scope 6 | 7 | error[E0425]: cannot find function, tuple struct or tuple variant `DefaultHandler` in this scope 8 | --> tests/ui/recursion2.rs:12:5 9 | | 10 | 12 | DefaultHandler() 11 | | ^^^^^^^^^^^^^^ not found in this scope 12 | 13 | error[E0658]: msp430-interrupt ABI is experimental and subject to change 14 | --> tests/ui/recursion2.rs:10:1 15 | | 16 | 10 | #[interrupt] 17 | | ^^^^^^^^^^^^ 18 | | 19 | = note: see issue #38487 for more information 20 | = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable 21 | = note: this error originates in the attribute macro `interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 22 | 23 | error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target 24 | --> tests/ui/recursion2.rs:10:1 25 | | 26 | 10 | #[interrupt] 27 | | ^^^^^^^^^^^^ 28 | | 29 | = note: this error originates in the attribute macro `interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 30 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-04-29" 3 | components = ["rust-src"] 4 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Startup code and minimal runtime for MSP430 microcontrollers 2 | //! 3 | //! This crate is based on [cortex-m-rt](https://docs.rs/cortex-m-rt) 4 | //! crate by Jorge Aparicio (@japaric). 5 | //! 6 | //! This crate contains all the required parts to build a `no_std` application (binary crate) that 7 | //! targets a MSP430 microcontroller. 8 | //! 9 | //! # Features 10 | //! 11 | //! This crates takes care of: 12 | //! 13 | //! - The memory layout of the program. In particular, it populates the vector table so the device 14 | //! can boot correctly, and properly dispatch interrupts. 15 | //! 16 | //! - Initializing `static` variables before the program entry point. 17 | //! 18 | //! This crate also provides the following attributes: 19 | //! 20 | //! - `#[entry]` to declare the entry point of the program 21 | //! - `#[pre_init]` to run code *before* `static` variables are initialized 22 | //! 23 | //! This crate also implements a related attribute called `#[interrupt]`, which allows you 24 | //! to define interrupt handlers. However, since which interrupts are available depends on the 25 | //! microcontroller in use, this attribute should be re-exported and used from a PAC crate. 26 | //! 27 | //! The documentation for these attributes can be found in the [Attribute Macros](#attributes) 28 | //! section. 29 | //! 30 | //! # Requirements 31 | //! 32 | //! ## `memory.x` 33 | //! 34 | //! This crate expects the user, or some other crate, to provide the memory layout of the target 35 | //! device via a linker script named `memory.x`. This section covers the contents of `memory.x` 36 | //! 37 | //! ### `MEMORY` 38 | //! 39 | //! The linker script must specify the memory available in the device as, at least, three `MEMORY` 40 | //! regions: one named `ROM`, one named `RAM`, and one named `VECTORS`. The `.text` and `.rodata` 41 | //! sections of the program will be placed in the `ROM` region, whereas the `.bss` and `.data` 42 | //! sections, as well as the heap, will be placed in the `RAM` region. The `.vector_table` section, 43 | //! which including the interrupt vectors and reset address, will be placed in the `VECTORS` 44 | //! region at the end of flash. The `ROM` region should end at the address the `VECTORS` region 45 | //! begins. 46 | //! 47 | //! A `VECTORS` region is required because between (_and within_) msp430 device families: 48 | //! * Devices do not have a constant single vector table size. 49 | //! * Devices do not have a constant vector table start address. 50 | //! Consult your Family User's Guide (e.g. MSP430x5xx Family User's Guide, slau208), 51 | //! particularly the Memory Map section, and your device's datasheet (e.g. msp430g2553) for 52 | //! information on vector table layout and size. _You may be able to get more program space if 53 | //! your device's datasheet explicitly marks a contiguous set of vectors as unused!_ 54 | //! 55 | //! 56 | //! ``` text 57 | //! /* Linker script for the MSP430G2553 */ 58 | //! MEMORY 59 | //! { 60 | //! RAM : ORIGIN = 0x0200, LENGTH = 0x0200 61 | //! ROM : ORIGIN = 0xC000, LENGTH = 0x3FE0 62 | //! VECTORS : ORIGIN = 0xFFE0, LENGTH = 0x20 63 | //! } 64 | //! ``` 65 | //! 66 | //! By default, the linker will check that the address after the vector table is 0x10000 to 67 | //! ensure that the vector table is placed correctly. For special cases like bootloader 68 | //! setups, you might want to place the vector table somewhere else. For theses cases, 69 | //! you can re-configure the expected end address by setting `__VECTORS_END_ADDR` in your 70 | //! `memory.x` file. 71 | //! 72 | //! # An example 73 | //! 74 | //! This section presents a minimal application built on top of `msp430-rt`. 75 | //! 76 | //! ``` ignore 77 | //! // IMPORTANT the standard `main` interface is not used because it requires nightly 78 | //! #![no_main] 79 | //! #![no_std] 80 | //! 81 | //! extern crate msp430_rt; 82 | //! // Simple panic handler that infinitely loops. 83 | //! extern crate panic_msp430; 84 | //! 85 | //! use msp430_rt::entry; 86 | //! 87 | //! // use `main` as the entry point of this application 88 | //! // `main` is not allowed to return 89 | //! #[entry] 90 | //! fn main() -> ! { 91 | //! // initialization 92 | //! 93 | //! loop { 94 | //! // application logic 95 | //! } 96 | //! } 97 | //! 98 | //! ``` 99 | //! 100 | //! To actually build this program you need to place a `memory.x` linker script somewhere the linker 101 | //! can find it, e.g. in the current directory; and then link the program using `msp430-rt`'s 102 | //! linker script: `link.x`. The required steps are shown below: 103 | //! 104 | //! ``` text 105 | //! $ cat > memory.x < ! { 212 | //! /* user code */ 213 | //! } 214 | //! 215 | //! // expands into 216 | //! 217 | //! #[no_mangle] 218 | //! extern "C" fn main() -> ! { 219 | //! /* user code */ 220 | //! } 221 | //! ``` 222 | //! 223 | //! The unmangled `main` symbol must have signature `extern "C" fn() -> !` or its invocation from 224 | //! `Reset` will result in undefined behavior. 225 | //! 226 | //! ## Incorporating device specific interrupts 227 | //! 228 | //! This section covers how an external crate can insert device specific interrupt handlers into the 229 | //! vector table. Most users don't need to concern themselves with these details, but if you are 230 | //! interested in how device crates generated using `svd2rust` integrate with `msp430-rt` read on. 231 | //! 232 | //! The information in this section applies when the `"device"` feature has been enabled. 233 | //! 234 | //! ### `__INTERRUPTS` 235 | //! 236 | //! The external crate must provide the interrupts portion of the vector table via a `static` 237 | //! variable named`__INTERRUPTS` (unmangled) that must be placed in the `.vector_table.interrupts` 238 | //! section of its object file. 239 | //! 240 | //! This `static` variable will be placed at `ORIGIN(VECTORS)`. This address corresponds to the 241 | //! spot where IRQ0 (IRQ number 0) is located. 242 | //! 243 | //! To conform to the MSP430 ABI `__INTERRUPTS` must be an array of function pointers; some spots 244 | //! in this array may need to be set to 0 if they are marked as *reserved* in the data sheet / 245 | //! reference manual. We recommend using a `union` to set the reserved spots to `0`; `None` 246 | //! (`Option`) may also work but it's not guaranteed that the `None` variant will *always* be 247 | //! represented by the value `0`. 248 | //! 249 | //! Let's illustrate with an artificial example where a device only has two interrupt: `Foo`, with 250 | //! IRQ number = 2, and `Bar`, with IRQ number = 4. 251 | //! 252 | //! ``` ignore 253 | //! union Vector { 254 | //! handler: extern "msp430-interrupt" fn(), 255 | //! reserved: usize, 256 | //! } 257 | //! 258 | //! extern "msp430-interrupt" { 259 | //! fn Foo(); 260 | //! fn Bar(); 261 | //! } 262 | //! 263 | //! #[link_section = ".vector_table.interrupts"] 264 | //! #[no_mangle] 265 | //! static __INTERRUPTS: [Vector; 15] = [ 266 | //! // 0-1: Reserved 267 | //! Vector { reserved: 0 }, 268 | //! Vector { reserved: 0 }, 269 | //! 270 | //! // 2: Foo 271 | //! Vector { handler: Foo }, 272 | //! 273 | //! // 3: Reserved 274 | //! Vector { reserved: 0 }, 275 | //! 276 | //! // 4: Bar 277 | //! Vector { handler: Bar }, 278 | //! 279 | //! // 5-14: Reserved 280 | //! Vector { reserved: 0 }, 281 | //! Vector { reserved: 0 }, 282 | //! Vector { reserved: 0 }, 283 | //! Vector { reserved: 0 }, 284 | //! Vector { reserved: 0 }, 285 | //! Vector { reserved: 0 }, 286 | //! Vector { reserved: 0 }, 287 | //! Vector { reserved: 0 }, 288 | //! Vector { reserved: 0 }, 289 | //! Vector { reserved: 0 }, 290 | //! ]; 291 | //! ``` 292 | //! 293 | //! ### `device.x` 294 | //! 295 | //! Linking in `__INTERRUPTS` creates a bunch of undefined references. If the user doesn't set a 296 | //! handler for *all* the device specific interrupts then linking will fail with `"undefined 297 | //! reference"` errors. 298 | //! 299 | //! We want to provide a default handler for all the interrupts while still letting the user 300 | //! individually override each interrupt handler. In C projects, this is usually accomplished using 301 | //! weak aliases declared in external assembly files. In Rust, we could achieve something similar 302 | //! using `global_asm!`, but that's an unstable feature. 303 | //! 304 | //! A solution that doesn't require `global_asm!` or external assembly files is to use the `PROVIDE` 305 | //! command in a linker script to create the weak aliases. This is the approach that `msp430-rt` 306 | //! uses; when the `"device"` feature is enabled `msp430-rt`'s linker script (`link.x`) depends on 307 | //! a linker script named `device.x`. The crate that provides `__INTERRUPTS` must also provide this 308 | //! file. 309 | //! 310 | //! For our running example the `device.x` linker script looks like this: 311 | //! 312 | //! ``` text 313 | //! /* device.x */ 314 | //! PROVIDE(Foo = DefaultHandler); 315 | //! PROVIDE(Bar = DefaultHandler); 316 | //! ``` 317 | //! 318 | //! This weakly aliases both `Foo` and `Bar`. `DefaultHandler` is the default interrupt handler. 319 | //! 320 | //! Because this linker script is provided by a dependency of the final application the dependency 321 | //! must contain build script that puts `device.x` somewhere the linker can find. An example of such 322 | //! build script is shown below: 323 | //! 324 | //! ``` ignore 325 | //! use std::{env, fs::File, io::Write, path::PathBuf}; 326 | //! 327 | //! fn main() { 328 | //! // Put the linker script somewhere the linker can find it 329 | //! let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 330 | //! File::create(out.join("device.x")) 331 | //! .unwrap() 332 | //! .write_all(include_bytes!("device.x")) 333 | //! .unwrap(); 334 | //! println!("cargo:rustc-link-search={}", out.display()); 335 | //! } 336 | //! ``` 337 | //! 338 | //! [attr-entry]: attr.entry.html 339 | //! [attr-exception]: attr.exception.html 340 | //! [attr-pre_init]: attr.pre_init.html 341 | 342 | #![deny(missing_docs)] 343 | #![feature(abi_msp430_interrupt)] 344 | #![no_std] 345 | 346 | use msp430::asm; 347 | pub use msp430_rt_macros::interrupt; 348 | pub use msp430_rt_macros::{entry, pre_init}; 349 | 350 | /// Returns a pointer to the start of the heap 351 | /// 352 | /// The returned pointer is guaranteed to be 4-byte aligned. 353 | #[inline] 354 | pub fn heap_start() -> *mut u32 { 355 | extern "C" { 356 | static mut __sheap: u32; 357 | } 358 | 359 | unsafe { &mut __sheap } 360 | } 361 | 362 | extern "msp430-interrupt" { 363 | fn Reset() -> !; 364 | } 365 | 366 | #[link_section = ".__RESET_VECTOR"] 367 | #[no_mangle] 368 | static __RESET_VECTOR: unsafe extern "msp430-interrupt" fn() -> ! = Reset; 369 | 370 | #[no_mangle] 371 | unsafe extern "C" fn PreInit_() {} 372 | 373 | #[no_mangle] 374 | extern "msp430-interrupt" fn DefaultHandler_() -> ! { 375 | // The interrupts are already disabled here. 376 | loop { 377 | // Prevent optimizations that can remove this loop. 378 | asm::barrier(); 379 | } 380 | } 381 | 382 | // Interrupts for generic application 383 | #[cfg(not(feature = "device"))] 384 | #[no_mangle] 385 | #[link_section = ".vector_table.interrupts"] 386 | static __INTERRUPTS: [unsafe extern "msp430-interrupt" fn(); 15] = [{ 387 | extern "msp430-interrupt" { 388 | fn DefaultHandler(); 389 | } 390 | 391 | DefaultHandler 392 | }; 15]; 393 | --------------------------------------------------------------------------------