├── .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 | [ ](https://github.com/yupferris/kaze)
6 | [ ](https://crates.io/crates/kaze)
7 | [ ](https://docs.rs/kaze)
8 | [ ](#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 |
--------------------------------------------------------------------------------