├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── kaze ├── Cargo.toml └── src │ ├── code_writer.rs │ ├── graph.rs │ ├── graph │ ├── constant.rs │ ├── context.rs │ ├── internal_signal.rs │ ├── mem.rs │ ├── module.rs │ ├── register.rs │ ├── signal.rs │ └── sugar.rs │ ├── lib.rs │ ├── runtime.rs │ ├── runtime │ ├── tracing.rs │ └── tracing │ │ └── vcd.rs │ ├── sim.rs │ ├── sim │ ├── compiler.rs │ └── ir.rs │ ├── state_elements.rs │ ├── validation.rs │ ├── verilog.rs │ └── verilog │ ├── compiler.rs │ └── ir.rs └── sim-tests ├── Cargo.toml ├── build.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.1.19] - 2021-03-14 10 | ### Fixed 11 | - Bits indexing bug in verilog gen when indexing results in a scalar `Signal` 12 | 13 | ## [0.1.18] - 2020-12-13 14 | ### Fixed 15 | - Duplicate trace member names in Rust sim gen in some cases 16 | 17 | ## [0.1.17] - 2020-12-13 18 | ### Added 19 | - Cycle delay helpers to `Signal` API (`reg_next`, `reg_next_with_default`) 20 | 21 | ## [0.1.16] - 2020-12-13 22 | ### Fixed 23 | - Stack overflow bugs that were still present when cloning IR expressions in the Rust sim compiler 24 | - Broken changelog link for [0.1.15] 25 | 26 | ## [0.1.15] - 2020-12-07 27 | ### Changed 28 | - Reduced the amount of temporary bindings used in the generated sim code, which reduces rustc compile time dramatically! 29 | - Mark generated Rust simulator impl's with `#[automatically_derived]` to skip expensive lints during compilation 30 | 31 | ## [0.1.14] - 2020-11-29 32 | ### Changed 33 | - Doc comments link to items by name instead of by path, as this is now supported as of [Rust 1.48.0](https://blog.rust-lang.org/2020/11/19/Rust-1.48.html). 34 | - Dependencies updated to latest versions 35 | 36 | ### Fixed 37 | - Stack overflow bugs by eliminating recursive graph traversals 38 | - Invalid/outdated code in README.md 39 | - Code formatting lints in kaze-sim-tests 40 | 41 | ## [0.1.13] - 2020-10-12 42 | ### Added 43 | - Tracing for generated sim modules 44 | 45 | ## [0.1.12] - 2020-08-29 46 | ### Fixed 47 | - Hack which relied on an automatically-derived `Default` impl to default-initialize most sim struct fields, which was no longer valid after `Mem` was implemented. Technically this is a breaking API change, but since `Default` was never meant to be used directly, user code shouldn't contain this. 48 | 49 | ## [0.1.11] - 2020-07-19 50 | ### Fixed 51 | - Indexing scalars produced invalid Verilog code 52 | 53 | ## [0.1.10] - 2020-07-17 54 | ### Added 55 | - Signed multiplication op to `Signal` API (`mul_signed`) 56 | 57 | ## [0.1.9] - 2020-07-01 58 | ### Added 59 | - Unsigned multiplication op to `Signal` API (`mul`) 60 | 61 | ## [0.1.8] - 2020-06-28 62 | ### Fixed 63 | - Clarified docs for `Mem` read port values when `enable` is not asserted 64 | - Various small doc fixes/regularizations 65 | 66 | ### Changed 67 | - Added more `if_` sugar variants for tuples with up to 12 elements (previously 8) 68 | 69 | ## [0.1.7] - 2020-03-27 70 | ### Added 71 | - Complete Verilog codegen 72 | - Validation tests for Verilog codegen 73 | - `Context::modules` method to borrow a `Context`'s `Module`s, primarily useful for iterating over them for generating Verilog code 74 | 75 | ### Changed 76 | - Simultaneous reads/writes to the same location in a `Mem` on a given cycle results in reads returning the value previously at that memory location, **not** the newly-written value 77 | 78 | ### Fixed 79 | - Wrong publish date for 0.1.6 in changelog 80 | 81 | ## [0.1.6] - 2020-02-22 82 | ### Fixed 83 | - Broken default value for `Mem`s with single-bit elements in generated simulators 84 | 85 | ## [0.1.5] - 2020-02-15 86 | ### Added 87 | - `Mem` construct for creating synchronous memories 88 | 89 | ### Changed 90 | - Internal sim compiler refactorings to simplify/unify some implementation details 91 | 92 | ### Fixed 93 | - Missing shift doc tests 94 | 95 | ## [0.1.4] - 2020-02-09 96 | ### Fixed 97 | - Link errors in top-level docs 98 | - Error in `rhs_arithmetic` docs for underflow case 99 | 100 | ## [0.1.3] - 2020-02-09 101 | ### Added 102 | - Subtraction and shift ops to `Signal` API (`sub`, `shl`, `shr`, `shr_arithmetic`) 103 | 104 | ### Changed 105 | - Small readme edits/link fixes 106 | 107 | ### Fixed 108 | - Module naming convention in top-level docs 109 | 110 | ## [0.1.2] - 2020-02-02 111 | ### Added 112 | - Implement Eq/PartialEq/Hash for `Signal` (note that these are not documented/tested, which we might want to revisit later) 113 | 114 | ### Changed 115 | - Switched naming convention for `Module`s from `snake_case` to `CamelCase` 116 | - Redesigned entire (unstable) sugar API 117 | - Small changelog formatting fixes 118 | 119 | ### Fixed 120 | - Removed the last remaining `unsafe` block in the API impl 121 | 122 | ## [0.1.1] - 2020-01-30 123 | ### Added 124 | - Signed comparison ops to `Signal` API (`lt_signed`, `le_signed`, `gt_signed`, `ge_signed`) 125 | - Error check for `concat` to ensure its input `Signal`s belong to the same `Module` 126 | - This changelog 127 | 128 | ### Changed 129 | - Small typo/link fixes in API docs 130 | - Small clarifications in top-level docs/examples 131 | - Broken link fixes in README 132 | - Changed tag format to be `vx.y.z` instead of `x.y.z`, and not use annotated tags 133 | 134 | ## [0.1.0] - 2020-01-25 (Initial release) 135 | 136 | [Unreleased]: https://github.com/yupferris/kaze/compare/v0.1.19...HEAD 137 | [0.1.19]: https://github.com/yupferris/kaze/compare/v0.1.18..v0.1.19 138 | [0.1.18]: https://github.com/yupferris/kaze/compare/v0.1.17..v0.1.18 139 | [0.1.17]: https://github.com/yupferris/kaze/compare/v0.1.16..v0.1.17 140 | [0.1.16]: https://github.com/yupferris/kaze/compare/v0.1.15..v0.1.16 141 | [0.1.15]: https://github.com/yupferris/kaze/compare/v0.1.14..v0.1.15 142 | [0.1.14]: https://github.com/yupferris/kaze/compare/v0.1.13..v0.1.14 143 | [0.1.13]: https://github.com/yupferris/kaze/compare/v0.1.12..v0.1.13 144 | [0.1.12]: https://github.com/yupferris/kaze/compare/v0.1.11..v0.1.12 145 | [0.1.11]: https://github.com/yupferris/kaze/compare/v0.1.10..v0.1.11 146 | [0.1.10]: https://github.com/yupferris/kaze/compare/v0.1.9..v0.1.10 147 | [0.1.9]: https://github.com/yupferris/kaze/compare/v0.1.8..v0.1.9 148 | [0.1.8]: https://github.com/yupferris/kaze/compare/v0.1.7..v0.1.8 149 | [0.1.7]: https://github.com/yupferris/kaze/compare/v0.1.6..v0.1.7 150 | [0.1.6]: https://github.com/yupferris/kaze/compare/v0.1.5..v0.1.6 151 | [0.1.5]: https://github.com/yupferris/kaze/compare/v0.1.4..v0.1.5 152 | [0.1.4]: https://github.com/yupferris/kaze/compare/v0.1.3..v0.1.4 153 | [0.1.3]: https://github.com/yupferris/kaze/compare/v0.1.2..v0.1.3 154 | [0.1.2]: https://github.com/yupferris/kaze/compare/v0.1.1..v0.1.2 155 | [0.1.1]: https://github.com/yupferris/kaze/compare/v0.1.0..v0.1.1 156 | [0.1.0]: https://github.com/yupferris/kaze/releases/tag/v0.1.0 157 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "kaze", 5 | "sim-tests", 6 | ] 7 | -------------------------------------------------------------------------------- /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 2019-2021 Jake "ferris" Taylor 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) 2019-2021 Jake "ferris" Taylor 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 | # kaze [風](https://jisho.org/search/%E9%A2%A8%20%23kanji) 2 | 3 | An [HDL](https://en.wikipedia.org/wiki/Hardware_description_language) embedded in [Rust](https://www.rust-lang.org/). 4 | 5 | [github](https://github.com/yupferris/kaze) 6 | [crates.io](https://crates.io/crates/kaze) 7 | [docs.rs](https://docs.rs/kaze) 8 | [license](#license) 9 | 10 | kaze provides an API to describe `Module`s composed of `Signal`s, which can then be used to generate Rust simulator code or Verilog modules. 11 | 12 | kaze's API is designed to be as minimal as possible while still being expressive. 13 | It's designed to prevent the user from being able to describe buggy or incorrect hardware as much as possible. 14 | This enables a user to hack on designs fearlessly, while the API and generators ensure that these designs are sound. 15 | 16 | ## Usage 17 | 18 | ```toml 19 | [dependencies] 20 | kaze = "0.1" 21 | ``` 22 | 23 | ## Example 24 | 25 | ```rust 26 | use kaze::*; 27 | 28 | fn main() -> std::io::Result<()> { 29 | // Create a context, which will contain our module(s) 30 | let c = Context::new(); 31 | 32 | // Create a module 33 | let inverter = c.module("Inverter"); 34 | let i = inverter.input("i", 1); // 1-bit input 35 | inverter.output("o", !i); // Output inverted input 36 | 37 | // Generate Rust simulator code 38 | sim::generate(inverter, sim::GenerationOptions::default(), std::io::stdout())?; 39 | 40 | // Generate Verilog code 41 | verilog::generate(inverter, std::io::stdout())?; 42 | 43 | Ok(()) 44 | } 45 | ``` 46 | 47 | ## Releases 48 | 49 | See [changelog](https://github.com/yupferris/kaze/blob/master/CHANGELOG.md) for release information. 50 | 51 | ## License 52 | 53 | Licensed under either of 54 | 55 | * Apache License, Version 2.0 56 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 57 | * MIT license 58 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 59 | 60 | at your option. 61 | 62 | ## Contribution 63 | 64 | Unless you explicitly state otherwise, any contribution intentionally submitted 65 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 66 | dual licensed as above, without any additional terms or conditions. 67 | -------------------------------------------------------------------------------- /kaze/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kaze" 3 | version = "0.1.19" # Must be kept up-to-date with html_root_url in lib.rs 4 | authors = ["Jake \"ferris\" Taylor "] 5 | edition = "2018" 6 | description = "An HDL embedded in Rust" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/yupferris/kaze" 9 | readme = "../README.md" 10 | keywords = ["hdl"] 11 | categories = ["development-tools", "emulators", "simulation"] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | typed-arena = "2.0.1" 17 | vcd = "0.6.1" 18 | -------------------------------------------------------------------------------- /kaze/src/code_writer.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Result, Write}; 2 | 3 | pub struct CodeWriter { 4 | w: W, 5 | indent_level: u32, 6 | } 7 | 8 | impl CodeWriter { 9 | pub fn new(w: W) -> CodeWriter { 10 | CodeWriter { w, indent_level: 0 } 11 | } 12 | 13 | pub fn indent(&mut self) { 14 | self.indent_level += 1; 15 | } 16 | 17 | pub fn unindent(&mut self) { 18 | if self.indent_level == 0 { 19 | panic!("Indent level underflow"); 20 | } 21 | self.indent_level -= 1; 22 | } 23 | 24 | pub fn append_indent(&mut self) -> Result<()> { 25 | for _ in 0..self.indent_level { 26 | write!(self.w, " ")?; 27 | } 28 | Ok(()) 29 | } 30 | 31 | pub fn append_newline(&mut self) -> Result<()> { 32 | writeln!(self.w, "")?; 33 | Ok(()) 34 | } 35 | 36 | pub fn append(&mut self, s: &str) -> Result<()> { 37 | write!(self.w, "{}", s)?; 38 | Ok(()) 39 | } 40 | 41 | pub fn append_line(&mut self, s: &str) -> Result<()> { 42 | self.append_indent()?; 43 | self.append(s)?; 44 | self.append_newline()?; 45 | Ok(()) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /kaze/src/graph.rs: -------------------------------------------------------------------------------- 1 | mod constant; 2 | mod context; 3 | pub(crate) mod internal_signal; 4 | mod mem; 5 | mod module; 6 | mod register; 7 | mod signal; 8 | mod sugar; 9 | 10 | pub use constant::*; 11 | pub use context::*; 12 | pub use mem::*; 13 | pub use module::*; 14 | pub use register::*; 15 | pub use signal::*; 16 | pub use sugar::*; 17 | -------------------------------------------------------------------------------- /kaze/src/graph/constant.rs: -------------------------------------------------------------------------------- 1 | /// A container for different types of integer constant values. 2 | /// 3 | /// This type isn't typically used explicitly, as the graph API always takes `Constant` parameters as `Into`, and `Constant` implements `From` for most of Rust's unsigned integer types. If an API entry point requires a `Constant`, prefer passing integer values/literals directly. 4 | /// 5 | /// # Examples 6 | /// 7 | /// ``` 8 | /// use kaze::*; 9 | /// 10 | /// let p = Context::new(); 11 | /// 12 | /// let m = p.module("m", "MyModule"); 13 | /// 14 | /// let a = m.lit(true, 16); 15 | /// let b = m.lit(0xdeadbeefu32, 47); 16 | /// let c = m.reg("data", 20); 17 | /// c.default_value(5u32); 18 | /// let d = m.lit(42u32, 8); 19 | /// ``` 20 | #[derive(Clone)] 21 | pub enum Constant { 22 | /// Contains a boolean value 23 | Bool(bool), 24 | /// Contains an unsigned, 32-bit value 25 | U32(u32), 26 | /// Contains an unsigned, 64-bit value 27 | U64(u64), 28 | /// Contains an unsigned, 128-bit value 29 | U128(u128), 30 | } 31 | 32 | impl Constant { 33 | // TODO: Specific tests? I don't necessarily want to make this part of the public API at least. 34 | pub(super) fn required_bits(&self) -> u32 { 35 | match *self { 36 | Constant::Bool(value) => 32 - (value as u32).leading_zeros(), 37 | Constant::U32(value) => 32 - value.leading_zeros(), 38 | Constant::U64(value) => 64 - value.leading_zeros(), 39 | Constant::U128(value) => 128 - value.leading_zeros(), 40 | } 41 | } 42 | 43 | pub(crate) fn numeric_value(&self) -> u128 { 44 | match *self { 45 | Constant::Bool(value) => value.into(), 46 | Constant::U32(value) => value.into(), 47 | Constant::U64(value) => value.into(), 48 | Constant::U128(value) => value, 49 | } 50 | } 51 | } 52 | 53 | impl From for Constant { 54 | fn from(value: bool) -> Self { 55 | Constant::Bool(value) 56 | } 57 | } 58 | 59 | impl From for Constant { 60 | fn from(value: u8) -> Self { 61 | Constant::U32(value as _) 62 | } 63 | } 64 | 65 | impl From for Constant { 66 | fn from(value: u16) -> Self { 67 | Constant::U32(value as _) 68 | } 69 | } 70 | 71 | impl From for Constant { 72 | fn from(value: u32) -> Self { 73 | Constant::U32(value) 74 | } 75 | } 76 | 77 | impl From for Constant { 78 | fn from(value: u64) -> Self { 79 | Constant::U64(value) 80 | } 81 | } 82 | 83 | impl From for Constant { 84 | fn from(value: u128) -> Self { 85 | Constant::U128(value) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /kaze/src/graph/context.rs: -------------------------------------------------------------------------------- 1 | use super::internal_signal::*; 2 | use super::mem::*; 3 | use super::module::*; 4 | use super::register::*; 5 | 6 | use typed_arena::Arena; 7 | 8 | use std::cell::RefCell; 9 | 10 | // TODO: Move, doc 11 | pub trait ModuleParent<'a> { 12 | // TODO: Update doc 13 | /// Creates a new [`Module`] called `name` in this `Context`. 14 | /// 15 | /// Conventionally, `name` should be `CamelCase`, though this is not enforced. 16 | /// 17 | /// # Panics 18 | /// 19 | /// Panics if a [`Module`] with the same `name` already exists in this `Context`. 20 | /// 21 | /// # Examples 22 | /// 23 | /// ``` 24 | /// use kaze::*; 25 | /// 26 | /// let c = Context::new(); 27 | /// 28 | /// let my_module = c.module("my_module", "MyModule"); 29 | /// let another_mod = c.module("another_mod", "AnotherMod"); 30 | /// ``` 31 | /// 32 | /// The following example panics by creating a `Module` with the same `name` as a previously-created `Module` in the same `Context`: 33 | /// 34 | /// ```should_panic 35 | /// use kaze::*; 36 | /// 37 | /// let c = Context::new(); 38 | /// 39 | /// let _ = c.module("a", "A"); // Unique name, OK 40 | /// let _ = c.module("b", "B"); // Unique name, OK 41 | /// 42 | /// let _ = c.module("a", "A"); // Non-unique name, panic! 43 | /// ``` 44 | fn module(&'a self, instance_name: impl Into, name: impl Into) -> &Module; 45 | } 46 | 47 | /// A top-level container/owner object for a [`Module`] graph. 48 | /// 49 | /// A `Context` owns all parts of a module graph, and provides an API for creating [`Module`] objects. 50 | /// 51 | /// # Examples 52 | /// 53 | /// ``` 54 | /// use kaze::*; 55 | /// 56 | /// let c = Context::new(); 57 | /// 58 | /// let m = c.module("m", "MyModule"); 59 | /// m.output("out", m.input("in", 1)); 60 | /// ``` 61 | #[must_use] 62 | pub struct Context<'a> { 63 | pub(super) module_arena: Arena>, 64 | pub(super) input_data_arena: Arena>, 65 | pub(super) input_arena: Arena>, 66 | pub(super) output_data_arena: Arena>, 67 | pub(super) output_arena: Arena>, 68 | pub(super) signal_arena: Arena>, 69 | pub(super) register_data_arena: Arena>, 70 | pub(super) register_arena: Arena>, 71 | pub(super) mem_arena: Arena>, 72 | 73 | pub(super) modules: RefCell>>, 74 | } 75 | 76 | impl<'a> Context<'a> { 77 | /// Creates a new, empty `Context`. 78 | /// 79 | /// # Examples 80 | /// 81 | /// ``` 82 | /// use kaze::*; 83 | /// 84 | /// let c = Context::new(); 85 | /// ``` 86 | pub fn new() -> Context<'a> { 87 | Context { 88 | module_arena: Arena::new(), 89 | input_data_arena: Arena::new(), 90 | input_arena: Arena::new(), 91 | output_data_arena: Arena::new(), 92 | output_arena: Arena::new(), 93 | signal_arena: Arena::new(), 94 | register_data_arena: Arena::new(), 95 | register_arena: Arena::new(), 96 | mem_arena: Arena::new(), 97 | 98 | modules: RefCell::new(Vec::new()), 99 | } 100 | } 101 | } 102 | 103 | impl<'a> ModuleParent<'a> for Context<'a> { 104 | // TODO: Docs, error handling 105 | fn module(&'a self, instance_name: impl Into, name: impl Into) -> &Module { 106 | let instance_name = instance_name.into(); 107 | let name = name.into(); 108 | let module = self 109 | .module_arena 110 | .alloc(Module::new(self, None, instance_name, name)); 111 | self.modules.borrow_mut().push(module); 112 | module 113 | } 114 | } 115 | 116 | #[cfg(test)] 117 | mod tests { 118 | use super::*; 119 | 120 | #[test] 121 | fn new_context_has_no_modules() { 122 | let c = Context::new(); 123 | 124 | assert!(c.modules.borrow().is_empty()); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /kaze/src/graph/internal_signal.rs: -------------------------------------------------------------------------------- 1 | use super::constant::*; 2 | use super::context::*; 3 | use super::mem::*; 4 | use super::module::*; 5 | use super::register::*; 6 | 7 | use std::hash::{Hash, Hasher}; 8 | use std::ptr; 9 | 10 | pub struct InternalSignal<'a> { 11 | pub(super) context: &'a Context<'a>, 12 | pub(crate) module: &'a Module<'a>, 13 | 14 | pub(crate) data: SignalData<'a>, 15 | } 16 | 17 | impl<'a> InternalSignal<'a> { 18 | pub fn bit_width(&'a self) -> u32 { 19 | match self.data { 20 | SignalData::Lit { bit_width, .. } => bit_width, 21 | SignalData::Input { data } => data.bit_width, 22 | // TODO: Test above 23 | SignalData::Output { data } => data.bit_width, 24 | SignalData::Reg { data } => data.bit_width, 25 | SignalData::UnOp { bit_width, .. } => bit_width, 26 | SignalData::SimpleBinOp { bit_width, .. } => bit_width, 27 | SignalData::AdditiveBinOp { bit_width, .. } => bit_width, 28 | SignalData::ComparisonBinOp { .. } => 1, 29 | SignalData::ShiftBinOp { bit_width, .. } => bit_width, 30 | SignalData::Mul { bit_width, .. } => bit_width, 31 | SignalData::MulSigned { bit_width, .. } => bit_width, 32 | SignalData::Bits { 33 | range_high, 34 | range_low, 35 | .. 36 | } => range_high - range_low + 1, 37 | SignalData::Repeat { bit_width, .. } => bit_width, 38 | SignalData::Concat { bit_width, .. } => bit_width, 39 | SignalData::Mux { bit_width, .. } => bit_width, 40 | SignalData::MemReadPortOutput { mem, .. } => mem.element_bit_width, 41 | } 42 | } 43 | 44 | pub(crate) fn module_instance_name_prefix(&self) -> String { 45 | let mut stack = Vec::new(); 46 | let mut module = Some(self.module); 47 | while let Some(m) = module { 48 | stack.push(m); 49 | module = m.parent; 50 | } 51 | 52 | let mut ret = String::new(); 53 | while let Some(m) = stack.pop() { 54 | ret = if ret.is_empty() { 55 | m.instance_name.clone() 56 | } else { 57 | format!("{}_{}", ret, m.instance_name) 58 | }; 59 | } 60 | 61 | ret 62 | } 63 | } 64 | 65 | impl<'a> Eq for &'a InternalSignal<'a> {} 66 | 67 | impl<'a> Hash for &'a InternalSignal<'a> { 68 | fn hash(&self, state: &mut H) { 69 | state.write_usize(*self as *const _ as usize) 70 | } 71 | } 72 | 73 | impl<'a> PartialEq for &'a InternalSignal<'a> { 74 | fn eq(&self, other: &Self) -> bool { 75 | ptr::eq(*self, *other) 76 | } 77 | } 78 | 79 | pub(crate) enum SignalData<'a> { 80 | Lit { 81 | value: Constant, 82 | bit_width: u32, 83 | }, 84 | 85 | Input { 86 | data: &'a InputData<'a>, 87 | }, 88 | Output { 89 | data: &'a OutputData<'a>, 90 | }, 91 | 92 | // TODO: Rename to Register? 93 | Reg { 94 | data: &'a RegisterData<'a>, 95 | }, 96 | 97 | UnOp { 98 | source: &'a InternalSignal<'a>, 99 | op: UnOp, 100 | bit_width: u32, 101 | }, 102 | SimpleBinOp { 103 | lhs: &'a InternalSignal<'a>, 104 | rhs: &'a InternalSignal<'a>, 105 | op: SimpleBinOp, 106 | bit_width: u32, 107 | }, 108 | AdditiveBinOp { 109 | lhs: &'a InternalSignal<'a>, 110 | rhs: &'a InternalSignal<'a>, 111 | op: AdditiveBinOp, 112 | bit_width: u32, 113 | }, 114 | ComparisonBinOp { 115 | lhs: &'a InternalSignal<'a>, 116 | rhs: &'a InternalSignal<'a>, 117 | op: ComparisonBinOp, 118 | }, 119 | ShiftBinOp { 120 | lhs: &'a InternalSignal<'a>, 121 | rhs: &'a InternalSignal<'a>, 122 | op: ShiftBinOp, 123 | bit_width: u32, 124 | }, 125 | 126 | Mul { 127 | lhs: &'a InternalSignal<'a>, 128 | rhs: &'a InternalSignal<'a>, 129 | bit_width: u32, 130 | }, 131 | MulSigned { 132 | lhs: &'a InternalSignal<'a>, 133 | rhs: &'a InternalSignal<'a>, 134 | bit_width: u32, 135 | }, 136 | 137 | Bits { 138 | source: &'a InternalSignal<'a>, 139 | range_high: u32, 140 | range_low: u32, 141 | }, 142 | 143 | Repeat { 144 | source: &'a InternalSignal<'a>, 145 | count: u32, 146 | bit_width: u32, 147 | }, 148 | Concat { 149 | lhs: &'a InternalSignal<'a>, 150 | rhs: &'a InternalSignal<'a>, 151 | bit_width: u32, 152 | }, 153 | 154 | Mux { 155 | cond: &'a InternalSignal<'a>, 156 | when_true: &'a InternalSignal<'a>, 157 | when_false: &'a InternalSignal<'a>, 158 | bit_width: u32, 159 | }, 160 | 161 | MemReadPortOutput { 162 | mem: &'a Mem<'a>, 163 | address: &'a InternalSignal<'a>, 164 | enable: &'a InternalSignal<'a>, 165 | }, 166 | } 167 | 168 | #[derive(Clone, Copy)] 169 | pub(crate) enum UnOp { 170 | Not, 171 | } 172 | 173 | #[derive(Clone, Copy)] 174 | pub(crate) enum SimpleBinOp { 175 | BitAnd, 176 | BitOr, 177 | BitXor, 178 | } 179 | 180 | #[derive(Clone, Copy)] 181 | pub(crate) enum ComparisonBinOp { 182 | Equal, 183 | GreaterThan, 184 | GreaterThanEqual, 185 | GreaterThanEqualSigned, 186 | GreaterThanSigned, 187 | LessThan, 188 | LessThanEqual, 189 | LessThanEqualSigned, 190 | LessThanSigned, 191 | NotEqual, 192 | } 193 | 194 | #[derive(Clone, Copy)] 195 | pub(crate) enum AdditiveBinOp { 196 | Add, 197 | Sub, 198 | } 199 | 200 | #[derive(Clone, Copy)] 201 | pub(crate) enum ShiftBinOp { 202 | Shl, 203 | Shr, 204 | ShrArithmetic, 205 | } 206 | 207 | pub trait GetInternalSignal<'a> { 208 | // TODO: Rename to `get_internal_signal` ? 209 | fn internal_signal(&'a self) -> &'a InternalSignal<'a>; 210 | } 211 | 212 | impl<'a> GetInternalSignal<'a> for InternalSignal<'a> { 213 | fn internal_signal(&'a self) -> &'a InternalSignal<'a> { 214 | self 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /kaze/src/graph/mem.rs: -------------------------------------------------------------------------------- 1 | use super::constant::*; 2 | use super::context::*; 3 | use super::internal_signal::*; 4 | use super::module::*; 5 | use super::signal::*; 6 | 7 | use std::cell::RefCell; 8 | use std::hash::{Hash, Hasher}; 9 | use std::ptr; 10 | 11 | /// A synchronous memory, created by the [`Module::mem`] method. 12 | /// 13 | /// Memories in kaze are always sequential/synchronous-read, sequential/synchronous-write memories. 14 | /// This means that when a read and/or write is asserted, the read/write will be visible on the cycle immediately following the cycle in which it's asserted. 15 | /// If both a write and a read to the same location occurs within the same cycle, the read will return the previous value at the memory location, **not** the newly-written value. 16 | /// 17 | /// Memories must have at least one read port specified. 18 | /// Multiple reads to the same location within the same cycle will return the same value. 19 | /// 20 | /// Memories may optionally have initial contents and/or a write port specified. 21 | /// If either of these are missing, the contents of the memory can't be determined, so this is a logical error. 22 | /// 23 | /// # Examples 24 | /// 25 | /// ``` 26 | /// use kaze::*; 27 | /// 28 | /// let c = Context::new(); 29 | /// 30 | /// let m = c.module("m", "MyModule"); 31 | /// 32 | /// let my_mem = m.mem("my_mem", 1, 32); 33 | /// // Optional, unless no write port is specified 34 | /// my_mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32]); 35 | /// // Optional, unless no initial contents are specified 36 | /// my_mem.write_port(m.high(), m.lit(0xabad1deau32, 32), m.high()); 37 | /// m.output("my_output", my_mem.read_port(m.high(), m.high())); 38 | /// ``` 39 | #[must_use] 40 | pub struct Mem<'a> { 41 | pub(super) context: &'a Context<'a>, 42 | pub(crate) module: &'a Module<'a>, 43 | 44 | pub(crate) name: String, 45 | pub(crate) address_bit_width: u32, 46 | pub(crate) element_bit_width: u32, 47 | 48 | pub(crate) initial_contents: RefCell>>, 49 | 50 | pub(crate) read_ports: RefCell, &'a InternalSignal<'a>)>>, 51 | pub(crate) write_port: RefCell< 52 | Option<( 53 | &'a InternalSignal<'a>, 54 | &'a InternalSignal<'a>, 55 | &'a InternalSignal<'a>, 56 | )>, 57 | >, 58 | } 59 | 60 | impl<'a> Mem<'a> { 61 | /// Specifies the initial contents for this `Mem`. 62 | /// 63 | /// Reads from this `Mem` will reflect the values specified unless writes have overwritten them (if the `Mem` has a write port). 64 | /// 65 | /// By default, a `Mem` does not have initial contents, and it is not required to specify them unless the `Mem` does not have a write port. 66 | /// If initial contents are not specified, then this `Mem`'s contents will be undefined initially. 67 | /// 68 | /// Note that these contents are **not** restored when the containing [`Module`]'s implicit reset is asserted. 69 | /// 70 | /// # Panics 71 | /// 72 | /// Panics if this `Mem` already has initial contents specified, if `contents.len()` doesn't match the number of elements in this `Mem`, or if any of the specified element values don't fit into this `Mem`'s element bit width. 73 | /// 74 | /// # Examples 75 | /// 76 | /// ``` 77 | /// use kaze::*; 78 | /// 79 | /// let c = Context::new(); 80 | /// 81 | /// let m = c.module("m", "MyModule"); 82 | /// 83 | /// let my_mem = m.mem("my_mem", 1, 32); 84 | /// // Optional, unless no write port is specified 85 | /// my_mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32]); 86 | /// // Optional, unless no initial contents are specified 87 | /// my_mem.write_port(m.high(), m.lit(0xabad1deau32, 32), m.high()); 88 | /// m.output("my_output", my_mem.read_port(m.high(), m.high())); 89 | /// ``` 90 | pub fn initial_contents>(&'a self, contents: &[C]) { 91 | if self.initial_contents.borrow().is_some() { 92 | panic!("Attempted to specify initial contents for memory \"{}\" in module \"{}\", but this memory already has initial contents.", self.name, self.module.name); 93 | } 94 | let expected_contents_len = 1 << self.address_bit_width; 95 | if contents.len() != expected_contents_len { 96 | panic!("Attempted to specify initial contents for memory \"{}\" in module \"{}\" that contains {} element(s), but this memory has {} address bit(s), and requires {} element(s).", self.name, self.module.name, contents.len(), self.address_bit_width, expected_contents_len); 97 | } 98 | *self.initial_contents.borrow_mut() = Some(contents.iter().cloned().enumerate().map(|(i, x)| { 99 | let ret = x.into(); 100 | if ret.required_bits() > self.element_bit_width { 101 | panic!("Attempted to specify initial contents for memory \"{}\" in module \"{}\", but this memory has an element width of {} bit(s), and these initial contents specify element {} with value {} which requires {} bit(s).", self.name, self.module.name, self.element_bit_width, i, ret.numeric_value(), ret.required_bits()); 102 | } 103 | ret 104 | }).collect()); 105 | } 106 | 107 | /// Specifies a read port for this `Mem` and returns a [`Signal`] representing the data read from this port. 108 | /// 109 | /// `Mem`s are required to have at least one read port, otherwise the memory contents could never be read, which would be a logical error. 110 | /// There is no upper bound to the number of read ports specified in kaze, however a target device may not be able to synthesize the resulting Verilog code if too many are used. 111 | /// 112 | /// Read ports always have an `address` signal and an `enable` signal. 113 | /// When `enable` is asserted, the returned [`Signal`] will reflect the data read from the location specified by `address` on the following cycle. 114 | /// If `enable` is not asserted, then the value of the returned [`Signal`] is unchanged on the following cycle and reflects the value of the most recent read (note that this may be undefined before a valid read has occurred). 115 | /// 116 | /// # Panics 117 | /// 118 | /// Panics if `address`'s bit width doesn't match this `Mem`'s address bit width, or if `enable`'s bit width is not `1`. 119 | /// 120 | /// # Examples 121 | /// 122 | /// ``` 123 | /// use kaze::*; 124 | /// 125 | /// let c = Context::new(); 126 | /// 127 | /// let m = c.module("m", "MyModule"); 128 | /// 129 | /// let my_mem = m.mem("my_mem", 1, 32); 130 | /// // Optional, unless no write port is specified 131 | /// my_mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32]); 132 | /// // Optional, unless no initial contents are specified 133 | /// my_mem.write_port(m.high(), m.lit(0xabad1deau32, 32), m.high()); 134 | /// m.output("my_output", my_mem.read_port(m.high(), m.high())); 135 | /// ``` 136 | pub fn read_port( 137 | &'a self, 138 | address: &'a dyn Signal<'a>, 139 | enable: &'a dyn Signal<'a>, 140 | ) -> &dyn Signal<'a> { 141 | let address = address.internal_signal(); 142 | let enable = enable.internal_signal(); 143 | // TODO: Limit amount of read ports added? 144 | if address.bit_width() != self.address_bit_width { 145 | panic!("Attempted to specify a read port for memory \"{}\" in module \"{}\" with an address signal with {} bit(s), but this memory has {} address bit(s).", self.name, self.module.name, address.bit_width(), self.address_bit_width); 146 | } 147 | if enable.bit_width() != 1 { 148 | panic!("Attempted to specify a read port for memory \"{}\" in module \"{}\" with an enable signal with {} bit(s), but memory read/write ports are required to be 1 bit wide.", self.name, self.module.name, enable.bit_width()); 149 | } 150 | let ret = self.context.signal_arena.alloc(InternalSignal { 151 | context: self.context, 152 | module: self.module, 153 | 154 | data: SignalData::MemReadPortOutput { 155 | mem: self, 156 | address, 157 | enable, 158 | }, 159 | }); 160 | self.read_ports.borrow_mut().push((address, enable)); 161 | ret 162 | } 163 | 164 | /// Specifies a write port for this `Mem`. 165 | /// 166 | /// By default, a `Mem` does not have any write ports, and it is not required to specify one unless the `Mem` does not have initial contents. 167 | /// 168 | /// Write ports always have an `address` signal, a `value` signal, and an `enable` signal. 169 | /// When `enable` is asserted, the value at the location specified by `address` will reflect the value of the `value` signal on the following cycle. 170 | /// If `enable` is not asserted, then the memory contents will not change. 171 | /// 172 | /// # Panics 173 | /// 174 | /// Panics if this `Mem` already has a write port specified, if `address`'s bit width doesn't match this `Mem`'s address bit width, if `value`'s bit width doesn't match this `Mem`'s element bit width, or if `enable`'s bit width is not `1`. 175 | /// 176 | /// # Examples 177 | /// 178 | /// ``` 179 | /// use kaze::*; 180 | /// 181 | /// let c = Context::new(); 182 | /// 183 | /// let m = c.module("m", "MyModule"); 184 | /// 185 | /// let my_mem = m.mem("my_mem", 1, 32); 186 | /// // Optional, unless no write port is specified 187 | /// my_mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32]); 188 | /// // Optional, unless no initial contents are specified 189 | /// my_mem.write_port(m.high(), m.lit(0xabad1deau32, 32), m.high()); 190 | /// m.output("my_output", my_mem.read_port(m.high(), m.high())); 191 | /// ``` 192 | // TODO: byte/word enable? How might that interface look? 193 | pub fn write_port( 194 | &'a self, 195 | address: &'a dyn Signal<'a>, 196 | value: &'a dyn Signal<'a>, 197 | enable: &'a dyn Signal<'a>, 198 | ) { 199 | let address = address.internal_signal(); 200 | let value = value.internal_signal(); 201 | let enable = enable.internal_signal(); 202 | if self.write_port.borrow().is_some() { 203 | panic!("Attempted to specify a write port for memory \"{}\" in module \"{}\", but this memory already has a write port.", self.name, self.module.name); 204 | } 205 | if address.bit_width() != self.address_bit_width { 206 | panic!("Attempted to specify a write port for memory \"{}\" in module \"{}\" with an address signal with {} bit(s), but this memory has {} address bit(s).", self.name, self.module.name, address.bit_width(), self.address_bit_width); 207 | } 208 | if value.bit_width() != self.element_bit_width { 209 | panic!("Attempted to specify a write port for memory \"{}\" in module \"{}\" with a value signal with {} bit(s), but this memory has {} element bit(s).", self.name, self.module.name, value.bit_width(), self.element_bit_width); 210 | } 211 | if enable.bit_width() != 1 { 212 | panic!("Attempted to specify a write port for memory \"{}\" in module \"{}\" with an enable signal with {} bit(s), but memory read/write ports are required to be 1 bit wide.", self.name, self.module.name, enable.bit_width()); 213 | } 214 | *self.write_port.borrow_mut() = Some((address, value, enable)); 215 | } 216 | } 217 | 218 | impl<'a> Eq for &'a Mem<'a> {} 219 | 220 | impl<'a> Hash for &'a Mem<'a> { 221 | fn hash(&self, state: &mut H) { 222 | state.write_usize(*self as *const _ as usize) 223 | } 224 | } 225 | 226 | impl<'a> PartialEq for &'a Mem<'a> { 227 | fn eq(&self, other: &Self) -> bool { 228 | ptr::eq(*self, *other) 229 | } 230 | } 231 | 232 | #[cfg(test)] 233 | mod tests { 234 | use crate::*; 235 | 236 | #[test] 237 | #[should_panic( 238 | expected = "Attempted to specify initial contents for memory \"mem\" in module \"A\", but this memory already has initial contents." 239 | )] 240 | fn initial_contents_already_specified_error() { 241 | let c = Context::new(); 242 | 243 | let m = c.module("a", "A"); 244 | let mem = m.mem("mem", 1, 1); 245 | 246 | mem.initial_contents(&[true, false]); 247 | 248 | // Panic 249 | mem.initial_contents(&[true, false]); 250 | } 251 | 252 | #[test] 253 | #[should_panic( 254 | expected = "Attempted to specify initial contents for memory \"mem\" in module \"A\" that contains 3 element(s), but this memory has 1 address bit(s), and requires 2 element(s)." 255 | )] 256 | fn initial_contents_length_error() { 257 | let c = Context::new(); 258 | 259 | let m = c.module("a", "A"); 260 | let mem = m.mem("mem", 1, 1); 261 | 262 | // Panic 263 | mem.initial_contents(&[true, false, true]); 264 | } 265 | 266 | #[test] 267 | #[should_panic( 268 | expected = "Attempted to specify initial contents for memory \"mem\" in module \"A\", but this memory has an element width of 1 bit(s), and these initial contents specify element 0 with value 2 which requires 2 bit(s)." 269 | )] 270 | fn initial_contents_element_bit_width_error() { 271 | let c = Context::new(); 272 | 273 | let m = c.module("a", "A"); 274 | let mem = m.mem("mem", 1, 1); 275 | 276 | // Panic 277 | mem.initial_contents(&[2u32, 0u32]); 278 | } 279 | 280 | #[test] 281 | #[should_panic( 282 | expected = "Attempted to specify a read port for memory \"mem\" in module \"A\" with an address signal with 2 bit(s), but this memory has 1 address bit(s)." 283 | )] 284 | fn read_port_address_bit_width_error() { 285 | let c = Context::new(); 286 | 287 | let m = c.module("a", "A"); 288 | let mem = m.mem("mem", 1, 1); 289 | 290 | // Panic 291 | let _ = mem.read_port(m.lit(0u32, 2), m.low()); 292 | } 293 | 294 | #[test] 295 | #[should_panic( 296 | expected = "Attempted to specify a read port for memory \"mem\" in module \"A\" with an enable signal with 2 bit(s), but memory read/write ports are required to be 1 bit wide." 297 | )] 298 | fn read_port_enable_bit_width_error() { 299 | let c = Context::new(); 300 | 301 | let m = c.module("a", "A"); 302 | let mem = m.mem("mem", 1, 1); 303 | 304 | // Panic 305 | let _ = mem.read_port(m.low(), m.lit(0u32, 2)); 306 | } 307 | 308 | #[test] 309 | #[should_panic( 310 | expected = "Attempted to specify a write port for memory \"mem\" in module \"A\", but this memory already has a write port." 311 | )] 312 | fn write_port_already_specified_error() { 313 | let c = Context::new(); 314 | 315 | let m = c.module("a", "A"); 316 | let mem = m.mem("mem", 1, 1); 317 | 318 | mem.write_port(m.low(), m.low(), m.low()); 319 | 320 | // Panic 321 | mem.write_port(m.low(), m.low(), m.low()); 322 | } 323 | 324 | #[test] 325 | #[should_panic( 326 | expected = "Attempted to specify a write port for memory \"mem\" in module \"A\" with an address signal with 2 bit(s), but this memory has 1 address bit(s)." 327 | )] 328 | fn write_port_address_bit_width_error() { 329 | let c = Context::new(); 330 | 331 | let m = c.module("a", "A"); 332 | let mem = m.mem("mem", 1, 1); 333 | 334 | // Panic 335 | mem.write_port(m.lit(0u32, 2), m.low(), m.low()); 336 | } 337 | 338 | #[test] 339 | #[should_panic( 340 | expected = "Attempted to specify a write port for memory \"mem\" in module \"A\" with a value signal with 2 bit(s), but this memory has 1 element bit(s)." 341 | )] 342 | fn write_port_value_bit_width_error() { 343 | let c = Context::new(); 344 | 345 | let m = c.module("a", "A"); 346 | let mem = m.mem("mem", 1, 1); 347 | 348 | // Panic 349 | mem.write_port(m.low(), m.lit(0u32, 2), m.low()); 350 | } 351 | 352 | #[test] 353 | #[should_panic( 354 | expected = "Attempted to specify a write port for memory \"mem\" in module \"A\" with an enable signal with 2 bit(s), but memory read/write ports are required to be 1 bit wide." 355 | )] 356 | fn write_port_enable_bit_width_error() { 357 | let c = Context::new(); 358 | 359 | let m = c.module("a", "A"); 360 | let mem = m.mem("mem", 1, 1); 361 | 362 | // Panic 363 | mem.write_port(m.low(), m.low(), m.lit(0u32, 2)); 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /kaze/src/graph/module.rs: -------------------------------------------------------------------------------- 1 | use super::constant::*; 2 | use super::context::*; 3 | use super::internal_signal::*; 4 | use super::mem::*; 5 | use super::register::*; 6 | use super::signal::*; 7 | 8 | use std::cell::RefCell; 9 | use std::collections::BTreeMap; 10 | use std::hash::{Hash, Hasher}; 11 | use std::ptr; 12 | 13 | /// A self-contained and potentially-reusable hardware design unit, created by the [`Context::module`] method. 14 | /// 15 | /// Once a `Module` is specified, it can be [instantiated](Self::instance) in another `Module` to form a hierarchy, or it can be used to generate [Rust simulator code](crate::sim::generate) or a [Verilog module](crate::verilog::generate). 16 | /// 17 | /// All `Module`s in kaze have an implicit reset and clock. These are only visible in generated code. It's assumed that all kaze modules operate in the same clock domain. 18 | /// 19 | /// # Examples 20 | /// 21 | /// ``` 22 | /// use kaze::*; 23 | /// 24 | /// let c = Context::new(); 25 | /// 26 | /// let m = c.module("m", "MyModule"); 27 | /// m.output("out", m.input("in", 1)); 28 | /// ``` 29 | // TODO: Validation error if a module has no inputs/outputs 30 | // TODO: Document composing modules (even if it's really basic) 31 | #[must_use] 32 | pub struct Module<'a> { 33 | context: &'a Context<'a>, 34 | 35 | pub(crate) parent: Option<&'a Module<'a>>, 36 | 37 | pub(crate) instance_name: String, 38 | pub(crate) name: String, 39 | 40 | // TODO: Do we need to duplicate the input/output names here? 41 | pub(crate) inputs: RefCell>>, 42 | pub(crate) outputs: RefCell>>, 43 | pub(crate) registers: RefCell>>, 44 | pub(crate) modules: RefCell>>, 45 | pub(crate) mems: RefCell>>, 46 | } 47 | 48 | impl<'a> Module<'a> { 49 | pub(super) fn new( 50 | context: &'a Context<'a>, 51 | parent: Option<&'a Module<'a>>, 52 | instance_name: String, 53 | name: String, 54 | ) -> Module<'a> { 55 | Module { 56 | context, 57 | 58 | parent, 59 | 60 | instance_name, 61 | name, 62 | 63 | inputs: RefCell::new(BTreeMap::new()), 64 | outputs: RefCell::new(BTreeMap::new()), 65 | registers: RefCell::new(Vec::new()), 66 | modules: RefCell::new(Vec::new()), 67 | mems: RefCell::new(Vec::new()), 68 | } 69 | } 70 | 71 | /// Creates a [`Signal`] that represents the constant literal specified by `value` with `bit_width` bits. 72 | /// 73 | /// The bit width of the type provided by `value` doesn't need to match `bit_width`, but the value represented by `value` must fit into `bit_width` bits. 74 | /// 75 | /// # Panics 76 | /// 77 | /// Panics if `bit_width` is less than [`MIN_SIGNAL_BIT_WIDTH`] or greater than [`MAX_SIGNAL_BIT_WIDTH`], respectively, or if the specified `value` doesn't fit into `bit_width` bits. 78 | /// 79 | /// # Examples 80 | /// 81 | /// ``` 82 | /// use kaze::*; 83 | /// 84 | /// let c = Context::new(); 85 | /// 86 | /// let m = c.module("m", "MyModule"); 87 | /// 88 | /// let eight_bit_const = m.lit(0xffu32, 8); 89 | /// let one_bit_const = m.lit(0u32, 1); 90 | /// let twenty_seven_bit_const = m.lit(true, 27); 91 | /// ``` 92 | pub fn lit(&'a self, value: impl Into, bit_width: u32) -> &dyn Signal<'a> { 93 | if bit_width < MIN_SIGNAL_BIT_WIDTH { 94 | panic!( 95 | "Cannot create a literal with {} bit(s). Signals must not be narrower than {} bit(s).", 96 | bit_width, MIN_SIGNAL_BIT_WIDTH 97 | ); 98 | } 99 | if bit_width > MAX_SIGNAL_BIT_WIDTH { 100 | panic!( 101 | "Cannot create a literal with {} bit(s). Signals must not be wider than {} bit(s).", 102 | bit_width, MAX_SIGNAL_BIT_WIDTH 103 | ); 104 | } 105 | let value = value.into(); 106 | let required_bits = value.required_bits(); 107 | if required_bits > bit_width { 108 | let numeric_value = value.numeric_value(); 109 | panic!("Cannot fit the specified value '{}' into the specified bit width '{}'. The value '{}' requires a bit width of at least {} bit(s).", numeric_value, bit_width, numeric_value, required_bits); 110 | } 111 | self.context.signal_arena.alloc(InternalSignal { 112 | context: self.context, 113 | module: self, 114 | 115 | data: SignalData::Lit { value, bit_width }, 116 | }) 117 | } 118 | 119 | /// Convenience method to create a [`Signal`] that represents a single `0` bit. 120 | /// 121 | /// # Examples 122 | /// 123 | /// ``` 124 | /// use kaze::*; 125 | /// 126 | /// let c = Context::new(); 127 | /// 128 | /// let m = c.module("m", "MyModule"); 129 | /// 130 | /// // The following two signals are semantically equivalent: 131 | /// let low1 = m.low(); 132 | /// let low2 = m.lit(false, 1); 133 | /// ``` 134 | pub fn low(&'a self) -> &dyn Signal<'a> { 135 | self.lit(false, 1) 136 | } 137 | 138 | /// Convenience method to create a [`Signal`] that represents a single `1` bit. 139 | /// 140 | /// # Examples 141 | /// 142 | /// ``` 143 | /// use kaze::*; 144 | /// 145 | /// let c = Context::new(); 146 | /// 147 | /// let m = c.module("m", "MyModule"); 148 | /// 149 | /// // The following two signals are semantically equivalent: 150 | /// let high1 = m.high(); 151 | /// let high2 = m.lit(true, 1); 152 | /// ``` 153 | pub fn high(&'a self) -> &dyn Signal<'a> { 154 | self.lit(true, 1) 155 | } 156 | 157 | /// Creates an input for this `Module` called `name` with `bit_width` bits, and returns a [`Signal`] that represents the value of this input. 158 | /// 159 | /// # Panics 160 | /// 161 | /// Panics if `bit_width` is less than [`MIN_SIGNAL_BIT_WIDTH`] or greater than [`MAX_SIGNAL_BIT_WIDTH`], respectively. 162 | /// 163 | /// # Examples 164 | /// 165 | /// ``` 166 | /// use kaze::*; 167 | /// 168 | /// let c = Context::new(); 169 | /// 170 | /// let m = c.module("m", "MyModule"); 171 | /// 172 | /// let my_input = m.input("my_input", 80); 173 | /// ``` 174 | pub fn input(&'a self, name: impl Into, bit_width: u32) -> &Input<'a> { 175 | let name = name.into(); 176 | // TODO: Error if name already exists in this context 177 | if bit_width < MIN_SIGNAL_BIT_WIDTH { 178 | panic!( 179 | "Cannot create an input with {} bit(s). Signals must not be narrower than {} bit(s).", 180 | bit_width, MIN_SIGNAL_BIT_WIDTH 181 | ); 182 | } 183 | if bit_width > MAX_SIGNAL_BIT_WIDTH { 184 | panic!( 185 | "Cannot create an input with {} bit(s). Signals must not be wider than {} bit(s).", 186 | bit_width, MAX_SIGNAL_BIT_WIDTH 187 | ); 188 | } 189 | let data = self.context.input_data_arena.alloc(InputData { 190 | name: name.clone(), 191 | bit_width, 192 | driven_value: RefCell::new(None), 193 | }); 194 | let value = self.context.signal_arena.alloc(InternalSignal { 195 | context: self.context, 196 | module: self, 197 | 198 | data: SignalData::Input { data }, 199 | }); 200 | let input = self.context.input_arena.alloc(Input { 201 | module: self, 202 | 203 | data, 204 | value, 205 | }); 206 | self.inputs.borrow_mut().insert(name, input); 207 | input 208 | } 209 | 210 | /// Creates an output for this `Module` called `name` with the same number of bits as `source`, and drives this output with `source`. 211 | /// 212 | /// # Panics 213 | /// 214 | /// Panics of `source` doesn't belong to this `Module`. 215 | /// 216 | /// # Examples 217 | /// 218 | /// ``` 219 | /// use kaze::*; 220 | /// 221 | /// let c = Context::new(); 222 | /// 223 | /// let m = c.module("m", "MyModule"); 224 | /// 225 | /// let some_signal = m.high(); 226 | /// m.output("my_output", some_signal); 227 | /// ``` 228 | pub fn output(&'a self, name: impl Into, source: &'a dyn Signal<'a>) -> &Output<'a> { 229 | let name = name.into(); 230 | let source = source.internal_signal(); 231 | if !ptr::eq(self, source.module) { 232 | panic!("Cannot output a signal from another module."); 233 | } 234 | // TODO: Error if name already exists in this context 235 | let data = self.context.output_data_arena.alloc(OutputData { 236 | module: self, 237 | 238 | name: name.clone(), 239 | source, 240 | bit_width: source.bit_width(), 241 | }); 242 | let output = self.context.output_arena.alloc(Output { data }); 243 | self.outputs.borrow_mut().insert(name, output); 244 | output 245 | } 246 | 247 | /// Creates a [`Register`] in this `Module` called `name` with `bit_width` bits. 248 | /// 249 | /// # Panics 250 | /// 251 | /// Panics if `bit_width` is less than [`MIN_SIGNAL_BIT_WIDTH`] or greater than [`MAX_SIGNAL_BIT_WIDTH`], respectively. 252 | /// 253 | /// # Examples 254 | /// 255 | /// ``` 256 | /// use kaze::*; 257 | /// 258 | /// let c = Context::new(); 259 | /// 260 | /// let m = c.module("m", "MyModule"); 261 | /// 262 | /// let my_reg = m.reg("my_reg", 32); 263 | /// my_reg.default_value(0xfadebabeu32); // Optional 264 | /// my_reg.drive_next(!my_reg); 265 | /// m.output("my_output", my_reg); 266 | /// ``` 267 | pub fn reg(&'a self, name: impl Into, bit_width: u32) -> &Register<'a> { 268 | // TODO: Error if name already exists in this context and update docs for Signal::reg_next and Signal::reg_next_with_default to reflect this 269 | if bit_width < MIN_SIGNAL_BIT_WIDTH { 270 | panic!( 271 | "Cannot create a register with {} bit(s). Signals must not be narrower than {} bit(s).", 272 | bit_width, MIN_SIGNAL_BIT_WIDTH 273 | ); 274 | } 275 | if bit_width > MAX_SIGNAL_BIT_WIDTH { 276 | panic!( 277 | "Cannot create a register with {} bit(s). Signals must not be wider than {} bit(s).", 278 | bit_width, MAX_SIGNAL_BIT_WIDTH 279 | ); 280 | } 281 | let data = self.context.register_data_arena.alloc(RegisterData { 282 | module: self, 283 | 284 | name: name.into(), 285 | initial_value: RefCell::new(None), 286 | bit_width, 287 | next: RefCell::new(None), 288 | }); 289 | let value = self.context.signal_arena.alloc(InternalSignal { 290 | context: self.context, 291 | module: self, 292 | 293 | data: SignalData::Reg { data }, 294 | }); 295 | self.registers.borrow_mut().push(value); 296 | self.context.register_arena.alloc(Register { data, value }) 297 | } 298 | 299 | /// Creates a 2:1 [multiplexer](https://en.wikipedia.org/wiki/Multiplexer) that represents `when_true`'s value when `cond` is high, and `when_false`'s value when `cond` is low. 300 | /// 301 | /// # Panics 302 | /// 303 | /// Panics if `cond`, `when_true`, or `when_false` belong to a different `Module` than `self`, if `cond`'s bit width is not 1, or if the bit widths of `when_true` and `when_false` aren't equal. 304 | /// 305 | /// # Examples 306 | /// 307 | /// ``` 308 | /// use kaze::*; 309 | /// 310 | /// let c = Context::new(); 311 | /// 312 | /// let m = c.module("m", "MyModule"); 313 | /// 314 | /// let cond = m.input("cond", 1); 315 | /// let a = m.input("a", 8); 316 | /// let b = m.input("b", 8); 317 | /// m.output("my_output", m.mux(cond, a, b)); // Outputs a when cond is high, b otherwise 318 | /// ``` 319 | pub fn mux( 320 | &'a self, 321 | cond: &'a dyn Signal<'a>, 322 | when_true: &'a dyn Signal<'a>, 323 | when_false: &'a dyn Signal<'a>, 324 | ) -> &dyn Signal<'a> { 325 | let cond = cond.internal_signal(); 326 | let when_true = when_true.internal_signal(); 327 | let when_false = when_false.internal_signal(); 328 | 329 | // TODO: This is an optimization to support sugar; if that doesn't go well, remove this 330 | if when_true == when_false { 331 | return when_true; 332 | } 333 | 334 | if !ptr::eq(self, cond.module) { 335 | panic!("Attempted to combine signals from different modules."); 336 | } 337 | if !ptr::eq(self, when_true.module) { 338 | panic!("Attempted to combine signals from different modules."); 339 | } 340 | if !ptr::eq(self, when_false.module) { 341 | panic!("Attempted to combine signals from different modules."); 342 | } 343 | if cond.bit_width() != 1 { 344 | panic!("Multiplexer conditionals can only be 1 bit wide."); 345 | } 346 | if when_true.bit_width() != when_false.bit_width() { 347 | panic!( 348 | "Cannot multiplex signals with different bit widths ({} and {}, respectively).", 349 | when_true.bit_width(), 350 | when_false.bit_width() 351 | ); 352 | } 353 | self.context.signal_arena.alloc(InternalSignal { 354 | context: self.context, 355 | module: self, 356 | 357 | data: SignalData::Mux { 358 | cond, 359 | when_true, 360 | when_false, 361 | bit_width: when_true.bit_width(), 362 | }, 363 | }) 364 | } 365 | 366 | /// Creates a [`Mem`] in this `Module` called `name` with `address_bit_width` address bits and `element_bit_width` element bits. 367 | /// 368 | /// The size of this memory will be `1 << address_bit_width` elements, each `element_bit_width` bits wide. 369 | /// 370 | /// # Panics 371 | /// 372 | /// Panics if `address_bit_width` or `element_bit_width` is less than [`MIN_SIGNAL_BIT_WIDTH`] or greater than [`MAX_SIGNAL_BIT_WIDTH`], respectively. 373 | /// 374 | /// # Examples 375 | /// 376 | /// ``` 377 | /// use kaze::*; 378 | /// 379 | /// let c = Context::new(); 380 | /// 381 | /// let m = c.module("m", "MyModule"); 382 | /// 383 | /// let my_mem = m.mem("my_mem", 1, 32); 384 | /// // Optional, unless no write port is specified 385 | /// my_mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32]); 386 | /// // Optional, unless no initial contents are specified 387 | /// my_mem.write_port(m.high(), m.lit(0xabad1deau32, 32), m.high()); 388 | /// m.output("my_output", my_mem.read_port(m.high(), m.high())); 389 | /// ``` 390 | pub fn mem( 391 | &'a self, 392 | name: impl Into, 393 | address_bit_width: u32, 394 | element_bit_width: u32, 395 | ) -> &Mem<'a> { 396 | // TODO: Error if name already exists in this context 397 | if address_bit_width < MIN_SIGNAL_BIT_WIDTH { 398 | panic!( 399 | "Cannot create a memory with {} address bit(s). Signals must not be narrower than {} bit(s).", 400 | address_bit_width, MIN_SIGNAL_BIT_WIDTH 401 | ); 402 | } 403 | if address_bit_width > MAX_SIGNAL_BIT_WIDTH { 404 | panic!( 405 | "Cannot create a memory with {} address bit(s). Signals must not be wider than {} bit(s).", 406 | address_bit_width, MAX_SIGNAL_BIT_WIDTH 407 | ); 408 | } 409 | if element_bit_width < MIN_SIGNAL_BIT_WIDTH { 410 | panic!( 411 | "Cannot create a memory with {} element bit(s). Signals must not be narrower than {} bit(s).", 412 | element_bit_width, MIN_SIGNAL_BIT_WIDTH 413 | ); 414 | } 415 | if element_bit_width > MAX_SIGNAL_BIT_WIDTH { 416 | panic!( 417 | "Cannot create a memory with {} element bit(s). Signals must not be wider than {} bit(s).", 418 | element_bit_width, MAX_SIGNAL_BIT_WIDTH 419 | ); 420 | } 421 | let ret = self.context.mem_arena.alloc(Mem { 422 | context: self.context, 423 | module: self, 424 | 425 | name: name.into(), 426 | address_bit_width, 427 | element_bit_width, 428 | 429 | initial_contents: RefCell::new(None), 430 | 431 | read_ports: RefCell::new(Vec::new()), 432 | write_port: RefCell::new(None), 433 | }); 434 | self.mems.borrow_mut().push(ret); 435 | ret 436 | } 437 | } 438 | 439 | impl<'a> ModuleParent<'a> for Module<'a> { 440 | // TODO: Docs, error handling 441 | fn module(&'a self, instance_name: impl Into, name: impl Into) -> &Module { 442 | let instance_name = instance_name.into(); 443 | let name = name.into(); 444 | let module = self.context.module_arena.alloc(Module::new( 445 | self.context, 446 | Some(self), 447 | instance_name, 448 | name, 449 | )); 450 | self.modules.borrow_mut().push(module); 451 | module 452 | } 453 | } 454 | 455 | impl<'a> Eq for &'a Module<'a> {} 456 | 457 | impl<'a> Hash for &'a Module<'a> { 458 | fn hash(&self, state: &mut H) { 459 | state.write_usize(*self as *const _ as usize) 460 | } 461 | } 462 | 463 | impl<'a> PartialEq for &'a Module<'a> { 464 | fn eq(&self, other: &Self) -> bool { 465 | ptr::eq(*self, *other) 466 | } 467 | } 468 | 469 | // TODO: Move? 470 | // TODO: Doc 471 | // TODO: bit width as const generic param? 472 | #[must_use] 473 | pub struct Input<'a> { 474 | // TODO: Double-check that we need this (I think we need it for error checking in drive) 475 | pub(crate) module: &'a Module<'a>, 476 | 477 | pub(crate) data: &'a InputData<'a>, 478 | pub(crate) value: &'a InternalSignal<'a>, 479 | } 480 | 481 | impl<'a> Input<'a> { 482 | // TODO: Doc 483 | // TODO: Merge error cases with Instance::drive_input? 484 | // TODO: Rename i? 485 | pub fn drive(&'a self, i: &'a dyn Signal<'a>) { 486 | let i = i.internal_signal(); 487 | // TODO: Change text from instance -> module in appropriate places? 488 | if let Some(parent) = self.module.parent { 489 | if !ptr::eq(parent, i.module) { 490 | // TODO: Clarify? 491 | panic!("Attempted to drive an instance input with a signal from a different module than that instance's parent module."); 492 | } 493 | } else { 494 | // TODO: Proper panic + test! 495 | panic!("OH NOES"); 496 | } 497 | let mut driven_value = self.data.driven_value.borrow_mut(); 498 | if driven_value.is_some() { 499 | panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but this input is already driven for this instance.", self.data.name, self.module.name); 500 | } 501 | if self.data.bit_width != i.bit_width() { 502 | panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but this input and the provided signal have different bit widths ({} and {}, respectively).", self.data.name, self.module.name, self.data.bit_width, i.bit_width()); 503 | } 504 | *driven_value = Some(i); 505 | } 506 | } 507 | 508 | impl<'a> GetInternalSignal<'a> for Input<'a> { 509 | fn internal_signal(&'a self) -> &'a InternalSignal<'a> { 510 | self.value 511 | } 512 | } 513 | 514 | impl<'a> GetInternalSignal<'a> for Output<'a> { 515 | fn internal_signal(&'a self) -> &'a InternalSignal<'a> { 516 | let parent = self.data.module.parent.expect("TODO better error pls"); 517 | self.data.module.context.signal_arena.alloc(InternalSignal { 518 | context: self.data.module.context, 519 | module: parent, 520 | 521 | data: SignalData::Output { data: self.data }, 522 | }) 523 | } 524 | } 525 | 526 | pub(crate) struct InputData<'a> { 527 | // TODO: Do we need this stored here too? 528 | pub name: String, 529 | pub bit_width: u32, 530 | // TODO: Rename? 531 | pub driven_value: RefCell>>, 532 | } 533 | 534 | // TODO: Move? 535 | // TODO: Doc 536 | // TODO: must_use? 537 | // TODO: bit width as const generic param? 538 | pub struct Output<'a> { 539 | pub(crate) data: &'a OutputData<'a>, 540 | } 541 | 542 | pub(crate) struct OutputData<'a> { 543 | // TODO: Do we need this? 544 | pub module: &'a Module<'a>, 545 | 546 | // TODO: Do we need this stored here too? 547 | pub name: String, 548 | pub source: &'a InternalSignal<'a>, 549 | pub bit_width: u32, 550 | } 551 | 552 | #[cfg(test)] 553 | mod tests { 554 | use super::*; 555 | 556 | #[test] 557 | #[should_panic( 558 | expected = "Cannot create a literal with 0 bit(s). Signals must not be narrower than 1 bit(s)." 559 | )] 560 | fn lit_bit_width_lt_min_error() { 561 | let c = Context::new(); 562 | 563 | let m = c.module("a", "A"); 564 | 565 | // Panic 566 | let _ = m.lit(false, 0); 567 | } 568 | 569 | #[test] 570 | #[should_panic( 571 | expected = "Cannot create a literal with 129 bit(s). Signals must not be wider than 128 bit(s)." 572 | )] 573 | fn lit_bit_width_gt_max_error() { 574 | let c = Context::new(); 575 | 576 | let m = c.module("a", "A"); 577 | 578 | // Panic 579 | let _ = m.lit(false, 129); 580 | } 581 | 582 | #[test] 583 | #[should_panic( 584 | expected = "Cannot fit the specified value '128' into the specified bit width '7'. The value '128' requires a bit width of at least 8 bit(s)." 585 | )] 586 | fn lit_value_cannot_bit_into_bit_width_error_1() { 587 | let c = Context::new(); 588 | 589 | let m = c.module("a", "A"); 590 | 591 | // Panic 592 | let _ = m.lit(128u32, 7); 593 | } 594 | 595 | #[test] 596 | #[should_panic( 597 | expected = "Cannot fit the specified value '128' into the specified bit width '2'. The value '128' requires a bit width of at least 8 bit(s)." 598 | )] 599 | fn lit_value_cannot_bit_into_bit_width_error_2() { 600 | let c = Context::new(); 601 | 602 | let m = c.module("a", "A"); 603 | 604 | // Panic 605 | let _ = m.lit(128u64, 2); 606 | } 607 | 608 | #[test] 609 | #[should_panic( 610 | expected = "Cannot fit the specified value '1023' into the specified bit width '4'. The value '1023' requires a bit width of at least 10 bit(s)." 611 | )] 612 | fn lit_value_cannot_bit_into_bit_width_error_3() { 613 | let c = Context::new(); 614 | 615 | let m = c.module("a", "A"); 616 | 617 | // Panic 618 | let _ = m.lit(1023u128, 4); 619 | } 620 | 621 | #[test] 622 | #[should_panic( 623 | expected = "Cannot fit the specified value '65536' into the specified bit width '1'. The value '65536' requires a bit width of at least 17 bit(s)." 624 | )] 625 | fn lit_value_cannot_bit_into_bit_width_error_4() { 626 | let c = Context::new(); 627 | 628 | let m = c.module("a", "A"); 629 | 630 | // Panic 631 | let _ = m.lit(65536u32, 1); 632 | } 633 | 634 | #[test] 635 | #[should_panic( 636 | expected = "Cannot create an input with 0 bit(s). Signals must not be narrower than 1 bit(s)." 637 | )] 638 | fn input_width_lt_min_error() { 639 | let c = Context::new(); 640 | 641 | let m = c.module("a", "A"); 642 | 643 | // Panic 644 | let _ = m.input("i", 0); 645 | } 646 | 647 | #[test] 648 | #[should_panic( 649 | expected = "Cannot create an input with 129 bit(s). Signals must not be wider than 128 bit(s)." 650 | )] 651 | fn input_width_gt_max_error() { 652 | let c = Context::new(); 653 | 654 | let m = c.module("a", "A"); 655 | 656 | // Panic 657 | let _ = m.input("i", 129); 658 | } 659 | 660 | #[test] 661 | #[should_panic(expected = "Cannot output a signal from another module.")] 662 | fn output_separate_module_error() { 663 | let c = Context::new(); 664 | 665 | let m1 = c.module("a", "A"); 666 | 667 | let m2 = c.module("b", "B"); 668 | let i = m2.high(); 669 | 670 | // Panic 671 | m1.output("a", i); 672 | } 673 | 674 | #[test] 675 | #[should_panic( 676 | expected = "Cannot create a register with 0 bit(s). Signals must not be narrower than 1 bit(s)." 677 | )] 678 | fn reg_bit_width_lt_min_error() { 679 | let c = Context::new(); 680 | 681 | let m = c.module("a", "A"); 682 | 683 | // Panic 684 | let _ = m.reg("r", 0); 685 | } 686 | 687 | #[test] 688 | #[should_panic( 689 | expected = "Cannot create a register with 129 bit(s). Signals must not be wider than 128 bit(s)." 690 | )] 691 | fn reg_bit_width_gt_max_error() { 692 | let c = Context::new(); 693 | 694 | let m = c.module("a", "A"); 695 | 696 | // Panic 697 | let _ = m.reg("r", 129); 698 | } 699 | 700 | #[test] 701 | #[should_panic(expected = "Attempted to combine signals from different modules.")] 702 | fn mux_cond_separate_module_error() { 703 | let c = Context::new(); 704 | 705 | let a = c.module("a", "A"); 706 | let l1 = a.lit(false, 1); 707 | 708 | let b = c.module("b", "B"); 709 | let l2 = b.lit(32u8, 8); 710 | let l3 = b.lit(32u8, 8); 711 | 712 | // Panic 713 | let _ = b.mux(l1, l2, l3); 714 | } 715 | 716 | #[test] 717 | #[should_panic(expected = "Attempted to combine signals from different modules.")] 718 | fn mux_when_true_separate_module_error() { 719 | let c = Context::new(); 720 | 721 | let a = c.module("a", "A"); 722 | let l1 = a.lit(32u8, 8); 723 | 724 | let b = c.module("b", "B"); 725 | let l2 = b.lit(true, 1); 726 | let l3 = b.lit(32u8, 8); 727 | 728 | // Panic 729 | let _ = b.mux(l2, l1, l3); 730 | } 731 | 732 | #[test] 733 | #[should_panic(expected = "Attempted to combine signals from different modules.")] 734 | fn mux_when_false_separate_module_error() { 735 | let c = Context::new(); 736 | 737 | let a = c.module("a", "A"); 738 | let l1 = a.lit(32u8, 8); 739 | 740 | let b = c.module("b", "B"); 741 | let l2 = b.lit(true, 1); 742 | let l3 = b.lit(32u8, 8); 743 | 744 | // Panic 745 | let _ = b.mux(l2, l3, l1); 746 | } 747 | 748 | #[test] 749 | #[should_panic(expected = "Multiplexer conditionals can only be 1 bit wide.")] 750 | fn mux_cond_bit_width_error() { 751 | let c = Context::new(); 752 | 753 | let a = c.module("a", "A"); 754 | let l1 = a.lit(2u8, 2); 755 | let l2 = a.lit(32u8, 8); 756 | let l3 = a.lit(32u8, 8); 757 | 758 | // Panic 759 | let _ = a.mux(l1, l2, l3); 760 | } 761 | 762 | #[test] 763 | #[should_panic( 764 | expected = "Cannot multiplex signals with different bit widths (3 and 5, respectively)." 765 | )] 766 | fn mux_true_false_bit_width_error() { 767 | let c = Context::new(); 768 | 769 | let a = c.module("a", "A"); 770 | let l1 = a.lit(false, 1); 771 | let l2 = a.lit(3u8, 3); 772 | let l3 = a.lit(3u8, 5); 773 | 774 | // Panic 775 | let _ = a.mux(l1, l2, l3); 776 | } 777 | 778 | #[test] 779 | #[should_panic( 780 | expected = "Cannot create a memory with 0 address bit(s). Signals must not be narrower than 1 bit(s)." 781 | )] 782 | fn mem_address_bit_width_lt_min_error() { 783 | let c = Context::new(); 784 | 785 | let m = c.module("a", "A"); 786 | 787 | // Panic 788 | let _ = m.mem("mem", 0, 1); 789 | } 790 | 791 | #[test] 792 | #[should_panic( 793 | expected = "Cannot create a memory with 129 address bit(s). Signals must not be wider than 128 bit(s)." 794 | )] 795 | fn mem_address_bit_width_gt_max_error() { 796 | let c = Context::new(); 797 | 798 | let m = c.module("a", "A"); 799 | 800 | // Panic 801 | let _ = m.mem("mem", 129, 1); 802 | } 803 | 804 | #[test] 805 | #[should_panic( 806 | expected = "Cannot create a memory with 0 element bit(s). Signals must not be narrower than 1 bit(s)." 807 | )] 808 | fn mem_element_bit_width_lt_min_error() { 809 | let c = Context::new(); 810 | 811 | let m = c.module("a", "A"); 812 | 813 | // Panic 814 | let _ = m.mem("mem", 1, 0); 815 | } 816 | 817 | #[test] 818 | #[should_panic( 819 | expected = "Cannot create a memory with 129 element bit(s). Signals must not be wider than 128 bit(s)." 820 | )] 821 | fn mem_element_bit_width_gt_max_error() { 822 | let c = Context::new(); 823 | 824 | let m = c.module("a", "A"); 825 | 826 | // Panic 827 | let _ = m.mem("mem", 1, 129); 828 | } 829 | 830 | #[test] 831 | #[should_panic( 832 | expected = "Attempted to drive an instance input with a signal from a different module than that instance's parent module." 833 | )] 834 | fn input_drive_different_module_than_parent_module_error() { 835 | let c = Context::new(); 836 | 837 | let m1 = c.module("a", "A"); 838 | let i1 = m1.input("a", 1); 839 | 840 | let m2 = c.module("b", "B"); 841 | 842 | let inner = m2.module("inner", "Inner"); 843 | let a = inner.input("a", 1); 844 | 845 | // Panic 846 | a.drive(i1); 847 | } 848 | 849 | #[test] 850 | #[should_panic( 851 | expected = "Attempted to drive an input called \"a\" on an instance of \"Inner\", but this input is already driven for this instance." 852 | )] 853 | fn input_drive_already_driven_error() { 854 | let c = Context::new(); 855 | 856 | let m = c.module("a", "A"); 857 | 858 | let inner = m.module("inner", "Inner"); 859 | let a = inner.input("a", 1); 860 | 861 | a.drive(m.input("i1", 1)); 862 | 863 | // Panic 864 | a.drive(m.input("i2", 1)); 865 | } 866 | 867 | #[test] 868 | #[should_panic( 869 | expected = "Attempted to drive an input called \"a\" on an instance of \"Inner\", but this input and the provided signal have different bit widths (1 and 32, respectively)." 870 | )] 871 | fn input_drive_incompatible_bit_widths_error() { 872 | let c = Context::new(); 873 | 874 | let m = c.module("a", "A"); 875 | 876 | let inner = m.module("inner", "Inner"); 877 | let a = inner.input("a", 1); 878 | 879 | // Panic 880 | a.drive(m.input("i1", 32)); 881 | } 882 | } 883 | -------------------------------------------------------------------------------- /kaze/src/graph/register.rs: -------------------------------------------------------------------------------- 1 | use super::constant::*; 2 | use super::internal_signal::*; 3 | use super::module::*; 4 | use super::signal::*; 5 | 6 | use std::cell::RefCell; 7 | use std::ptr; 8 | 9 | /// A hardware register, created by the [`Module::reg`] method. 10 | /// 11 | /// A `Register` is a stateful component that behaves like a [D flip-flop](https://en.wikipedia.org/wiki/Flip-flop_(electronics)#D_flip-flop) (more precisely as a [positive-edge-triggered D flip-flop](https://en.wikipedia.org/wiki/Flip-flop_(electronics)#Classical_positive-edge-triggered_D_flip-flop)). 12 | /// 13 | /// It always has a current value represented by the [`value`] field (often referred to as `Q`) and a next value specified by the [`drive_next`] method (often referred to as `D`). 14 | /// It will hold its [`value`] until a positive edge of its [`Module`]'s implicit clock occurs, at which point [`value`] will be updated to reflect the next value. 15 | /// 16 | /// Optionally, it also has a default value specified by the [`default_value`] method. If at any time its [`Module`]'s implicit reset is driven low, the register's [`value`] will reflect the default value. 17 | /// Default values are used to provide a known register state on system power-on and reset, but are often omitted to reduce combinational logic (which ultimately is how default values are typically implemented), especially for registers on timing-critical data paths. 18 | /// 19 | /// # Examples 20 | /// 21 | /// ``` 22 | /// use kaze::*; 23 | /// 24 | /// let c = Context::new(); 25 | /// 26 | /// let m = c.module("m", "MyModule"); 27 | /// 28 | /// let my_reg = m.reg("my_reg", 32); 29 | /// my_reg.default_value(0xfadebabeu32); // Optional 30 | /// my_reg.drive_next(!my_reg); 31 | /// m.output("my_output", my_reg); 32 | /// ``` 33 | /// 34 | /// [`default_value`]: Self::default_value 35 | /// [`drive_next`]: Self::drive_next 36 | /// [`value`]: Self::value 37 | #[must_use] 38 | pub struct Register<'a> { 39 | pub(crate) data: &'a RegisterData<'a>, 40 | /// This `Register`'s current value. 41 | pub(crate) value: &'a InternalSignal<'a>, 42 | } 43 | 44 | impl<'a> Register<'a> { 45 | /// Specifies the default value for this `Register`. 46 | /// 47 | /// This `Register`'s [`value`] will reflect this default value when this `Register`'s [`Module`]'s implicit reset is asserted. 48 | /// 49 | /// By default, a `Register` does not have a default value, and it is not required to specify one. If a default value is not specified, then this `Register`'s [`value`] will not change when its [`Module`]'s implicit reset is asserted. 50 | /// 51 | /// # Panics 52 | /// 53 | /// Panics if this `Register` already has a default value specified, or if the specified `value` doesn't fit into this `Register`'s bit width. 54 | /// 55 | /// # Examples 56 | /// 57 | /// ``` 58 | /// use kaze::*; 59 | /// 60 | /// let c = Context::new(); 61 | /// 62 | /// let m = c.module("m", "MyModule"); 63 | /// 64 | /// let my_reg = m.reg("my_reg", 32); 65 | /// my_reg.default_value(0xfadebabeu32); // Optional 66 | /// my_reg.drive_next(!my_reg); 67 | /// m.output("my_output", my_reg); 68 | /// ``` 69 | /// 70 | /// [`value`]: Self::value 71 | pub fn default_value(&'a self, value: impl Into) { 72 | if self.data.initial_value.borrow().is_some() { 73 | panic!("Attempted to specify a default value for register \"{}\" in module \"{}\", but this register already has a default value.", self.data.name, self.data.module.name); 74 | } 75 | let value = value.into(); 76 | let required_bits = value.required_bits(); 77 | if required_bits > self.data.bit_width { 78 | let numeric_value = value.numeric_value(); 79 | panic!("Cannot fit the specified value '{}' into register \"{}\"'s bit width '{}'. The value '{}' requires a bit width of at least {} bit(s).", numeric_value, self.data.name, self.data.bit_width, numeric_value, required_bits); 80 | } 81 | *self.data.initial_value.borrow_mut() = Some(value); 82 | } 83 | 84 | /// Specifies the next value for this `Register`. 85 | /// 86 | /// A `Register` will hold its [`value`] until a positive edge of its [`Module`]'s implicit clock occurs, at which point [`value`] will be updated to reflect this next value. 87 | /// 88 | /// # Panics 89 | /// 90 | /// Panics if `self` and `n` belong to different [`Module`]s, if the bit widths of `self` and `n` aren't equal, or if this `Register`'s next value is already driven. 91 | /// 92 | /// # Examples 93 | /// 94 | /// ``` 95 | /// use kaze::*; 96 | /// 97 | /// let c = Context::new(); 98 | /// 99 | /// let m = c.module("m", "MyModule"); 100 | /// 101 | /// let my_reg = m.reg("my_reg", 32); 102 | /// my_reg.default_value(0xfadebabeu32); // Optional 103 | /// my_reg.drive_next(!my_reg); // my_reg's value will toggle with each positive clock edge 104 | /// m.output("my_output", my_reg); 105 | /// ``` 106 | /// 107 | /// [`value`]: Self::value 108 | pub fn drive_next(&'a self, n: &'a dyn Signal<'a>) { 109 | let n = n.internal_signal(); 110 | if !ptr::eq(self.data.module, n.module) { 111 | panic!("Attempted to drive register \"{}\"'s next value with a signal from another module.", self.data.name); 112 | } 113 | if n.bit_width() != self.data.bit_width { 114 | panic!("Attempted to drive register \"{}\"'s next value with a signal that has a different bit width than the register ({} and {}, respectively).", self.data.name, n.bit_width(), self.data.bit_width); 115 | } 116 | if self.data.next.borrow().is_some() { 117 | panic!("Attempted to drive register \"{}\"'s next value in module \"{}\", but this register's next value is already driven.", self.data.name, self.data.module.name); 118 | } 119 | *self.data.next.borrow_mut() = Some(n); 120 | } 121 | } 122 | 123 | pub(crate) struct RegisterData<'a> { 124 | pub module: &'a Module<'a>, 125 | 126 | pub name: String, 127 | pub initial_value: RefCell>, 128 | pub bit_width: u32, 129 | pub next: RefCell>>, 130 | } 131 | 132 | impl<'a> GetInternalSignal<'a> for Register<'a> { 133 | fn internal_signal(&'a self) -> &'a InternalSignal<'a> { 134 | self.value 135 | } 136 | } 137 | 138 | #[cfg(test)] 139 | mod tests { 140 | use crate::*; 141 | 142 | #[test] 143 | #[should_panic( 144 | expected = "Attempted to specify a default value for register \"r\" in module \"A\", but this register already has a default value." 145 | )] 146 | fn default_value_already_specified_error() { 147 | let c = Context::new(); 148 | 149 | let m = c.module("a", "A"); 150 | let r = m.reg("r", 32); 151 | 152 | r.default_value(0xfadebabeu32); 153 | 154 | // Panic 155 | r.default_value(0xdeadbeefu32); 156 | } 157 | 158 | #[test] 159 | #[should_panic( 160 | expected = "Cannot fit the specified value '128' into register \"r\"'s bit width '7'. The value '128' requires a bit width of at least 8 bit(s)." 161 | )] 162 | fn default_value_cannot_bit_into_bit_width_error_1() { 163 | let c = Context::new(); 164 | 165 | let m = c.module("a", "A"); 166 | let r = m.reg("r", 7); 167 | 168 | // Panic 169 | r.default_value(128u32); 170 | } 171 | 172 | #[test] 173 | #[should_panic( 174 | expected = "Cannot fit the specified value '128' into register \"r\"'s bit width '2'. The value '128' requires a bit width of at least 8 bit(s)." 175 | )] 176 | fn default_value_cannot_bit_into_bit_width_error_2() { 177 | let c = Context::new(); 178 | 179 | let m = c.module("a", "A"); 180 | let r = m.reg("r", 2); 181 | 182 | // Panic 183 | r.default_value(128u64); 184 | } 185 | 186 | #[test] 187 | #[should_panic( 188 | expected = "Cannot fit the specified value '1023' into register \"r\"'s bit width '4'. The value '1023' requires a bit width of at least 10 bit(s)." 189 | )] 190 | fn default_value_cannot_bit_into_bit_width_error_3() { 191 | let c = Context::new(); 192 | 193 | let m = c.module("a", "A"); 194 | let r = m.reg("r", 4); 195 | 196 | // Panic 197 | r.default_value(1023u128); 198 | } 199 | 200 | #[test] 201 | #[should_panic( 202 | expected = "Cannot fit the specified value '65536' into register \"r\"'s bit width '1'. The value '65536' requires a bit width of at least 17 bit(s)." 203 | )] 204 | fn default_value_cannot_bit_into_bit_width_error_4() { 205 | let c = Context::new(); 206 | 207 | let m = c.module("a", "A"); 208 | let r = m.reg("r", 1); 209 | 210 | // Panic 211 | r.default_value(65536u32); 212 | } 213 | 214 | #[test] 215 | #[should_panic( 216 | expected = "Attempted to drive register \"r\"'s next value with a signal from another module." 217 | )] 218 | fn drive_next_separate_module_error() { 219 | let c = Context::new(); 220 | 221 | let m1 = c.module("a", "A"); 222 | let l = m1.lit(true, 1); 223 | 224 | let m2 = c.module("b", "B"); 225 | let r = m2.reg("r", 1); 226 | 227 | // Panic 228 | r.drive_next(l); 229 | } 230 | 231 | #[test] 232 | #[should_panic( 233 | expected = "Attempted to drive register \"r\"'s next value with a signal that has a different bit width than the register (5 and 3, respectively)." 234 | )] 235 | fn drive_next_incompatible_bit_width_error() { 236 | let c = Context::new(); 237 | 238 | let m = c.module("a", "A"); 239 | let r = m.reg("r", 3); 240 | let i = m.input("i", 5); 241 | 242 | // Panic 243 | r.drive_next(i); 244 | } 245 | 246 | #[test] 247 | #[should_panic( 248 | expected = "Attempted to drive register \"r\"'s next value in module \"A\", but this register's next value is already driven." 249 | )] 250 | fn drive_next_already_driven_error() { 251 | let c = Context::new(); 252 | 253 | let m = c.module("a", "A"); 254 | let r = m.reg("r", 32); 255 | let i = m.input("i", 32); 256 | 257 | r.drive_next(i); 258 | 259 | // Panic 260 | r.drive_next(i); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /kaze/src/graph/sugar.rs: -------------------------------------------------------------------------------- 1 | use super::signal::*; 2 | 3 | /// **UNSTABLE:** Provides a convenient way to write conditional combinational logic. 4 | /// 5 | /// # Panics 6 | /// 7 | /// Since this construct wraps the returned values with [`Signal::mux`], any panic conditions from that method apply to the generated code as well. 8 | /// 9 | /// # Examples 10 | /// 11 | /// ``` 12 | /// use kaze::*; 13 | /// 14 | /// let p = Context::new(); 15 | /// 16 | /// let m = p.module("m", "MyModule"); 17 | /// let i = m.input("i", 1); 18 | /// let invert = m.input("invert", 1); 19 | /// let o = if_(invert, { 20 | /// !i 21 | /// }).else_({ 22 | /// i 23 | /// }); 24 | /// m.output("o", o); 25 | /// ``` 26 | // TODO: Can we constrain T more than this to make sure it's only a supported type? 27 | pub fn if_<'a, T>(cond: &'a dyn Signal<'a>, when_true: T) -> If<'a, T> { 28 | If::new(cond, when_true) 29 | } 30 | 31 | #[doc(hidden)] 32 | pub struct If<'a, T> { 33 | cond: &'a dyn Signal<'a>, 34 | when_true: T, 35 | } 36 | 37 | impl<'a, T> If<'a, T> { 38 | fn new(cond: &'a dyn Signal<'a>, when_true: T) -> If<'a, T> { 39 | If { cond, when_true } 40 | } 41 | 42 | pub fn else_if(self, cond: &'a dyn Signal<'a>, when_true: T) -> ElseIf<'a, T> { 43 | ElseIf { 44 | parent: ElseIfParent::If(self), 45 | cond, 46 | when_true, 47 | } 48 | } 49 | } 50 | 51 | impl<'a, T: Into<&'a dyn Signal<'a>>> If<'a, T> { 52 | pub fn else_>>(self, when_false: F) -> &'a dyn Signal<'a> { 53 | self.cond.mux(self.when_true.into(), when_false.into()) 54 | } 55 | } 56 | 57 | macro_rules! replace_tt { ($t:tt, $($i:tt)*) => { $($i)* } } 58 | 59 | macro_rules! generate_if { 60 | (($($number: tt, $t: tt, $f: tt),*)) => { 61 | impl<'a, $($t: Into<&'a dyn Signal<'a>>),*,> If<'a, ($($t),*,)> { 62 | pub fn else_<$($f: Into<&'a dyn Signal<'a>>),*,>(self, when_false: ($($f),*,)) -> ($(&'a replace_tt!($number, dyn Signal<'a>)),*,) { 63 | ( 64 | $(self.cond.mux(self.when_true.$number.into(), when_false.$number.into())),*, 65 | ) 66 | } 67 | } 68 | }; 69 | } 70 | 71 | generate_if!((0, T0, F0)); 72 | generate_if!((0, T0, F0, 1, T1, F1)); 73 | generate_if!((0, T0, F0, 1, T1, F1, 2, T2, F2)); 74 | generate_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3)); 75 | generate_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4)); 76 | generate_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5)); 77 | generate_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6)); 78 | generate_if!(( 79 | 0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7 80 | )); 81 | generate_if!(( 82 | 0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7, 8, T8, 83 | F8 84 | )); 85 | generate_if!(( 86 | 0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7, 8, T8, 87 | F8, 9, T9, F9 88 | )); 89 | generate_if!(( 90 | 0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7, 8, T8, 91 | F8, 9, T9, F9, 10, T10, F10 92 | )); 93 | generate_if!(( 94 | 0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7, 8, T8, 95 | F8, 9, T9, F9, 10, T10, F10, 11, T11, F11 96 | )); 97 | 98 | enum ElseIfParent<'a, T> { 99 | If(If<'a, T>), 100 | ElseIf(Box>), 101 | } 102 | 103 | #[doc(hidden)] 104 | pub struct ElseIf<'a, T> { 105 | parent: ElseIfParent<'a, T>, 106 | cond: &'a dyn Signal<'a>, 107 | when_true: T, 108 | } 109 | 110 | impl<'a, T> ElseIf<'a, T> { 111 | pub fn else_if(self, cond: &'a dyn Signal<'a>, when_true: T) -> ElseIf<'a, T> { 112 | ElseIf { 113 | parent: ElseIfParent::ElseIf(Box::new(self)), 114 | cond, 115 | when_true, 116 | } 117 | } 118 | } 119 | 120 | impl<'a, T: Into<&'a dyn Signal<'a>>> ElseIf<'a, T> { 121 | pub fn else_>>(self, when_false: F) -> &'a dyn Signal<'a> { 122 | let ret = self.cond.mux(self.when_true.into(), when_false.into()); 123 | match self.parent { 124 | ElseIfParent::If(parent) => parent.else_(ret), 125 | ElseIfParent::ElseIf(parent) => parent.else_(ret), 126 | } 127 | } 128 | } 129 | 130 | macro_rules! generate_else_if { 131 | (($($number: tt, $t: tt, $f: tt),*)) => { 132 | impl<'a, $($t: Into<&'a dyn Signal<'a>>),*,> ElseIf<'a, ($($t),*,)> { 133 | pub fn else_<$($f: Into<&'a dyn Signal<'a>>),*,>(self, when_false: ($($f),*,)) -> ($(&'a replace_tt!($number, dyn Signal<'a>)),*,) { 134 | let ret = ( 135 | $(self.cond.mux(self.when_true.$number.into(), when_false.$number.into())),*, 136 | ); 137 | match self.parent { 138 | ElseIfParent::If(parent) => parent.else_(ret), 139 | ElseIfParent::ElseIf(parent) => parent.else_(ret), 140 | } 141 | } 142 | } 143 | }; 144 | } 145 | 146 | generate_else_if!((0, T0, F0)); 147 | generate_else_if!((0, T0, F0, 1, T1, F1)); 148 | generate_else_if!((0, T0, F0, 1, T1, F1, 2, T2, F2)); 149 | generate_else_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3)); 150 | generate_else_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4)); 151 | generate_else_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5)); 152 | generate_else_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6)); 153 | generate_else_if!(( 154 | 0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7 155 | )); 156 | generate_else_if!(( 157 | 0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7, 8, T8, 158 | F8 159 | )); 160 | generate_else_if!(( 161 | 0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7, 8, T8, 162 | F8, 9, T9, F9 163 | )); 164 | generate_else_if!(( 165 | 0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7, 8, T8, 166 | F8, 9, T9, F9, 10, T10, F10 167 | )); 168 | generate_else_if!(( 169 | 0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7, 8, T8, 170 | F8, 9, T9, F9, 10, T10, F10, 11, T11, F11 171 | )); 172 | -------------------------------------------------------------------------------- /kaze/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An [HDL](https://en.wikipedia.org/wiki/Hardware_description_language) embedded in [Rust](https://www.rust-lang.org/). 2 | //! 3 | //! kaze provides an API to describe [`Module`]s composed of [`Signal`]s, which can then be used to generate [Rust simulator code](sim::generate) or [Verilog modules](verilog::generate). 4 | //! 5 | //! kaze's API is designed to be as minimal as possible while still being expressive. 6 | //! It's designed to prevent the user from being able to describe buggy or incorrect hardware as much as possible. 7 | //! This enables a user to hack on designs fearlessly, while the API and generators ensure that these designs are sound. 8 | //! 9 | //! # Usage 10 | //! 11 | //! ```toml 12 | //! [dependencies] 13 | //! kaze = "0.1" 14 | //! ``` 15 | //! 16 | //! # Examples 17 | //! 18 | //! ```rust 19 | //! # fn main() -> std::io::Result<()> { 20 | //! use kaze::*; 21 | //! 22 | //! // Create a context, which will contain our module(s) 23 | //! let p = Context::new(); 24 | //! 25 | //! // Create a module 26 | //! let inverter = p.module("inverter", "Inverter"); 27 | //! let i = inverter.input("i", 1); // 1-bit input 28 | //! inverter.output("o", !i); // Output inverted input 29 | //! 30 | //! // Generate Rust simulator code 31 | //! sim::generate(inverter, sim::GenerationOptions::default(), std::io::stdout())?; 32 | //! 33 | //! // Generate Verilog code 34 | //! //verilog::generate(inverter, std::io::stdout())?; 35 | //! # Ok(()) 36 | //! # } 37 | //! ``` 38 | 39 | // Must be kept up-to-date with version in Cargo.toml 40 | #![doc(html_root_url = "https://docs.rs/kaze/0.1.19")] 41 | 42 | mod code_writer; 43 | mod graph; 44 | pub mod runtime; 45 | pub mod sim; 46 | mod state_elements; 47 | mod validation; 48 | pub mod verilog; 49 | 50 | pub use graph::*; 51 | -------------------------------------------------------------------------------- /kaze/src/runtime.rs: -------------------------------------------------------------------------------- 1 | //! Rust simulator runtime dependencies. These are only required for simulators with tracing enabled. 2 | 3 | pub mod tracing; 4 | -------------------------------------------------------------------------------- /kaze/src/runtime/tracing.rs: -------------------------------------------------------------------------------- 1 | //! Rust simulator runtime dependencies for tracing. 2 | 3 | pub mod vcd; 4 | 5 | use std::io; 6 | 7 | // TODO: Do we want to re-use graph::Constant for this? They're equivalent but currently distinct in their usage, so I'm not sure it's the right API design decision. 8 | #[derive(Debug, Eq, PartialEq)] 9 | pub enum TraceValue { 10 | /// Contains a boolean value 11 | Bool(bool), 12 | /// Contains an unsigned, 32-bit value 13 | U32(u32), 14 | /// Contains an unsigned, 64-bit value 15 | U64(u64), 16 | /// Contains an unsigned, 128-bit value 17 | U128(u128), 18 | } 19 | 20 | #[derive(Debug, Eq, PartialEq)] 21 | pub enum TraceValueType { 22 | Bool, 23 | U32, 24 | U64, 25 | U128, 26 | } 27 | 28 | impl TraceValueType { 29 | pub(crate) fn from_bit_width(bit_width: u32) -> TraceValueType { 30 | if bit_width == 1 { 31 | TraceValueType::Bool 32 | } else if bit_width <= 32 { 33 | TraceValueType::U32 34 | } else if bit_width <= 64 { 35 | TraceValueType::U64 36 | } else if bit_width <= 128 { 37 | TraceValueType::U128 38 | } else { 39 | unreachable!() 40 | } 41 | } 42 | } 43 | 44 | pub trait Trace { 45 | type SignalId; 46 | 47 | fn push_module(&mut self, name: &'static str) -> io::Result<()>; 48 | fn pop_module(&mut self) -> io::Result<()>; 49 | fn add_signal( 50 | &mut self, 51 | name: &'static str, 52 | bit_width: u32, 53 | type_: TraceValueType, 54 | ) -> io::Result; 55 | 56 | fn update_time_stamp(&mut self, time_stamp: u64) -> io::Result<()>; 57 | fn update_signal(&mut self, signal_id: &Self::SignalId, value: TraceValue) -> io::Result<()>; 58 | } 59 | -------------------------------------------------------------------------------- /kaze/src/runtime/tracing/vcd.rs: -------------------------------------------------------------------------------- 1 | //! [VCD](https://en.wikipedia.org/wiki/Value_change_dump) format tracing implementation. 2 | 3 | extern crate vcd; 4 | 5 | use super::*; 6 | 7 | use std::io; 8 | 9 | pub enum TimeScaleUnit { 10 | S, 11 | Ms, 12 | Us, 13 | Ns, 14 | Ps, 15 | Fs, 16 | } 17 | 18 | impl From for vcd::TimescaleUnit { 19 | fn from(time_scale_unit: TimeScaleUnit) -> Self { 20 | match time_scale_unit { 21 | TimeScaleUnit::S => vcd::TimescaleUnit::S, 22 | TimeScaleUnit::Ms => vcd::TimescaleUnit::MS, 23 | TimeScaleUnit::Us => vcd::TimescaleUnit::US, 24 | TimeScaleUnit::Ns => vcd::TimescaleUnit::NS, 25 | TimeScaleUnit::Ps => vcd::TimescaleUnit::PS, 26 | TimeScaleUnit::Fs => vcd::TimescaleUnit::FS, 27 | } 28 | } 29 | } 30 | 31 | pub struct VcdTrace { 32 | module_hierarchy_depth: u32, 33 | 34 | signals: Vec, 35 | 36 | w: vcd::Writer, 37 | } 38 | 39 | impl VcdTrace { 40 | pub fn new(w: W, time_scale: u32, time_scale_unit: TimeScaleUnit) -> io::Result> { 41 | let mut w = vcd::Writer::new(w); 42 | 43 | w.timescale(time_scale, time_scale_unit.into())?; 44 | 45 | Ok(VcdTrace { 46 | module_hierarchy_depth: 0, 47 | 48 | signals: Vec::new(), 49 | 50 | w, 51 | }) 52 | } 53 | } 54 | 55 | impl Trace for VcdTrace { 56 | type SignalId = usize; 57 | 58 | fn push_module(&mut self, name: &'static str) -> io::Result<()> { 59 | self.w.add_module(name)?; 60 | 61 | self.module_hierarchy_depth += 1; 62 | 63 | Ok(()) 64 | } 65 | 66 | fn pop_module(&mut self) -> io::Result<()> { 67 | self.w.upscope()?; 68 | 69 | self.module_hierarchy_depth -= 1; 70 | 71 | if self.module_hierarchy_depth == 0 { 72 | self.w.enddefinitions()?; 73 | } 74 | 75 | Ok(()) 76 | } 77 | 78 | fn add_signal( 79 | &mut self, 80 | name: &'static str, 81 | bit_width: u32, 82 | type_: TraceValueType, 83 | ) -> io::Result { 84 | let ret = self.signals.len(); 85 | 86 | self.signals.push(VcdTraceSignal { 87 | bit_width, 88 | type_, 89 | // TODO: Is wire the right construct here always? 90 | id: self.w.add_wire(bit_width, name)?, 91 | }); 92 | 93 | Ok(ret) 94 | } 95 | 96 | fn update_time_stamp(&mut self, time_stamp: u64) -> io::Result<()> { 97 | self.w.timestamp(time_stamp) 98 | } 99 | 100 | fn update_signal(&mut self, signal_id: &Self::SignalId, value: TraceValue) -> io::Result<()> { 101 | // TODO: Type check incoming value! 102 | let signal = &self.signals[*signal_id]; 103 | 104 | if let TraceValueType::Bool = signal.type_ { 105 | self.w.change_scalar( 106 | signal.id, 107 | match value { 108 | TraceValue::Bool(value) => value, 109 | TraceValue::U32(_) | TraceValue::U64(_) | TraceValue::U128(_) => unreachable!(), 110 | }, 111 | )?; 112 | } else { 113 | let value = match value { 114 | TraceValue::Bool(_) => unreachable!(), 115 | TraceValue::U32(value) => value as _, 116 | TraceValue::U64(value) => value as _, 117 | TraceValue::U128(value) => value, 118 | }; 119 | let mut scalar_values = [vcd::Value::V0; 128]; 120 | for i in 0..signal.bit_width as usize { 121 | scalar_values[i] = ((value >> (signal.bit_width as usize - 1 - i)) & 1 != 0).into(); 122 | } 123 | self.w 124 | .change_vector(signal.id, &scalar_values[0..signal.bit_width as usize])?; 125 | } 126 | 127 | Ok(()) 128 | } 129 | } 130 | 131 | struct VcdTraceSignal { 132 | bit_width: u32, 133 | type_: TraceValueType, 134 | id: vcd::IdCode, 135 | } 136 | -------------------------------------------------------------------------------- /kaze/src/sim.rs: -------------------------------------------------------------------------------- 1 | //! Rust simulator code generation. 2 | 3 | mod compiler; 4 | mod ir; 5 | 6 | use compiler::*; 7 | use ir::*; 8 | 9 | use typed_arena::Arena; 10 | 11 | use crate::code_writer; 12 | use crate::graph; 13 | use crate::runtime::tracing::*; 14 | use crate::state_elements::*; 15 | use crate::validation::*; 16 | 17 | use std::collections::HashMap; 18 | use std::io::{Result, Write}; 19 | 20 | #[derive(Default)] 21 | pub struct GenerationOptions { 22 | pub override_module_name: Option, 23 | pub tracing: bool, 24 | } 25 | 26 | // TODO: Note that mutable writer reference can be passed, see https://rust-lang.github.io/api-guidelines/interoperability.html#c-rw-value 27 | pub fn generate<'a, W: Write>( 28 | m: &'a graph::Module<'a>, 29 | options: GenerationOptions, 30 | w: W, 31 | ) -> Result<()> { 32 | validate_module_hierarchy(m); 33 | 34 | // TODO: Consider exposing as a codegen option (and testing both variants) 35 | let included_ports = if options.tracing { 36 | IncludedPorts::All 37 | } else { 38 | IncludedPorts::ReachableFromTopLevelOutputs 39 | }; 40 | 41 | let mut signal_reference_counts = HashMap::new(); 42 | let state_elements = StateElements::new(m, included_ports, &mut signal_reference_counts); 43 | 44 | struct TraceSignal { 45 | name: String, 46 | member_name: String, 47 | value_name: String, 48 | bit_width: u32, 49 | type_: TraceValueType, 50 | } 51 | let mut trace_signals: HashMap<&'a graph::Module<'a>, Vec> = HashMap::new(); 52 | let mut num_trace_signals = 0; 53 | let mut add_trace_signal = |module, name, value_name, bit_width| { 54 | if options.tracing { 55 | let member_name = format!("__trace_signal_id_{}_{}", name, num_trace_signals); 56 | let module_trace_signals = trace_signals.entry(module).or_insert(Vec::new()); 57 | module_trace_signals.push(TraceSignal { 58 | name, 59 | member_name, 60 | value_name, 61 | bit_width, 62 | type_: TraceValueType::from_bit_width(bit_width), 63 | }); 64 | num_trace_signals += 1; 65 | } 66 | }; 67 | 68 | let expr_arena = Arena::new(); 69 | let mut prop_context = AssignmentContext::new(&expr_arena); 70 | let mut c = Compiler::new(&state_elements, &signal_reference_counts, &expr_arena); 71 | for (name, input) in m.inputs.borrow().iter() { 72 | add_trace_signal(m, name.clone(), name.clone(), input.data.bit_width); 73 | } 74 | for (name, output) in m.outputs.borrow().iter() { 75 | let expr = c.compile_signal(output.data.source, &mut prop_context); 76 | prop_context.push(Assignment { 77 | target: expr_arena.alloc(Expr::Ref { 78 | name: name.clone(), 79 | scope: Scope::Member, 80 | }), 81 | expr, 82 | }); 83 | 84 | add_trace_signal(m, name.clone(), name.clone(), output.data.bit_width); 85 | } 86 | struct InnerField { 87 | name: String, 88 | bit_width: u32, 89 | } 90 | let mut inner_fields = Vec::new(); 91 | if options.tracing { 92 | fn visit_module<'graph, 'context, 'expr_arena>( 93 | module: &'graph graph::Module<'graph>, 94 | c: &mut Compiler<'graph, 'context, 'expr_arena>, 95 | inner_fields: &mut Vec, 96 | prop_context: &mut AssignmentContext<'expr_arena>, 97 | expr_arena: &'expr_arena Arena, 98 | add_trace_signal: &mut impl FnMut(&'graph graph::Module<'graph>, String, String, u32), 99 | ) -> Result<()> { 100 | // TODO: Identify and fix duplicate signals in traces 101 | for (name, &input) in module.inputs.borrow().iter() { 102 | // TODO: De-dupe inner field allocs 103 | let field_name = format!("__inner_{}_{}", name, inner_fields.len()); 104 | inner_fields.push(InnerField { 105 | name: field_name.clone(), 106 | bit_width: input.data.bit_width, 107 | }); 108 | let expr = 109 | c.compile_signal(input.data.driven_value.borrow().unwrap(), prop_context); 110 | prop_context.push(Assignment { 111 | target: expr_arena.alloc(Expr::Ref { 112 | name: field_name.clone(), 113 | scope: Scope::Member, 114 | }), 115 | expr, 116 | }); 117 | 118 | add_trace_signal(module, name.clone(), field_name, input.data.bit_width); 119 | } 120 | for (name, &output) in module.outputs.borrow().iter() { 121 | // TODO: De-dupe inner field allocs 122 | let field_name = format!("__inner_{}_{}", name, inner_fields.len()); 123 | inner_fields.push(InnerField { 124 | name: field_name.clone(), 125 | bit_width: output.data.bit_width, 126 | }); 127 | let expr = c.compile_signal(output.data.source, prop_context); 128 | prop_context.push(Assignment { 129 | target: expr_arena.alloc(Expr::Ref { 130 | name: field_name.clone(), 131 | scope: Scope::Member, 132 | }), 133 | expr, 134 | }); 135 | 136 | add_trace_signal(module, name.clone(), field_name, output.data.bit_width); 137 | } 138 | for child in module.modules.borrow().iter() { 139 | visit_module( 140 | child, 141 | c, 142 | inner_fields, 143 | prop_context, 144 | expr_arena, 145 | add_trace_signal, 146 | )?; 147 | } 148 | 149 | Ok(()) 150 | } 151 | for child in m.modules.borrow().iter() { 152 | visit_module( 153 | child, 154 | &mut c, 155 | &mut inner_fields, 156 | &mut prop_context, 157 | &expr_arena, 158 | &mut add_trace_signal, 159 | )?; 160 | } 161 | } 162 | for (graph_mem, mem) in state_elements.mems.iter() { 163 | for ((address, enable), read_signal_names) in mem.read_signal_names.iter() { 164 | let address = c.compile_signal(address, &mut prop_context); 165 | prop_context.push(Assignment { 166 | target: expr_arena.alloc(Expr::Ref { 167 | name: read_signal_names.address_name.clone(), 168 | scope: Scope::Member, 169 | }), 170 | expr: address, 171 | }); 172 | let enable = c.compile_signal(enable, &mut prop_context); 173 | prop_context.push(Assignment { 174 | target: expr_arena.alloc(Expr::Ref { 175 | name: read_signal_names.enable_name.clone(), 176 | scope: Scope::Member, 177 | }), 178 | expr: enable, 179 | }); 180 | 181 | add_trace_signal( 182 | graph_mem.module, 183 | read_signal_names.address_name.clone(), 184 | read_signal_names.address_name.clone(), 185 | graph_mem.address_bit_width, 186 | ); 187 | add_trace_signal( 188 | graph_mem.module, 189 | read_signal_names.enable_name.clone(), 190 | read_signal_names.enable_name.clone(), 191 | 1, 192 | ); 193 | } 194 | if let Some((address, value, enable)) = *graph_mem.write_port.borrow() { 195 | let address = c.compile_signal(address, &mut prop_context); 196 | prop_context.push(Assignment { 197 | target: expr_arena.alloc(Expr::Ref { 198 | name: mem.write_address_name.clone(), 199 | scope: Scope::Member, 200 | }), 201 | expr: address, 202 | }); 203 | let value = c.compile_signal(value, &mut prop_context); 204 | prop_context.push(Assignment { 205 | target: expr_arena.alloc(Expr::Ref { 206 | name: mem.write_value_name.clone(), 207 | scope: Scope::Member, 208 | }), 209 | expr: value, 210 | }); 211 | let enable = c.compile_signal(enable, &mut prop_context); 212 | prop_context.push(Assignment { 213 | target: expr_arena.alloc(Expr::Ref { 214 | name: mem.write_enable_name.clone(), 215 | scope: Scope::Member, 216 | }), 217 | expr: enable, 218 | }); 219 | 220 | add_trace_signal( 221 | graph_mem.module, 222 | mem.write_address_name.clone(), 223 | mem.write_address_name.clone(), 224 | graph_mem.address_bit_width, 225 | ); 226 | add_trace_signal( 227 | graph_mem.module, 228 | mem.write_value_name.clone(), 229 | mem.write_value_name.clone(), 230 | graph_mem.element_bit_width, 231 | ); 232 | add_trace_signal( 233 | graph_mem.module, 234 | mem.write_enable_name.clone(), 235 | mem.write_enable_name.clone(), 236 | 1, 237 | ); 238 | } 239 | } 240 | for (_, reg) in state_elements.regs.iter() { 241 | let signal = reg.data.next.borrow().unwrap(); 242 | let expr = c.compile_signal(signal, &mut prop_context); 243 | prop_context.push(Assignment { 244 | target: expr_arena.alloc(Expr::Ref { 245 | name: reg.next_name.clone(), 246 | scope: Scope::Member, 247 | }), 248 | expr, 249 | }); 250 | 251 | add_trace_signal( 252 | signal.module, 253 | reg.data.name.clone(), 254 | reg.value_name.clone(), 255 | signal.bit_width(), 256 | ); 257 | } 258 | 259 | let mut w = code_writer::CodeWriter::new(w); 260 | 261 | let module_name = options 262 | .override_module_name 263 | .unwrap_or_else(|| m.name.clone()); 264 | 265 | w.append_indent()?; 266 | w.append(&format!("pub struct {}", module_name))?; 267 | if options.tracing { 268 | w.append("")?; 269 | } 270 | w.append("{")?; 271 | w.append_newline()?; 272 | w.indent(); 273 | 274 | let inputs = m.inputs.borrow(); 275 | if !inputs.is_empty() { 276 | w.append_line("// Inputs")?; 277 | for (name, input) in inputs.iter() { 278 | w.append_line(&format!( 279 | "pub {}: {}, // {} bit(s)", 280 | name, 281 | ValueType::from_bit_width(input.data.bit_width).name(), 282 | input.data.bit_width 283 | ))?; 284 | } 285 | } 286 | 287 | let outputs = m.outputs.borrow(); 288 | if !outputs.is_empty() { 289 | w.append_line("// Outputs")?; 290 | for (name, output) in outputs.iter() { 291 | w.append_line(&format!( 292 | "pub {}: {}, // {} bit(s)", 293 | name, 294 | ValueType::from_bit_width(output.data.bit_width).name(), 295 | output.data.bit_width 296 | ))?; 297 | } 298 | } 299 | 300 | if !state_elements.regs.is_empty() { 301 | w.append_newline()?; 302 | w.append_line("// Regs")?; 303 | for (_, reg) in state_elements.regs.iter() { 304 | let type_name = ValueType::from_bit_width(reg.data.bit_width).name(); 305 | w.append_line(&format!( 306 | "{}: {}, // {} bit(s)", 307 | reg.value_name, type_name, reg.data.bit_width 308 | ))?; 309 | w.append_line(&format!("{}: {},", reg.next_name, type_name))?; 310 | } 311 | } 312 | 313 | if !state_elements.mems.is_empty() { 314 | w.append_newline()?; 315 | w.append_line("// Mems")?; 316 | for (_, mem) in state_elements.mems.iter() { 317 | let address_type_name = ValueType::from_bit_width(mem.mem.address_bit_width).name(); 318 | let element_type_name = ValueType::from_bit_width(mem.mem.element_bit_width).name(); 319 | w.append_line(&format!( 320 | "{}: Box<[{}]>, // {} bit elements", 321 | mem.mem_name, element_type_name, mem.mem.element_bit_width 322 | ))?; 323 | for (_, read_signal_names) in mem.read_signal_names.iter() { 324 | w.append_line(&format!( 325 | "{}: {},", 326 | read_signal_names.address_name, address_type_name 327 | ))?; 328 | w.append_line(&format!( 329 | "{}: {},", 330 | read_signal_names.enable_name, 331 | ValueType::Bool.name() 332 | ))?; 333 | w.append_line(&format!( 334 | "{}: {},", 335 | read_signal_names.value_name, element_type_name 336 | ))?; 337 | } 338 | if mem.mem.write_port.borrow().is_some() { 339 | w.append_line(&format!( 340 | "{}: {},", 341 | mem.write_address_name, address_type_name 342 | ))?; 343 | w.append_line(&format!("{}: {},", mem.write_value_name, element_type_name))?; 344 | w.append_line(&format!( 345 | "{}: {},", 346 | mem.write_enable_name, 347 | ValueType::Bool.name() 348 | ))?; 349 | } 350 | } 351 | } 352 | 353 | if !inner_fields.is_empty() { 354 | w.append_newline()?; 355 | w.append_line("// Inner")?; 356 | for field in &inner_fields { 357 | let type_name = ValueType::from_bit_width(field.bit_width).name(); 358 | w.append_line(&format!( 359 | "{}: {}, // {} bit(s)", 360 | field.name, type_name, field.bit_width 361 | ))?; 362 | } 363 | } 364 | 365 | if options.tracing { 366 | w.append_newline()?; 367 | w.append_line("__trace: T,")?; 368 | for module_trace_signals in trace_signals.values() { 369 | for trace_signal in module_trace_signals.iter() { 370 | w.append_line(&format!("{}: T::SignalId,", trace_signal.member_name))?; 371 | } 372 | } 373 | } 374 | 375 | w.unindent(); 376 | w.append_line("}")?; 377 | w.append_newline()?; 378 | 379 | w.append_line("#[allow(unused_parens)]")?; 380 | w.append_line("#[automatically_derived]")?; 381 | w.append_indent()?; 382 | w.append("impl")?; 383 | if options.tracing { 384 | w.append("")?; 385 | } 386 | w.append(&format!(" {}", module_name))?; 387 | if options.tracing { 388 | w.append("")?; 389 | } 390 | w.append(" {")?; 391 | w.append_newline()?; 392 | w.indent(); 393 | 394 | w.append_indent()?; 395 | w.append("pub fn new(")?; 396 | if options.tracing { 397 | w.append(&format!( 398 | "mut trace: T) -> std::io::Result<{}> {{", 399 | module_name 400 | ))?; 401 | } else { 402 | w.append(&format!(") -> {} {{", module_name))?; 403 | } 404 | w.append_newline()?; 405 | w.indent(); 406 | 407 | if options.tracing { 408 | fn visit_module<'a, W: Write>( 409 | module: &'a graph::Module<'a>, 410 | trace_signals: &HashMap<&'a graph::Module<'a>, Vec>, 411 | w: &mut code_writer::CodeWriter, 412 | ) -> Result<()> { 413 | w.append_line(&format!( 414 | "trace.push_module(\"{}\")?;", 415 | module.instance_name 416 | ))?; 417 | 418 | if let Some(module_trace_signals) = trace_signals.get(&module) { 419 | for trace_signal in module_trace_signals.iter() { 420 | w.append_line(&format!("let {} = trace.add_signal(\"{}\", {}, kaze::runtime::tracing::TraceValueType::{})?;", trace_signal.member_name, trace_signal.name, trace_signal.bit_width, match trace_signal.type_ { 421 | TraceValueType::Bool => "Bool", 422 | TraceValueType::U32 => "U32", 423 | TraceValueType::U64 => "U64", 424 | TraceValueType::U128 => "U128", 425 | }))?; 426 | } 427 | } 428 | 429 | for child in module.modules.borrow().iter() { 430 | visit_module(child, trace_signals, w)?; 431 | } 432 | 433 | w.append_line("trace.pop_module()?;")?; 434 | 435 | Ok(()) 436 | } 437 | visit_module(m, &trace_signals, &mut w)?; 438 | w.append_newline()?; 439 | } 440 | 441 | w.append_indent()?; 442 | if options.tracing { 443 | w.append("Ok(")?; 444 | } 445 | w.append(&format!("{} {{", module_name))?; 446 | w.append_newline()?; 447 | w.indent(); 448 | 449 | if !inputs.is_empty() { 450 | w.append_line("// Inputs")?; 451 | for (name, input) in inputs.iter() { 452 | w.append_line(&format!( 453 | "{}: {}, // {} bit(s)", 454 | name, 455 | ValueType::from_bit_width(input.data.bit_width).zero_str(), 456 | input.data.bit_width 457 | ))?; 458 | } 459 | } 460 | 461 | if !outputs.is_empty() { 462 | w.append_line("// Outputs")?; 463 | for (name, output) in outputs.iter() { 464 | w.append_line(&format!( 465 | "{}: {}, // {} bit(s)", 466 | name, 467 | ValueType::from_bit_width(output.data.bit_width).zero_str(), 468 | output.data.bit_width 469 | ))?; 470 | } 471 | } 472 | 473 | if !state_elements.regs.is_empty() { 474 | w.append_newline()?; 475 | w.append_line("// Regs")?; 476 | for (_, reg) in state_elements.regs.iter() { 477 | w.append_line(&format!( 478 | "{}: {}, // {} bit(s)", 479 | reg.value_name, 480 | ValueType::from_bit_width(reg.data.bit_width).zero_str(), 481 | reg.data.bit_width 482 | ))?; 483 | w.append_line(&format!( 484 | "{}: {},", 485 | reg.next_name, 486 | ValueType::from_bit_width(reg.data.bit_width).zero_str() 487 | ))?; 488 | } 489 | } 490 | 491 | if !state_elements.mems.is_empty() { 492 | w.append_newline()?; 493 | w.append_line("// Mems")?; 494 | for (_, mem) in state_elements.mems.iter() { 495 | let address_type = ValueType::from_bit_width(mem.mem.address_bit_width); 496 | let element_type = ValueType::from_bit_width(mem.mem.element_bit_width); 497 | if let Some(ref initial_contents) = *mem.mem.initial_contents.borrow() { 498 | w.append_line(&format!("{}: vec![", mem.mem_name))?; 499 | w.indent(); 500 | for element in initial_contents.iter() { 501 | w.append_line(&match *element { 502 | graph::Constant::Bool(value) => format!("{},", value), 503 | graph::Constant::U32(value) => format!("0x{:x},", value), 504 | graph::Constant::U64(value) => format!("0x{:x},", value), 505 | graph::Constant::U128(value) => format!("0x{:x},", value), 506 | })?; 507 | } 508 | w.unindent(); 509 | w.append_line("].into_boxed_slice(),")?; 510 | } else { 511 | w.append_line(&format!( 512 | "{}: vec![{}; {}].into_boxed_slice(),", 513 | mem.mem_name, 514 | element_type.zero_str(), 515 | 1 << mem.mem.address_bit_width 516 | ))?; 517 | } 518 | for (_, read_signal_names) in mem.read_signal_names.iter() { 519 | w.append_line(&format!( 520 | "{}: {},", 521 | read_signal_names.address_name, 522 | address_type.zero_str() 523 | ))?; 524 | w.append_line(&format!( 525 | "{}: {},", 526 | read_signal_names.enable_name, 527 | ValueType::Bool.zero_str() 528 | ))?; 529 | w.append_line(&format!( 530 | "{}: {},", 531 | read_signal_names.value_name, 532 | element_type.zero_str() 533 | ))?; 534 | } 535 | if mem.mem.write_port.borrow().is_some() { 536 | w.append_line(&format!( 537 | "{}: {},", 538 | mem.write_address_name, 539 | address_type.zero_str() 540 | ))?; 541 | w.append_line(&format!( 542 | "{}: {},", 543 | mem.write_value_name, 544 | element_type.zero_str() 545 | ))?; 546 | w.append_line(&format!( 547 | "{}: {},", 548 | mem.write_enable_name, 549 | ValueType::Bool.zero_str() 550 | ))?; 551 | } 552 | } 553 | } 554 | 555 | if !inner_fields.is_empty() { 556 | w.append_newline()?; 557 | for field in &inner_fields { 558 | w.append_line(&format!( 559 | "{}: {}, // {} bit(s)", 560 | field.name, 561 | ValueType::from_bit_width(field.bit_width).zero_str(), 562 | field.bit_width 563 | ))?; 564 | } 565 | } 566 | 567 | if options.tracing { 568 | w.append_newline()?; 569 | w.append_line("__trace: trace,")?; 570 | for module_trace_signals in trace_signals.values() { 571 | for trace_signal in module_trace_signals.iter() { 572 | w.append_line(&format!("{},", trace_signal.member_name))?; 573 | } 574 | } 575 | } 576 | 577 | w.unindent(); 578 | w.append_indent()?; 579 | w.append("}")?; 580 | if options.tracing { 581 | w.append(")")?; 582 | } 583 | w.append_newline()?; 584 | w.unindent(); 585 | w.append_line("}")?; 586 | 587 | let mut reset_context = AssignmentContext::new(&expr_arena); 588 | let mut posedge_clk_context = AssignmentContext::new(&expr_arena); 589 | 590 | for (_, reg) in state_elements.regs.iter() { 591 | let target = expr_arena.alloc(Expr::Ref { 592 | name: reg.value_name.clone(), 593 | scope: Scope::Member, 594 | }); 595 | 596 | if let Some(ref initial_value) = *reg.data.initial_value.borrow() { 597 | reset_context.push(Assignment { 598 | target, 599 | expr: Expr::from_constant(initial_value, reg.data.bit_width, &expr_arena), 600 | }); 601 | } 602 | 603 | posedge_clk_context.push(Assignment { 604 | target, 605 | expr: expr_arena.alloc(Expr::Ref { 606 | name: reg.next_name.clone(), 607 | scope: Scope::Member, 608 | }), 609 | }); 610 | } 611 | 612 | for (_, mem) in state_elements.mems.iter() { 613 | for (_, read_signal_names) in mem.read_signal_names.iter() { 614 | let address = expr_arena.alloc(Expr::Ref { 615 | name: read_signal_names.address_name.clone(), 616 | scope: Scope::Member, 617 | }); 618 | let enable = expr_arena.alloc(Expr::Ref { 619 | name: read_signal_names.enable_name.clone(), 620 | scope: Scope::Member, 621 | }); 622 | let value = expr_arena.alloc(Expr::Ref { 623 | name: read_signal_names.value_name.clone(), 624 | scope: Scope::Member, 625 | }); 626 | let element = expr_arena.alloc(Expr::ArrayIndex { 627 | target: expr_arena.alloc(Expr::Ref { 628 | name: mem.mem_name.clone(), 629 | scope: Scope::Member, 630 | }), 631 | index: address, 632 | }); 633 | // TODO: Conditional assign statement instead of always writing ternary 634 | posedge_clk_context.push(Assignment { 635 | target: value, 636 | expr: expr_arena.alloc(Expr::Ternary { 637 | cond: enable, 638 | when_true: element, 639 | when_false: value, 640 | }), 641 | }); 642 | } 643 | if mem.mem.write_port.borrow().is_some() { 644 | let address = expr_arena.alloc(Expr::Ref { 645 | name: mem.write_address_name.clone(), 646 | scope: Scope::Member, 647 | }); 648 | let value = expr_arena.alloc(Expr::Ref { 649 | name: mem.write_value_name.clone(), 650 | scope: Scope::Member, 651 | }); 652 | let enable = expr_arena.alloc(Expr::Ref { 653 | name: mem.write_enable_name.clone(), 654 | scope: Scope::Member, 655 | }); 656 | let element = expr_arena.alloc(Expr::ArrayIndex { 657 | target: expr_arena.alloc(Expr::Ref { 658 | name: mem.mem_name.clone(), 659 | scope: Scope::Member, 660 | }), 661 | index: address, 662 | }); 663 | // TODO: Conditional assign statement instead of always writing ternary 664 | posedge_clk_context.push(Assignment { 665 | target: element, 666 | expr: expr_arena.alloc(Expr::Ternary { 667 | cond: enable, 668 | when_true: value, 669 | when_false: element, 670 | }), 671 | }); 672 | } 673 | } 674 | 675 | if !reset_context.is_empty() { 676 | w.append_newline()?; 677 | w.append_line("pub fn reset(&mut self) {")?; 678 | w.indent(); 679 | 680 | reset_context.write(&mut w)?; 681 | 682 | w.unindent(); 683 | w.append_line("}")?; 684 | } 685 | 686 | if !posedge_clk_context.is_empty() { 687 | w.append_newline()?; 688 | w.append_line("pub fn posedge_clk(&mut self) {")?; 689 | w.indent(); 690 | 691 | posedge_clk_context.write(&mut w)?; 692 | 693 | w.unindent(); 694 | w.append_line("}")?; 695 | } 696 | 697 | w.append_newline()?; 698 | w.append_line("pub fn prop(&mut self) {")?; 699 | w.indent(); 700 | 701 | prop_context.write(&mut w)?; 702 | 703 | w.unindent(); 704 | w.append_line("}")?; 705 | 706 | if options.tracing { 707 | w.append_newline()?; 708 | w.append_line("pub fn update_trace(&mut self, time_stamp: u64) -> std::io::Result<()> {")?; 709 | w.indent(); 710 | 711 | w.append_line("self.__trace.update_time_stamp(time_stamp)?;")?; 712 | w.append_newline()?; 713 | 714 | for module_trace_signals in trace_signals.values() { 715 | for trace_signal in module_trace_signals.iter() { 716 | w.append_line(&format!("self.__trace.update_signal(&self.{}, kaze::runtime::tracing::TraceValue::{}(self.{}))?;", trace_signal.member_name, match trace_signal.type_ { 717 | TraceValueType::Bool => "Bool", 718 | TraceValueType::U32 => "U32", 719 | TraceValueType::U64 => "U64", 720 | TraceValueType::U128 => "U128", 721 | }, trace_signal.value_name))?; 722 | } 723 | } 724 | w.append_newline()?; 725 | 726 | w.append_line("Ok(())")?; 727 | 728 | w.unindent(); 729 | w.append_line("}")?; 730 | } 731 | 732 | w.unindent(); 733 | w.append_line("}")?; 734 | w.append_newline()?; 735 | 736 | Ok(()) 737 | } 738 | 739 | #[cfg(test)] 740 | mod tests { 741 | use super::*; 742 | 743 | use crate::*; 744 | 745 | #[test] 746 | #[should_panic( 747 | expected = "Cannot generate code for module \"A\" because module \"A\" contains an instance of module \"B\" called \"b\" whose input \"i\" is not driven." 748 | )] 749 | fn undriven_instance_input_error() { 750 | let c = Context::new(); 751 | 752 | let a = c.module("a", "A"); 753 | let b = a.module("b", "B"); 754 | let _ = b.input("i", 1); 755 | 756 | // Panic 757 | generate(a, GenerationOptions::default(), Vec::new()).unwrap(); 758 | } 759 | 760 | #[test] 761 | #[should_panic( 762 | expected = "Cannot generate code for module \"A\" because module \"A\" contains a register called \"r\" which is not driven." 763 | )] 764 | fn undriven_register_error1() { 765 | let c = Context::new(); 766 | 767 | let a = c.module("a", "A"); 768 | let _ = a.reg("r", 1); 769 | 770 | // Panic 771 | generate(a, GenerationOptions::default(), Vec::new()).unwrap(); 772 | } 773 | 774 | #[test] 775 | #[should_panic( 776 | expected = "Cannot generate code for module \"A\" because module \"B\" contains a register called \"r\" which is not driven." 777 | )] 778 | fn undriven_register_error2() { 779 | let c = Context::new(); 780 | 781 | let a = c.module("a", "A"); 782 | let b = a.module("b", "B"); 783 | let _ = b.reg("r", 1); 784 | 785 | // Panic 786 | generate(a, GenerationOptions::default(), Vec::new()).unwrap(); 787 | } 788 | 789 | #[test] 790 | #[should_panic( 791 | expected = "Cannot generate code for module \"A\" because module \"A\" contains a memory called \"m\" which doesn't have any read ports." 792 | )] 793 | fn mem_without_read_ports_error1() { 794 | let c = Context::new(); 795 | 796 | let a = c.module("a", "A"); 797 | let _ = a.mem("m", 1, 1); 798 | 799 | // Panic 800 | generate(a, GenerationOptions::default(), Vec::new()).unwrap(); 801 | } 802 | 803 | #[test] 804 | #[should_panic( 805 | expected = "Cannot generate code for module \"A\" because module \"B\" contains a memory called \"m\" which doesn't have any read ports." 806 | )] 807 | fn mem_without_read_ports_error2() { 808 | let c = Context::new(); 809 | 810 | let a = c.module("a", "A"); 811 | let b = a.module("b", "B"); 812 | let _ = b.mem("m", 1, 1); 813 | 814 | // Panic 815 | generate(a, GenerationOptions::default(), Vec::new()).unwrap(); 816 | } 817 | 818 | #[test] 819 | #[should_panic( 820 | expected = "Cannot generate code for module \"A\" because module \"A\" contains a memory called \"m\" which doesn't have initial contents or a write port specified. At least one of the two is required." 821 | )] 822 | fn mem_without_initial_contents_or_write_port_error1() { 823 | let c = Context::new(); 824 | 825 | let a = c.module("a", "A"); 826 | let m = a.mem("m", 1, 1); 827 | let _ = m.read_port(a.low(), a.low()); 828 | 829 | // Panic 830 | generate(a, GenerationOptions::default(), Vec::new()).unwrap(); 831 | } 832 | 833 | #[test] 834 | #[should_panic( 835 | expected = "Cannot generate code for module \"A\" because module \"B\" contains a memory called \"m\" which doesn't have initial contents or a write port specified. At least one of the two is required." 836 | )] 837 | fn mem_without_initial_contents_or_write_port_error2() { 838 | let c = Context::new(); 839 | 840 | let a = c.module("a", "A"); 841 | let b = a.module("b", "B"); 842 | let m = b.mem("m", 1, 1); 843 | let _ = m.read_port(b.low(), b.low()); 844 | 845 | // Panic 846 | generate(a, GenerationOptions::default(), Vec::new()).unwrap(); 847 | } 848 | 849 | #[test] 850 | #[should_panic( 851 | expected = "Cannot generate code for module \"b\" because module \"a\" contains an output called \"o\" which forms a combinational loop with itself." 852 | )] 853 | fn combinational_loop_error() { 854 | let c = Context::new(); 855 | 856 | let b = c.module("b", "b"); 857 | let a = b.module("a", "a"); 858 | let a_i = a.input("i", 1); 859 | let a_o = a.output("o", a_i); 860 | a_i.drive(a_o); 861 | 862 | // Panic 863 | generate(b, GenerationOptions::default(), Vec::new()).unwrap(); 864 | } 865 | } 866 | -------------------------------------------------------------------------------- /kaze/src/sim/ir.rs: -------------------------------------------------------------------------------- 1 | use crate::code_writer; 2 | use crate::graph; 3 | 4 | use typed_arena::Arena; 5 | 6 | use std::io::{Result, Write}; 7 | 8 | pub struct AssignmentContext<'arena> { 9 | arena: &'arena Arena>, 10 | assignments: Vec>, 11 | local_count: u32, 12 | } 13 | 14 | impl<'arena> AssignmentContext<'arena> { 15 | pub fn new(arena: &'arena Arena>) -> AssignmentContext<'arena> { 16 | AssignmentContext { 17 | arena, 18 | assignments: Vec::new(), 19 | local_count: 0, 20 | } 21 | } 22 | 23 | pub fn gen_temp(&mut self, expr: &'arena Expr<'arena>) -> &'arena Expr<'arena> { 24 | match expr { 25 | // We don't need to generate a temp for Constants or Refs 26 | Expr::Constant { .. } | Expr::Ref { .. } => expr, 27 | _ => { 28 | let name = format!("__temp_{}", self.local_count); 29 | self.local_count += 1; 30 | 31 | self.assignments.push(Assignment { 32 | target: self.arena.alloc(Expr::Ref { 33 | name: name.clone(), 34 | scope: Scope::Local, 35 | }), 36 | expr, 37 | }); 38 | 39 | self.arena.alloc(Expr::Ref { 40 | name, 41 | scope: Scope::Local, 42 | }) 43 | } 44 | } 45 | } 46 | 47 | pub fn is_empty(&self) -> bool { 48 | self.assignments.is_empty() 49 | } 50 | 51 | pub fn push(&mut self, assignment: Assignment<'arena>) { 52 | self.assignments.push(assignment); 53 | } 54 | 55 | pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> { 56 | for assignment in self.assignments.iter() { 57 | assignment.write(w)?; 58 | } 59 | 60 | Ok(()) 61 | } 62 | } 63 | 64 | pub struct Assignment<'arena> { 65 | pub target: &'arena Expr<'arena>, 66 | pub expr: &'arena Expr<'arena>, 67 | } 68 | 69 | impl<'arena> Assignment<'arena> { 70 | pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> { 71 | w.append_indent()?; 72 | // TODO: I hate these kind of conditionals... 73 | if let Expr::Ref { ref scope, .. } = self.target { 74 | match scope { 75 | Scope::Local => { 76 | w.append("let ")?; 77 | } 78 | Scope::Member => (), 79 | } 80 | } 81 | self.target.write(w)?; 82 | w.append(" = ")?; 83 | self.expr.write(w)?; 84 | w.append(";")?; 85 | w.append_newline()?; 86 | 87 | Ok(()) 88 | } 89 | } 90 | 91 | pub enum Expr<'arena> { 92 | ArrayIndex { 93 | target: &'arena Expr<'arena>, 94 | index: &'arena Expr<'arena>, 95 | }, 96 | BinaryFunctionCall { 97 | name: String, 98 | lhs: &'arena Expr<'arena>, 99 | rhs: &'arena Expr<'arena>, 100 | }, 101 | Cast { 102 | source: &'arena Expr<'arena>, 103 | target_type: ValueType, 104 | }, 105 | Constant { 106 | value: Constant, 107 | }, 108 | InfixBinOp { 109 | lhs: &'arena Expr<'arena>, 110 | rhs: &'arena Expr<'arena>, 111 | op: InfixBinOp, 112 | }, 113 | Ref { 114 | name: String, 115 | scope: Scope, 116 | }, 117 | Ternary { 118 | cond: &'arena Expr<'arena>, 119 | when_true: &'arena Expr<'arena>, 120 | when_false: &'arena Expr<'arena>, 121 | }, 122 | UnaryMemberCall { 123 | target: &'arena Expr<'arena>, 124 | name: String, 125 | arg: &'arena Expr<'arena>, 126 | }, 127 | UnOp { 128 | source: &'arena Expr<'arena>, 129 | op: UnOp, 130 | }, 131 | } 132 | 133 | impl<'arena> Expr<'arena> { 134 | pub fn from_constant( 135 | value: &graph::Constant, 136 | bit_width: u32, 137 | arena: &'arena Arena>, 138 | ) -> &'arena Expr<'arena> { 139 | let value = value.numeric_value(); 140 | 141 | let target_type = ValueType::from_bit_width(bit_width); 142 | arena.alloc(Expr::Constant { 143 | value: match target_type { 144 | ValueType::Bool => Constant::Bool(value != 0), 145 | ValueType::I32 | ValueType::I64 | ValueType::I128 => unreachable!(), 146 | ValueType::U32 => Constant::U32(value as _), 147 | ValueType::U64 => Constant::U64(value as _), 148 | ValueType::U128 => Constant::U128(value), 149 | }, 150 | }) 151 | } 152 | 153 | pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> { 154 | enum Command<'arena> { 155 | Expr { expr: &'arena Expr<'arena> }, 156 | Str { s: &'arena str }, 157 | } 158 | 159 | let mut commands = Vec::new(); 160 | commands.push(Command::Expr { expr: self }); 161 | 162 | while let Some(command) = commands.pop() { 163 | match command { 164 | Command::Expr { expr } => match *expr { 165 | Expr::ArrayIndex { 166 | ref target, 167 | ref index, 168 | } => { 169 | commands.push(Command::Str { s: " as usize]" }); 170 | commands.push(Command::Expr { expr: index }); 171 | commands.push(Command::Str { s: "[" }); 172 | commands.push(Command::Expr { expr: target }); 173 | } 174 | Expr::BinaryFunctionCall { 175 | ref name, 176 | ref lhs, 177 | ref rhs, 178 | } => { 179 | commands.push(Command::Str { s: ")" }); 180 | commands.push(Command::Expr { expr: rhs }); 181 | commands.push(Command::Str { s: ", " }); 182 | commands.push(Command::Expr { expr: lhs }); 183 | w.append(&format!("{}(", name))?; 184 | } 185 | Expr::Cast { 186 | ref source, 187 | target_type, 188 | } => { 189 | commands.push(Command::Str { s: ")" }); 190 | commands.push(Command::Str { 191 | s: &target_type.name(), 192 | }); 193 | commands.push(Command::Str { s: " as " }); 194 | commands.push(Command::Expr { expr: source }); 195 | w.append("(")?; 196 | } 197 | Expr::Constant { ref value } => { 198 | w.append(&match value { 199 | Constant::Bool(value) => format!("{}", value), 200 | Constant::U32(value) => format!("0x{:x}u32", value), 201 | Constant::U64(value) => format!("0x{:x}u64", value), 202 | Constant::U128(value) => format!("0x{:x}u128", value), 203 | })?; 204 | } 205 | Expr::InfixBinOp { 206 | ref lhs, 207 | ref rhs, 208 | op, 209 | } => { 210 | commands.push(Command::Str { s: ")" }); 211 | commands.push(Command::Expr { expr: rhs }); 212 | commands.push(Command::Str { s: " " }); 213 | commands.push(Command::Str { 214 | s: match op { 215 | InfixBinOp::BitAnd => "&", 216 | InfixBinOp::BitOr => "|", 217 | InfixBinOp::BitXor => "^", 218 | InfixBinOp::Equal => "==", 219 | InfixBinOp::NotEqual => "!=", 220 | InfixBinOp::LessThan => "<", 221 | InfixBinOp::LessThanEqual => "<=", 222 | InfixBinOp::GreaterThan => ">", 223 | InfixBinOp::GreaterThanEqual => ">=", 224 | InfixBinOp::Shl => "<<", 225 | InfixBinOp::Shr => ">>", 226 | InfixBinOp::Mul => "*", 227 | }, 228 | }); 229 | commands.push(Command::Str { s: " " }); 230 | commands.push(Command::Expr { expr: lhs }); 231 | w.append("(")?; 232 | } 233 | Expr::Ref { ref name, scope } => { 234 | if let Scope::Member = scope { 235 | w.append("self.")?; 236 | } 237 | w.append(name)?; 238 | } 239 | Expr::Ternary { 240 | ref cond, 241 | ref when_true, 242 | ref when_false, 243 | } => { 244 | commands.push(Command::Str { s: "}" }); 245 | commands.push(Command::Expr { expr: when_false }); 246 | commands.push(Command::Str { s: " } else { " }); 247 | commands.push(Command::Expr { expr: when_true }); 248 | commands.push(Command::Str { s: " { " }); 249 | commands.push(Command::Expr { expr: cond }); 250 | w.append("if ")?; 251 | } 252 | Expr::UnaryMemberCall { 253 | ref target, 254 | ref name, 255 | ref arg, 256 | } => { 257 | commands.push(Command::Str { s: ")" }); 258 | commands.push(Command::Expr { expr: arg }); 259 | commands.push(Command::Str { s: "(" }); 260 | commands.push(Command::Str { s: name }); 261 | commands.push(Command::Str { s: "." }); 262 | commands.push(Command::Expr { expr: target }); 263 | } 264 | Expr::UnOp { ref source, op } => { 265 | w.append(match op { 266 | UnOp::Not => "!", 267 | })?; 268 | commands.push(Command::Expr { expr: source }); 269 | } 270 | }, 271 | Command::Str { s } => { 272 | w.append(s)?; 273 | } 274 | } 275 | } 276 | 277 | Ok(()) 278 | } 279 | } 280 | 281 | pub enum Constant { 282 | Bool(bool), 283 | U32(u32), 284 | U64(u64), 285 | U128(u128), 286 | } 287 | 288 | #[derive(Clone, Copy)] 289 | pub enum InfixBinOp { 290 | BitAnd, 291 | BitOr, 292 | BitXor, 293 | Equal, 294 | NotEqual, 295 | LessThan, 296 | LessThanEqual, 297 | GreaterThan, 298 | GreaterThanEqual, 299 | Shl, 300 | Shr, 301 | Mul, 302 | } 303 | 304 | #[derive(Clone, Copy)] 305 | pub enum Scope { 306 | Local, 307 | Member, 308 | } 309 | 310 | #[derive(Clone, Copy)] 311 | pub enum UnOp { 312 | Not, 313 | } 314 | 315 | #[derive(Clone, Copy, PartialEq)] 316 | pub enum ValueType { 317 | Bool, 318 | I32, 319 | I64, 320 | I128, 321 | U32, 322 | U64, 323 | U128, 324 | } 325 | 326 | impl ValueType { 327 | pub fn from_bit_width(bit_width: u32) -> ValueType { 328 | if bit_width == 1 { 329 | ValueType::Bool 330 | } else if bit_width <= 32 { 331 | ValueType::U32 332 | } else if bit_width <= 64 { 333 | ValueType::U64 334 | } else if bit_width <= 128 { 335 | ValueType::U128 336 | } else { 337 | unreachable!() 338 | } 339 | } 340 | 341 | pub fn to_signed(&self) -> ValueType { 342 | match self { 343 | ValueType::Bool | ValueType::I32 | ValueType::I64 | ValueType::I128 => unreachable!(), 344 | ValueType::U32 => ValueType::I32, 345 | ValueType::U64 => ValueType::I64, 346 | ValueType::U128 => ValueType::I128, 347 | } 348 | } 349 | 350 | pub fn name(&self) -> &'static str { 351 | match self { 352 | ValueType::Bool => "bool", 353 | ValueType::I32 => "i32", 354 | ValueType::I64 => "i64", 355 | ValueType::I128 => "i128", 356 | ValueType::U32 => "u32", 357 | ValueType::U64 => "u64", 358 | ValueType::U128 => "u128", 359 | } 360 | } 361 | 362 | pub fn bit_width(&self) -> u32 { 363 | match self { 364 | ValueType::Bool => 1, 365 | ValueType::I32 | ValueType::U32 => 32, 366 | ValueType::I64 | ValueType::U64 => 64, 367 | ValueType::I128 | ValueType::U128 => 128, 368 | } 369 | } 370 | 371 | pub fn zero_str(&self) -> &'static str { 372 | match self { 373 | ValueType::Bool => "false", 374 | _ => "0", 375 | } 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /kaze/src/state_elements.rs: -------------------------------------------------------------------------------- 1 | use crate::graph; 2 | use crate::graph::internal_signal; 3 | 4 | use std::collections::HashMap; 5 | 6 | pub(super) struct Register<'a> { 7 | pub data: &'a graph::RegisterData<'a>, 8 | pub value_name: String, 9 | pub next_name: String, 10 | } 11 | 12 | pub(super) struct Mem<'a> { 13 | pub mem: &'a graph::Mem<'a>, 14 | pub mem_name: String, 15 | pub read_signal_names: HashMap< 16 | ( 17 | &'a internal_signal::InternalSignal<'a>, 18 | &'a internal_signal::InternalSignal<'a>, 19 | ), 20 | ReadSignalNames, 21 | >, 22 | pub write_address_name: String, 23 | pub write_value_name: String, 24 | pub write_enable_name: String, 25 | } 26 | 27 | pub struct ReadSignalNames { 28 | pub address_name: String, 29 | pub enable_name: String, 30 | pub value_name: String, 31 | } 32 | 33 | // TODO: Move? 34 | // TODO: Cover registers as well 35 | #[derive(Clone, Copy)] 36 | pub(super) enum IncludedPorts { 37 | All, 38 | ReachableFromTopLevelOutputs, 39 | } 40 | 41 | pub(super) struct StateElements<'a> { 42 | pub mems: HashMap<&'a graph::Mem<'a>, Mem<'a>>, 43 | pub regs: HashMap<&'a internal_signal::InternalSignal<'a>, Register<'a>>, 44 | } 45 | 46 | impl<'a> StateElements<'a> { 47 | pub fn new( 48 | m: &'a graph::Module<'a>, 49 | // TODO: Cover registers as well 50 | included_ports: IncludedPorts, 51 | signal_reference_counts: &mut HashMap<&'a internal_signal::InternalSignal<'a>, u32>, 52 | ) -> StateElements<'a> { 53 | let mut mems = HashMap::new(); 54 | let mut regs = HashMap::new(); 55 | 56 | visit_module( 57 | m, 58 | included_ports, 59 | &mut mems, 60 | &mut regs, 61 | signal_reference_counts, 62 | ); 63 | 64 | StateElements { mems, regs } 65 | } 66 | } 67 | 68 | fn visit_module<'a>( 69 | m: &'a graph::Module<'a>, 70 | included_ports: IncludedPorts, 71 | mems: &mut HashMap<&'a graph::Mem<'a>, Mem<'a>>, 72 | regs: &mut HashMap<&'a internal_signal::InternalSignal<'a>, Register<'a>>, 73 | signal_reference_counts: &mut HashMap<&'a internal_signal::InternalSignal<'a>, u32>, 74 | ) { 75 | match included_ports { 76 | // TODO: Match all of these with traces 77 | // TODO: Test 78 | IncludedPorts::All => { 79 | for (_, &input) in m.inputs.borrow().iter() { 80 | visit_signal(input.value, mems, regs, signal_reference_counts); 81 | } 82 | for (_, &output) in m.outputs.borrow().iter() { 83 | visit_signal(output.data.source, mems, regs, signal_reference_counts); 84 | } 85 | for ®ister in m.registers.borrow().iter() { 86 | match register.data { 87 | internal_signal::SignalData::Reg { ref data } => { 88 | visit_signal( 89 | data.next.borrow().unwrap(), 90 | mems, 91 | regs, 92 | signal_reference_counts, 93 | ); 94 | } 95 | _ => unreachable!(), 96 | } 97 | } 98 | for &module in m.modules.borrow().iter() { 99 | visit_module(module, included_ports, mems, regs, signal_reference_counts); 100 | } 101 | // TODO: Cover all mems as well 102 | } 103 | IncludedPorts::ReachableFromTopLevelOutputs => { 104 | for (_, &output) in m.outputs.borrow().iter() { 105 | visit_signal(output.data.source, mems, regs, signal_reference_counts); 106 | } 107 | } 108 | } 109 | } 110 | 111 | // TODO: Move this to ctor and iterate over input module outputs there? 112 | fn visit_signal<'a>( 113 | signal: &'a internal_signal::InternalSignal<'a>, 114 | mems: &mut HashMap<&'a graph::Mem<'a>, Mem<'a>>, 115 | regs: &mut HashMap<&'a internal_signal::InternalSignal<'a>, Register<'a>>, 116 | signal_reference_counts: &mut HashMap<&'a internal_signal::InternalSignal<'a>, u32>, 117 | ) { 118 | // TODO: Do we even need this with just the one member? 119 | struct Frame<'a> { 120 | signal: &'a internal_signal::InternalSignal<'a>, 121 | } 122 | 123 | let mut frames = Vec::new(); 124 | frames.push(Frame { signal }); 125 | 126 | while let Some(frame) = frames.pop() { 127 | let signal = frame.signal; 128 | 129 | let reference_count = signal_reference_counts.entry(signal).or_insert(0); 130 | *reference_count += 1; 131 | 132 | if *reference_count > 1 { 133 | continue; 134 | } 135 | 136 | match signal.data { 137 | internal_signal::SignalData::Lit { .. } => (), 138 | 139 | internal_signal::SignalData::Input { data } => { 140 | if let Some(driven_value) = data.driven_value.borrow().clone() { 141 | frames.push(Frame { 142 | signal: driven_value, 143 | }); 144 | } 145 | } 146 | internal_signal::SignalData::Output { data } => { 147 | frames.push(Frame { 148 | signal: data.source, 149 | }); 150 | } 151 | 152 | internal_signal::SignalData::Reg { data } => { 153 | let key = signal; 154 | let value_name = format!( 155 | "__reg_{}_{}_{}", 156 | signal.module_instance_name_prefix(), 157 | data.name, 158 | regs.len() 159 | ); 160 | let next_name = format!("{}_next", value_name); 161 | regs.insert( 162 | key, 163 | Register { 164 | data, 165 | value_name, 166 | next_name, 167 | }, 168 | ); 169 | frames.push(Frame { 170 | signal: data.next.borrow().unwrap(), 171 | }); 172 | } 173 | 174 | internal_signal::SignalData::UnOp { source, .. } => { 175 | frames.push(Frame { signal: source }); 176 | } 177 | internal_signal::SignalData::SimpleBinOp { lhs, rhs, .. } => { 178 | frames.push(Frame { signal: lhs }); 179 | frames.push(Frame { signal: rhs }); 180 | } 181 | internal_signal::SignalData::AdditiveBinOp { lhs, rhs, .. } => { 182 | frames.push(Frame { signal: lhs }); 183 | frames.push(Frame { signal: rhs }); 184 | } 185 | internal_signal::SignalData::ComparisonBinOp { lhs, rhs, .. } => { 186 | frames.push(Frame { signal: lhs }); 187 | frames.push(Frame { signal: rhs }); 188 | } 189 | internal_signal::SignalData::ShiftBinOp { lhs, rhs, .. } => { 190 | frames.push(Frame { signal: lhs }); 191 | frames.push(Frame { signal: rhs }); 192 | } 193 | 194 | internal_signal::SignalData::Mul { lhs, rhs, .. } => { 195 | frames.push(Frame { signal: lhs }); 196 | frames.push(Frame { signal: rhs }); 197 | } 198 | internal_signal::SignalData::MulSigned { lhs, rhs, .. } => { 199 | frames.push(Frame { signal: lhs }); 200 | frames.push(Frame { signal: rhs }); 201 | } 202 | 203 | internal_signal::SignalData::Bits { source, .. } => { 204 | frames.push(Frame { signal: source }); 205 | } 206 | 207 | internal_signal::SignalData::Repeat { source, .. } => { 208 | frames.push(Frame { signal: source }); 209 | } 210 | internal_signal::SignalData::Concat { lhs, rhs, .. } => { 211 | frames.push(Frame { signal: lhs }); 212 | frames.push(Frame { signal: rhs }); 213 | } 214 | 215 | internal_signal::SignalData::Mux { 216 | cond, 217 | when_true, 218 | when_false, 219 | .. 220 | } => { 221 | frames.push(Frame { signal: cond }); 222 | frames.push(Frame { signal: when_true }); 223 | frames.push(Frame { signal: when_false }); 224 | } 225 | 226 | internal_signal::SignalData::MemReadPortOutput { mem, .. } => { 227 | let key = mem; 228 | let mem_name = format!( 229 | "__mem_{}_{}_{}", 230 | signal.module_instance_name_prefix(), 231 | mem.name, 232 | mems.len() 233 | ); 234 | // TODO: It might actually be too conservative to trace all read ports, 235 | // as we only know that the write port and _this_ read port are reachable 236 | // at this point, but we have to keep some extra state to know whether or 237 | // not we've hit each read port otherwise. 238 | let mut read_signal_names = HashMap::new(); 239 | for (index, (address, enable)) in mem.read_ports.borrow().iter().enumerate() { 240 | let name_prefix = format!("{}_read_port_{}_", mem_name, index); 241 | read_signal_names.insert( 242 | (*address, *enable), 243 | ReadSignalNames { 244 | address_name: format!("{}address", name_prefix), 245 | enable_name: format!("{}enable", name_prefix), 246 | value_name: format!("{}value", name_prefix), 247 | }, 248 | ); 249 | } 250 | let name_prefix = format!("{}_write_port_", mem_name); 251 | let write_address_name = format!("{}address", name_prefix); 252 | let write_value_name = format!("{}value", name_prefix); 253 | let write_enable_name = format!("{}enable", name_prefix); 254 | mems.insert( 255 | key, 256 | Mem { 257 | mem, 258 | mem_name, 259 | write_address_name, 260 | write_value_name, 261 | write_enable_name, 262 | read_signal_names, 263 | }, 264 | ); 265 | for (address, enable) in mem.read_ports.borrow().iter() { 266 | frames.push(Frame { signal: address }); 267 | frames.push(Frame { signal: enable }); 268 | } 269 | if let Some((address, value, enable)) = *mem.write_port.borrow() { 270 | frames.push(Frame { signal: address }); 271 | frames.push(Frame { signal: value }); 272 | frames.push(Frame { signal: enable }); 273 | } 274 | } 275 | } 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /kaze/src/validation.rs: -------------------------------------------------------------------------------- 1 | use crate::graph; 2 | use crate::graph::internal_signal; 3 | 4 | pub fn validate_module_hierarchy<'a>(m: &'a graph::Module<'a>) { 5 | detect_undriven_registers_and_inputs(m, m); 6 | detect_mem_errors(m, m); 7 | detect_combinational_loops(m, m); 8 | } 9 | 10 | fn detect_undriven_registers_and_inputs<'a>(m: &graph::Module<'a>, root: &graph::Module<'a>) { 11 | for register in m.registers.borrow().iter() { 12 | match register.data { 13 | internal_signal::SignalData::Reg { ref data } => { 14 | if data.next.borrow().is_none() { 15 | panic!("Cannot generate code for module \"{}\" because module \"{}\" contains a register called \"{}\" which is not driven.", root.name, m.name, data.name); 16 | } 17 | } 18 | _ => unreachable!(), 19 | } 20 | } 21 | 22 | for module in m.modules.borrow().iter() { 23 | for (name, input) in module.inputs.borrow().iter() { 24 | if input.data.driven_value.borrow().is_none() { 25 | panic!("Cannot generate code for module \"{}\" because module \"{}\" contains an instance of module \"{}\" called \"{}\" whose input \"{}\" is not driven.", root.name, m.name, module.name, module.instance_name, name); 26 | } 27 | } 28 | 29 | detect_undriven_registers_and_inputs(module, root); 30 | } 31 | } 32 | 33 | fn detect_mem_errors<'a>(m: &graph::Module<'a>, root: &graph::Module<'a>) { 34 | for mem in m.mems.borrow().iter() { 35 | if mem.read_ports.borrow().is_empty() { 36 | panic!("Cannot generate code for module \"{}\" because module \"{}\" contains a memory called \"{}\" which doesn't have any read ports.", root.name, m.name, mem.name); 37 | } 38 | 39 | if mem.initial_contents.borrow().is_none() && mem.write_port.borrow().is_none() { 40 | panic!("Cannot generate code for module \"{}\" because module \"{}\" contains a memory called \"{}\" which doesn't have initial contents or a write port specified. At least one of the two is required.", root.name, m.name, mem.name); 41 | } 42 | } 43 | 44 | for module in m.modules.borrow().iter() { 45 | detect_mem_errors(module, root); 46 | } 47 | } 48 | 49 | fn detect_combinational_loops<'a>(m: &graph::Module<'a>, root: &graph::Module<'a>) { 50 | for module in m.modules.borrow().iter() { 51 | for (_, output) in module.outputs.borrow().iter() { 52 | trace_signal(output.data.source, output.data.source, root); 53 | } 54 | 55 | detect_combinational_loops(module, root); 56 | } 57 | } 58 | 59 | fn trace_signal<'a>( 60 | signal: &'a internal_signal::InternalSignal<'a>, 61 | source_output: &'a internal_signal::InternalSignal<'a>, 62 | root: &graph::Module<'a>, 63 | ) { 64 | struct Frame<'a> { 65 | signal: &'a internal_signal::InternalSignal<'a>, 66 | } 67 | 68 | let mut frames = Vec::new(); 69 | frames.push(Frame { signal }); 70 | 71 | while let Some(frame) = frames.pop() { 72 | let signal = frame.signal; 73 | 74 | match signal.data { 75 | internal_signal::SignalData::Lit { .. } => (), 76 | 77 | internal_signal::SignalData::Input { data } => { 78 | if let Some(driven_value) = data.driven_value.borrow().clone() { 79 | frames.push(Frame { 80 | signal: driven_value, 81 | }); 82 | } 83 | } 84 | internal_signal::SignalData::Output { data } => { 85 | if data.source == source_output { 86 | panic!("Cannot generate code for module \"{}\" because module \"{}\" contains an output called \"{}\" which forms a combinational loop with itself.", root.name, data.module.name, data.name); 87 | } 88 | frames.push(Frame { 89 | signal: data.source, 90 | }); 91 | } 92 | 93 | internal_signal::SignalData::Reg { .. } => (), 94 | 95 | internal_signal::SignalData::UnOp { ref source, .. } => { 96 | frames.push(Frame { signal: source }); 97 | } 98 | internal_signal::SignalData::SimpleBinOp { 99 | ref lhs, ref rhs, .. 100 | } => { 101 | frames.push(Frame { signal: lhs }); 102 | frames.push(Frame { signal: rhs }); 103 | } 104 | internal_signal::SignalData::AdditiveBinOp { 105 | ref lhs, ref rhs, .. 106 | } => { 107 | frames.push(Frame { signal: lhs }); 108 | frames.push(Frame { signal: rhs }); 109 | } 110 | internal_signal::SignalData::ComparisonBinOp { 111 | ref lhs, ref rhs, .. 112 | } => { 113 | frames.push(Frame { signal: lhs }); 114 | frames.push(Frame { signal: rhs }); 115 | } 116 | internal_signal::SignalData::ShiftBinOp { 117 | ref lhs, ref rhs, .. 118 | } => { 119 | frames.push(Frame { signal: lhs }); 120 | frames.push(Frame { signal: rhs }); 121 | } 122 | 123 | internal_signal::SignalData::Mul { 124 | ref lhs, ref rhs, .. 125 | } => { 126 | frames.push(Frame { signal: lhs }); 127 | frames.push(Frame { signal: rhs }); 128 | } 129 | internal_signal::SignalData::MulSigned { 130 | ref lhs, ref rhs, .. 131 | } => { 132 | frames.push(Frame { signal: lhs }); 133 | frames.push(Frame { signal: rhs }); 134 | } 135 | 136 | internal_signal::SignalData::Bits { ref source, .. } => { 137 | frames.push(Frame { signal: source }); 138 | } 139 | 140 | internal_signal::SignalData::Repeat { ref source, .. } => { 141 | frames.push(Frame { signal: source }); 142 | } 143 | internal_signal::SignalData::Concat { 144 | ref lhs, ref rhs, .. 145 | } => { 146 | frames.push(Frame { signal: lhs }); 147 | frames.push(Frame { signal: rhs }); 148 | } 149 | 150 | internal_signal::SignalData::Mux { 151 | ref cond, 152 | ref when_true, 153 | ref when_false, 154 | .. 155 | } => { 156 | frames.push(Frame { signal: cond }); 157 | frames.push(Frame { signal: when_true }); 158 | frames.push(Frame { signal: when_false }); 159 | } 160 | 161 | internal_signal::SignalData::MemReadPortOutput { .. } => (), 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /kaze/src/verilog.rs: -------------------------------------------------------------------------------- 1 | //! Verilog code generation. 2 | 3 | mod compiler; 4 | mod ir; 5 | 6 | use compiler::*; 7 | use ir::*; 8 | 9 | use crate::code_writer; 10 | use crate::graph; 11 | use crate::state_elements::*; 12 | use crate::validation::*; 13 | 14 | use std::collections::HashMap; 15 | use std::io::{Result, Write}; 16 | 17 | // TODO: Note that mutable writer reference can be passed, see https://rust-lang.github.io/api-guidelines/interoperability.html#c-rw-value 18 | pub fn generate<'a, W: Write>(m: &'a graph::Module<'a>, w: W) -> Result<()> { 19 | validate_module_hierarchy(m); 20 | 21 | let mut signal_reference_counts = HashMap::new(); 22 | let state_elements = StateElements::new( 23 | m, 24 | IncludedPorts::ReachableFromTopLevelOutputs, 25 | &mut signal_reference_counts, 26 | ); 27 | 28 | let mut c = Compiler::new(); 29 | 30 | let mut assignments = AssignmentContext::new(); 31 | for (name, &output) in m.outputs.borrow().iter() { 32 | let expr = c.compile_signal(output.data.source, &state_elements, &mut assignments); 33 | assignments.push(Assignment { 34 | target_name: name.clone(), 35 | expr, 36 | }); 37 | } 38 | 39 | let mut node_decls = Vec::new(); 40 | 41 | for (mem, mem_decls) in state_elements.mems.iter() { 42 | for ((address, enable), read_signal_names) in mem_decls.read_signal_names.iter() { 43 | let expr = c.compile_signal(address, &state_elements, &mut assignments); 44 | node_decls.push(NodeDecl { 45 | net_type: NetType::Wire, 46 | name: read_signal_names.address_name.clone(), 47 | bit_width: address.bit_width(), 48 | }); 49 | assignments.push(Assignment { 50 | target_name: read_signal_names.address_name.clone(), 51 | expr, 52 | }); 53 | let expr = c.compile_signal(enable, &state_elements, &mut assignments); 54 | node_decls.push(NodeDecl { 55 | net_type: NetType::Wire, 56 | name: read_signal_names.enable_name.clone(), 57 | bit_width: enable.bit_width(), 58 | }); 59 | assignments.push(Assignment { 60 | target_name: read_signal_names.enable_name.clone(), 61 | expr, 62 | }); 63 | node_decls.push(NodeDecl { 64 | net_type: NetType::Reg, 65 | name: read_signal_names.value_name.clone(), 66 | bit_width: mem.element_bit_width, 67 | }); 68 | } 69 | if let Some((address, value, enable)) = *mem.write_port.borrow() { 70 | let expr = c.compile_signal(address, &state_elements, &mut assignments); 71 | node_decls.push(NodeDecl { 72 | net_type: NetType::Wire, 73 | name: mem_decls.write_address_name.clone(), 74 | bit_width: address.bit_width(), 75 | }); 76 | assignments.push(Assignment { 77 | target_name: mem_decls.write_address_name.clone(), 78 | expr, 79 | }); 80 | let expr = c.compile_signal(value, &state_elements, &mut assignments); 81 | node_decls.push(NodeDecl { 82 | net_type: NetType::Wire, 83 | name: mem_decls.write_value_name.clone(), 84 | bit_width: value.bit_width(), 85 | }); 86 | assignments.push(Assignment { 87 | target_name: mem_decls.write_value_name.clone(), 88 | expr, 89 | }); 90 | let expr = c.compile_signal(enable, &state_elements, &mut assignments); 91 | node_decls.push(NodeDecl { 92 | net_type: NetType::Wire, 93 | name: mem_decls.write_enable_name.clone(), 94 | bit_width: enable.bit_width(), 95 | }); 96 | assignments.push(Assignment { 97 | target_name: mem_decls.write_enable_name.clone(), 98 | expr, 99 | }); 100 | } 101 | } 102 | 103 | for reg in state_elements.regs.values() { 104 | node_decls.push(NodeDecl { 105 | net_type: NetType::Reg, 106 | name: reg.value_name.clone(), 107 | bit_width: reg.data.bit_width, 108 | }); 109 | node_decls.push(NodeDecl { 110 | net_type: NetType::Wire, 111 | name: reg.next_name.clone(), 112 | bit_width: reg.data.bit_width, 113 | }); 114 | 115 | let expr = c.compile_signal( 116 | reg.data.next.borrow().unwrap(), 117 | &state_elements, 118 | &mut assignments, 119 | ); 120 | assignments.push(Assignment { 121 | target_name: reg.next_name.clone(), 122 | expr, 123 | }); 124 | } 125 | 126 | let mut w = code_writer::CodeWriter::new(w); 127 | 128 | w.append_line(&format!("module {}(", m.name))?; 129 | w.indent(); 130 | 131 | // TODO: Make conditional based on the presence of (resetable) state elements 132 | w.append_line("input wire reset_n,")?; 133 | w.append_indent()?; 134 | w.append("input wire clk")?; 135 | if !m.inputs.borrow().is_empty() || !m.outputs.borrow().is_empty() { 136 | w.append(",")?; 137 | w.append_newline()?; 138 | } 139 | w.append_newline()?; 140 | let inputs = m.inputs.borrow(); 141 | let num_inputs = inputs.len(); 142 | for (i, (name, &input)) in inputs.iter().enumerate() { 143 | w.append_indent()?; 144 | w.append("input wire ")?; 145 | if input.data.bit_width > 1 { 146 | w.append(&format!("[{}:{}] ", input.data.bit_width - 1, 0))?; 147 | } 148 | w.append(name)?; 149 | if !m.outputs.borrow().is_empty() || i < num_inputs - 1 { 150 | w.append(",")?; 151 | } 152 | w.append_newline()?; 153 | } 154 | let outputs = m.outputs.borrow(); 155 | let num_outputs = outputs.len(); 156 | for (i, (name, &output)) in outputs.iter().enumerate() { 157 | w.append_indent()?; 158 | w.append("output wire ")?; 159 | if output.data.bit_width > 1 { 160 | w.append(&format!("[{}:{}] ", output.data.bit_width - 1, 0))?; 161 | } 162 | w.append(name)?; 163 | if i < num_outputs - 1 { 164 | w.append(",")?; 165 | } 166 | w.append_newline()?; 167 | } 168 | w.append_line(");")?; 169 | w.append_newline()?; 170 | 171 | if !node_decls.is_empty() { 172 | for node_decl in node_decls { 173 | node_decl.write(&mut w)?; 174 | } 175 | w.append_newline()?; 176 | } 177 | 178 | for (mem, mem_decls) in state_elements.mems.iter() { 179 | w.append_indent()?; 180 | w.append("reg ")?; 181 | if mem.element_bit_width > 1 { 182 | w.append(&format!("[{}:{}] ", mem.element_bit_width - 1, 0))?; 183 | } 184 | w.append(&format!( 185 | "{}[{}:{}];", 186 | mem_decls.mem_name, 187 | 0, 188 | (1 << mem.address_bit_width) - 1 189 | ))?; 190 | w.append_newline()?; 191 | w.append_newline()?; 192 | if let Some(ref initial_contents) = *mem.initial_contents.borrow() { 193 | w.append_line("initial begin")?; 194 | w.indent(); 195 | for (i, element) in initial_contents.iter().enumerate() { 196 | w.append_line(&format!( 197 | "{}[{}] = {}'h{:x};", 198 | mem_decls.mem_name, 199 | i, 200 | mem.element_bit_width, 201 | element.numeric_value() 202 | ))?; 203 | } 204 | w.unindent(); 205 | w.append_line("end")?; 206 | w.append_newline()?; 207 | } 208 | if !mem_decls.read_signal_names.is_empty() || mem.write_port.borrow().is_some() { 209 | w.append_line("always @(posedge clk) begin")?; 210 | w.indent(); 211 | } 212 | for (_, read_signal_names) in mem_decls.read_signal_names.iter() { 213 | w.append_line(&format!("if ({}) begin", read_signal_names.enable_name))?; 214 | w.indent(); 215 | w.append_line(&format!( 216 | "{} <= {}[{}];", 217 | read_signal_names.value_name, mem_decls.mem_name, read_signal_names.address_name 218 | ))?; 219 | w.unindent(); 220 | w.append_line("end")?; 221 | } 222 | if mem.write_port.borrow().is_some() { 223 | w.append_line(&format!("if ({}) begin", mem_decls.write_enable_name))?; 224 | w.indent(); 225 | w.append_line(&format!( 226 | "{}[{}] <= {};", 227 | mem_decls.mem_name, mem_decls.write_address_name, mem_decls.write_value_name 228 | ))?; 229 | w.unindent(); 230 | w.append_line("end")?; 231 | } 232 | if !mem_decls.read_signal_names.is_empty() || mem.write_port.borrow().is_some() { 233 | w.unindent(); 234 | w.append_line("end")?; 235 | w.append_newline()?; 236 | } 237 | } 238 | 239 | for reg in state_elements.regs.values() { 240 | w.append_indent()?; 241 | w.append("always @(posedge clk")?; 242 | if reg.data.initial_value.borrow().is_some() { 243 | w.append(", negedge reset_n")?; 244 | } 245 | w.append(") begin")?; 246 | w.append_newline()?; 247 | w.indent(); 248 | if let Some(ref initial_value) = *reg.data.initial_value.borrow() { 249 | w.append_line("if (~reset_n) begin")?; 250 | w.indent(); 251 | w.append_line(&format!( 252 | "{} <= {}'h{:x};", 253 | reg.value_name, 254 | reg.data.bit_width, 255 | initial_value.numeric_value() 256 | ))?; 257 | w.unindent(); 258 | w.append_line("end")?; 259 | w.append_line("else begin")?; 260 | w.indent(); 261 | } 262 | w.append_line(&format!("{} <= {};", reg.value_name, reg.next_name))?; 263 | if reg.data.initial_value.borrow().is_some() { 264 | w.unindent(); 265 | w.append_line("end")?; 266 | } 267 | w.unindent(); 268 | w.append_line("end")?; 269 | w.append_newline()?; 270 | } 271 | 272 | if !assignments.is_empty() { 273 | assignments.write(&mut w)?; 274 | w.append_newline()?; 275 | } 276 | 277 | w.unindent(); 278 | w.append_line("endmodule")?; 279 | w.append_newline()?; 280 | 281 | Ok(()) 282 | } 283 | 284 | #[cfg(test)] 285 | mod tests { 286 | use super::*; 287 | 288 | use crate::*; 289 | 290 | #[test] 291 | #[should_panic( 292 | expected = "Cannot generate code for module \"A\" because module \"A\" contains an instance of module \"B\" called \"b\" whose input \"i\" is not driven." 293 | )] 294 | fn undriven_instance_input_error() { 295 | let c = Context::new(); 296 | 297 | let a = c.module("a", "A"); 298 | let b = a.module("b", "B"); 299 | let _ = b.input("i", 1); 300 | 301 | // Panic 302 | generate(a, Vec::new()).unwrap(); 303 | } 304 | 305 | #[test] 306 | #[should_panic( 307 | expected = "Cannot generate code for module \"A\" because module \"A\" contains a register called \"r\" which is not driven." 308 | )] 309 | fn undriven_register_error1() { 310 | let c = Context::new(); 311 | 312 | let a = c.module("a", "A"); 313 | let _ = a.reg("r", 1); 314 | 315 | // Panic 316 | generate(a, Vec::new()).unwrap(); 317 | } 318 | 319 | #[test] 320 | #[should_panic( 321 | expected = "Cannot generate code for module \"A\" because module \"B\" contains a register called \"r\" which is not driven." 322 | )] 323 | fn undriven_register_error2() { 324 | let c = Context::new(); 325 | 326 | let a = c.module("a", "A"); 327 | let b = a.module("b", "B"); 328 | let _ = b.reg("r", 1); 329 | 330 | // Panic 331 | generate(a, Vec::new()).unwrap(); 332 | } 333 | 334 | #[test] 335 | #[should_panic( 336 | expected = "Cannot generate code for module \"A\" because module \"A\" contains a memory called \"m\" which doesn't have any read ports." 337 | )] 338 | fn mem_without_read_ports_error1() { 339 | let c = Context::new(); 340 | 341 | let a = c.module("a", "A"); 342 | let _ = a.mem("m", 1, 1); 343 | 344 | // Panic 345 | generate(a, Vec::new()).unwrap(); 346 | } 347 | 348 | #[test] 349 | #[should_panic( 350 | expected = "Cannot generate code for module \"A\" because module \"B\" contains a memory called \"m\" which doesn't have any read ports." 351 | )] 352 | fn mem_without_read_ports_error2() { 353 | let c = Context::new(); 354 | 355 | let a = c.module("a", "A"); 356 | let b = a.module("b", "B"); 357 | let _ = b.mem("m", 1, 1); 358 | 359 | // Panic 360 | generate(a, Vec::new()).unwrap(); 361 | } 362 | 363 | #[test] 364 | #[should_panic( 365 | expected = "Cannot generate code for module \"A\" because module \"A\" contains a memory called \"m\" which doesn't have initial contents or a write port specified. At least one of the two is required." 366 | )] 367 | fn mem_without_initial_contents_or_write_port_error1() { 368 | let c = Context::new(); 369 | 370 | let a = c.module("a", "A"); 371 | let m = a.mem("m", 1, 1); 372 | let _ = m.read_port(a.low(), a.low()); 373 | 374 | // Panic 375 | generate(a, Vec::new()).unwrap(); 376 | } 377 | 378 | #[test] 379 | #[should_panic( 380 | expected = "Cannot generate code for module \"A\" because module \"B\" contains a memory called \"m\" which doesn't have initial contents or a write port specified. At least one of the two is required." 381 | )] 382 | fn mem_without_initial_contents_or_write_port_error2() { 383 | let c = Context::new(); 384 | 385 | let a = c.module("a", "A"); 386 | let b = a.module("b", "B"); 387 | let m = b.mem("m", 1, 1); 388 | let _ = m.read_port(b.low(), b.low()); 389 | 390 | // Panic 391 | generate(a, Vec::new()).unwrap(); 392 | } 393 | 394 | #[test] 395 | #[should_panic( 396 | expected = "Cannot generate code for module \"b\" because module \"a\" contains an output called \"o\" which forms a combinational loop with itself." 397 | )] 398 | fn combinational_loop_error() { 399 | let c = Context::new(); 400 | 401 | let b = c.module("b", "b"); 402 | let a = b.module("a", "a"); 403 | let a_i = a.input("i", 1); 404 | let a_o = a.output("o", a_i); 405 | a_i.drive(a_o); 406 | 407 | // Panic 408 | generate(b, Vec::new()).unwrap(); 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /kaze/src/verilog/compiler.rs: -------------------------------------------------------------------------------- 1 | use super::ir::*; 2 | 3 | use crate::internal_signal; 4 | use crate::state_elements::*; 5 | 6 | use std::collections::HashMap; 7 | 8 | pub(super) struct Compiler<'graph> { 9 | signal_exprs: HashMap<&'graph internal_signal::InternalSignal<'graph>, Expr>, 10 | } 11 | 12 | impl<'graph, 'context> Compiler<'graph> { 13 | pub fn new() -> Compiler<'graph> { 14 | Compiler { 15 | signal_exprs: HashMap::new(), 16 | } 17 | } 18 | 19 | pub fn compile_signal( 20 | &mut self, 21 | signal: &'graph internal_signal::InternalSignal<'graph>, 22 | state_elements: &'context StateElements<'graph>, 23 | a: &mut AssignmentContext, 24 | ) -> Expr { 25 | enum Frame<'graph> { 26 | Enter(&'graph internal_signal::InternalSignal<'graph>), 27 | Leave(&'graph internal_signal::InternalSignal<'graph>), 28 | } 29 | 30 | let mut frames = Vec::new(); 31 | frames.push(Frame::Enter(signal)); 32 | 33 | let mut results = Vec::new(); 34 | 35 | while let Some(frame) = frames.pop() { 36 | if let Some(expr) = match frame { 37 | Frame::Enter(signal) => { 38 | if let Some(expr) = self.signal_exprs.get(&signal) { 39 | results.push(expr.clone()); 40 | continue; 41 | } 42 | 43 | match signal.data { 44 | internal_signal::SignalData::Lit { 45 | ref value, 46 | bit_width, 47 | } => Some(Expr::from_constant(value, bit_width)), 48 | 49 | internal_signal::SignalData::Input { data } => { 50 | if let Some(driven_value) = data.driven_value.borrow().clone() { 51 | frames.push(Frame::Leave(signal)); 52 | frames.push(Frame::Enter(driven_value)); 53 | None 54 | } else { 55 | Some(Expr::Ref { 56 | name: data.name.clone(), 57 | }) 58 | } 59 | } 60 | internal_signal::SignalData::Output { data } => { 61 | frames.push(Frame::Leave(signal)); 62 | frames.push(Frame::Enter(data.source)); 63 | None 64 | } 65 | 66 | internal_signal::SignalData::Reg { .. } => Some(Expr::Ref { 67 | name: state_elements.regs[&signal].value_name.clone(), 68 | }), 69 | 70 | internal_signal::SignalData::UnOp { source, .. } => { 71 | frames.push(Frame::Leave(signal)); 72 | frames.push(Frame::Enter(source)); 73 | None 74 | } 75 | internal_signal::SignalData::SimpleBinOp { lhs, rhs, .. } => { 76 | frames.push(Frame::Leave(signal)); 77 | frames.push(Frame::Enter(lhs)); 78 | frames.push(Frame::Enter(rhs)); 79 | None 80 | } 81 | internal_signal::SignalData::AdditiveBinOp { lhs, rhs, .. } => { 82 | frames.push(Frame::Leave(signal)); 83 | frames.push(Frame::Enter(lhs)); 84 | frames.push(Frame::Enter(rhs)); 85 | None 86 | } 87 | internal_signal::SignalData::ComparisonBinOp { lhs, rhs, .. } => { 88 | frames.push(Frame::Leave(signal)); 89 | frames.push(Frame::Enter(lhs)); 90 | frames.push(Frame::Enter(rhs)); 91 | None 92 | } 93 | internal_signal::SignalData::ShiftBinOp { lhs, rhs, .. } => { 94 | frames.push(Frame::Leave(signal)); 95 | frames.push(Frame::Enter(lhs)); 96 | frames.push(Frame::Enter(rhs)); 97 | None 98 | } 99 | 100 | internal_signal::SignalData::Mul { lhs, rhs, .. } => { 101 | frames.push(Frame::Leave(signal)); 102 | frames.push(Frame::Enter(lhs)); 103 | frames.push(Frame::Enter(rhs)); 104 | None 105 | } 106 | internal_signal::SignalData::MulSigned { lhs, rhs, .. } => { 107 | frames.push(Frame::Leave(signal)); 108 | frames.push(Frame::Enter(lhs)); 109 | frames.push(Frame::Enter(rhs)); 110 | None 111 | } 112 | 113 | internal_signal::SignalData::Bits { source, .. } => { 114 | frames.push(Frame::Leave(signal)); 115 | frames.push(Frame::Enter(source)); 116 | None 117 | } 118 | 119 | internal_signal::SignalData::Repeat { source, .. } => { 120 | frames.push(Frame::Leave(signal)); 121 | frames.push(Frame::Enter(source)); 122 | None 123 | } 124 | internal_signal::SignalData::Concat { lhs, rhs, .. } => { 125 | frames.push(Frame::Leave(signal)); 126 | frames.push(Frame::Enter(lhs)); 127 | frames.push(Frame::Enter(rhs)); 128 | None 129 | } 130 | 131 | internal_signal::SignalData::Mux { 132 | cond, 133 | when_true, 134 | when_false, 135 | .. 136 | } => { 137 | frames.push(Frame::Leave(signal)); 138 | frames.push(Frame::Enter(cond)); 139 | frames.push(Frame::Enter(when_true)); 140 | frames.push(Frame::Enter(when_false)); 141 | None 142 | } 143 | 144 | internal_signal::SignalData::MemReadPortOutput { 145 | mem, 146 | address, 147 | enable, 148 | } => { 149 | let mem = &state_elements.mems[&mem]; 150 | let read_signal_names = &mem.read_signal_names[&(address, enable)]; 151 | Some(Expr::Ref { 152 | name: read_signal_names.value_name.clone(), 153 | }) 154 | } 155 | } 156 | } 157 | Frame::Leave(signal) => { 158 | match signal.data { 159 | internal_signal::SignalData::Lit { .. } => unreachable!(), 160 | 161 | internal_signal::SignalData::Input { data } => { 162 | if data.driven_value.borrow().is_none() { 163 | unreachable!(); 164 | } 165 | 166 | Some(a.gen_temp( 167 | results.pop().unwrap(), 168 | signal.bit_width(), 169 | format!("{}_{}", signal.module_instance_name_prefix(), data.name), 170 | )) 171 | } 172 | internal_signal::SignalData::Output { data } => Some(a.gen_temp( 173 | results.pop().unwrap(), 174 | signal.bit_width(), 175 | format!( 176 | "{}_{}", 177 | data.source.module_instance_name_prefix(), 178 | data.name 179 | ), 180 | )), 181 | 182 | internal_signal::SignalData::Reg { .. } => unreachable!(), 183 | 184 | internal_signal::SignalData::UnOp { op, bit_width, .. } => { 185 | let source = results.pop().unwrap(); 186 | Some(a.gen_temp( 187 | Expr::UnOp { 188 | source: Box::new(source), 189 | op: match op { 190 | internal_signal::UnOp::Not => UnOp::Not, 191 | }, 192 | }, 193 | bit_width, 194 | signal.module_instance_name_prefix(), 195 | )) 196 | } 197 | internal_signal::SignalData::SimpleBinOp { op, bit_width, .. } => { 198 | let lhs = results.pop().unwrap(); 199 | let rhs = results.pop().unwrap(); 200 | Some(a.gen_temp( 201 | Expr::BinOp { 202 | lhs: Box::new(lhs), 203 | rhs: Box::new(rhs), 204 | op: match op { 205 | internal_signal::SimpleBinOp::BitAnd => BinOp::BitAnd, 206 | internal_signal::SimpleBinOp::BitOr => BinOp::BitOr, 207 | internal_signal::SimpleBinOp::BitXor => BinOp::BitXor, 208 | }, 209 | }, 210 | bit_width, 211 | signal.module_instance_name_prefix(), 212 | )) 213 | } 214 | internal_signal::SignalData::AdditiveBinOp { op, bit_width, .. } => { 215 | let lhs = results.pop().unwrap(); 216 | let rhs = results.pop().unwrap(); 217 | Some(a.gen_temp( 218 | Expr::BinOp { 219 | lhs: Box::new(lhs), 220 | rhs: Box::new(rhs), 221 | op: match op { 222 | internal_signal::AdditiveBinOp::Add => BinOp::Add, 223 | internal_signal::AdditiveBinOp::Sub => BinOp::Sub, 224 | }, 225 | }, 226 | bit_width, 227 | signal.module_instance_name_prefix(), 228 | )) 229 | } 230 | internal_signal::SignalData::ComparisonBinOp { op, .. } => { 231 | let bit_width = signal.bit_width(); 232 | let mut lhs = results.pop().unwrap(); 233 | let mut rhs = results.pop().unwrap(); 234 | match op { 235 | internal_signal::ComparisonBinOp::GreaterThanEqualSigned 236 | | internal_signal::ComparisonBinOp::GreaterThanSigned 237 | | internal_signal::ComparisonBinOp::LessThanEqualSigned 238 | | internal_signal::ComparisonBinOp::LessThanSigned => { 239 | lhs = Expr::Signed { 240 | source: Box::new(lhs), 241 | }; 242 | rhs = Expr::Signed { 243 | source: Box::new(rhs), 244 | }; 245 | } 246 | _ => (), 247 | } 248 | Some(a.gen_temp( 249 | Expr::BinOp { 250 | lhs: Box::new(lhs), 251 | rhs: Box::new(rhs), 252 | op: match op { 253 | internal_signal::ComparisonBinOp::Equal => BinOp::Equal, 254 | internal_signal::ComparisonBinOp::NotEqual => BinOp::NotEqual, 255 | internal_signal::ComparisonBinOp::LessThan 256 | | internal_signal::ComparisonBinOp::LessThanSigned => BinOp::LessThan, 257 | internal_signal::ComparisonBinOp::LessThanEqual 258 | | internal_signal::ComparisonBinOp::LessThanEqualSigned => { 259 | BinOp::LessThanEqual 260 | } 261 | internal_signal::ComparisonBinOp::GreaterThan 262 | | internal_signal::ComparisonBinOp::GreaterThanSigned => { 263 | BinOp::GreaterThan 264 | } 265 | internal_signal::ComparisonBinOp::GreaterThanEqual 266 | | internal_signal::ComparisonBinOp::GreaterThanEqualSigned => { 267 | BinOp::GreaterThanEqual 268 | } 269 | }, 270 | }, 271 | bit_width, 272 | signal.module_instance_name_prefix(), 273 | )) 274 | } 275 | internal_signal::SignalData::ShiftBinOp { op, bit_width, .. } => { 276 | let lhs = results.pop().unwrap(); 277 | let lhs = match op { 278 | internal_signal::ShiftBinOp::Shl 279 | | internal_signal::ShiftBinOp::Shr => lhs, 280 | internal_signal::ShiftBinOp::ShrArithmetic => Expr::Signed { 281 | source: Box::new(lhs), 282 | }, 283 | }; 284 | let rhs = results.pop().unwrap(); 285 | Some(a.gen_temp( 286 | Expr::BinOp { 287 | lhs: Box::new(lhs), 288 | rhs: Box::new(rhs), 289 | op: match op { 290 | internal_signal::ShiftBinOp::Shl => BinOp::Shl, 291 | internal_signal::ShiftBinOp::Shr => BinOp::Shr, 292 | internal_signal::ShiftBinOp::ShrArithmetic => { 293 | BinOp::ShrArithmetic 294 | } 295 | }, 296 | }, 297 | bit_width, 298 | signal.module_instance_name_prefix(), 299 | )) 300 | } 301 | 302 | internal_signal::SignalData::Mul { bit_width, .. } => { 303 | let lhs = results.pop().unwrap(); 304 | let rhs = results.pop().unwrap(); 305 | Some(a.gen_temp( 306 | Expr::BinOp { 307 | lhs: Box::new(lhs), 308 | rhs: Box::new(rhs), 309 | op: BinOp::Mul, 310 | }, 311 | bit_width, 312 | signal.module_instance_name_prefix(), 313 | )) 314 | } 315 | internal_signal::SignalData::MulSigned { bit_width, .. } => { 316 | let lhs = results.pop().unwrap(); 317 | let rhs = results.pop().unwrap(); 318 | let lhs = Expr::Signed { 319 | source: Box::new(lhs), 320 | }; 321 | let rhs = Expr::Signed { 322 | source: Box::new(rhs), 323 | }; 324 | Some(a.gen_temp( 325 | Expr::BinOp { 326 | lhs: Box::new(lhs), 327 | rhs: Box::new(rhs), 328 | op: BinOp::Mul, 329 | }, 330 | bit_width, 331 | signal.module_instance_name_prefix(), 332 | )) 333 | } 334 | 335 | internal_signal::SignalData::Bits { 336 | source, 337 | range_high, 338 | range_low, 339 | } => { 340 | let source_bit_width = source.bit_width(); 341 | let bit_width = signal.bit_width(); 342 | let source = results.pop().unwrap(); 343 | // Verilog doesn't allow indexing scalars 344 | Some(if source_bit_width == 1 { 345 | source 346 | } else { 347 | a.gen_temp( 348 | Expr::Bits { 349 | source: Box::new(source), 350 | range_high, 351 | range_low, 352 | }, 353 | bit_width, 354 | signal.module_instance_name_prefix(), 355 | ) 356 | }) 357 | } 358 | 359 | internal_signal::SignalData::Repeat { 360 | count, bit_width, .. 361 | } => { 362 | let source = results.pop().unwrap(); 363 | Some(a.gen_temp( 364 | Expr::Repeat { 365 | source: Box::new(source), 366 | count, 367 | }, 368 | bit_width, 369 | signal.module_instance_name_prefix(), 370 | )) 371 | } 372 | internal_signal::SignalData::Concat { bit_width, .. } => { 373 | let lhs = results.pop().unwrap(); 374 | let rhs = results.pop().unwrap(); 375 | Some(a.gen_temp( 376 | Expr::Concat { 377 | lhs: Box::new(lhs), 378 | rhs: Box::new(rhs), 379 | }, 380 | bit_width, 381 | signal.module_instance_name_prefix(), 382 | )) 383 | } 384 | 385 | internal_signal::SignalData::Mux { bit_width, .. } => { 386 | let cond = results.pop().unwrap(); 387 | let when_true = results.pop().unwrap(); 388 | let when_false = results.pop().unwrap(); 389 | Some(a.gen_temp( 390 | Expr::Ternary { 391 | cond: Box::new(cond), 392 | when_true: Box::new(when_true), 393 | when_false: Box::new(when_false), 394 | }, 395 | bit_width, 396 | signal.module_instance_name_prefix(), 397 | )) 398 | } 399 | 400 | internal_signal::SignalData::MemReadPortOutput { .. } => unreachable!(), 401 | } 402 | } 403 | } { 404 | self.signal_exprs.insert(signal, expr.clone()); 405 | results.push(expr); 406 | } 407 | } 408 | 409 | results.pop().unwrap() 410 | } 411 | } 412 | -------------------------------------------------------------------------------- /kaze/src/verilog/ir.rs: -------------------------------------------------------------------------------- 1 | use crate::code_writer; 2 | use crate::graph; 3 | 4 | use std::io::{Result, Write}; 5 | 6 | pub struct NodeDecl { 7 | pub net_type: NetType, 8 | pub name: String, 9 | pub bit_width: u32, 10 | } 11 | 12 | impl NodeDecl { 13 | pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> { 14 | w.append_indent()?; 15 | self.net_type.write(w)?; 16 | w.append(" ")?; 17 | if self.bit_width > 1 { 18 | w.append(&format!("[{}:{}] ", self.bit_width - 1, 0))?; 19 | } 20 | w.append(&format!("{};", self.name))?; 21 | w.append_newline()?; 22 | 23 | Ok(()) 24 | } 25 | } 26 | 27 | pub enum NetType { 28 | Reg, 29 | Wire, 30 | } 31 | 32 | impl NetType { 33 | pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> { 34 | w.append(match self { 35 | NetType::Reg => "reg", 36 | NetType::Wire => "wire", 37 | }) 38 | } 39 | } 40 | 41 | pub struct AssignmentContext { 42 | assignments: Vec, 43 | local_decls: Vec, 44 | } 45 | 46 | impl AssignmentContext { 47 | pub fn new() -> AssignmentContext { 48 | AssignmentContext { 49 | assignments: Vec::new(), 50 | local_decls: Vec::new(), 51 | } 52 | } 53 | 54 | pub fn gen_temp(&mut self, expr: Expr, bit_width: u32, name_prefix: String) -> Expr { 55 | let name = format!("__temp_{}_{}", name_prefix, self.local_decls.len()); 56 | 57 | self.local_decls.push(NodeDecl { 58 | net_type: NetType::Wire, 59 | name: name.clone(), 60 | bit_width, 61 | }); 62 | 63 | self.assignments.push(Assignment { 64 | target_name: name.clone(), 65 | expr, 66 | }); 67 | 68 | Expr::Ref { name } 69 | } 70 | 71 | pub fn is_empty(&self) -> bool { 72 | self.assignments.is_empty() 73 | } 74 | 75 | pub fn push(&mut self, assignment: Assignment) { 76 | self.assignments.push(assignment); 77 | } 78 | 79 | pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> { 80 | if !self.local_decls.is_empty() { 81 | for node_decl in self.local_decls.iter() { 82 | node_decl.write(w)?; 83 | } 84 | w.append_newline()?; 85 | } 86 | 87 | for assignment in self.assignments.iter() { 88 | assignment.write(w)?; 89 | } 90 | 91 | Ok(()) 92 | } 93 | } 94 | 95 | pub struct Assignment { 96 | pub target_name: String, 97 | pub expr: Expr, 98 | } 99 | 100 | impl Assignment { 101 | fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> { 102 | w.append_indent()?; 103 | w.append(&format!("assign {}", self.target_name))?; 104 | w.append(" = ")?; 105 | self.expr.write(w)?; 106 | w.append(";")?; 107 | w.append_newline()?; 108 | 109 | Ok(()) 110 | } 111 | } 112 | 113 | #[derive(Clone)] 114 | pub enum Expr { 115 | BinOp { 116 | lhs: Box, 117 | rhs: Box, 118 | op: BinOp, 119 | }, 120 | Bits { 121 | source: Box, 122 | range_high: u32, 123 | range_low: u32, 124 | }, 125 | Concat { 126 | lhs: Box, 127 | rhs: Box, 128 | }, 129 | Constant { 130 | bit_width: u32, 131 | value: u128, 132 | }, 133 | Ref { 134 | name: String, 135 | }, 136 | Repeat { 137 | source: Box, 138 | count: u32, 139 | }, 140 | Signed { 141 | source: Box, 142 | }, 143 | Ternary { 144 | cond: Box, 145 | when_true: Box, 146 | when_false: Box, 147 | }, 148 | UnOp { 149 | source: Box, 150 | op: UnOp, 151 | }, 152 | } 153 | 154 | impl Expr { 155 | pub fn from_constant(value: &graph::Constant, bit_width: u32) -> Expr { 156 | Expr::Constant { 157 | bit_width, 158 | value: value.numeric_value(), 159 | } 160 | } 161 | 162 | pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> { 163 | match self { 164 | Expr::BinOp { lhs, rhs, op } => { 165 | lhs.write(w)?; 166 | w.append(&format!( 167 | " {} ", 168 | match op { 169 | BinOp::Add => "+", 170 | BinOp::BitAnd => "&", 171 | BinOp::BitOr => "|", 172 | BinOp::BitXor => "^", 173 | BinOp::Equal => "==", 174 | BinOp::NotEqual => "!=", 175 | BinOp::LessThan => "<", 176 | BinOp::LessThanEqual => "<=", 177 | BinOp::GreaterThan => ">", 178 | BinOp::GreaterThanEqual => ">=", 179 | BinOp::Shl => "<<", 180 | BinOp::Shr => ">>", 181 | BinOp::ShrArithmetic => ">>>", 182 | BinOp::Sub => "-", 183 | BinOp::Mul => "*", 184 | } 185 | ))?; 186 | rhs.write(w)?; 187 | } 188 | Expr::Bits { 189 | source, 190 | range_high, 191 | range_low, 192 | } => { 193 | source.write(w)?; 194 | if range_high != range_low { 195 | w.append(&format!("[{}:{}]", range_high, range_low))?; 196 | } else { 197 | w.append(&format!("[{}]", range_high))?; 198 | } 199 | } 200 | Expr::Concat { lhs, rhs } => { 201 | w.append("{")?; 202 | lhs.write(w)?; 203 | w.append(", ")?; 204 | rhs.write(w)?; 205 | w.append("}")?; 206 | } 207 | Expr::Constant { bit_width, value } => { 208 | w.append(&format!("{}'h{:x}", bit_width, value))?; 209 | } 210 | Expr::Ref { name } => { 211 | w.append(name)?; 212 | } 213 | Expr::Repeat { source, count } => { 214 | w.append(&format!("{{{}{{", count))?; 215 | source.write(w)?; 216 | w.append("}}")?; 217 | } 218 | Expr::Signed { source } => { 219 | w.append("$signed(")?; 220 | source.write(w)?; 221 | w.append(")")?; 222 | } 223 | Expr::Ternary { 224 | cond, 225 | when_true, 226 | when_false, 227 | } => { 228 | cond.write(w)?; 229 | w.append(" ? ")?; 230 | when_true.write(w)?; 231 | w.append(" : ")?; 232 | when_false.write(w)?; 233 | } 234 | Expr::UnOp { source, op } => { 235 | w.append(match op { 236 | UnOp::Not => "~", 237 | })?; 238 | source.write(w)?; 239 | } 240 | } 241 | 242 | Ok(()) 243 | } 244 | } 245 | 246 | #[derive(Clone)] 247 | pub enum BinOp { 248 | Add, 249 | BitAnd, 250 | BitOr, 251 | BitXor, 252 | Equal, 253 | NotEqual, 254 | LessThan, 255 | LessThanEqual, 256 | GreaterThan, 257 | GreaterThanEqual, 258 | Shl, 259 | Shr, 260 | ShrArithmetic, 261 | Sub, 262 | Mul, 263 | } 264 | 265 | #[derive(Clone)] 266 | pub enum UnOp { 267 | Not, 268 | } 269 | -------------------------------------------------------------------------------- /sim-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sim-tests" 3 | version = "0.1.0" 4 | authors = ["Jake \"ferris\" Taylor "] 5 | edition = "2018" 6 | license = "MIT OR Apache-2.0" 7 | build = "build.rs" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [build-dependencies] 12 | kaze = { path = "../kaze" } 13 | 14 | [dependencies] 15 | kaze = { path = "../kaze" } 16 | --------------------------------------------------------------------------------