Repository: yupferris/kaze
Branch: master
Commit: 58c04571b2ed
Files: 32
Total size: 406.4 KB
Directory structure:
gitextract_k8l9vxpv/
├── .gitignore
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── kaze/
│ ├── Cargo.toml
│ └── src/
│ ├── code_writer.rs
│ ├── graph/
│ │ ├── constant.rs
│ │ ├── context.rs
│ │ ├── internal_signal.rs
│ │ ├── mem.rs
│ │ ├── module.rs
│ │ ├── register.rs
│ │ ├── signal.rs
│ │ └── sugar.rs
│ ├── graph.rs
│ ├── lib.rs
│ ├── runtime/
│ │ ├── tracing/
│ │ │ └── vcd.rs
│ │ └── tracing.rs
│ ├── runtime.rs
│ ├── sim/
│ │ ├── compiler.rs
│ │ └── ir.rs
│ ├── sim.rs
│ ├── state_elements.rs
│ ├── validation.rs
│ ├── verilog/
│ │ ├── compiler.rs
│ │ └── ir.rs
│ └── verilog.rs
└── sim-tests/
├── Cargo.toml
├── build.rs
└── src/
└── lib.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
Cargo.lock
target/
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.1.19] - 2021-03-14
### Fixed
- Bits indexing bug in verilog gen when indexing results in a scalar `Signal`
## [0.1.18] - 2020-12-13
### Fixed
- Duplicate trace member names in Rust sim gen in some cases
## [0.1.17] - 2020-12-13
### Added
- Cycle delay helpers to `Signal` API (`reg_next`, `reg_next_with_default`)
## [0.1.16] - 2020-12-13
### Fixed
- Stack overflow bugs that were still present when cloning IR expressions in the Rust sim compiler
- Broken changelog link for [0.1.15]
## [0.1.15] - 2020-12-07
### Changed
- Reduced the amount of temporary bindings used in the generated sim code, which reduces rustc compile time dramatically!
- Mark generated Rust simulator impl's with `#[automatically_derived]` to skip expensive lints during compilation
## [0.1.14] - 2020-11-29
### Changed
- 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).
- Dependencies updated to latest versions
### Fixed
- Stack overflow bugs by eliminating recursive graph traversals
- Invalid/outdated code in README.md
- Code formatting lints in kaze-sim-tests
## [0.1.13] - 2020-10-12
### Added
- Tracing for generated sim modules
## [0.1.12] - 2020-08-29
### Fixed
- 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.
## [0.1.11] - 2020-07-19
### Fixed
- Indexing scalars produced invalid Verilog code
## [0.1.10] - 2020-07-17
### Added
- Signed multiplication op to `Signal` API (`mul_signed`)
## [0.1.9] - 2020-07-01
### Added
- Unsigned multiplication op to `Signal` API (`mul`)
## [0.1.8] - 2020-06-28
### Fixed
- Clarified docs for `Mem` read port values when `enable` is not asserted
- Various small doc fixes/regularizations
### Changed
- Added more `if_` sugar variants for tuples with up to 12 elements (previously 8)
## [0.1.7] - 2020-03-27
### Added
- Complete Verilog codegen
- Validation tests for Verilog codegen
- `Context::modules` method to borrow a `Context`'s `Module`s, primarily useful for iterating over them for generating Verilog code
### Changed
- 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
### Fixed
- Wrong publish date for 0.1.6 in changelog
## [0.1.6] - 2020-02-22
### Fixed
- Broken default value for `Mem`s with single-bit elements in generated simulators
## [0.1.5] - 2020-02-15
### Added
- `Mem` construct for creating synchronous memories
### Changed
- Internal sim compiler refactorings to simplify/unify some implementation details
### Fixed
- Missing shift doc tests
## [0.1.4] - 2020-02-09
### Fixed
- Link errors in top-level docs
- Error in `rhs_arithmetic` docs for underflow case
## [0.1.3] - 2020-02-09
### Added
- Subtraction and shift ops to `Signal` API (`sub`, `shl`, `shr`, `shr_arithmetic`)
### Changed
- Small readme edits/link fixes
### Fixed
- Module naming convention in top-level docs
## [0.1.2] - 2020-02-02
### Added
- Implement Eq/PartialEq/Hash for `Signal` (note that these are not documented/tested, which we might want to revisit later)
### Changed
- Switched naming convention for `Module`s from `snake_case` to `CamelCase`
- Redesigned entire (unstable) sugar API
- Small changelog formatting fixes
### Fixed
- Removed the last remaining `unsafe` block in the API impl
## [0.1.1] - 2020-01-30
### Added
- Signed comparison ops to `Signal` API (`lt_signed`, `le_signed`, `gt_signed`, `ge_signed`)
- Error check for `concat` to ensure its input `Signal`s belong to the same `Module`
- This changelog
### Changed
- Small typo/link fixes in API docs
- Small clarifications in top-level docs/examples
- Broken link fixes in README
- Changed tag format to be `vx.y.z` instead of `x.y.z`, and not use annotated tags
## [0.1.0] - 2020-01-25 (Initial release)
[Unreleased]: https://github.com/yupferris/kaze/compare/v0.1.19...HEAD
[0.1.19]: https://github.com/yupferris/kaze/compare/v0.1.18..v0.1.19
[0.1.18]: https://github.com/yupferris/kaze/compare/v0.1.17..v0.1.18
[0.1.17]: https://github.com/yupferris/kaze/compare/v0.1.16..v0.1.17
[0.1.16]: https://github.com/yupferris/kaze/compare/v0.1.15..v0.1.16
[0.1.15]: https://github.com/yupferris/kaze/compare/v0.1.14..v0.1.15
[0.1.14]: https://github.com/yupferris/kaze/compare/v0.1.13..v0.1.14
[0.1.13]: https://github.com/yupferris/kaze/compare/v0.1.12..v0.1.13
[0.1.12]: https://github.com/yupferris/kaze/compare/v0.1.11..v0.1.12
[0.1.11]: https://github.com/yupferris/kaze/compare/v0.1.10..v0.1.11
[0.1.10]: https://github.com/yupferris/kaze/compare/v0.1.9..v0.1.10
[0.1.9]: https://github.com/yupferris/kaze/compare/v0.1.8..v0.1.9
[0.1.8]: https://github.com/yupferris/kaze/compare/v0.1.7..v0.1.8
[0.1.7]: https://github.com/yupferris/kaze/compare/v0.1.6..v0.1.7
[0.1.6]: https://github.com/yupferris/kaze/compare/v0.1.5..v0.1.6
[0.1.5]: https://github.com/yupferris/kaze/compare/v0.1.4..v0.1.5
[0.1.4]: https://github.com/yupferris/kaze/compare/v0.1.3..v0.1.4
[0.1.3]: https://github.com/yupferris/kaze/compare/v0.1.2..v0.1.3
[0.1.2]: https://github.com/yupferris/kaze/compare/v0.1.1..v0.1.2
[0.1.1]: https://github.com/yupferris/kaze/compare/v0.1.0..v0.1.1
[0.1.0]: https://github.com/yupferris/kaze/releases/tag/v0.1.0
================================================
FILE: Cargo.toml
================================================
[workspace]
members = [
"kaze",
"sim-tests",
]
================================================
FILE: LICENSE-APACHE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2019-2021 Jake "ferris" Taylor
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: LICENSE-MIT
================================================
Copyright (c) 2019-2021 Jake "ferris" Taylor
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# kaze [風](https://jisho.org/search/%E9%A2%A8%20%23kanji)
An [HDL](https://en.wikipedia.org/wiki/Hardware_description_language) embedded in [Rust](https://www.rust-lang.org/).
[ ](https://github.com/yupferris/kaze)
[ ](https://crates.io/crates/kaze)
[ ](https://docs.rs/kaze)
[ ](#license)
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.
kaze's API is designed to be as minimal as possible while still being expressive.
It's designed to prevent the user from being able to describe buggy or incorrect hardware as much as possible.
This enables a user to hack on designs fearlessly, while the API and generators ensure that these designs are sound.
## Usage
```toml
[dependencies]
kaze = "0.1"
```
## Example
```rust
use kaze::*;
fn main() -> std::io::Result<()> {
// Create a context, which will contain our module(s)
let c = Context::new();
// Create a module
let inverter = c.module("Inverter");
let i = inverter.input("i", 1); // 1-bit input
inverter.output("o", !i); // Output inverted input
// Generate Rust simulator code
sim::generate(inverter, sim::GenerationOptions::default(), std::io::stdout())?;
// Generate Verilog code
verilog::generate(inverter, std::io::stdout())?;
Ok(())
}
```
## Releases
See [changelog](https://github.com/yupferris/kaze/blob/master/CHANGELOG.md) for release information.
## License
Licensed under either of
* Apache License, Version 2.0
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license
([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
================================================
FILE: kaze/Cargo.toml
================================================
[package]
name = "kaze"
version = "0.1.19" # Must be kept up-to-date with html_root_url in lib.rs
authors = ["Jake \"ferris\" Taylor "]
edition = "2018"
description = "An HDL embedded in Rust"
license = "MIT OR Apache-2.0"
repository = "https://github.com/yupferris/kaze"
readme = "../README.md"
keywords = ["hdl"]
categories = ["development-tools", "emulators", "simulation"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
typed-arena = "2.0.1"
vcd = "0.6.1"
================================================
FILE: kaze/src/code_writer.rs
================================================
use std::io::{Result, Write};
pub struct CodeWriter {
w: W,
indent_level: u32,
}
impl CodeWriter {
pub fn new(w: W) -> CodeWriter {
CodeWriter { w, indent_level: 0 }
}
pub fn indent(&mut self) {
self.indent_level += 1;
}
pub fn unindent(&mut self) {
if self.indent_level == 0 {
panic!("Indent level underflow");
}
self.indent_level -= 1;
}
pub fn append_indent(&mut self) -> Result<()> {
for _ in 0..self.indent_level {
write!(self.w, " ")?;
}
Ok(())
}
pub fn append_newline(&mut self) -> Result<()> {
writeln!(self.w, "")?;
Ok(())
}
pub fn append(&mut self, s: &str) -> Result<()> {
write!(self.w, "{}", s)?;
Ok(())
}
pub fn append_line(&mut self, s: &str) -> Result<()> {
self.append_indent()?;
self.append(s)?;
self.append_newline()?;
Ok(())
}
}
================================================
FILE: kaze/src/graph/constant.rs
================================================
/// A container for different types of integer constant values.
///
/// 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.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let p = Context::new();
///
/// let m = p.module("m", "MyModule");
///
/// let a = m.lit(true, 16);
/// let b = m.lit(0xdeadbeefu32, 47);
/// let c = m.reg("data", 20);
/// c.default_value(5u32);
/// let d = m.lit(42u32, 8);
/// ```
#[derive(Clone)]
pub enum Constant {
/// Contains a boolean value
Bool(bool),
/// Contains an unsigned, 32-bit value
U32(u32),
/// Contains an unsigned, 64-bit value
U64(u64),
/// Contains an unsigned, 128-bit value
U128(u128),
}
impl Constant {
// TODO: Specific tests? I don't necessarily want to make this part of the public API at least.
pub(super) fn required_bits(&self) -> u32 {
match *self {
Constant::Bool(value) => 32 - (value as u32).leading_zeros(),
Constant::U32(value) => 32 - value.leading_zeros(),
Constant::U64(value) => 64 - value.leading_zeros(),
Constant::U128(value) => 128 - value.leading_zeros(),
}
}
pub(crate) fn numeric_value(&self) -> u128 {
match *self {
Constant::Bool(value) => value.into(),
Constant::U32(value) => value.into(),
Constant::U64(value) => value.into(),
Constant::U128(value) => value,
}
}
}
impl From for Constant {
fn from(value: bool) -> Self {
Constant::Bool(value)
}
}
impl From for Constant {
fn from(value: u8) -> Self {
Constant::U32(value as _)
}
}
impl From for Constant {
fn from(value: u16) -> Self {
Constant::U32(value as _)
}
}
impl From for Constant {
fn from(value: u32) -> Self {
Constant::U32(value)
}
}
impl From for Constant {
fn from(value: u64) -> Self {
Constant::U64(value)
}
}
impl From for Constant {
fn from(value: u128) -> Self {
Constant::U128(value)
}
}
================================================
FILE: kaze/src/graph/context.rs
================================================
use super::internal_signal::*;
use super::mem::*;
use super::module::*;
use super::register::*;
use typed_arena::Arena;
use std::cell::RefCell;
// TODO: Move, doc
pub trait ModuleParent<'a> {
// TODO: Update doc
/// Creates a new [`Module`] called `name` in this `Context`.
///
/// Conventionally, `name` should be `CamelCase`, though this is not enforced.
///
/// # Panics
///
/// Panics if a [`Module`] with the same `name` already exists in this `Context`.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let my_module = c.module("my_module", "MyModule");
/// let another_mod = c.module("another_mod", "AnotherMod");
/// ```
///
/// The following example panics by creating a `Module` with the same `name` as a previously-created `Module` in the same `Context`:
///
/// ```should_panic
/// use kaze::*;
///
/// let c = Context::new();
///
/// let _ = c.module("a", "A"); // Unique name, OK
/// let _ = c.module("b", "B"); // Unique name, OK
///
/// let _ = c.module("a", "A"); // Non-unique name, panic!
/// ```
fn module(&'a self, instance_name: impl Into, name: impl Into) -> &Module;
}
/// A top-level container/owner object for a [`Module`] graph.
///
/// A `Context` owns all parts of a module graph, and provides an API for creating [`Module`] objects.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
/// m.output("out", m.input("in", 1));
/// ```
#[must_use]
pub struct Context<'a> {
pub(super) module_arena: Arena>,
pub(super) input_data_arena: Arena>,
pub(super) input_arena: Arena >,
pub(super) output_data_arena: Arena>,
pub(super) output_arena: Arena>,
pub(super) signal_arena: Arena>,
pub(super) register_data_arena: Arena>,
pub(super) register_arena: Arena>,
pub(super) mem_arena: Arena>,
pub(super) modules: RefCell>>,
}
impl<'a> Context<'a> {
/// Creates a new, empty `Context`.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
/// ```
pub fn new() -> Context<'a> {
Context {
module_arena: Arena::new(),
input_data_arena: Arena::new(),
input_arena: Arena::new(),
output_data_arena: Arena::new(),
output_arena: Arena::new(),
signal_arena: Arena::new(),
register_data_arena: Arena::new(),
register_arena: Arena::new(),
mem_arena: Arena::new(),
modules: RefCell::new(Vec::new()),
}
}
}
impl<'a> ModuleParent<'a> for Context<'a> {
// TODO: Docs, error handling
fn module(&'a self, instance_name: impl Into, name: impl Into) -> &Module {
let instance_name = instance_name.into();
let name = name.into();
let module = self
.module_arena
.alloc(Module::new(self, None, instance_name, name));
self.modules.borrow_mut().push(module);
module
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_context_has_no_modules() {
let c = Context::new();
assert!(c.modules.borrow().is_empty());
}
}
================================================
FILE: kaze/src/graph/internal_signal.rs
================================================
use super::constant::*;
use super::context::*;
use super::mem::*;
use super::module::*;
use super::register::*;
use std::hash::{Hash, Hasher};
use std::ptr;
pub struct InternalSignal<'a> {
pub(super) context: &'a Context<'a>,
pub(crate) module: &'a Module<'a>,
pub(crate) data: SignalData<'a>,
}
impl<'a> InternalSignal<'a> {
pub fn bit_width(&'a self) -> u32 {
match self.data {
SignalData::Lit { bit_width, .. } => bit_width,
SignalData::Input { data } => data.bit_width,
// TODO: Test above
SignalData::Output { data } => data.bit_width,
SignalData::Reg { data } => data.bit_width,
SignalData::UnOp { bit_width, .. } => bit_width,
SignalData::SimpleBinOp { bit_width, .. } => bit_width,
SignalData::AdditiveBinOp { bit_width, .. } => bit_width,
SignalData::ComparisonBinOp { .. } => 1,
SignalData::ShiftBinOp { bit_width, .. } => bit_width,
SignalData::Mul { bit_width, .. } => bit_width,
SignalData::MulSigned { bit_width, .. } => bit_width,
SignalData::Bits {
range_high,
range_low,
..
} => range_high - range_low + 1,
SignalData::Repeat { bit_width, .. } => bit_width,
SignalData::Concat { bit_width, .. } => bit_width,
SignalData::Mux { bit_width, .. } => bit_width,
SignalData::MemReadPortOutput { mem, .. } => mem.element_bit_width,
}
}
pub(crate) fn module_instance_name_prefix(&self) -> String {
let mut stack = Vec::new();
let mut module = Some(self.module);
while let Some(m) = module {
stack.push(m);
module = m.parent;
}
let mut ret = String::new();
while let Some(m) = stack.pop() {
ret = if ret.is_empty() {
m.instance_name.clone()
} else {
format!("{}_{}", ret, m.instance_name)
};
}
ret
}
}
impl<'a> Eq for &'a InternalSignal<'a> {}
impl<'a> Hash for &'a InternalSignal<'a> {
fn hash(&self, state: &mut H) {
state.write_usize(*self as *const _ as usize)
}
}
impl<'a> PartialEq for &'a InternalSignal<'a> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(*self, *other)
}
}
pub(crate) enum SignalData<'a> {
Lit {
value: Constant,
bit_width: u32,
},
Input {
data: &'a InputData<'a>,
},
Output {
data: &'a OutputData<'a>,
},
// TODO: Rename to Register?
Reg {
data: &'a RegisterData<'a>,
},
UnOp {
source: &'a InternalSignal<'a>,
op: UnOp,
bit_width: u32,
},
SimpleBinOp {
lhs: &'a InternalSignal<'a>,
rhs: &'a InternalSignal<'a>,
op: SimpleBinOp,
bit_width: u32,
},
AdditiveBinOp {
lhs: &'a InternalSignal<'a>,
rhs: &'a InternalSignal<'a>,
op: AdditiveBinOp,
bit_width: u32,
},
ComparisonBinOp {
lhs: &'a InternalSignal<'a>,
rhs: &'a InternalSignal<'a>,
op: ComparisonBinOp,
},
ShiftBinOp {
lhs: &'a InternalSignal<'a>,
rhs: &'a InternalSignal<'a>,
op: ShiftBinOp,
bit_width: u32,
},
Mul {
lhs: &'a InternalSignal<'a>,
rhs: &'a InternalSignal<'a>,
bit_width: u32,
},
MulSigned {
lhs: &'a InternalSignal<'a>,
rhs: &'a InternalSignal<'a>,
bit_width: u32,
},
Bits {
source: &'a InternalSignal<'a>,
range_high: u32,
range_low: u32,
},
Repeat {
source: &'a InternalSignal<'a>,
count: u32,
bit_width: u32,
},
Concat {
lhs: &'a InternalSignal<'a>,
rhs: &'a InternalSignal<'a>,
bit_width: u32,
},
Mux {
cond: &'a InternalSignal<'a>,
when_true: &'a InternalSignal<'a>,
when_false: &'a InternalSignal<'a>,
bit_width: u32,
},
MemReadPortOutput {
mem: &'a Mem<'a>,
address: &'a InternalSignal<'a>,
enable: &'a InternalSignal<'a>,
},
}
#[derive(Clone, Copy)]
pub(crate) enum UnOp {
Not,
}
#[derive(Clone, Copy)]
pub(crate) enum SimpleBinOp {
BitAnd,
BitOr,
BitXor,
}
#[derive(Clone, Copy)]
pub(crate) enum ComparisonBinOp {
Equal,
GreaterThan,
GreaterThanEqual,
GreaterThanEqualSigned,
GreaterThanSigned,
LessThan,
LessThanEqual,
LessThanEqualSigned,
LessThanSigned,
NotEqual,
}
#[derive(Clone, Copy)]
pub(crate) enum AdditiveBinOp {
Add,
Sub,
}
#[derive(Clone, Copy)]
pub(crate) enum ShiftBinOp {
Shl,
Shr,
ShrArithmetic,
}
pub trait GetInternalSignal<'a> {
// TODO: Rename to `get_internal_signal` ?
fn internal_signal(&'a self) -> &'a InternalSignal<'a>;
}
impl<'a> GetInternalSignal<'a> for InternalSignal<'a> {
fn internal_signal(&'a self) -> &'a InternalSignal<'a> {
self
}
}
================================================
FILE: kaze/src/graph/mem.rs
================================================
use super::constant::*;
use super::context::*;
use super::internal_signal::*;
use super::module::*;
use super::signal::*;
use std::cell::RefCell;
use std::hash::{Hash, Hasher};
use std::ptr;
/// A synchronous memory, created by the [`Module::mem`] method.
///
/// Memories in kaze are always sequential/synchronous-read, sequential/synchronous-write memories.
/// 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.
/// 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.
///
/// Memories must have at least one read port specified.
/// Multiple reads to the same location within the same cycle will return the same value.
///
/// Memories may optionally have initial contents and/or a write port specified.
/// If either of these are missing, the contents of the memory can't be determined, so this is a logical error.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let my_mem = m.mem("my_mem", 1, 32);
/// // Optional, unless no write port is specified
/// my_mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32]);
/// // Optional, unless no initial contents are specified
/// my_mem.write_port(m.high(), m.lit(0xabad1deau32, 32), m.high());
/// m.output("my_output", my_mem.read_port(m.high(), m.high()));
/// ```
#[must_use]
pub struct Mem<'a> {
pub(super) context: &'a Context<'a>,
pub(crate) module: &'a Module<'a>,
pub(crate) name: String,
pub(crate) address_bit_width: u32,
pub(crate) element_bit_width: u32,
pub(crate) initial_contents: RefCell>>,
pub(crate) read_ports: RefCell, &'a InternalSignal<'a>)>>,
pub(crate) write_port: RefCell<
Option<(
&'a InternalSignal<'a>,
&'a InternalSignal<'a>,
&'a InternalSignal<'a>,
)>,
>,
}
impl<'a> Mem<'a> {
/// Specifies the initial contents for this `Mem`.
///
/// Reads from this `Mem` will reflect the values specified unless writes have overwritten them (if the `Mem` has a write port).
///
/// 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.
/// If initial contents are not specified, then this `Mem`'s contents will be undefined initially.
///
/// Note that these contents are **not** restored when the containing [`Module`]'s implicit reset is asserted.
///
/// # Panics
///
/// 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.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let my_mem = m.mem("my_mem", 1, 32);
/// // Optional, unless no write port is specified
/// my_mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32]);
/// // Optional, unless no initial contents are specified
/// my_mem.write_port(m.high(), m.lit(0xabad1deau32, 32), m.high());
/// m.output("my_output", my_mem.read_port(m.high(), m.high()));
/// ```
pub fn initial_contents>(&'a self, contents: &[C]) {
if self.initial_contents.borrow().is_some() {
panic!("Attempted to specify initial contents for memory \"{}\" in module \"{}\", but this memory already has initial contents.", self.name, self.module.name);
}
let expected_contents_len = 1 << self.address_bit_width;
if contents.len() != expected_contents_len {
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);
}
*self.initial_contents.borrow_mut() = Some(contents.iter().cloned().enumerate().map(|(i, x)| {
let ret = x.into();
if ret.required_bits() > self.element_bit_width {
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());
}
ret
}).collect());
}
/// Specifies a read port for this `Mem` and returns a [`Signal`] representing the data read from this port.
///
/// `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.
/// 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.
///
/// Read ports always have an `address` signal and an `enable` signal.
/// When `enable` is asserted, the returned [`Signal`] will reflect the data read from the location specified by `address` on the following cycle.
/// 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).
///
/// # Panics
///
/// Panics if `address`'s bit width doesn't match this `Mem`'s address bit width, or if `enable`'s bit width is not `1`.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let my_mem = m.mem("my_mem", 1, 32);
/// // Optional, unless no write port is specified
/// my_mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32]);
/// // Optional, unless no initial contents are specified
/// my_mem.write_port(m.high(), m.lit(0xabad1deau32, 32), m.high());
/// m.output("my_output", my_mem.read_port(m.high(), m.high()));
/// ```
pub fn read_port(
&'a self,
address: &'a dyn Signal<'a>,
enable: &'a dyn Signal<'a>,
) -> &dyn Signal<'a> {
let address = address.internal_signal();
let enable = enable.internal_signal();
// TODO: Limit amount of read ports added?
if address.bit_width() != self.address_bit_width {
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);
}
if enable.bit_width() != 1 {
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());
}
let ret = self.context.signal_arena.alloc(InternalSignal {
context: self.context,
module: self.module,
data: SignalData::MemReadPortOutput {
mem: self,
address,
enable,
},
});
self.read_ports.borrow_mut().push((address, enable));
ret
}
/// Specifies a write port for this `Mem`.
///
/// 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.
///
/// Write ports always have an `address` signal, a `value` signal, and an `enable` signal.
/// When `enable` is asserted, the value at the location specified by `address` will reflect the value of the `value` signal on the following cycle.
/// If `enable` is not asserted, then the memory contents will not change.
///
/// # Panics
///
/// 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`.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let my_mem = m.mem("my_mem", 1, 32);
/// // Optional, unless no write port is specified
/// my_mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32]);
/// // Optional, unless no initial contents are specified
/// my_mem.write_port(m.high(), m.lit(0xabad1deau32, 32), m.high());
/// m.output("my_output", my_mem.read_port(m.high(), m.high()));
/// ```
// TODO: byte/word enable? How might that interface look?
pub fn write_port(
&'a self,
address: &'a dyn Signal<'a>,
value: &'a dyn Signal<'a>,
enable: &'a dyn Signal<'a>,
) {
let address = address.internal_signal();
let value = value.internal_signal();
let enable = enable.internal_signal();
if self.write_port.borrow().is_some() {
panic!("Attempted to specify a write port for memory \"{}\" in module \"{}\", but this memory already has a write port.", self.name, self.module.name);
}
if address.bit_width() != self.address_bit_width {
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);
}
if value.bit_width() != self.element_bit_width {
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);
}
if enable.bit_width() != 1 {
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());
}
*self.write_port.borrow_mut() = Some((address, value, enable));
}
}
impl<'a> Eq for &'a Mem<'a> {}
impl<'a> Hash for &'a Mem<'a> {
fn hash(&self, state: &mut H) {
state.write_usize(*self as *const _ as usize)
}
}
impl<'a> PartialEq for &'a Mem<'a> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(*self, *other)
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
#[should_panic(
expected = "Attempted to specify initial contents for memory \"mem\" in module \"A\", but this memory already has initial contents."
)]
fn initial_contents_already_specified_error() {
let c = Context::new();
let m = c.module("a", "A");
let mem = m.mem("mem", 1, 1);
mem.initial_contents(&[true, false]);
// Panic
mem.initial_contents(&[true, false]);
}
#[test]
#[should_panic(
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)."
)]
fn initial_contents_length_error() {
let c = Context::new();
let m = c.module("a", "A");
let mem = m.mem("mem", 1, 1);
// Panic
mem.initial_contents(&[true, false, true]);
}
#[test]
#[should_panic(
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)."
)]
fn initial_contents_element_bit_width_error() {
let c = Context::new();
let m = c.module("a", "A");
let mem = m.mem("mem", 1, 1);
// Panic
mem.initial_contents(&[2u32, 0u32]);
}
#[test]
#[should_panic(
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)."
)]
fn read_port_address_bit_width_error() {
let c = Context::new();
let m = c.module("a", "A");
let mem = m.mem("mem", 1, 1);
// Panic
let _ = mem.read_port(m.lit(0u32, 2), m.low());
}
#[test]
#[should_panic(
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."
)]
fn read_port_enable_bit_width_error() {
let c = Context::new();
let m = c.module("a", "A");
let mem = m.mem("mem", 1, 1);
// Panic
let _ = mem.read_port(m.low(), m.lit(0u32, 2));
}
#[test]
#[should_panic(
expected = "Attempted to specify a write port for memory \"mem\" in module \"A\", but this memory already has a write port."
)]
fn write_port_already_specified_error() {
let c = Context::new();
let m = c.module("a", "A");
let mem = m.mem("mem", 1, 1);
mem.write_port(m.low(), m.low(), m.low());
// Panic
mem.write_port(m.low(), m.low(), m.low());
}
#[test]
#[should_panic(
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)."
)]
fn write_port_address_bit_width_error() {
let c = Context::new();
let m = c.module("a", "A");
let mem = m.mem("mem", 1, 1);
// Panic
mem.write_port(m.lit(0u32, 2), m.low(), m.low());
}
#[test]
#[should_panic(
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)."
)]
fn write_port_value_bit_width_error() {
let c = Context::new();
let m = c.module("a", "A");
let mem = m.mem("mem", 1, 1);
// Panic
mem.write_port(m.low(), m.lit(0u32, 2), m.low());
}
#[test]
#[should_panic(
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."
)]
fn write_port_enable_bit_width_error() {
let c = Context::new();
let m = c.module("a", "A");
let mem = m.mem("mem", 1, 1);
// Panic
mem.write_port(m.low(), m.low(), m.lit(0u32, 2));
}
}
================================================
FILE: kaze/src/graph/module.rs
================================================
use super::constant::*;
use super::context::*;
use super::internal_signal::*;
use super::mem::*;
use super::register::*;
use super::signal::*;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::hash::{Hash, Hasher};
use std::ptr;
/// A self-contained and potentially-reusable hardware design unit, created by the [`Context::module`] method.
///
/// 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).
///
/// 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.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
/// m.output("out", m.input("in", 1));
/// ```
// TODO: Validation error if a module has no inputs/outputs
// TODO: Document composing modules (even if it's really basic)
#[must_use]
pub struct Module<'a> {
context: &'a Context<'a>,
pub(crate) parent: Option<&'a Module<'a>>,
pub(crate) instance_name: String,
pub(crate) name: String,
// TODO: Do we need to duplicate the input/output names here?
pub(crate) inputs: RefCell>>,
pub(crate) outputs: RefCell>>,
pub(crate) registers: RefCell>>,
pub(crate) modules: RefCell>>,
pub(crate) mems: RefCell>>,
}
impl<'a> Module<'a> {
pub(super) fn new(
context: &'a Context<'a>,
parent: Option<&'a Module<'a>>,
instance_name: String,
name: String,
) -> Module<'a> {
Module {
context,
parent,
instance_name,
name,
inputs: RefCell::new(BTreeMap::new()),
outputs: RefCell::new(BTreeMap::new()),
registers: RefCell::new(Vec::new()),
modules: RefCell::new(Vec::new()),
mems: RefCell::new(Vec::new()),
}
}
/// Creates a [`Signal`] that represents the constant literal specified by `value` with `bit_width` bits.
///
/// 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.
///
/// # Panics
///
/// 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.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let eight_bit_const = m.lit(0xffu32, 8);
/// let one_bit_const = m.lit(0u32, 1);
/// let twenty_seven_bit_const = m.lit(true, 27);
/// ```
pub fn lit(&'a self, value: impl Into, bit_width: u32) -> &dyn Signal<'a> {
if bit_width < MIN_SIGNAL_BIT_WIDTH {
panic!(
"Cannot create a literal with {} bit(s). Signals must not be narrower than {} bit(s).",
bit_width, MIN_SIGNAL_BIT_WIDTH
);
}
if bit_width > MAX_SIGNAL_BIT_WIDTH {
panic!(
"Cannot create a literal with {} bit(s). Signals must not be wider than {} bit(s).",
bit_width, MAX_SIGNAL_BIT_WIDTH
);
}
let value = value.into();
let required_bits = value.required_bits();
if required_bits > bit_width {
let numeric_value = value.numeric_value();
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);
}
self.context.signal_arena.alloc(InternalSignal {
context: self.context,
module: self,
data: SignalData::Lit { value, bit_width },
})
}
/// Convenience method to create a [`Signal`] that represents a single `0` bit.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// // The following two signals are semantically equivalent:
/// let low1 = m.low();
/// let low2 = m.lit(false, 1);
/// ```
pub fn low(&'a self) -> &dyn Signal<'a> {
self.lit(false, 1)
}
/// Convenience method to create a [`Signal`] that represents a single `1` bit.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// // The following two signals are semantically equivalent:
/// let high1 = m.high();
/// let high2 = m.lit(true, 1);
/// ```
pub fn high(&'a self) -> &dyn Signal<'a> {
self.lit(true, 1)
}
/// Creates an input for this `Module` called `name` with `bit_width` bits, and returns a [`Signal`] that represents the value of this input.
///
/// # Panics
///
/// Panics if `bit_width` is less than [`MIN_SIGNAL_BIT_WIDTH`] or greater than [`MAX_SIGNAL_BIT_WIDTH`], respectively.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let my_input = m.input("my_input", 80);
/// ```
pub fn input(&'a self, name: impl Into, bit_width: u32) -> &Input<'a> {
let name = name.into();
// TODO: Error if name already exists in this context
if bit_width < MIN_SIGNAL_BIT_WIDTH {
panic!(
"Cannot create an input with {} bit(s). Signals must not be narrower than {} bit(s).",
bit_width, MIN_SIGNAL_BIT_WIDTH
);
}
if bit_width > MAX_SIGNAL_BIT_WIDTH {
panic!(
"Cannot create an input with {} bit(s). Signals must not be wider than {} bit(s).",
bit_width, MAX_SIGNAL_BIT_WIDTH
);
}
let data = self.context.input_data_arena.alloc(InputData {
name: name.clone(),
bit_width,
driven_value: RefCell::new(None),
});
let value = self.context.signal_arena.alloc(InternalSignal {
context: self.context,
module: self,
data: SignalData::Input { data },
});
let input = self.context.input_arena.alloc(Input {
module: self,
data,
value,
});
self.inputs.borrow_mut().insert(name, input);
input
}
/// Creates an output for this `Module` called `name` with the same number of bits as `source`, and drives this output with `source`.
///
/// # Panics
///
/// Panics of `source` doesn't belong to this `Module`.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let some_signal = m.high();
/// m.output("my_output", some_signal);
/// ```
pub fn output(&'a self, name: impl Into, source: &'a dyn Signal<'a>) -> &Output<'a> {
let name = name.into();
let source = source.internal_signal();
if !ptr::eq(self, source.module) {
panic!("Cannot output a signal from another module.");
}
// TODO: Error if name already exists in this context
let data = self.context.output_data_arena.alloc(OutputData {
module: self,
name: name.clone(),
source,
bit_width: source.bit_width(),
});
let output = self.context.output_arena.alloc(Output { data });
self.outputs.borrow_mut().insert(name, output);
output
}
/// Creates a [`Register`] in this `Module` called `name` with `bit_width` bits.
///
/// # Panics
///
/// Panics if `bit_width` is less than [`MIN_SIGNAL_BIT_WIDTH`] or greater than [`MAX_SIGNAL_BIT_WIDTH`], respectively.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let my_reg = m.reg("my_reg", 32);
/// my_reg.default_value(0xfadebabeu32); // Optional
/// my_reg.drive_next(!my_reg);
/// m.output("my_output", my_reg);
/// ```
pub fn reg(&'a self, name: impl Into, bit_width: u32) -> &Register<'a> {
// 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
if bit_width < MIN_SIGNAL_BIT_WIDTH {
panic!(
"Cannot create a register with {} bit(s). Signals must not be narrower than {} bit(s).",
bit_width, MIN_SIGNAL_BIT_WIDTH
);
}
if bit_width > MAX_SIGNAL_BIT_WIDTH {
panic!(
"Cannot create a register with {} bit(s). Signals must not be wider than {} bit(s).",
bit_width, MAX_SIGNAL_BIT_WIDTH
);
}
let data = self.context.register_data_arena.alloc(RegisterData {
module: self,
name: name.into(),
initial_value: RefCell::new(None),
bit_width,
next: RefCell::new(None),
});
let value = self.context.signal_arena.alloc(InternalSignal {
context: self.context,
module: self,
data: SignalData::Reg { data },
});
self.registers.borrow_mut().push(value);
self.context.register_arena.alloc(Register { data, value })
}
/// 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.
///
/// # Panics
///
/// 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.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let cond = m.input("cond", 1);
/// let a = m.input("a", 8);
/// let b = m.input("b", 8);
/// m.output("my_output", m.mux(cond, a, b)); // Outputs a when cond is high, b otherwise
/// ```
pub fn mux(
&'a self,
cond: &'a dyn Signal<'a>,
when_true: &'a dyn Signal<'a>,
when_false: &'a dyn Signal<'a>,
) -> &dyn Signal<'a> {
let cond = cond.internal_signal();
let when_true = when_true.internal_signal();
let when_false = when_false.internal_signal();
// TODO: This is an optimization to support sugar; if that doesn't go well, remove this
if when_true == when_false {
return when_true;
}
if !ptr::eq(self, cond.module) {
panic!("Attempted to combine signals from different modules.");
}
if !ptr::eq(self, when_true.module) {
panic!("Attempted to combine signals from different modules.");
}
if !ptr::eq(self, when_false.module) {
panic!("Attempted to combine signals from different modules.");
}
if cond.bit_width() != 1 {
panic!("Multiplexer conditionals can only be 1 bit wide.");
}
if when_true.bit_width() != when_false.bit_width() {
panic!(
"Cannot multiplex signals with different bit widths ({} and {}, respectively).",
when_true.bit_width(),
when_false.bit_width()
);
}
self.context.signal_arena.alloc(InternalSignal {
context: self.context,
module: self,
data: SignalData::Mux {
cond,
when_true,
when_false,
bit_width: when_true.bit_width(),
},
})
}
/// Creates a [`Mem`] in this `Module` called `name` with `address_bit_width` address bits and `element_bit_width` element bits.
///
/// The size of this memory will be `1 << address_bit_width` elements, each `element_bit_width` bits wide.
///
/// # Panics
///
/// Panics if `address_bit_width` or `element_bit_width` is less than [`MIN_SIGNAL_BIT_WIDTH`] or greater than [`MAX_SIGNAL_BIT_WIDTH`], respectively.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let my_mem = m.mem("my_mem", 1, 32);
/// // Optional, unless no write port is specified
/// my_mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32]);
/// // Optional, unless no initial contents are specified
/// my_mem.write_port(m.high(), m.lit(0xabad1deau32, 32), m.high());
/// m.output("my_output", my_mem.read_port(m.high(), m.high()));
/// ```
pub fn mem(
&'a self,
name: impl Into,
address_bit_width: u32,
element_bit_width: u32,
) -> &Mem<'a> {
// TODO: Error if name already exists in this context
if address_bit_width < MIN_SIGNAL_BIT_WIDTH {
panic!(
"Cannot create a memory with {} address bit(s). Signals must not be narrower than {} bit(s).",
address_bit_width, MIN_SIGNAL_BIT_WIDTH
);
}
if address_bit_width > MAX_SIGNAL_BIT_WIDTH {
panic!(
"Cannot create a memory with {} address bit(s). Signals must not be wider than {} bit(s).",
address_bit_width, MAX_SIGNAL_BIT_WIDTH
);
}
if element_bit_width < MIN_SIGNAL_BIT_WIDTH {
panic!(
"Cannot create a memory with {} element bit(s). Signals must not be narrower than {} bit(s).",
element_bit_width, MIN_SIGNAL_BIT_WIDTH
);
}
if element_bit_width > MAX_SIGNAL_BIT_WIDTH {
panic!(
"Cannot create a memory with {} element bit(s). Signals must not be wider than {} bit(s).",
element_bit_width, MAX_SIGNAL_BIT_WIDTH
);
}
let ret = self.context.mem_arena.alloc(Mem {
context: self.context,
module: self,
name: name.into(),
address_bit_width,
element_bit_width,
initial_contents: RefCell::new(None),
read_ports: RefCell::new(Vec::new()),
write_port: RefCell::new(None),
});
self.mems.borrow_mut().push(ret);
ret
}
}
impl<'a> ModuleParent<'a> for Module<'a> {
// TODO: Docs, error handling
fn module(&'a self, instance_name: impl Into, name: impl Into) -> &Module {
let instance_name = instance_name.into();
let name = name.into();
let module = self.context.module_arena.alloc(Module::new(
self.context,
Some(self),
instance_name,
name,
));
self.modules.borrow_mut().push(module);
module
}
}
impl<'a> Eq for &'a Module<'a> {}
impl<'a> Hash for &'a Module<'a> {
fn hash(&self, state: &mut H) {
state.write_usize(*self as *const _ as usize)
}
}
impl<'a> PartialEq for &'a Module<'a> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(*self, *other)
}
}
// TODO: Move?
// TODO: Doc
// TODO: bit width as const generic param?
#[must_use]
pub struct Input<'a> {
// TODO: Double-check that we need this (I think we need it for error checking in drive)
pub(crate) module: &'a Module<'a>,
pub(crate) data: &'a InputData<'a>,
pub(crate) value: &'a InternalSignal<'a>,
}
impl<'a> Input<'a> {
// TODO: Doc
// TODO: Merge error cases with Instance::drive_input?
// TODO: Rename i?
pub fn drive(&'a self, i: &'a dyn Signal<'a>) {
let i = i.internal_signal();
// TODO: Change text from instance -> module in appropriate places?
if let Some(parent) = self.module.parent {
if !ptr::eq(parent, i.module) {
// TODO: Clarify?
panic!("Attempted to drive an instance input with a signal from a different module than that instance's parent module.");
}
} else {
// TODO: Proper panic + test!
panic!("OH NOES");
}
let mut driven_value = self.data.driven_value.borrow_mut();
if driven_value.is_some() {
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);
}
if self.data.bit_width != i.bit_width() {
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());
}
*driven_value = Some(i);
}
}
impl<'a> GetInternalSignal<'a> for Input<'a> {
fn internal_signal(&'a self) -> &'a InternalSignal<'a> {
self.value
}
}
impl<'a> GetInternalSignal<'a> for Output<'a> {
fn internal_signal(&'a self) -> &'a InternalSignal<'a> {
let parent = self.data.module.parent.expect("TODO better error pls");
self.data.module.context.signal_arena.alloc(InternalSignal {
context: self.data.module.context,
module: parent,
data: SignalData::Output { data: self.data },
})
}
}
pub(crate) struct InputData<'a> {
// TODO: Do we need this stored here too?
pub name: String,
pub bit_width: u32,
// TODO: Rename?
pub driven_value: RefCell>>,
}
// TODO: Move?
// TODO: Doc
// TODO: must_use?
// TODO: bit width as const generic param?
pub struct Output<'a> {
pub(crate) data: &'a OutputData<'a>,
}
pub(crate) struct OutputData<'a> {
// TODO: Do we need this?
pub module: &'a Module<'a>,
// TODO: Do we need this stored here too?
pub name: String,
pub source: &'a InternalSignal<'a>,
pub bit_width: u32,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic(
expected = "Cannot create a literal with 0 bit(s). Signals must not be narrower than 1 bit(s)."
)]
fn lit_bit_width_lt_min_error() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.lit(false, 0);
}
#[test]
#[should_panic(
expected = "Cannot create a literal with 129 bit(s). Signals must not be wider than 128 bit(s)."
)]
fn lit_bit_width_gt_max_error() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.lit(false, 129);
}
#[test]
#[should_panic(
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)."
)]
fn lit_value_cannot_bit_into_bit_width_error_1() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.lit(128u32, 7);
}
#[test]
#[should_panic(
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)."
)]
fn lit_value_cannot_bit_into_bit_width_error_2() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.lit(128u64, 2);
}
#[test]
#[should_panic(
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)."
)]
fn lit_value_cannot_bit_into_bit_width_error_3() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.lit(1023u128, 4);
}
#[test]
#[should_panic(
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)."
)]
fn lit_value_cannot_bit_into_bit_width_error_4() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.lit(65536u32, 1);
}
#[test]
#[should_panic(
expected = "Cannot create an input with 0 bit(s). Signals must not be narrower than 1 bit(s)."
)]
fn input_width_lt_min_error() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.input("i", 0);
}
#[test]
#[should_panic(
expected = "Cannot create an input with 129 bit(s). Signals must not be wider than 128 bit(s)."
)]
fn input_width_gt_max_error() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.input("i", 129);
}
#[test]
#[should_panic(expected = "Cannot output a signal from another module.")]
fn output_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let m2 = c.module("b", "B");
let i = m2.high();
// Panic
m1.output("a", i);
}
#[test]
#[should_panic(
expected = "Cannot create a register with 0 bit(s). Signals must not be narrower than 1 bit(s)."
)]
fn reg_bit_width_lt_min_error() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.reg("r", 0);
}
#[test]
#[should_panic(
expected = "Cannot create a register with 129 bit(s). Signals must not be wider than 128 bit(s)."
)]
fn reg_bit_width_gt_max_error() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.reg("r", 129);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn mux_cond_separate_module_error() {
let c = Context::new();
let a = c.module("a", "A");
let l1 = a.lit(false, 1);
let b = c.module("b", "B");
let l2 = b.lit(32u8, 8);
let l3 = b.lit(32u8, 8);
// Panic
let _ = b.mux(l1, l2, l3);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn mux_when_true_separate_module_error() {
let c = Context::new();
let a = c.module("a", "A");
let l1 = a.lit(32u8, 8);
let b = c.module("b", "B");
let l2 = b.lit(true, 1);
let l3 = b.lit(32u8, 8);
// Panic
let _ = b.mux(l2, l1, l3);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn mux_when_false_separate_module_error() {
let c = Context::new();
let a = c.module("a", "A");
let l1 = a.lit(32u8, 8);
let b = c.module("b", "B");
let l2 = b.lit(true, 1);
let l3 = b.lit(32u8, 8);
// Panic
let _ = b.mux(l2, l3, l1);
}
#[test]
#[should_panic(expected = "Multiplexer conditionals can only be 1 bit wide.")]
fn mux_cond_bit_width_error() {
let c = Context::new();
let a = c.module("a", "A");
let l1 = a.lit(2u8, 2);
let l2 = a.lit(32u8, 8);
let l3 = a.lit(32u8, 8);
// Panic
let _ = a.mux(l1, l2, l3);
}
#[test]
#[should_panic(
expected = "Cannot multiplex signals with different bit widths (3 and 5, respectively)."
)]
fn mux_true_false_bit_width_error() {
let c = Context::new();
let a = c.module("a", "A");
let l1 = a.lit(false, 1);
let l2 = a.lit(3u8, 3);
let l3 = a.lit(3u8, 5);
// Panic
let _ = a.mux(l1, l2, l3);
}
#[test]
#[should_panic(
expected = "Cannot create a memory with 0 address bit(s). Signals must not be narrower than 1 bit(s)."
)]
fn mem_address_bit_width_lt_min_error() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.mem("mem", 0, 1);
}
#[test]
#[should_panic(
expected = "Cannot create a memory with 129 address bit(s). Signals must not be wider than 128 bit(s)."
)]
fn mem_address_bit_width_gt_max_error() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.mem("mem", 129, 1);
}
#[test]
#[should_panic(
expected = "Cannot create a memory with 0 element bit(s). Signals must not be narrower than 1 bit(s)."
)]
fn mem_element_bit_width_lt_min_error() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.mem("mem", 1, 0);
}
#[test]
#[should_panic(
expected = "Cannot create a memory with 129 element bit(s). Signals must not be wider than 128 bit(s)."
)]
fn mem_element_bit_width_gt_max_error() {
let c = Context::new();
let m = c.module("a", "A");
// Panic
let _ = m.mem("mem", 1, 129);
}
#[test]
#[should_panic(
expected = "Attempted to drive an instance input with a signal from a different module than that instance's parent module."
)]
fn input_drive_different_module_than_parent_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let inner = m2.module("inner", "Inner");
let a = inner.input("a", 1);
// Panic
a.drive(i1);
}
#[test]
#[should_panic(
expected = "Attempted to drive an input called \"a\" on an instance of \"Inner\", but this input is already driven for this instance."
)]
fn input_drive_already_driven_error() {
let c = Context::new();
let m = c.module("a", "A");
let inner = m.module("inner", "Inner");
let a = inner.input("a", 1);
a.drive(m.input("i1", 1));
// Panic
a.drive(m.input("i2", 1));
}
#[test]
#[should_panic(
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)."
)]
fn input_drive_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let inner = m.module("inner", "Inner");
let a = inner.input("a", 1);
// Panic
a.drive(m.input("i1", 32));
}
}
================================================
FILE: kaze/src/graph/register.rs
================================================
use super::constant::*;
use super::internal_signal::*;
use super::module::*;
use super::signal::*;
use std::cell::RefCell;
use std::ptr;
/// A hardware register, created by the [`Module::reg`] method.
///
/// 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)).
///
/// 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`).
/// 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.
///
/// 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.
/// 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.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let my_reg = m.reg("my_reg", 32);
/// my_reg.default_value(0xfadebabeu32); // Optional
/// my_reg.drive_next(!my_reg);
/// m.output("my_output", my_reg);
/// ```
///
/// [`default_value`]: Self::default_value
/// [`drive_next`]: Self::drive_next
/// [`value`]: Self::value
#[must_use]
pub struct Register<'a> {
pub(crate) data: &'a RegisterData<'a>,
/// This `Register`'s current value.
pub(crate) value: &'a InternalSignal<'a>,
}
impl<'a> Register<'a> {
/// Specifies the default value for this `Register`.
///
/// This `Register`'s [`value`] will reflect this default value when this `Register`'s [`Module`]'s implicit reset is asserted.
///
/// 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.
///
/// # Panics
///
/// Panics if this `Register` already has a default value specified, or if the specified `value` doesn't fit into this `Register`'s bit width.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let my_reg = m.reg("my_reg", 32);
/// my_reg.default_value(0xfadebabeu32); // Optional
/// my_reg.drive_next(!my_reg);
/// m.output("my_output", my_reg);
/// ```
///
/// [`value`]: Self::value
pub fn default_value(&'a self, value: impl Into) {
if self.data.initial_value.borrow().is_some() {
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);
}
let value = value.into();
let required_bits = value.required_bits();
if required_bits > self.data.bit_width {
let numeric_value = value.numeric_value();
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);
}
*self.data.initial_value.borrow_mut() = Some(value);
}
/// Specifies the next value for this `Register`.
///
/// 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.
///
/// # Panics
///
/// 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.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let my_reg = m.reg("my_reg", 32);
/// my_reg.default_value(0xfadebabeu32); // Optional
/// my_reg.drive_next(!my_reg); // my_reg's value will toggle with each positive clock edge
/// m.output("my_output", my_reg);
/// ```
///
/// [`value`]: Self::value
pub fn drive_next(&'a self, n: &'a dyn Signal<'a>) {
let n = n.internal_signal();
if !ptr::eq(self.data.module, n.module) {
panic!("Attempted to drive register \"{}\"'s next value with a signal from another module.", self.data.name);
}
if n.bit_width() != self.data.bit_width {
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);
}
if self.data.next.borrow().is_some() {
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);
}
*self.data.next.borrow_mut() = Some(n);
}
}
pub(crate) struct RegisterData<'a> {
pub module: &'a Module<'a>,
pub name: String,
pub initial_value: RefCell>,
pub bit_width: u32,
pub next: RefCell >>,
}
impl<'a> GetInternalSignal<'a> for Register<'a> {
fn internal_signal(&'a self) -> &'a InternalSignal<'a> {
self.value
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
#[should_panic(
expected = "Attempted to specify a default value for register \"r\" in module \"A\", but this register already has a default value."
)]
fn default_value_already_specified_error() {
let c = Context::new();
let m = c.module("a", "A");
let r = m.reg("r", 32);
r.default_value(0xfadebabeu32);
// Panic
r.default_value(0xdeadbeefu32);
}
#[test]
#[should_panic(
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)."
)]
fn default_value_cannot_bit_into_bit_width_error_1() {
let c = Context::new();
let m = c.module("a", "A");
let r = m.reg("r", 7);
// Panic
r.default_value(128u32);
}
#[test]
#[should_panic(
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)."
)]
fn default_value_cannot_bit_into_bit_width_error_2() {
let c = Context::new();
let m = c.module("a", "A");
let r = m.reg("r", 2);
// Panic
r.default_value(128u64);
}
#[test]
#[should_panic(
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)."
)]
fn default_value_cannot_bit_into_bit_width_error_3() {
let c = Context::new();
let m = c.module("a", "A");
let r = m.reg("r", 4);
// Panic
r.default_value(1023u128);
}
#[test]
#[should_panic(
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)."
)]
fn default_value_cannot_bit_into_bit_width_error_4() {
let c = Context::new();
let m = c.module("a", "A");
let r = m.reg("r", 1);
// Panic
r.default_value(65536u32);
}
#[test]
#[should_panic(
expected = "Attempted to drive register \"r\"'s next value with a signal from another module."
)]
fn drive_next_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let l = m1.lit(true, 1);
let m2 = c.module("b", "B");
let r = m2.reg("r", 1);
// Panic
r.drive_next(l);
}
#[test]
#[should_panic(
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)."
)]
fn drive_next_incompatible_bit_width_error() {
let c = Context::new();
let m = c.module("a", "A");
let r = m.reg("r", 3);
let i = m.input("i", 5);
// Panic
r.drive_next(i);
}
#[test]
#[should_panic(
expected = "Attempted to drive register \"r\"'s next value in module \"A\", but this register's next value is already driven."
)]
fn drive_next_already_driven_error() {
let c = Context::new();
let m = c.module("a", "A");
let r = m.reg("r", 32);
let i = m.input("i", 32);
r.drive_next(i);
// Panic
r.drive_next(i);
}
}
================================================
FILE: kaze/src/graph/signal.rs
================================================
use super::constant::*;
use super::internal_signal::*;
use std::ops::{Add, BitAnd, BitOr, BitXor, Mul, Not, Shl, Shr, Sub};
use std::ptr;
/// The minimum allowed bit width for any given [`Signal`].
///
/// This is currently set to `1`, and is not likely to change in future versions of this library.
pub const MIN_SIGNAL_BIT_WIDTH: u32 = 1;
/// The maximum allowed bit width for any given [`Signal`].
///
/// This is currently set to `128` to simplify simulator code generation, since it allows the generated code to rely purely on native integer types provided by Rust's standard library for storage, arithmetic, etc. Larger widths may be supported in a future version of this library.
pub const MAX_SIGNAL_BIT_WIDTH: u32 = 128;
/// A collection of 1 or more bits driven by some source.
///
/// A `Signal` can be created by several [`Module`] methods (eg. [`lit`]) or as a result of combining existing `Signal`s (eg. [`concat`]). `Signal`s are local to their respective [`Module`]s.
///
/// A `Signal` behaves similarly to a `wire` in Verilog, except that it's always driven.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
/// let a = m.lit(0xffu8, 8); // 8-bit signal
/// let b = m.input("my_input", 27); // 27-bit signal
/// let c = b.bits(7, 0); // 8-bit signal
/// let d = a + c; // 8-bit signal
/// m.output("my_output", d); // 8-bit output driven by d
/// ```
///
/// [`concat`]: Self::concat
/// [`lit`]: Module::lit
pub trait Signal<'a>: GetInternalSignal<'a> {
/// Returns the bit width of the given `Signal`.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// assert_eq!(m.lit(42u32, 7).bit_width(), 7);
/// assert_eq!(m.input("i", 27).bit_width(), 27);
/// assert_eq!(m.reg("some_reg", 46).bit_width(), 46);
/// assert_eq!((!m.low()).bit_width(), 1);
/// assert_eq!((m.lit(25u8, 8) + m.lit(42u8, 8)).bit_width(), 8);
/// assert_eq!((m.lit(1u8, 1) * m.lit(2u8, 2)).bit_width(), 3);
/// assert_eq!(m.lit(1u8, 1).mul_signed(m.lit(2u8, 2)).bit_width(), 3);
/// assert_eq!(m.lit(false, 1).reg_next("some_other_reg").bit_width(), 1);
/// assert_eq!(m.lit(true, 1).reg_next_with_default("yet_another_reg", false).bit_width(), 1);
/// assert_eq!((m.high() & m.low()).bit_width(), 1);
/// assert_eq!((m.high() | m.low()).bit_width(), 1);
/// assert_eq!(m.lit(12u32, 100).bit(30).bit_width(), 1);
/// assert_eq!(m.lit(1u32, 99).bits(37, 29).bit_width(), 9);
/// assert_eq!(m.high().repeat(35).bit_width(), 35);
/// assert_eq!(m.lit(1u32, 20).concat(m.high()).bit_width(), 21);
/// assert_eq!((m.lit(0x80u32, 8) << m.lit(true, 1)).bit_width(), 8);
/// assert_eq!((m.lit(0x80u32, 8) >> m.lit(true, 1)).bit_width(), 8);
/// assert_eq!(m.lit(0x80u32, 8).shr_arithmetic(m.lit(true, 1)).bit_width(), 8);
/// assert_eq!(m.lit(0xaau32, 8).eq(m.lit(0xaau32, 8)).bit_width(), 1);
/// assert_eq!(m.lit(0xaau32, 8).ne(m.lit(0xaau32, 8)).bit_width(), 1);
/// assert_eq!(m.lit(0xaau32, 8).lt(m.lit(0xaau32, 8)).bit_width(), 1);
/// assert_eq!(m.lit(0xaau32, 8).le(m.lit(0xaau32, 8)).bit_width(), 1);
/// assert_eq!(m.lit(0xaau32, 8).gt(m.lit(0xaau32, 8)).bit_width(), 1);
/// assert_eq!(m.lit(0xaau32, 8).ge(m.lit(0xaau32, 8)).bit_width(), 1);
/// assert_eq!(m.mux(m.low(), m.lit(5u32, 4), m.lit(6u32, 4)).bit_width(), 4);
/// ```
#[must_use]
fn bit_width(&'a self) -> u32 {
let s = self.internal_signal();
s.bit_width()
}
/// Creates a `Signal` that represents the value of the single bit of this `Signal` at index `index`, where `index` equal to `0` represents this `Signal`'s least significant bit.
///
/// # Panics
///
/// Panics if `index` is greater than or equal to this `Signal`'s `bit_width`.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit = m.lit(0b0110u32, 4);
/// let bit_0 = lit.bit(0); // Represents 0
/// let bit_1 = lit.bit(1); // Represents 1
/// let bit_2 = lit.bit(2); // Represents 1
/// let bit_3 = lit.bit(3); // Represents 0
/// ```
fn bit(&'a self, index: u32) -> &'a dyn Signal<'a> {
let s = self.internal_signal();
if index >= s.bit_width() {
panic!("Attempted to take bit index {} from a signal with a width of {} bits. Bit indices must be in the range [0, {}] for a signal with a width of {} bits.", index, s.bit_width(), s.bit_width() - 1, s.bit_width());
}
s.context.signal_arena.alloc(InternalSignal {
context: s.context,
module: s.module,
data: SignalData::Bits {
source: s,
range_high: index,
range_low: index,
},
})
}
/// Creates a `Signal` that represents a contiguous subset of the bits of this `Signal`, starting at `range_low` as the least significant bit and ending at `range_high` as the most significant bit, inclusive.
///
/// # Panics
///
/// Panics if either `range_low` or `range_high` is greater than or equal to the bit width of this `Signal`, or if `range_low` is greater than `range_high`.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit = m.lit(0b0110u32, 4);
/// let bits_210 = lit.bits(2, 0); // Represents 0b110
/// let bits_321 = lit.bits(3, 1); // Represents 0b011
/// let bits_10 = lit.bits(1, 0); // Represents 0b10
/// let bits_32 = lit.bits(3, 2); // Represents 0b01
/// let bits_2 = lit.bits(2, 2); // Represents 1, equivalent to lit.bit(2)
/// ```
fn bits(&'a self, range_high: u32, range_low: u32) -> &'a dyn Signal<'a> {
let s = self.internal_signal();
if range_low >= s.bit_width() {
panic!("Cannot specify a range of bits where the lower bound is greater than or equal to the number of bits in the source signal. The bounds must be in the range [0, {}] for a signal with a width of {} bits, but a lower bound of {} was given.", s.bit_width() - 1, s.bit_width(), range_low);
}
if range_high >= s.bit_width() {
panic!("Cannot specify a range of bits where the upper bound is greater than or equal to the number of bits in the source signal. The bounds must be in the range [0, {}] for a signal with a width of {} bits, but an upper bound of {} was given.", s.bit_width() - 1, s.bit_width(), range_high);
}
if range_low > range_high {
panic!("Cannot specify a range of bits where the lower bound is greater than the upper bound.");
}
s.context.signal_arena.alloc(InternalSignal {
context: s.context,
module: s.module,
data: SignalData::Bits {
source: s,
range_high,
range_low,
},
})
}
/// Creates a `Signal` that represents this `Signal` repeated `count` times.
///
/// # Panics
///
/// Panics if `self.bit_width() * count` is less than [`MIN_SIGNAL_BIT_WIDTH`] or greater than [`MAX_SIGNAL_BIT_WIDTH`].
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit = m.lit(0xau32, 4);
/// let repeat_1 = lit.repeat(1); // Equivalent to just lit
/// let repeat_2 = lit.repeat(2); // Equivalent to 8-bit lit with value 0xaa
/// let repeat_5 = lit.repeat(5); // Equivalent to 20-bit lit with value 0xaaaaa
/// let repeat_8 = lit.repeat(8); // Equivalent to 32-bit lit with value 0xaaaaaaaa
/// ```
fn repeat(&'a self, count: u32) -> &'a dyn Signal<'a> {
let s = self.internal_signal();
let bit_width = s.bit_width() * count;
if bit_width < MIN_SIGNAL_BIT_WIDTH {
panic!("Attempted to repeat a {}-bit signal {} times, but this would result in a bit width of {}, which is less than the minimal signal bit width of {} bit(s).", s.bit_width(), count, bit_width, MIN_SIGNAL_BIT_WIDTH);
}
if bit_width > MAX_SIGNAL_BIT_WIDTH {
panic!("Attempted to repeat a {}-bit signal {} times, but this would result in a bit width of {}, which is greater than the maximum signal bit width of {} bit(s).", s.bit_width(), count, bit_width, MAX_SIGNAL_BIT_WIDTH);
}
s.context.signal_arena.alloc(InternalSignal {
context: s.context,
module: s.module,
data: SignalData::Repeat {
source: s,
count,
bit_width,
},
})
}
/// Creates a `Signal` that represents this `Signal` concatenated with `rhs`.
///
/// `self` represents the upper bits in the resulting `Signal`, and `rhs` represents the lower bits.
///
/// # Panics
///
/// Panics if `self.bit_width() + rhs.bit_width()` is greater than [`MAX_SIGNAL_BIT_WIDTH`].
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xffu32, 8);
/// let concat_1 = lit_a.concat(lit_b); // Equivalent to 12-bit lit with value 0xaff
/// let concat_2 = lit_b.concat(lit_a); // Equivalent to 12-bit lit with value 0xffa
/// let concat_3 = lit_a.concat(lit_a); // Equivalent to 8-bit lit with value 0xaa
/// ```
fn concat(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
let bit_width = lhs.bit_width() + rhs.bit_width();
if bit_width > MAX_SIGNAL_BIT_WIDTH {
panic!("Attempted to concatenate signals with {} bit(s) and {} bit(s) respectively, but this would result in a bit width of {}, which is greater than the maximum signal bit width of {} bit(s).", lhs.bit_width(), rhs.bit_width(), bit_width, MAX_SIGNAL_BIT_WIDTH);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::Concat {
lhs,
rhs,
bit_width,
},
})
}
/// Creates a `Signal` that represents the single-bit result of a bitwise boolean equality comparison between `self` and `rhs`.
///
/// # Panics
///
/// Panics if `self` and `rhs` belong to different [`Module`]s, or if the bit widths of `self` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xbu32, 4);
/// let eq_1 = lit_a.eq(lit_a); // Equivalent to m.high()
/// let eq_2 = lit_b.eq(lit_b); // Equivalent to m.high()
/// let eq_3 = lit_a.eq(lit_b); // Equivalent to m.low()
/// let eq_4 = lit_b.eq(lit_a); // Equivalent to m.low()
/// ```
fn eq(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ComparisonBinOp {
lhs,
rhs,
op: ComparisonBinOp::Equal,
},
})
}
/// Creates a `Signal` that represents the single-bit result of a bitwise boolean inequality comparison between `self` and `rhs`.
///
/// # Panics
///
/// Panics if `self` and `rhs` belong to different [`Module`]s, or if the bit widths of `self` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xbu32, 4);
/// let ne_1 = lit_a.ne(lit_a); // Equivalent to m.low()
/// let ne_2 = lit_b.ne(lit_b); // Equivalent to m.low()
/// let ne_3 = lit_a.ne(lit_b); // Equivalent to m.high()
/// let ne_4 = lit_b.ne(lit_a); // Equivalent to m.high()
/// ```
fn ne(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ComparisonBinOp {
lhs,
rhs,
op: ComparisonBinOp::NotEqual,
},
})
}
/// Creates a `Signal` that represents the single-bit result of an unsigned `<` comparison between `self` and `rhs`.
///
/// # Panics
///
/// Panics if `self` and `rhs` belong to different [`Module`]s, or if the bit widths of `self` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xbu32, 4);
/// let lt_1 = lit_a.lt(lit_a); // Equivalent to m.low()
/// let lt_2 = lit_b.lt(lit_b); // Equivalent to m.low()
/// let lt_3 = lit_a.lt(lit_b); // Equivalent to m.high()
/// let lt_4 = lit_b.lt(lit_a); // Equivalent to m.low()
/// ```
fn lt(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ComparisonBinOp {
lhs,
rhs,
op: ComparisonBinOp::LessThan,
},
})
}
/// Creates a `Signal` that represents the single-bit result of an unsigned `<=` comparison between `self` and `rhs`.
///
/// # Panics
///
/// Panics if `self` and `rhs` belong to different [`Module`]s, or if the bit widths of `self` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xbu32, 4);
/// let le_1 = lit_a.le(lit_a); // Equivalent to m.high()
/// let le_2 = lit_b.le(lit_b); // Equivalent to m.high()
/// let le_3 = lit_a.le(lit_b); // Equivalent to m.high()
/// let le_4 = lit_b.le(lit_a); // Equivalent to m.low()
/// ```
fn le(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ComparisonBinOp {
lhs,
rhs,
op: ComparisonBinOp::LessThanEqual,
},
})
}
/// Creates a `Signal` that represents the single-bit result of an unsigned `>` comparison between `self` and `rhs`.
///
/// # Panics
///
/// Panics if `self` and `rhs` belong to different [`Module`]s, or if the bit widths of `self` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xbu32, 4);
/// let gt_1 = lit_a.gt(lit_a); // Equivalent to m.low()
/// let gt_2 = lit_b.gt(lit_b); // Equivalent to m.low()
/// let gt_3 = lit_a.gt(lit_b); // Equivalent to m.low()
/// let gt_4 = lit_b.gt(lit_a); // Equivalent to m.high()
/// ```
fn gt(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ComparisonBinOp {
lhs,
rhs,
op: ComparisonBinOp::GreaterThan,
},
})
}
/// Creates a `Signal` that represents the single-bit result of an unsigned `>=` comparison between `self` and `rhs`.
///
/// # Panics
///
/// Panics if `self` and `rhs` belong to different [`Module`]s, or if the bit widths of `self` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xbu32, 4);
/// let ge_1 = lit_a.ge(lit_a); // Equivalent to m.high()
/// let ge_2 = lit_b.ge(lit_b); // Equivalent to m.high()
/// let ge_3 = lit_a.ge(lit_b); // Equivalent to m.low()
/// let ge_4 = lit_b.ge(lit_a); // Equivalent to m.high()
/// ```
fn ge(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ComparisonBinOp {
lhs,
rhs,
op: ComparisonBinOp::GreaterThanEqual,
},
})
}
/// Creates a `Signal` that represents the single-bit result of a signed `<` comparison between `self` and `rhs`.
///
/// # Panics
///
/// Panics if `self` and `rhs` belong to different [`Module`]s, if the bit widths of `self` and `rhs` aren't equal, or if the bit widths of `self` and `rhs` are 1.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xbu32, 4);
/// let lt_signed_1 = lit_a.lt_signed(lit_a); // Equivalent to m.low()
/// let lt_signed_2 = lit_b.lt_signed(lit_b); // Equivalent to m.low()
/// let lt_signed_3 = lit_a.lt_signed(lit_b); // Equivalent to m.high()
/// let lt_signed_4 = lit_b.lt_signed(lit_a); // Equivalent to m.low()
/// ```
fn lt_signed(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
if lhs.bit_width() == 1 {
panic!("Cannot perform signed comparison of 1-bit signals.");
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ComparisonBinOp {
lhs,
rhs,
op: ComparisonBinOp::LessThanSigned,
},
})
}
/// Creates a `Signal` that represents the single-bit result of a signed `<=` comparison between `self` and `rhs`.
///
/// # Panics
///
/// Panics if `self` and `rhs` belong to different [`Module`]s, if the bit widths of `self` and `rhs` aren't equal, or if the bit widths of `self` and `rhs` are 1.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xbu32, 4);
/// let le_signed_1 = lit_a.le_signed(lit_a); // Equivalent to m.high()
/// let le_signed_2 = lit_b.le_signed(lit_b); // Equivalent to m.high()
/// let le_signed_3 = lit_a.le_signed(lit_b); // Equivalent to m.high()
/// let le_signed_4 = lit_b.le_signed(lit_a); // Equivalent to m.low()
/// ```
fn le_signed(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
if lhs.bit_width() == 1 {
panic!("Cannot perform signed comparison of 1-bit signals.");
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ComparisonBinOp {
lhs,
rhs,
op: ComparisonBinOp::LessThanEqualSigned,
},
})
}
/// Creates a `Signal` that represents the single-bit result of a signed `>` comparison between `self` and `rhs`.
///
/// # Panics
///
/// Panics if `self` and `rhs` belong to different [`Module`]s, if the bit widths of `self` and `rhs` aren't equal, or if the bit widths of `self` and `rhs` are 1.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xbu32, 4);
/// let gt_signed_1 = lit_a.gt_signed(lit_a); // Equivalent to m.low()
/// let gt_signed_2 = lit_b.gt_signed(lit_b); // Equivalent to m.low()
/// let gt_signed_3 = lit_a.gt_signed(lit_b); // Equivalent to m.low()
/// let gt_signed_4 = lit_b.gt_signed(lit_a); // Equivalent to m.high()
/// ```
fn gt_signed(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
if lhs.bit_width() == 1 {
panic!("Cannot perform signed comparison of 1-bit signals.");
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ComparisonBinOp {
lhs,
rhs,
op: ComparisonBinOp::GreaterThanSigned,
},
})
}
/// Creates a `Signal` that represents the single-bit result of a signed `>=` comparison between `self` and `rhs`.
///
/// # Panics
///
/// Panics if `self` and `rhs` belong to different [`Module`]s, if the bit widths of `self` and `rhs` aren't equal, or if the bit widths of `self` and `rhs` are 1.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lit_a = m.lit(0xau32, 4);
/// let lit_b = m.lit(0xbu32, 4);
/// let ge_signed_1 = lit_a.ge_signed(lit_a); // Equivalent to m.high()
/// let ge_signed_2 = lit_b.ge_signed(lit_b); // Equivalent to m.high()
/// let ge_signed_3 = lit_a.ge_signed(lit_b); // Equivalent to m.low()
/// let ge_signed_4 = lit_b.ge_signed(lit_a); // Equivalent to m.high()
/// ```
fn ge_signed(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
if lhs.bit_width() == 1 {
panic!("Cannot perform signed comparison of 1-bit signals.");
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ComparisonBinOp {
lhs,
rhs,
op: ComparisonBinOp::GreaterThanEqualSigned,
},
})
}
/// Combines two `Signal`s, producing a new `Signal` that represents `self` arithmetically shifted right by `rhs` bits.
///
/// The result is truncated to `self`'s `bit_width`. If `rhs` specifies a value that's greater than or equal to `self`'s `bit_width`, the resulting value will be all `self`'s top bit repeated `self`'s `bit_width` times.
///
/// # Panics
///
/// Panics if `lhs` and `rhs` belong to different [`Module`]s.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lhs = m.lit(0x80000000u32, 32);
/// let rhs = m.lit(1u32, 1);
/// let shifted = lhs.shr_arithmetic(rhs); // Equivalent to m.lit(0xc0000000u32, 32)
/// ```
fn shr_arithmetic(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ShiftBinOp {
lhs,
rhs,
op: ShiftBinOp::ShrArithmetic,
bit_width: lhs.bit_width(),
},
})
}
/// Combines two `Signal`s, producing a new `Signal` that represents the signed product of the original two `Signal`s.
///
/// The product's `bit_width` is equal to `self.bit_width() + rhs.bit_width()`.
///
/// # Panics
///
/// Panics if `lhs` and `rhs` belong to different [`Module`]s, or if `self.bit_width() + rhs.bit_width()` is greater than [`MAX_SIGNAL_BIT_WIDTH`].
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lhs = m.lit(4u32, 3); // -4
/// let rhs = m.lit(5u32, 4);
/// let sum = lhs.mul_signed(rhs); // Equivalent to m.lit(108u32, 7), -20
/// ```
fn mul_signed(&'a self, rhs: &'a dyn Signal<'a>) -> &dyn Signal<'a> {
let lhs = self.internal_signal();
let rhs = rhs.internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
let bit_width = lhs.bit_width() + rhs.bit_width();
if bit_width > MAX_SIGNAL_BIT_WIDTH {
panic!("Attempted to multiply a {}-bit with a {}-bit signal, but this would result in a bit width of {}, which is greater than the maximum signal bit width of {} bit(s).", lhs.bit_width(), rhs.bit_width(), bit_width, MAX_SIGNAL_BIT_WIDTH);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::MulSigned {
lhs,
rhs,
bit_width,
},
})
}
/// Creates a 2:1 [multiplexer](https://en.wikipedia.org/wiki/Multiplexer) that represents `when_true`'s value when `self` is high, and `when_false`'s value when `self` is low.
///
/// This is a convenience wrapper for [`Module::mux`].
///
/// # Panics
///
/// Panics if `when_true` or `when_false` belong to a different [`Module`] than `self`, if `self`'s bit width is not 1, or if the bit widths of `when_true` and `when_false` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let cond = m.input("cond", 1);
/// let a = m.input("a", 8);
/// let b = m.input("b", 8);
/// m.output("my_output", cond.mux(a, b)); // Outputs a when cond is high, b otherwise
/// ```
// TODO: This is currently only used to support sugar; if it doesn't work out, remove this
fn mux(
&'a self,
when_true: &'a dyn Signal<'a>,
when_false: &'a dyn Signal<'a>,
) -> &'a dyn Signal<'a> {
let s = self.internal_signal();
s.module.mux(s, when_true, when_false)
}
}
macro_rules! impl_extensions {
($($t:ty),*) => ($(
impl<'a, S: Into<&'a dyn Signal<'a>>> Add for &'a $t {
type Output = &'a dyn Signal<'a>;
/// Combines two `Signal`s, producing a new `Signal` that represents the sum of the original two `Signal`s.
///
/// The sum is truncated to the `Signal`'s `bit_width`. If a carry bit is desired, the operands can be [`concat`]enated with a `0` bit before the operation.
///
/// # Panics
///
/// Panics if `lhs` and `rhs` belong to different [`Module`]s, or if the bit widths of `lhs` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lhs = m.lit(1u32, 32);
/// let rhs = m.lit(2u32, 32);
/// let sum = lhs + rhs; // Equivalent to m.lit(3u32, 32)
///
/// let lhs = m.low().concat(m.lit(0xffffffffu32, 32)); // Concat 0 bits with operands for carry
/// let rhs = m.low().concat(m.lit(0x00000001u32, 32));
/// let carry_sum = lhs + rhs; // Equivalent to m.lit(0x100000000u64, 33)
/// let sum = carry_sum.bits(31, 0); // Equivalent to m.lit(0u32, 32)
/// let carry = carry_sum.bit(32); // Equivalent to m.lit(true, 1)
/// ```
///
/// [`concat`]: Signal::concat
fn add(self, rhs: S) -> Self::Output {
let lhs = self.internal_signal();
let rhs = rhs.into().internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::AdditiveBinOp {
lhs,
rhs,
op: AdditiveBinOp::Add,
bit_width: lhs.bit_width(),
},
})
}
}
impl<'a, S: Into<&'a dyn Signal<'a>>> BitAnd for &'a $t {
type Output = &'a dyn Signal<'a>;
/// Combines two `Signal`s, producing a new `Signal` whose bits represent the bitwise `&` of each of the bits of the original two `Signal`s.
///
/// # Panics
///
/// Panics if `lhs` and `rhs` belong to different [`Module`]s, or if the bit widths of `lhs` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lhs = m.low();
/// let rhs = m.high();
/// let single_bitand = lhs & rhs;
///
/// let lhs = m.input("in1", 3);
/// let rhs = m.input("in2", 3);
/// let multi_bitand = lhs & rhs;
/// ```
fn bitand(self, rhs: S) -> Self::Output {
let lhs = self.internal_signal();
let rhs = rhs.into().internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::SimpleBinOp {
lhs,
rhs,
op: SimpleBinOp::BitAnd,
bit_width: lhs.bit_width(),
},
})
}
}
impl<'a, S: Into<&'a dyn Signal<'a>>> BitOr for &'a $t {
type Output = &'a dyn Signal<'a>;
/// Combines two `Signal`s, producing a new `Signal` whose bits represent the bitwise `|` of each of the bits of the original two `Signal`s.
///
/// # Panics
///
/// Panics if `lhs` and `rhs` belong to different [`Module`]s, or if the bit widths of `lhs` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lhs = m.low();
/// let rhs = m.high();
/// let single_bitor = lhs | rhs;
///
/// let lhs = m.input("in1", 3);
/// let rhs = m.input("in2", 3);
/// let multi_bitor = lhs | rhs;
/// ```
fn bitor(self, rhs: S) -> Self::Output {
let lhs = self.internal_signal();
let rhs = rhs.into().internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::SimpleBinOp {
lhs,
rhs,
op: SimpleBinOp::BitOr,
bit_width: lhs.bit_width(),
},
})
}
}
impl<'a, S: Into<&'a dyn Signal<'a>>> BitXor for &'a $t {
type Output = &'a dyn Signal<'a>;
/// Combines two `Signal`s, producing a new `Signal` whose bits represent the bitwise `^` of each of the bits of the original two `Signal`s.
///
/// # Panics
///
/// Panics if `lhs` and `rhs` belong to different [`Module`]s, or if the bit widths of `lhs` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lhs = m.low();
/// let rhs = m.high();
/// let single_bitxor = lhs ^ rhs;
///
/// let lhs = m.input("in1", 3);
/// let rhs = m.input("in2", 3);
/// let multi_bitxor = lhs ^ rhs;
/// ```
fn bitxor(self, rhs: S) -> Self::Output {
let lhs = self.internal_signal();
let rhs = rhs.into().internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::SimpleBinOp {
lhs,
rhs,
op: SimpleBinOp::BitXor,
bit_width: lhs.bit_width(),
},
})
}
}
impl<'a, S: Into<&'a dyn Signal<'a>>> Mul for &'a $t {
type Output = &'a dyn Signal<'a>;
/// Combines two `Signal`s, producing a new `Signal` that represents the unsigned product of the original two `Signal`s.
///
/// The product's `bit_width` is equal to `self.bit_width() + rhs.bit_width()`.
///
/// # Panics
///
/// Panics if `lhs` and `rhs` belong to different [`Module`]s, or if `self.bit_width() + rhs.bit_width()` is greater than [`MAX_SIGNAL_BIT_WIDTH`].
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lhs = m.lit(4u32, 3);
/// let rhs = m.lit(5u32, 4);
/// let sum = lhs * rhs; // Equivalent to m.lit(20u32, 7)
/// ```
fn mul(self, rhs: S) -> Self::Output {
let lhs = self.internal_signal();
let rhs = rhs.into().internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
let bit_width = lhs.bit_width() + rhs.bit_width();
if bit_width > MAX_SIGNAL_BIT_WIDTH {
panic!("Attempted to multiply a {}-bit with a {}-bit signal, but this would result in a bit width of {}, which is greater than the maximum signal bit width of {} bit(s).", self.bit_width(), rhs.bit_width(), bit_width, MAX_SIGNAL_BIT_WIDTH);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::Mul {
lhs,
rhs,
bit_width,
},
})
}
}
impl<'a> Not for &'a $t {
type Output = &'a dyn Signal<'a>;
/// Produces a new `Signal` whose bits represent the bitwise `!` of each of the bits of the original `Signal`.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let input1 = m.input("input1", 1);
/// let single_not = !input1;
///
/// let input2 = m.input("input2", 6);
/// let multi_not = !input2;
/// ```
fn not(self) -> Self::Output {
let s = self.internal_signal();
s.context.signal_arena.alloc(InternalSignal {
context: s.context,
module: s.module,
data: SignalData::UnOp {
source: s,
op: UnOp::Not,
bit_width: self.bit_width(),
},
})
}
}
impl<'a, S: Into<&'a dyn Signal<'a>>> Shl for &'a $t {
type Output = &'a dyn Signal<'a>;
/// Combines two `Signal`s, producing a new `Signal` that represents `self` logically shifted left by `rhs` bits.
///
/// The result is truncated to `self`'s `bit_width`. If `rhs` specifies a value that's greater than or equal to `self`'s `bit_width`, the resulting value will be zero.
///
/// # Panics
///
/// Panics if `lhs` and `rhs` belong to different [`Module`]s.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lhs = m.lit(3u32, 32);
/// let rhs = m.lit(2u32, 2);
/// let shifted = lhs << rhs; // Equivalent to m.lit(12u32, 32)
/// ```
fn shl(self, rhs: S) -> Self::Output {
let lhs = self.internal_signal();
let rhs = rhs.into().internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ShiftBinOp {
lhs,
rhs,
op: ShiftBinOp::Shl,
bit_width: lhs.bit_width(),
},
})
}
}
impl<'a, S: Into<&'a dyn Signal<'a>>> Shr for &'a $t {
type Output = &'a dyn Signal<'a>;
/// Combines two `Signal`s, producing a new `Signal` that represents `self` logically shifted right by `rhs` bits.
///
/// The result is truncated to `self`'s `bit_width`. If `rhs` specifies a value that's greater than or equal to `self`'s `bit_width`, the resulting value will be zero.
///
/// # Panics
///
/// Panics if `lhs` and `rhs` belong to different [`Module`]s.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lhs = m.lit(12u32, 32);
/// let rhs = m.lit(2u32, 2);
/// let shifted = lhs >> rhs; // Equivalent to m.lit(3u32, 32)
/// ```
fn shr(self, rhs: S) -> Self::Output {
let lhs = self.internal_signal();
let rhs = rhs.into().internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::ShiftBinOp {
lhs,
rhs,
op: ShiftBinOp::Shr,
bit_width: lhs.bit_width(),
},
})
}
}
impl<'a, S: Into<&'a dyn Signal<'a>>> Sub for &'a $t {
type Output = &'a dyn Signal<'a>;
/// Combines two `Signal`s, producing a new `Signal` that represents the difference of the original two `Signal`s.
///
/// The difference is truncated to the `Signal`'s `bit_width`.
///
/// # Panics
///
/// Panics if `lhs` and `rhs` belong to different [`Module`]s, or if the bit widths of `lhs` and `rhs` aren't equal.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let lhs = m.lit(3u32, 32);
/// let rhs = m.lit(2u32, 32);
/// let difference = lhs - rhs; // Equivalent to m.lit(1u32, 32)
/// ```
fn sub(self, rhs: S) -> Self::Output {
let lhs = self.internal_signal();
let rhs = rhs.into().internal_signal();
if !ptr::eq(lhs.module, rhs.module) {
panic!("Attempted to combine signals from different modules.");
}
if lhs.bit_width() != rhs.bit_width() {
panic!(
"Signals have different bit widths ({} and {}, respectively).",
lhs.bit_width(),
rhs.bit_width()
);
}
lhs.context.signal_arena.alloc(InternalSignal {
context: lhs.context,
module: lhs.module,
data: SignalData::AdditiveBinOp {
lhs,
rhs,
op: AdditiveBinOp::Sub,
bit_width: self.bit_width(),
},
})
}
}
impl<'a, S: Into> RegNext<'a, S> for &'a $t {
fn reg_next(self, name: S) -> &'a dyn Signal<'a> {
let s = self.internal_signal();
let reg = s.module.reg(name, s.bit_width());
reg.drive_next(s);
reg
}
}
impl<'a, S: Into, C: Into> RegNextWithDefault<'a, S, C> for &'a $t {
fn reg_next_with_default(
self,
name: S,
default_value: C,
) -> &'a dyn Signal<'a> {
let s = self.internal_signal();
let reg = s.module.reg(name, s.bit_width());
reg.default_value(default_value);
reg.drive_next(s);
reg
}
}
)*);
}
// TODO: Move extension stuff?
use super::module::{Input, Output};
use super::register::Register;
impl_extensions! { dyn Signal<'a>, Input<'a>, Output<'a>, Register<'a> }
impl<'a, T: GetInternalSignal<'a>> Signal<'a> for T {}
impl<'a, T: GetInternalSignal<'a>> From<&'a T> for &'a dyn Signal<'a> {
fn from(s: &'a T) -> &'a dyn Signal<'a> {
s.internal_signal()
}
}
pub trait RegNext<'a, S: Into> {
/// Creates a [`Signal`] that represents the same value as this [`Signal`], but delayed by one cycle.
///
/// This is achieved by creating a new [`Register`] called `name`, and specifying this [`Signal`] as the next value for the [`Register`]. Note that no default value is provided for this [`Register`], so the returned [`Signal`]'s value is undefined until the first clock edge, and its value is not affected by its [`Module`]'s implicit reset. If a default value is desired, use [`reg_next_with_default`] instead.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let a = m.lit(true, 1);
/// let a_delayed = a.reg_next("a_delayed");
/// ```
///
/// [`reg_next_with_default`]: Self::reg_next_with_default
fn reg_next(self, name: S) -> &'a dyn Signal<'a>;
}
pub trait RegNextWithDefault<'a, S: Into, C: Into> {
/// Creates a [`Signal`] that represents the same value as this [`Signal`], but delayed by one cycle, except when this [`Signal`]'s [`Module`]'s implicit reset is asserted, at which point it will represent `default_value`.
///
/// This is achieved by creating a new [`Register`] called `name`, specifying `default_value` as its default value, and specifying this [`Signal`] as the next value for the [`Register`]. If a default value is not desired, use [`reg_next`] instead.
///
/// # Panics
///
/// Panics if the specified `value` doesn't fit into this [`Signal`]'s bit width.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("m", "MyModule");
///
/// let a = m.lit(true, 1);
/// let a_delayed = a.reg_next_with_default("a_delayed", false);
/// ```
///
/// [`reg_next`]: Self::reg_next
fn reg_next_with_default(self, name: S, default_value: C) -> &'a dyn Signal<'a>;
}
#[cfg(test)]
mod tests {
use crate::graph::*;
#[test]
#[should_panic(
expected = "Attempted to take bit index 3 from a signal with a width of 3 bits. Bit indices must be in the range [0, 2] for a signal with a width of 3 bits."
)]
fn bit_index_oob_error() {
let c = Context::new();
let m = c.module("a", "A");
let i = m.input("i", 3);
let _ = i.bit(0); // OK
let _ = i.bit(1); // OK
let _ = i.bit(2); // OK
let _ = i.bit(3); // Panic, `index` too high
}
#[test]
#[should_panic(
expected = "Cannot specify a range of bits where the lower bound is greater than or equal to the number of bits in the source signal. The bounds must be in the range [0, 2] for a signal with a width of 3 bits, but a lower bound of 3 was given."
)]
fn bits_range_low_oob_error() {
let c = Context::new();
let m = c.module("a", "A");
let i = m.input("i", 3);
// Panic
let _ = i.bits(4, 3);
}
#[test]
#[should_panic(
expected = "Cannot specify a range of bits where the upper bound is greater than or equal to the number of bits in the source signal. The bounds must be in the range [0, 2] for a signal with a width of 3 bits, but an upper bound of 3 was given."
)]
fn bits_range_high_oob_error() {
let c = Context::new();
let m = c.module("a", "A");
let i = m.input("i", 3);
// Panic
let _ = i.bits(3, 2);
}
#[test]
#[should_panic(
expected = "Cannot specify a range of bits where the lower bound is greater than the upper bound."
)]
fn bits_range_low_gt_high_error() {
let c = Context::new();
let m = c.module("a", "A");
let i = m.input("i", 3);
// Panic
let _ = i.bits(0, 1);
}
#[test]
#[should_panic(
expected = "Attempted to repeat a 1-bit signal 0 times, but this would result in a bit width of 0, which is less than the minimal signal bit width of 1 bit(s)."
)]
fn repeat_count_zero_error() {
let c = Context::new();
let m = c.module("a", "A");
let i = m.input("i", 1);
// Panic
let _ = i.repeat(0);
}
#[test]
#[should_panic(
expected = "Attempted to repeat a 1-bit signal 129 times, but this would result in a bit width of 129, which is greater than the maximum signal bit width of 128 bit(s)."
)]
fn repeat_count_oob_error() {
let c = Context::new();
let m = c.module("a", "A");
let i = m.input("i", 1);
// Panic
let _ = i.repeat(129);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn concat_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.concat(i2);
}
#[test]
#[should_panic(
expected = "Attempted to concatenate signals with 128 bit(s) and 1 bit(s) respectively, but this would result in a bit width of 129, which is greater than the maximum signal bit width of 128 bit(s)."
)]
fn concat_oob_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("i1", 128);
let i2 = m.input("i2", 1);
// Panic
let _ = i1.concat(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn eq_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.eq(i2);
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn eq_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1.eq(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn ne_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.ne(i2);
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn ne_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1.ne(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn lt_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.lt(i2);
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn lt_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1.lt(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn le_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.le(i2);
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn le_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1.le(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn gt_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.gt(i2);
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn gt_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1.gt(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn ge_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.ge(i2);
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn ge_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1.ge(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn lt_signed_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.lt_signed(i2);
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn lt_signed_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1.lt_signed(i2);
}
#[test]
#[should_panic(expected = "Cannot perform signed comparison of 1-bit signals.")]
fn lt_signed_bit_width_1_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 1);
let i2 = m.input("b", 1);
// Panic
let _ = i1.lt_signed(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn le_signed_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.le_signed(i2);
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn le_signed_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1.le_signed(i2);
}
#[test]
#[should_panic(expected = "Cannot perform signed comparison of 1-bit signals.")]
fn le_signed_bit_width_1_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 1);
let i2 = m.input("b", 1);
// Panic
let _ = i1.le_signed(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn gt_signed_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.gt_signed(i2);
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn gt_signed_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1.gt_signed(i2);
}
#[test]
#[should_panic(expected = "Cannot perform signed comparison of 1-bit signals.")]
fn gt_signed_bit_width_1_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 1);
let i2 = m.input("b", 1);
// Panic
let _ = i1.gt_signed(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn ge_signed_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.ge_signed(i2);
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn ge_signed_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1.ge_signed(i2);
}
#[test]
#[should_panic(expected = "Cannot perform signed comparison of 1-bit signals.")]
fn ge_signed_bit_width_1_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 1);
let i2 = m.input("b", 1);
// Panic
let _ = i1.ge_signed(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn shr_arithmetic_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.shr_arithmetic(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn mul_signed_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1.mul_signed(i2);
}
#[test]
#[should_panic(
expected = "Attempted to multiply a 128-bit with a 1-bit signal, but this would result in a bit width of 129, which is greater than the maximum signal bit width of 128 bit(s)."
)]
fn mul_signed_oob_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 128);
let i2 = m.input("b", 1);
// Panic
let _ = i1.mul_signed(i2);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn mux_cond_separate_module_error() {
let c = Context::new();
let a = c.module("a", "A");
let l1 = a.lit(false, 1);
let b = c.module("b", "B");
let l2 = b.lit(32u8, 8);
let l3 = b.lit(32u8, 8);
// Panic
let _ = l1.mux(l2, l3);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn mux_when_true_separate_module_error() {
let c = Context::new();
let a = c.module("a", "A");
let l1 = a.lit(32u8, 8);
let b = c.module("b", "B");
let l2 = b.lit(true, 1);
let l3 = b.lit(32u8, 8);
// Panic
let _ = l2.mux(l1, l3);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn mux_when_false_separate_module_error() {
let c = Context::new();
let a = c.module("a", "A");
let l1 = a.lit(32u8, 8);
let b = c.module("b", "B");
let l2 = b.lit(true, 1);
let l3 = b.lit(32u8, 8);
// Panic
let _ = l2.mux(l3, l1);
}
#[test]
#[should_panic(expected = "Multiplexer conditionals can only be 1 bit wide.")]
fn mux_cond_bit_width_error() {
let c = Context::new();
let a = c.module("a", "A");
let l1 = a.lit(2u8, 2);
let l2 = a.lit(32u8, 8);
let l3 = a.lit(32u8, 8);
// Panic
let _ = l1.mux(l2, l3);
}
#[test]
#[should_panic(
expected = "Cannot multiplex signals with different bit widths (3 and 5, respectively)."
)]
fn mux_true_false_bit_width_error() {
let c = Context::new();
let a = c.module("a", "A");
let l1 = a.lit(false, 1);
let l2 = a.lit(3u8, 3);
let l3 = a.lit(3u8, 5);
// Panic
let _ = a.mux(l1, l2, l3);
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn add_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1 + i2;
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn add_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1 + i2;
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn bitand_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1 & i2;
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn bitand_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1 & i2;
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn bitor_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1 | i2;
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn bitor_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1 | i2;
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn bitxor_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1 ^ i2;
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn bitxor_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1 ^ i2;
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn mul_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1 * i2;
}
#[test]
#[should_panic(
expected = "Attempted to multiply a 128-bit with a 1-bit signal, but this would result in a bit width of 129, which is greater than the maximum signal bit width of 128 bit(s)."
)]
fn mul_oob_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 128);
let i2 = m.input("b", 1);
// Panic
let _ = i1 * i2;
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn shl_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1 << i2;
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn shr_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1 >> i2;
}
#[test]
#[should_panic(expected = "Attempted to combine signals from different modules.")]
fn sub_separate_module_error() {
let c = Context::new();
let m1 = c.module("a", "A");
let i1 = m1.input("a", 1);
let m2 = c.module("b", "B");
let i2 = m2.high();
// Panic
let _ = i1 - i2;
}
#[test]
#[should_panic(expected = "Signals have different bit widths (3 and 5, respectively).")]
fn sub_incompatible_bit_widths_error() {
let c = Context::new();
let m = c.module("a", "A");
let i1 = m.input("a", 3);
let i2 = m.input("b", 5);
// Panic
let _ = i1 - i2;
}
}
================================================
FILE: kaze/src/graph/sugar.rs
================================================
use super::signal::*;
/// **UNSTABLE:** Provides a convenient way to write conditional combinational logic.
///
/// # Panics
///
/// Since this construct wraps the returned values with [`Signal::mux`], any panic conditions from that method apply to the generated code as well.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let p = Context::new();
///
/// let m = p.module("m", "MyModule");
/// let i = m.input("i", 1);
/// let invert = m.input("invert", 1);
/// let o = if_(invert, {
/// !i
/// }).else_({
/// i
/// });
/// m.output("o", o);
/// ```
// TODO: Can we constrain T more than this to make sure it's only a supported type?
pub fn if_<'a, T>(cond: &'a dyn Signal<'a>, when_true: T) -> If<'a, T> {
If::new(cond, when_true)
}
#[doc(hidden)]
pub struct If<'a, T> {
cond: &'a dyn Signal<'a>,
when_true: T,
}
impl<'a, T> If<'a, T> {
fn new(cond: &'a dyn Signal<'a>, when_true: T) -> If<'a, T> {
If { cond, when_true }
}
pub fn else_if(self, cond: &'a dyn Signal<'a>, when_true: T) -> ElseIf<'a, T> {
ElseIf {
parent: ElseIfParent::If(self),
cond,
when_true,
}
}
}
impl<'a, T: Into<&'a dyn Signal<'a>>> If<'a, T> {
pub fn else_>>(self, when_false: F) -> &'a dyn Signal<'a> {
self.cond.mux(self.when_true.into(), when_false.into())
}
}
macro_rules! replace_tt { ($t:tt, $($i:tt)*) => { $($i)* } }
macro_rules! generate_if {
(($($number: tt, $t: tt, $f: tt),*)) => {
impl<'a, $($t: Into<&'a dyn Signal<'a>>),*,> If<'a, ($($t),*,)> {
pub fn else_<$($f: Into<&'a dyn Signal<'a>>),*,>(self, when_false: ($($f),*,)) -> ($(&'a replace_tt!($number, dyn Signal<'a>)),*,) {
(
$(self.cond.mux(self.when_true.$number.into(), when_false.$number.into())),*,
)
}
}
};
}
generate_if!((0, T0, F0));
generate_if!((0, T0, F0, 1, T1, F1));
generate_if!((0, T0, F0, 1, T1, F1, 2, T2, F2));
generate_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3));
generate_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4));
generate_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5));
generate_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6));
generate_if!((
0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7
));
generate_if!((
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,
F8
));
generate_if!((
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,
F8, 9, T9, F9
));
generate_if!((
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,
F8, 9, T9, F9, 10, T10, F10
));
generate_if!((
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,
F8, 9, T9, F9, 10, T10, F10, 11, T11, F11
));
enum ElseIfParent<'a, T> {
If(If<'a, T>),
ElseIf(Box>),
}
#[doc(hidden)]
pub struct ElseIf<'a, T> {
parent: ElseIfParent<'a, T>,
cond: &'a dyn Signal<'a>,
when_true: T,
}
impl<'a, T> ElseIf<'a, T> {
pub fn else_if(self, cond: &'a dyn Signal<'a>, when_true: T) -> ElseIf<'a, T> {
ElseIf {
parent: ElseIfParent::ElseIf(Box::new(self)),
cond,
when_true,
}
}
}
impl<'a, T: Into<&'a dyn Signal<'a>>> ElseIf<'a, T> {
pub fn else_>>(self, when_false: F) -> &'a dyn Signal<'a> {
let ret = self.cond.mux(self.when_true.into(), when_false.into());
match self.parent {
ElseIfParent::If(parent) => parent.else_(ret),
ElseIfParent::ElseIf(parent) => parent.else_(ret),
}
}
}
macro_rules! generate_else_if {
(($($number: tt, $t: tt, $f: tt),*)) => {
impl<'a, $($t: Into<&'a dyn Signal<'a>>),*,> ElseIf<'a, ($($t),*,)> {
pub fn else_<$($f: Into<&'a dyn Signal<'a>>),*,>(self, when_false: ($($f),*,)) -> ($(&'a replace_tt!($number, dyn Signal<'a>)),*,) {
let ret = (
$(self.cond.mux(self.when_true.$number.into(), when_false.$number.into())),*,
);
match self.parent {
ElseIfParent::If(parent) => parent.else_(ret),
ElseIfParent::ElseIf(parent) => parent.else_(ret),
}
}
}
};
}
generate_else_if!((0, T0, F0));
generate_else_if!((0, T0, F0, 1, T1, F1));
generate_else_if!((0, T0, F0, 1, T1, F1, 2, T2, F2));
generate_else_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3));
generate_else_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4));
generate_else_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5));
generate_else_if!((0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6));
generate_else_if!((
0, T0, F0, 1, T1, F1, 2, T2, F2, 3, T3, F3, 4, T4, F4, 5, T5, F5, 6, T6, F6, 7, T7, F7
));
generate_else_if!((
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,
F8
));
generate_else_if!((
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,
F8, 9, T9, F9
));
generate_else_if!((
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,
F8, 9, T9, F9, 10, T10, F10
));
generate_else_if!((
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,
F8, 9, T9, F9, 10, T10, F10, 11, T11, F11
));
================================================
FILE: kaze/src/graph.rs
================================================
mod constant;
mod context;
pub(crate) mod internal_signal;
mod mem;
mod module;
mod register;
mod signal;
mod sugar;
pub use constant::*;
pub use context::*;
pub use mem::*;
pub use module::*;
pub use register::*;
pub use signal::*;
pub use sugar::*;
================================================
FILE: kaze/src/lib.rs
================================================
//! An [HDL](https://en.wikipedia.org/wiki/Hardware_description_language) embedded in [Rust](https://www.rust-lang.org/).
//!
//! 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).
//!
//! kaze's API is designed to be as minimal as possible while still being expressive.
//! It's designed to prevent the user from being able to describe buggy or incorrect hardware as much as possible.
//! This enables a user to hack on designs fearlessly, while the API and generators ensure that these designs are sound.
//!
//! # Usage
//!
//! ```toml
//! [dependencies]
//! kaze = "0.1"
//! ```
//!
//! # Examples
//!
//! ```rust
//! # fn main() -> std::io::Result<()> {
//! use kaze::*;
//!
//! // Create a context, which will contain our module(s)
//! let p = Context::new();
//!
//! // Create a module
//! let inverter = p.module("inverter", "Inverter");
//! let i = inverter.input("i", 1); // 1-bit input
//! inverter.output("o", !i); // Output inverted input
//!
//! // Generate Rust simulator code
//! sim::generate(inverter, sim::GenerationOptions::default(), std::io::stdout())?;
//!
//! // Generate Verilog code
//! //verilog::generate(inverter, std::io::stdout())?;
//! # Ok(())
//! # }
//! ```
// Must be kept up-to-date with version in Cargo.toml
#![doc(html_root_url = "https://docs.rs/kaze/0.1.19")]
mod code_writer;
mod graph;
pub mod runtime;
pub mod sim;
mod state_elements;
mod validation;
pub mod verilog;
pub use graph::*;
================================================
FILE: kaze/src/runtime/tracing/vcd.rs
================================================
//! [VCD](https://en.wikipedia.org/wiki/Value_change_dump) format tracing implementation.
extern crate vcd;
use super::*;
use std::io;
pub enum TimeScaleUnit {
S,
Ms,
Us,
Ns,
Ps,
Fs,
}
impl From for vcd::TimescaleUnit {
fn from(time_scale_unit: TimeScaleUnit) -> Self {
match time_scale_unit {
TimeScaleUnit::S => vcd::TimescaleUnit::S,
TimeScaleUnit::Ms => vcd::TimescaleUnit::MS,
TimeScaleUnit::Us => vcd::TimescaleUnit::US,
TimeScaleUnit::Ns => vcd::TimescaleUnit::NS,
TimeScaleUnit::Ps => vcd::TimescaleUnit::PS,
TimeScaleUnit::Fs => vcd::TimescaleUnit::FS,
}
}
}
pub struct VcdTrace {
module_hierarchy_depth: u32,
signals: Vec,
w: vcd::Writer,
}
impl VcdTrace {
pub fn new(w: W, time_scale: u32, time_scale_unit: TimeScaleUnit) -> io::Result> {
let mut w = vcd::Writer::new(w);
w.timescale(time_scale, time_scale_unit.into())?;
Ok(VcdTrace {
module_hierarchy_depth: 0,
signals: Vec::new(),
w,
})
}
}
impl Trace for VcdTrace {
type SignalId = usize;
fn push_module(&mut self, name: &'static str) -> io::Result<()> {
self.w.add_module(name)?;
self.module_hierarchy_depth += 1;
Ok(())
}
fn pop_module(&mut self) -> io::Result<()> {
self.w.upscope()?;
self.module_hierarchy_depth -= 1;
if self.module_hierarchy_depth == 0 {
self.w.enddefinitions()?;
}
Ok(())
}
fn add_signal(
&mut self,
name: &'static str,
bit_width: u32,
type_: TraceValueType,
) -> io::Result {
let ret = self.signals.len();
self.signals.push(VcdTraceSignal {
bit_width,
type_,
// TODO: Is wire the right construct here always?
id: self.w.add_wire(bit_width, name)?,
});
Ok(ret)
}
fn update_time_stamp(&mut self, time_stamp: u64) -> io::Result<()> {
self.w.timestamp(time_stamp)
}
fn update_signal(&mut self, signal_id: &Self::SignalId, value: TraceValue) -> io::Result<()> {
// TODO: Type check incoming value!
let signal = &self.signals[*signal_id];
if let TraceValueType::Bool = signal.type_ {
self.w.change_scalar(
signal.id,
match value {
TraceValue::Bool(value) => value,
TraceValue::U32(_) | TraceValue::U64(_) | TraceValue::U128(_) => unreachable!(),
},
)?;
} else {
let value = match value {
TraceValue::Bool(_) => unreachable!(),
TraceValue::U32(value) => value as _,
TraceValue::U64(value) => value as _,
TraceValue::U128(value) => value,
};
let mut scalar_values = [vcd::Value::V0; 128];
for i in 0..signal.bit_width as usize {
scalar_values[i] = ((value >> (signal.bit_width as usize - 1 - i)) & 1 != 0).into();
}
self.w
.change_vector(signal.id, &scalar_values[0..signal.bit_width as usize])?;
}
Ok(())
}
}
struct VcdTraceSignal {
bit_width: u32,
type_: TraceValueType,
id: vcd::IdCode,
}
================================================
FILE: kaze/src/runtime/tracing.rs
================================================
//! Rust simulator runtime dependencies for tracing.
pub mod vcd;
use std::io;
// 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.
#[derive(Debug, Eq, PartialEq)]
pub enum TraceValue {
/// Contains a boolean value
Bool(bool),
/// Contains an unsigned, 32-bit value
U32(u32),
/// Contains an unsigned, 64-bit value
U64(u64),
/// Contains an unsigned, 128-bit value
U128(u128),
}
#[derive(Debug, Eq, PartialEq)]
pub enum TraceValueType {
Bool,
U32,
U64,
U128,
}
impl TraceValueType {
pub(crate) fn from_bit_width(bit_width: u32) -> TraceValueType {
if bit_width == 1 {
TraceValueType::Bool
} else if bit_width <= 32 {
TraceValueType::U32
} else if bit_width <= 64 {
TraceValueType::U64
} else if bit_width <= 128 {
TraceValueType::U128
} else {
unreachable!()
}
}
}
pub trait Trace {
type SignalId;
fn push_module(&mut self, name: &'static str) -> io::Result<()>;
fn pop_module(&mut self) -> io::Result<()>;
fn add_signal(
&mut self,
name: &'static str,
bit_width: u32,
type_: TraceValueType,
) -> io::Result;
fn update_time_stamp(&mut self, time_stamp: u64) -> io::Result<()>;
fn update_signal(&mut self, signal_id: &Self::SignalId, value: TraceValue) -> io::Result<()>;
}
================================================
FILE: kaze/src/runtime.rs
================================================
//! Rust simulator runtime dependencies. These are only required for simulators with tracing enabled.
pub mod tracing;
================================================
FILE: kaze/src/sim/compiler.rs
================================================
use super::ir::*;
use crate::graph::internal_signal;
use crate::state_elements::*;
use typed_arena::Arena;
use std::collections::HashMap;
// TODO: Can we merge the context and expr_arena lifetimes?
pub(super) struct Compiler<'graph, 'context, 'expr_arena> {
state_elements: &'context StateElements<'graph>,
signal_reference_counts:
&'context HashMap<&'graph internal_signal::InternalSignal<'graph>, u32>,
expr_arena: &'expr_arena Arena>,
signal_exprs:
HashMap<&'graph internal_signal::InternalSignal<'graph>, &'expr_arena Expr<'expr_arena>>,
}
impl<'graph, 'context, 'expr_arena> Compiler<'graph, 'context, 'expr_arena> {
pub fn new(
state_elements: &'context StateElements<'graph>,
signal_reference_counts: &'context HashMap<
&'graph internal_signal::InternalSignal<'graph>,
u32,
>,
expr_arena: &'expr_arena Arena>,
) -> Compiler<'graph, 'context, 'expr_arena> {
Compiler {
state_elements,
signal_reference_counts,
expr_arena,
signal_exprs: HashMap::new(),
}
}
pub fn compile_signal(
&mut self,
signal: &'graph internal_signal::InternalSignal<'graph>,
a: &mut AssignmentContext<'expr_arena>,
) -> &'expr_arena Expr<'expr_arena> {
enum Frame<'graph> {
Enter(&'graph internal_signal::InternalSignal<'graph>),
Leave(&'graph internal_signal::InternalSignal<'graph>),
}
let mut frames = Vec::new();
frames.push(Frame::Enter(signal));
let mut results = Vec::new();
while let Some(frame) = frames.pop() {
if let Some((key, mut expr)) = match frame {
Frame::Enter(signal) => {
let key = signal;
if let Some(expr) = self.signal_exprs.get(&key) {
results.push(*expr);
continue;
}
match signal.data {
internal_signal::SignalData::Lit {
ref value,
bit_width,
} => Some((key, Expr::from_constant(value, bit_width, &self.expr_arena))),
internal_signal::SignalData::Input { data } => {
if let Some(driven_value) = data.driven_value.borrow().clone() {
frames.push(Frame::Enter(driven_value));
None
} else {
let bit_width = data.bit_width;
let target_type = ValueType::from_bit_width(bit_width);
let expr = self.expr_arena.alloc(Expr::Ref {
name: data.name.clone(),
scope: Scope::Member,
});
Some((key, self.gen_mask(expr, bit_width, target_type)))
}
}
internal_signal::SignalData::Output { data } => {
frames.push(Frame::Enter(data.source));
None
}
internal_signal::SignalData::Reg { .. } => Some((
key,
&*self.expr_arena.alloc(Expr::Ref {
name: self.state_elements.regs[&key].value_name.clone(),
scope: Scope::Member,
}),
)),
internal_signal::SignalData::UnOp { source, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(source));
None
}
internal_signal::SignalData::SimpleBinOp { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::AdditiveBinOp { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::ComparisonBinOp { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::ShiftBinOp { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::Mul { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::MulSigned { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::Bits { source, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(source));
None
}
internal_signal::SignalData::Repeat { source, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(source));
None
}
internal_signal::SignalData::Concat { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::Mux {
cond,
when_true,
when_false,
..
} => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(cond));
frames.push(Frame::Enter(when_true));
frames.push(Frame::Enter(when_false));
None
}
internal_signal::SignalData::MemReadPortOutput {
mem,
address,
enable,
} => {
let mem = &self.state_elements.mems[&mem];
let read_signal_names = &mem.read_signal_names[&(address, enable)];
Some((
key,
&*self.expr_arena.alloc(Expr::Ref {
name: read_signal_names.value_name.clone(),
scope: Scope::Member,
}),
))
}
}
}
Frame::Leave(signal) => {
let key = signal;
match signal.data {
internal_signal::SignalData::Lit { .. } => unreachable!(),
internal_signal::SignalData::Input { .. } => unreachable!(),
internal_signal::SignalData::Output { .. } => unreachable!(),
internal_signal::SignalData::Reg { .. } => unreachable!(),
internal_signal::SignalData::UnOp { op, bit_width, .. } => {
let expr = results.pop().unwrap();
let expr = self.expr_arena.alloc(Expr::UnOp {
source: expr,
op: match op {
internal_signal::UnOp::Not => UnOp::Not,
},
});
let target_type = ValueType::from_bit_width(bit_width);
Some((key, self.gen_mask(expr, bit_width, target_type)))
}
internal_signal::SignalData::SimpleBinOp { op, .. } => {
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
Some((
key,
&*self.expr_arena.alloc(Expr::InfixBinOp {
lhs,
rhs,
op: match op {
internal_signal::SimpleBinOp::BitAnd => InfixBinOp::BitAnd,
internal_signal::SimpleBinOp::BitOr => InfixBinOp::BitOr,
internal_signal::SimpleBinOp::BitXor => InfixBinOp::BitXor,
},
}),
))
}
internal_signal::SignalData::AdditiveBinOp { lhs, op, .. } => {
let source_bit_width = lhs.bit_width();
let source_type = ValueType::from_bit_width(source_bit_width);
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
let op_input_type = match source_type {
ValueType::Bool => ValueType::U32,
_ => source_type,
};
let lhs = self.gen_cast(lhs, source_type, op_input_type);
let rhs = self.gen_cast(rhs, source_type, op_input_type);
let expr = self.expr_arena.alloc(Expr::UnaryMemberCall {
target: lhs,
name: match op {
internal_signal::AdditiveBinOp::Add => "wrapping_add".into(),
internal_signal::AdditiveBinOp::Sub => "wrapping_sub".into(),
},
arg: rhs,
});
let op_output_type = op_input_type;
let target_bit_width = signal.bit_width();
let target_type = ValueType::from_bit_width(target_bit_width);
let expr = self.gen_cast(expr, op_output_type, target_type);
Some((key, self.gen_mask(expr, target_bit_width, target_type)))
}
internal_signal::SignalData::ComparisonBinOp { lhs, op, .. } => {
let source_bit_width = lhs.bit_width();
let source_type = ValueType::from_bit_width(source_bit_width);
let mut lhs = results.pop().unwrap();
let mut rhs = results.pop().unwrap();
match op {
internal_signal::ComparisonBinOp::GreaterThanEqualSigned
| internal_signal::ComparisonBinOp::GreaterThanSigned
| internal_signal::ComparisonBinOp::LessThanEqualSigned
| internal_signal::ComparisonBinOp::LessThanSigned => {
let source_type_signed = source_type.to_signed();
lhs = self.gen_cast(lhs, source_type, source_type_signed);
rhs = self.gen_cast(rhs, source_type, source_type_signed);
lhs = self.gen_sign_extend_shifts(
lhs,
source_bit_width,
source_type_signed,
);
rhs = self.gen_sign_extend_shifts(
rhs,
source_bit_width,
source_type_signed,
);
}
_ => (),
}
Some((
key,
&*self.expr_arena.alloc(Expr::InfixBinOp {
lhs,
rhs,
op: match op {
internal_signal::ComparisonBinOp::Equal => InfixBinOp::Equal,
internal_signal::ComparisonBinOp::NotEqual => InfixBinOp::NotEqual,
internal_signal::ComparisonBinOp::LessThan
| internal_signal::ComparisonBinOp::LessThanSigned => {
InfixBinOp::LessThan
}
internal_signal::ComparisonBinOp::LessThanEqual
| internal_signal::ComparisonBinOp::LessThanEqualSigned => {
InfixBinOp::LessThanEqual
}
internal_signal::ComparisonBinOp::GreaterThan
| internal_signal::ComparisonBinOp::GreaterThanSigned => {
InfixBinOp::GreaterThan
}
internal_signal::ComparisonBinOp::GreaterThanEqual
| internal_signal::ComparisonBinOp::GreaterThanEqualSigned => {
InfixBinOp::GreaterThanEqual
}
},
}),
))
}
internal_signal::SignalData::ShiftBinOp {
lhs,
rhs,
op,
bit_width,
} => {
let lhs_source_bit_width = lhs.bit_width();
let lhs_source_type = ValueType::from_bit_width(lhs_source_bit_width);
let rhs_source_bit_width = rhs.bit_width();
let rhs_source_type = ValueType::from_bit_width(rhs_source_bit_width);
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
let lhs_op_input_type = match lhs_source_type {
ValueType::Bool => ValueType::U32,
_ => lhs_source_type,
};
let lhs = self.gen_cast(lhs, lhs_source_type, lhs_op_input_type);
let lhs = match op {
internal_signal::ShiftBinOp::Shl
| internal_signal::ShiftBinOp::Shr => lhs,
internal_signal::ShiftBinOp::ShrArithmetic => {
let lhs_op_input_type_signed = lhs_op_input_type.to_signed();
let lhs = self.gen_cast(
lhs,
lhs_op_input_type,
lhs_op_input_type_signed,
);
self.gen_sign_extend_shifts(
lhs,
lhs_source_bit_width,
lhs_op_input_type_signed,
)
}
};
let rhs_op_input_type = match rhs_source_type {
ValueType::Bool => ValueType::U32,
_ => rhs_source_type,
};
let rhs = self.gen_cast(rhs, rhs_source_type, rhs_op_input_type);
let rhs = self.expr_arena.alloc(Expr::BinaryFunctionCall {
name: "std::cmp::min".into(),
lhs: rhs,
rhs: self.expr_arena.alloc(Expr::Constant {
value: match rhs_op_input_type {
ValueType::Bool
| ValueType::I32
| ValueType::I64
| ValueType::I128 => unreachable!(),
ValueType::U32 => Constant::U32(std::u32::MAX),
ValueType::U64 => Constant::U64(std::u32::MAX as _),
ValueType::U128 => Constant::U128(std::u32::MAX as _),
},
}),
});
let rhs = self.gen_cast(rhs, lhs_op_input_type, ValueType::U32);
let expr = self.expr_arena.alloc(Expr::UnaryMemberCall {
target: lhs,
name: match op {
internal_signal::ShiftBinOp::Shl => "checked_shl".into(),
internal_signal::ShiftBinOp::Shr
| internal_signal::ShiftBinOp::ShrArithmetic => {
"checked_shr".into()
}
},
arg: rhs,
});
let expr = self.expr_arena.alloc(Expr::UnaryMemberCall {
target: expr,
name: "unwrap_or".into(),
arg: match op {
internal_signal::ShiftBinOp::Shl
| internal_signal::ShiftBinOp::Shr => {
self.expr_arena.alloc(Expr::Constant {
value: match lhs_op_input_type {
ValueType::Bool
| ValueType::I32
| ValueType::I64
| ValueType::I128 => unreachable!(),
ValueType::U32 => Constant::U32(0),
ValueType::U64 => Constant::U64(0),
ValueType::U128 => Constant::U128(0),
},
})
}
internal_signal::ShiftBinOp::ShrArithmetic => {
self.expr_arena.alloc(Expr::InfixBinOp {
lhs,
rhs: self.expr_arena.alloc(Expr::Constant {
value: Constant::U32(
lhs_op_input_type.bit_width() - 1,
),
}),
op: InfixBinOp::Shr,
})
}
},
});
let op_output_type = lhs_op_input_type;
let expr = match op {
internal_signal::ShiftBinOp::Shl
| internal_signal::ShiftBinOp::Shr => expr,
internal_signal::ShiftBinOp::ShrArithmetic => {
let lhs_op_output_type_signed = op_output_type.to_signed();
self.gen_cast(expr, lhs_op_output_type_signed, op_output_type)
}
};
let target_bit_width = bit_width;
let target_type = ValueType::from_bit_width(target_bit_width);
let expr = self.gen_cast(expr, op_output_type, target_type);
Some((key, self.gen_mask(expr, target_bit_width, target_type)))
}
internal_signal::SignalData::Mul {
lhs,
rhs,
bit_width,
} => {
let lhs_type = ValueType::from_bit_width(lhs.bit_width());
let rhs_type = ValueType::from_bit_width(rhs.bit_width());
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
let target_type = ValueType::from_bit_width(bit_width);
let lhs = self.gen_cast(lhs, lhs_type, target_type);
let rhs = self.gen_cast(rhs, rhs_type, target_type);
Some((
key,
&*self.expr_arena.alloc(Expr::InfixBinOp {
lhs,
rhs,
op: InfixBinOp::Mul,
}),
))
}
internal_signal::SignalData::MulSigned {
lhs,
rhs,
bit_width,
} => {
let lhs_bit_width = lhs.bit_width();
let rhs_bit_width = rhs.bit_width();
let lhs_type = ValueType::from_bit_width(lhs_bit_width);
let rhs_type = ValueType::from_bit_width(rhs_bit_width);
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
let target_bit_width = bit_width;
let target_type = ValueType::from_bit_width(target_bit_width);
let target_type_signed = target_type.to_signed();
let lhs = self.gen_cast(lhs, lhs_type, target_type_signed);
let rhs = self.gen_cast(rhs, rhs_type, target_type_signed);
let lhs =
self.gen_sign_extend_shifts(lhs, lhs_bit_width, target_type_signed);
let rhs =
self.gen_sign_extend_shifts(rhs, rhs_bit_width, target_type_signed);
let expr = self.expr_arena.alloc(Expr::InfixBinOp {
lhs,
rhs,
op: InfixBinOp::Mul,
});
let expr = self.gen_cast(expr, target_type_signed, target_type);
Some((key, self.gen_mask(expr, target_bit_width, target_type)))
}
internal_signal::SignalData::Bits {
source, range_low, ..
} => {
let expr = results.pop().unwrap();
let expr = self.gen_shift_right(expr, range_low);
let target_bit_width = signal.bit_width();
let target_type = ValueType::from_bit_width(target_bit_width);
let expr = self.gen_cast(
expr,
ValueType::from_bit_width(source.bit_width()),
target_type,
);
Some((key, self.gen_mask(expr, target_bit_width, target_type)))
}
internal_signal::SignalData::Repeat {
source,
count,
bit_width,
} => {
let expr = results.pop().unwrap();
let mut expr = self.gen_cast(
expr,
ValueType::from_bit_width(source.bit_width()),
ValueType::from_bit_width(bit_width),
);
if count > 1 {
let source_expr = a.gen_temp(expr);
for i in 1..count {
let rhs =
self.gen_shift_left(source_expr, i * source.bit_width());
expr = self.expr_arena.alloc(Expr::InfixBinOp {
lhs: expr,
rhs,
op: InfixBinOp::BitOr,
});
}
}
Some((key, expr))
}
internal_signal::SignalData::Concat {
lhs,
rhs,
bit_width,
} => {
let lhs_type = ValueType::from_bit_width(lhs.bit_width());
let rhs_bit_width = rhs.bit_width();
let rhs_type = ValueType::from_bit_width(rhs_bit_width);
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
let target_type = ValueType::from_bit_width(bit_width);
let lhs = self.gen_cast(lhs, lhs_type, target_type);
let rhs = self.gen_cast(rhs, rhs_type, target_type);
let lhs = self.gen_shift_left(lhs, rhs_bit_width);
Some((
key,
&*self.expr_arena.alloc(Expr::InfixBinOp {
lhs,
rhs,
op: InfixBinOp::BitOr,
}),
))
}
internal_signal::SignalData::Mux { .. } => {
let cond = results.pop().unwrap();
let when_true = results.pop().unwrap();
let when_false = results.pop().unwrap();
Some((
key,
&*self.expr_arena.alloc(Expr::Ternary {
cond,
when_true,
when_false,
}),
))
}
internal_signal::SignalData::MemReadPortOutput { .. } => unreachable!(),
}
}
} {
// Generate a temp if this signal is referenced more than once
if self.signal_reference_counts[&key] > 1 {
expr = a.gen_temp(expr);
}
self.signal_exprs.insert(key, expr);
results.push(expr);
}
}
results.pop().unwrap()
}
fn gen_mask(
&mut self,
expr: &'expr_arena Expr<'expr_arena>,
bit_width: u32,
target_type: ValueType,
) -> &'expr_arena Expr<'expr_arena> {
if bit_width == target_type.bit_width() {
return expr;
}
let mask = (1u128 << bit_width) - 1;
self.expr_arena.alloc(Expr::InfixBinOp {
lhs: expr,
rhs: self.expr_arena.alloc(Expr::Constant {
value: match target_type {
ValueType::Bool | ValueType::I32 | ValueType::I64 | ValueType::I128 => {
unreachable!()
}
ValueType::U32 => Constant::U32(mask as _),
ValueType::U64 => Constant::U64(mask as _),
ValueType::U128 => Constant::U128(mask),
},
}),
op: InfixBinOp::BitAnd,
})
}
fn gen_shift_left(
&mut self,
expr: &'expr_arena Expr<'expr_arena>,
shift: u32,
) -> &'expr_arena Expr<'expr_arena> {
if shift == 0 {
return expr;
}
self.expr_arena.alloc(Expr::InfixBinOp {
lhs: expr,
rhs: self.expr_arena.alloc(Expr::Constant {
value: Constant::U32(shift),
}),
op: InfixBinOp::Shl,
})
}
fn gen_shift_right(
&mut self,
expr: &'expr_arena Expr<'expr_arena>,
shift: u32,
) -> &'expr_arena Expr<'expr_arena> {
if shift == 0 {
return expr;
}
self.expr_arena.alloc(Expr::InfixBinOp {
lhs: expr,
rhs: self.expr_arena.alloc(Expr::Constant {
value: Constant::U32(shift),
}),
op: InfixBinOp::Shr,
})
}
fn gen_cast(
&mut self,
expr: &'expr_arena Expr<'expr_arena>,
source_type: ValueType,
target_type: ValueType,
) -> &'expr_arena Expr<'expr_arena> {
if source_type == target_type {
return expr;
}
if target_type == ValueType::Bool {
let expr = self.gen_mask(expr, 1, source_type);
return self.expr_arena.alloc(Expr::InfixBinOp {
lhs: expr,
rhs: self.expr_arena.alloc(Expr::Constant {
value: match source_type {
ValueType::Bool | ValueType::I32 | ValueType::I64 | ValueType::I128 => {
unreachable!()
}
ValueType::U32 => Constant::U32(0),
ValueType::U64 => Constant::U64(0),
ValueType::U128 => Constant::U128(0),
},
}),
op: InfixBinOp::NotEqual,
});
}
self.expr_arena.alloc(Expr::Cast {
source: expr,
target_type,
})
}
fn gen_sign_extend_shifts(
&mut self,
expr: &'expr_arena Expr,
source_bit_width: u32,
target_type: ValueType,
) -> &'expr_arena Expr<'expr_arena> {
let shift = target_type.bit_width() - source_bit_width;
let expr = self.gen_shift_left(expr, shift);
self.gen_shift_right(expr, shift)
}
}
================================================
FILE: kaze/src/sim/ir.rs
================================================
use crate::code_writer;
use crate::graph;
use typed_arena::Arena;
use std::io::{Result, Write};
pub struct AssignmentContext<'arena> {
arena: &'arena Arena>,
assignments: Vec>,
local_count: u32,
}
impl<'arena> AssignmentContext<'arena> {
pub fn new(arena: &'arena Arena>) -> AssignmentContext<'arena> {
AssignmentContext {
arena,
assignments: Vec::new(),
local_count: 0,
}
}
pub fn gen_temp(&mut self, expr: &'arena Expr<'arena>) -> &'arena Expr<'arena> {
match expr {
// We don't need to generate a temp for Constants or Refs
Expr::Constant { .. } | Expr::Ref { .. } => expr,
_ => {
let name = format!("__temp_{}", self.local_count);
self.local_count += 1;
self.assignments.push(Assignment {
target: self.arena.alloc(Expr::Ref {
name: name.clone(),
scope: Scope::Local,
}),
expr,
});
self.arena.alloc(Expr::Ref {
name,
scope: Scope::Local,
})
}
}
}
pub fn is_empty(&self) -> bool {
self.assignments.is_empty()
}
pub fn push(&mut self, assignment: Assignment<'arena>) {
self.assignments.push(assignment);
}
pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> {
for assignment in self.assignments.iter() {
assignment.write(w)?;
}
Ok(())
}
}
pub struct Assignment<'arena> {
pub target: &'arena Expr<'arena>,
pub expr: &'arena Expr<'arena>,
}
impl<'arena> Assignment<'arena> {
pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> {
w.append_indent()?;
// TODO: I hate these kind of conditionals...
if let Expr::Ref { ref scope, .. } = self.target {
match scope {
Scope::Local => {
w.append("let ")?;
}
Scope::Member => (),
}
}
self.target.write(w)?;
w.append(" = ")?;
self.expr.write(w)?;
w.append(";")?;
w.append_newline()?;
Ok(())
}
}
pub enum Expr<'arena> {
ArrayIndex {
target: &'arena Expr<'arena>,
index: &'arena Expr<'arena>,
},
BinaryFunctionCall {
name: String,
lhs: &'arena Expr<'arena>,
rhs: &'arena Expr<'arena>,
},
Cast {
source: &'arena Expr<'arena>,
target_type: ValueType,
},
Constant {
value: Constant,
},
InfixBinOp {
lhs: &'arena Expr<'arena>,
rhs: &'arena Expr<'arena>,
op: InfixBinOp,
},
Ref {
name: String,
scope: Scope,
},
Ternary {
cond: &'arena Expr<'arena>,
when_true: &'arena Expr<'arena>,
when_false: &'arena Expr<'arena>,
},
UnaryMemberCall {
target: &'arena Expr<'arena>,
name: String,
arg: &'arena Expr<'arena>,
},
UnOp {
source: &'arena Expr<'arena>,
op: UnOp,
},
}
impl<'arena> Expr<'arena> {
pub fn from_constant(
value: &graph::Constant,
bit_width: u32,
arena: &'arena Arena>,
) -> &'arena Expr<'arena> {
let value = value.numeric_value();
let target_type = ValueType::from_bit_width(bit_width);
arena.alloc(Expr::Constant {
value: match target_type {
ValueType::Bool => Constant::Bool(value != 0),
ValueType::I32 | ValueType::I64 | ValueType::I128 => unreachable!(),
ValueType::U32 => Constant::U32(value as _),
ValueType::U64 => Constant::U64(value as _),
ValueType::U128 => Constant::U128(value),
},
})
}
pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> {
enum Command<'arena> {
Expr { expr: &'arena Expr<'arena> },
Str { s: &'arena str },
}
let mut commands = Vec::new();
commands.push(Command::Expr { expr: self });
while let Some(command) = commands.pop() {
match command {
Command::Expr { expr } => match *expr {
Expr::ArrayIndex {
ref target,
ref index,
} => {
commands.push(Command::Str { s: " as usize]" });
commands.push(Command::Expr { expr: index });
commands.push(Command::Str { s: "[" });
commands.push(Command::Expr { expr: target });
}
Expr::BinaryFunctionCall {
ref name,
ref lhs,
ref rhs,
} => {
commands.push(Command::Str { s: ")" });
commands.push(Command::Expr { expr: rhs });
commands.push(Command::Str { s: ", " });
commands.push(Command::Expr { expr: lhs });
w.append(&format!("{}(", name))?;
}
Expr::Cast {
ref source,
target_type,
} => {
commands.push(Command::Str { s: ")" });
commands.push(Command::Str {
s: &target_type.name(),
});
commands.push(Command::Str { s: " as " });
commands.push(Command::Expr { expr: source });
w.append("(")?;
}
Expr::Constant { ref value } => {
w.append(&match value {
Constant::Bool(value) => format!("{}", value),
Constant::U32(value) => format!("0x{:x}u32", value),
Constant::U64(value) => format!("0x{:x}u64", value),
Constant::U128(value) => format!("0x{:x}u128", value),
})?;
}
Expr::InfixBinOp {
ref lhs,
ref rhs,
op,
} => {
commands.push(Command::Str { s: ")" });
commands.push(Command::Expr { expr: rhs });
commands.push(Command::Str { s: " " });
commands.push(Command::Str {
s: match op {
InfixBinOp::BitAnd => "&",
InfixBinOp::BitOr => "|",
InfixBinOp::BitXor => "^",
InfixBinOp::Equal => "==",
InfixBinOp::NotEqual => "!=",
InfixBinOp::LessThan => "<",
InfixBinOp::LessThanEqual => "<=",
InfixBinOp::GreaterThan => ">",
InfixBinOp::GreaterThanEqual => ">=",
InfixBinOp::Shl => "<<",
InfixBinOp::Shr => ">>",
InfixBinOp::Mul => "*",
},
});
commands.push(Command::Str { s: " " });
commands.push(Command::Expr { expr: lhs });
w.append("(")?;
}
Expr::Ref { ref name, scope } => {
if let Scope::Member = scope {
w.append("self.")?;
}
w.append(name)?;
}
Expr::Ternary {
ref cond,
ref when_true,
ref when_false,
} => {
commands.push(Command::Str { s: "}" });
commands.push(Command::Expr { expr: when_false });
commands.push(Command::Str { s: " } else { " });
commands.push(Command::Expr { expr: when_true });
commands.push(Command::Str { s: " { " });
commands.push(Command::Expr { expr: cond });
w.append("if ")?;
}
Expr::UnaryMemberCall {
ref target,
ref name,
ref arg,
} => {
commands.push(Command::Str { s: ")" });
commands.push(Command::Expr { expr: arg });
commands.push(Command::Str { s: "(" });
commands.push(Command::Str { s: name });
commands.push(Command::Str { s: "." });
commands.push(Command::Expr { expr: target });
}
Expr::UnOp { ref source, op } => {
w.append(match op {
UnOp::Not => "!",
})?;
commands.push(Command::Expr { expr: source });
}
},
Command::Str { s } => {
w.append(s)?;
}
}
}
Ok(())
}
}
pub enum Constant {
Bool(bool),
U32(u32),
U64(u64),
U128(u128),
}
#[derive(Clone, Copy)]
pub enum InfixBinOp {
BitAnd,
BitOr,
BitXor,
Equal,
NotEqual,
LessThan,
LessThanEqual,
GreaterThan,
GreaterThanEqual,
Shl,
Shr,
Mul,
}
#[derive(Clone, Copy)]
pub enum Scope {
Local,
Member,
}
#[derive(Clone, Copy)]
pub enum UnOp {
Not,
}
#[derive(Clone, Copy, PartialEq)]
pub enum ValueType {
Bool,
I32,
I64,
I128,
U32,
U64,
U128,
}
impl ValueType {
pub fn from_bit_width(bit_width: u32) -> ValueType {
if bit_width == 1 {
ValueType::Bool
} else if bit_width <= 32 {
ValueType::U32
} else if bit_width <= 64 {
ValueType::U64
} else if bit_width <= 128 {
ValueType::U128
} else {
unreachable!()
}
}
pub fn to_signed(&self) -> ValueType {
match self {
ValueType::Bool | ValueType::I32 | ValueType::I64 | ValueType::I128 => unreachable!(),
ValueType::U32 => ValueType::I32,
ValueType::U64 => ValueType::I64,
ValueType::U128 => ValueType::I128,
}
}
pub fn name(&self) -> &'static str {
match self {
ValueType::Bool => "bool",
ValueType::I32 => "i32",
ValueType::I64 => "i64",
ValueType::I128 => "i128",
ValueType::U32 => "u32",
ValueType::U64 => "u64",
ValueType::U128 => "u128",
}
}
pub fn bit_width(&self) -> u32 {
match self {
ValueType::Bool => 1,
ValueType::I32 | ValueType::U32 => 32,
ValueType::I64 | ValueType::U64 => 64,
ValueType::I128 | ValueType::U128 => 128,
}
}
pub fn zero_str(&self) -> &'static str {
match self {
ValueType::Bool => "false",
_ => "0",
}
}
}
================================================
FILE: kaze/src/sim.rs
================================================
//! Rust simulator code generation.
mod compiler;
mod ir;
use compiler::*;
use ir::*;
use typed_arena::Arena;
use crate::code_writer;
use crate::graph;
use crate::runtime::tracing::*;
use crate::state_elements::*;
use crate::validation::*;
use std::collections::HashMap;
use std::io::{Result, Write};
#[derive(Default)]
pub struct GenerationOptions {
pub override_module_name: Option,
pub tracing: bool,
}
// TODO: Note that mutable writer reference can be passed, see https://rust-lang.github.io/api-guidelines/interoperability.html#c-rw-value
pub fn generate<'a, W: Write>(
m: &'a graph::Module<'a>,
options: GenerationOptions,
w: W,
) -> Result<()> {
validate_module_hierarchy(m);
// TODO: Consider exposing as a codegen option (and testing both variants)
let included_ports = if options.tracing {
IncludedPorts::All
} else {
IncludedPorts::ReachableFromTopLevelOutputs
};
let mut signal_reference_counts = HashMap::new();
let state_elements = StateElements::new(m, included_ports, &mut signal_reference_counts);
struct TraceSignal {
name: String,
member_name: String,
value_name: String,
bit_width: u32,
type_: TraceValueType,
}
let mut trace_signals: HashMap<&'a graph::Module<'a>, Vec> = HashMap::new();
let mut num_trace_signals = 0;
let mut add_trace_signal = |module, name, value_name, bit_width| {
if options.tracing {
let member_name = format!("__trace_signal_id_{}_{}", name, num_trace_signals);
let module_trace_signals = trace_signals.entry(module).or_insert(Vec::new());
module_trace_signals.push(TraceSignal {
name,
member_name,
value_name,
bit_width,
type_: TraceValueType::from_bit_width(bit_width),
});
num_trace_signals += 1;
}
};
let expr_arena = Arena::new();
let mut prop_context = AssignmentContext::new(&expr_arena);
let mut c = Compiler::new(&state_elements, &signal_reference_counts, &expr_arena);
for (name, input) in m.inputs.borrow().iter() {
add_trace_signal(m, name.clone(), name.clone(), input.data.bit_width);
}
for (name, output) in m.outputs.borrow().iter() {
let expr = c.compile_signal(output.data.source, &mut prop_context);
prop_context.push(Assignment {
target: expr_arena.alloc(Expr::Ref {
name: name.clone(),
scope: Scope::Member,
}),
expr,
});
add_trace_signal(m, name.clone(), name.clone(), output.data.bit_width);
}
struct InnerField {
name: String,
bit_width: u32,
}
let mut inner_fields = Vec::new();
if options.tracing {
fn visit_module<'graph, 'context, 'expr_arena>(
module: &'graph graph::Module<'graph>,
c: &mut Compiler<'graph, 'context, 'expr_arena>,
inner_fields: &mut Vec,
prop_context: &mut AssignmentContext<'expr_arena>,
expr_arena: &'expr_arena Arena,
add_trace_signal: &mut impl FnMut(&'graph graph::Module<'graph>, String, String, u32),
) -> Result<()> {
// TODO: Identify and fix duplicate signals in traces
for (name, &input) in module.inputs.borrow().iter() {
// TODO: De-dupe inner field allocs
let field_name = format!("__inner_{}_{}", name, inner_fields.len());
inner_fields.push(InnerField {
name: field_name.clone(),
bit_width: input.data.bit_width,
});
let expr =
c.compile_signal(input.data.driven_value.borrow().unwrap(), prop_context);
prop_context.push(Assignment {
target: expr_arena.alloc(Expr::Ref {
name: field_name.clone(),
scope: Scope::Member,
}),
expr,
});
add_trace_signal(module, name.clone(), field_name, input.data.bit_width);
}
for (name, &output) in module.outputs.borrow().iter() {
// TODO: De-dupe inner field allocs
let field_name = format!("__inner_{}_{}", name, inner_fields.len());
inner_fields.push(InnerField {
name: field_name.clone(),
bit_width: output.data.bit_width,
});
let expr = c.compile_signal(output.data.source, prop_context);
prop_context.push(Assignment {
target: expr_arena.alloc(Expr::Ref {
name: field_name.clone(),
scope: Scope::Member,
}),
expr,
});
add_trace_signal(module, name.clone(), field_name, output.data.bit_width);
}
for child in module.modules.borrow().iter() {
visit_module(
child,
c,
inner_fields,
prop_context,
expr_arena,
add_trace_signal,
)?;
}
Ok(())
}
for child in m.modules.borrow().iter() {
visit_module(
child,
&mut c,
&mut inner_fields,
&mut prop_context,
&expr_arena,
&mut add_trace_signal,
)?;
}
}
for (graph_mem, mem) in state_elements.mems.iter() {
for ((address, enable), read_signal_names) in mem.read_signal_names.iter() {
let address = c.compile_signal(address, &mut prop_context);
prop_context.push(Assignment {
target: expr_arena.alloc(Expr::Ref {
name: read_signal_names.address_name.clone(),
scope: Scope::Member,
}),
expr: address,
});
let enable = c.compile_signal(enable, &mut prop_context);
prop_context.push(Assignment {
target: expr_arena.alloc(Expr::Ref {
name: read_signal_names.enable_name.clone(),
scope: Scope::Member,
}),
expr: enable,
});
add_trace_signal(
graph_mem.module,
read_signal_names.address_name.clone(),
read_signal_names.address_name.clone(),
graph_mem.address_bit_width,
);
add_trace_signal(
graph_mem.module,
read_signal_names.enable_name.clone(),
read_signal_names.enable_name.clone(),
1,
);
}
if let Some((address, value, enable)) = *graph_mem.write_port.borrow() {
let address = c.compile_signal(address, &mut prop_context);
prop_context.push(Assignment {
target: expr_arena.alloc(Expr::Ref {
name: mem.write_address_name.clone(),
scope: Scope::Member,
}),
expr: address,
});
let value = c.compile_signal(value, &mut prop_context);
prop_context.push(Assignment {
target: expr_arena.alloc(Expr::Ref {
name: mem.write_value_name.clone(),
scope: Scope::Member,
}),
expr: value,
});
let enable = c.compile_signal(enable, &mut prop_context);
prop_context.push(Assignment {
target: expr_arena.alloc(Expr::Ref {
name: mem.write_enable_name.clone(),
scope: Scope::Member,
}),
expr: enable,
});
add_trace_signal(
graph_mem.module,
mem.write_address_name.clone(),
mem.write_address_name.clone(),
graph_mem.address_bit_width,
);
add_trace_signal(
graph_mem.module,
mem.write_value_name.clone(),
mem.write_value_name.clone(),
graph_mem.element_bit_width,
);
add_trace_signal(
graph_mem.module,
mem.write_enable_name.clone(),
mem.write_enable_name.clone(),
1,
);
}
}
for (_, reg) in state_elements.regs.iter() {
let signal = reg.data.next.borrow().unwrap();
let expr = c.compile_signal(signal, &mut prop_context);
prop_context.push(Assignment {
target: expr_arena.alloc(Expr::Ref {
name: reg.next_name.clone(),
scope: Scope::Member,
}),
expr,
});
add_trace_signal(
signal.module,
reg.data.name.clone(),
reg.value_name.clone(),
signal.bit_width(),
);
}
let mut w = code_writer::CodeWriter::new(w);
let module_name = options
.override_module_name
.unwrap_or_else(|| m.name.clone());
w.append_indent()?;
w.append(&format!("pub struct {}", module_name))?;
if options.tracing {
w.append("")?;
}
w.append("{")?;
w.append_newline()?;
w.indent();
let inputs = m.inputs.borrow();
if !inputs.is_empty() {
w.append_line("// Inputs")?;
for (name, input) in inputs.iter() {
w.append_line(&format!(
"pub {}: {}, // {} bit(s)",
name,
ValueType::from_bit_width(input.data.bit_width).name(),
input.data.bit_width
))?;
}
}
let outputs = m.outputs.borrow();
if !outputs.is_empty() {
w.append_line("// Outputs")?;
for (name, output) in outputs.iter() {
w.append_line(&format!(
"pub {}: {}, // {} bit(s)",
name,
ValueType::from_bit_width(output.data.bit_width).name(),
output.data.bit_width
))?;
}
}
if !state_elements.regs.is_empty() {
w.append_newline()?;
w.append_line("// Regs")?;
for (_, reg) in state_elements.regs.iter() {
let type_name = ValueType::from_bit_width(reg.data.bit_width).name();
w.append_line(&format!(
"{}: {}, // {} bit(s)",
reg.value_name, type_name, reg.data.bit_width
))?;
w.append_line(&format!("{}: {},", reg.next_name, type_name))?;
}
}
if !state_elements.mems.is_empty() {
w.append_newline()?;
w.append_line("// Mems")?;
for (_, mem) in state_elements.mems.iter() {
let address_type_name = ValueType::from_bit_width(mem.mem.address_bit_width).name();
let element_type_name = ValueType::from_bit_width(mem.mem.element_bit_width).name();
w.append_line(&format!(
"{}: Box<[{}]>, // {} bit elements",
mem.mem_name, element_type_name, mem.mem.element_bit_width
))?;
for (_, read_signal_names) in mem.read_signal_names.iter() {
w.append_line(&format!(
"{}: {},",
read_signal_names.address_name, address_type_name
))?;
w.append_line(&format!(
"{}: {},",
read_signal_names.enable_name,
ValueType::Bool.name()
))?;
w.append_line(&format!(
"{}: {},",
read_signal_names.value_name, element_type_name
))?;
}
if mem.mem.write_port.borrow().is_some() {
w.append_line(&format!(
"{}: {},",
mem.write_address_name, address_type_name
))?;
w.append_line(&format!("{}: {},", mem.write_value_name, element_type_name))?;
w.append_line(&format!(
"{}: {},",
mem.write_enable_name,
ValueType::Bool.name()
))?;
}
}
}
if !inner_fields.is_empty() {
w.append_newline()?;
w.append_line("// Inner")?;
for field in &inner_fields {
let type_name = ValueType::from_bit_width(field.bit_width).name();
w.append_line(&format!(
"{}: {}, // {} bit(s)",
field.name, type_name, field.bit_width
))?;
}
}
if options.tracing {
w.append_newline()?;
w.append_line("__trace: T,")?;
for module_trace_signals in trace_signals.values() {
for trace_signal in module_trace_signals.iter() {
w.append_line(&format!("{}: T::SignalId,", trace_signal.member_name))?;
}
}
}
w.unindent();
w.append_line("}")?;
w.append_newline()?;
w.append_line("#[allow(unused_parens)]")?;
w.append_line("#[automatically_derived]")?;
w.append_indent()?;
w.append("impl")?;
if options.tracing {
w.append("")?;
}
w.append(&format!(" {}", module_name))?;
if options.tracing {
w.append("")?;
}
w.append(" {")?;
w.append_newline()?;
w.indent();
w.append_indent()?;
w.append("pub fn new(")?;
if options.tracing {
w.append(&format!(
"mut trace: T) -> std::io::Result<{}> {{",
module_name
))?;
} else {
w.append(&format!(") -> {} {{", module_name))?;
}
w.append_newline()?;
w.indent();
if options.tracing {
fn visit_module<'a, W: Write>(
module: &'a graph::Module<'a>,
trace_signals: &HashMap<&'a graph::Module<'a>, Vec>,
w: &mut code_writer::CodeWriter,
) -> Result<()> {
w.append_line(&format!(
"trace.push_module(\"{}\")?;",
module.instance_name
))?;
if let Some(module_trace_signals) = trace_signals.get(&module) {
for trace_signal in module_trace_signals.iter() {
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_ {
TraceValueType::Bool => "Bool",
TraceValueType::U32 => "U32",
TraceValueType::U64 => "U64",
TraceValueType::U128 => "U128",
}))?;
}
}
for child in module.modules.borrow().iter() {
visit_module(child, trace_signals, w)?;
}
w.append_line("trace.pop_module()?;")?;
Ok(())
}
visit_module(m, &trace_signals, &mut w)?;
w.append_newline()?;
}
w.append_indent()?;
if options.tracing {
w.append("Ok(")?;
}
w.append(&format!("{} {{", module_name))?;
w.append_newline()?;
w.indent();
if !inputs.is_empty() {
w.append_line("// Inputs")?;
for (name, input) in inputs.iter() {
w.append_line(&format!(
"{}: {}, // {} bit(s)",
name,
ValueType::from_bit_width(input.data.bit_width).zero_str(),
input.data.bit_width
))?;
}
}
if !outputs.is_empty() {
w.append_line("// Outputs")?;
for (name, output) in outputs.iter() {
w.append_line(&format!(
"{}: {}, // {} bit(s)",
name,
ValueType::from_bit_width(output.data.bit_width).zero_str(),
output.data.bit_width
))?;
}
}
if !state_elements.regs.is_empty() {
w.append_newline()?;
w.append_line("// Regs")?;
for (_, reg) in state_elements.regs.iter() {
w.append_line(&format!(
"{}: {}, // {} bit(s)",
reg.value_name,
ValueType::from_bit_width(reg.data.bit_width).zero_str(),
reg.data.bit_width
))?;
w.append_line(&format!(
"{}: {},",
reg.next_name,
ValueType::from_bit_width(reg.data.bit_width).zero_str()
))?;
}
}
if !state_elements.mems.is_empty() {
w.append_newline()?;
w.append_line("// Mems")?;
for (_, mem) in state_elements.mems.iter() {
let address_type = ValueType::from_bit_width(mem.mem.address_bit_width);
let element_type = ValueType::from_bit_width(mem.mem.element_bit_width);
if let Some(ref initial_contents) = *mem.mem.initial_contents.borrow() {
w.append_line(&format!("{}: vec![", mem.mem_name))?;
w.indent();
for element in initial_contents.iter() {
w.append_line(&match *element {
graph::Constant::Bool(value) => format!("{},", value),
graph::Constant::U32(value) => format!("0x{:x},", value),
graph::Constant::U64(value) => format!("0x{:x},", value),
graph::Constant::U128(value) => format!("0x{:x},", value),
})?;
}
w.unindent();
w.append_line("].into_boxed_slice(),")?;
} else {
w.append_line(&format!(
"{}: vec![{}; {}].into_boxed_slice(),",
mem.mem_name,
element_type.zero_str(),
1 << mem.mem.address_bit_width
))?;
}
for (_, read_signal_names) in mem.read_signal_names.iter() {
w.append_line(&format!(
"{}: {},",
read_signal_names.address_name,
address_type.zero_str()
))?;
w.append_line(&format!(
"{}: {},",
read_signal_names.enable_name,
ValueType::Bool.zero_str()
))?;
w.append_line(&format!(
"{}: {},",
read_signal_names.value_name,
element_type.zero_str()
))?;
}
if mem.mem.write_port.borrow().is_some() {
w.append_line(&format!(
"{}: {},",
mem.write_address_name,
address_type.zero_str()
))?;
w.append_line(&format!(
"{}: {},",
mem.write_value_name,
element_type.zero_str()
))?;
w.append_line(&format!(
"{}: {},",
mem.write_enable_name,
ValueType::Bool.zero_str()
))?;
}
}
}
if !inner_fields.is_empty() {
w.append_newline()?;
for field in &inner_fields {
w.append_line(&format!(
"{}: {}, // {} bit(s)",
field.name,
ValueType::from_bit_width(field.bit_width).zero_str(),
field.bit_width
))?;
}
}
if options.tracing {
w.append_newline()?;
w.append_line("__trace: trace,")?;
for module_trace_signals in trace_signals.values() {
for trace_signal in module_trace_signals.iter() {
w.append_line(&format!("{},", trace_signal.member_name))?;
}
}
}
w.unindent();
w.append_indent()?;
w.append("}")?;
if options.tracing {
w.append(")")?;
}
w.append_newline()?;
w.unindent();
w.append_line("}")?;
let mut reset_context = AssignmentContext::new(&expr_arena);
let mut posedge_clk_context = AssignmentContext::new(&expr_arena);
for (_, reg) in state_elements.regs.iter() {
let target = expr_arena.alloc(Expr::Ref {
name: reg.value_name.clone(),
scope: Scope::Member,
});
if let Some(ref initial_value) = *reg.data.initial_value.borrow() {
reset_context.push(Assignment {
target,
expr: Expr::from_constant(initial_value, reg.data.bit_width, &expr_arena),
});
}
posedge_clk_context.push(Assignment {
target,
expr: expr_arena.alloc(Expr::Ref {
name: reg.next_name.clone(),
scope: Scope::Member,
}),
});
}
for (_, mem) in state_elements.mems.iter() {
for (_, read_signal_names) in mem.read_signal_names.iter() {
let address = expr_arena.alloc(Expr::Ref {
name: read_signal_names.address_name.clone(),
scope: Scope::Member,
});
let enable = expr_arena.alloc(Expr::Ref {
name: read_signal_names.enable_name.clone(),
scope: Scope::Member,
});
let value = expr_arena.alloc(Expr::Ref {
name: read_signal_names.value_name.clone(),
scope: Scope::Member,
});
let element = expr_arena.alloc(Expr::ArrayIndex {
target: expr_arena.alloc(Expr::Ref {
name: mem.mem_name.clone(),
scope: Scope::Member,
}),
index: address,
});
// TODO: Conditional assign statement instead of always writing ternary
posedge_clk_context.push(Assignment {
target: value,
expr: expr_arena.alloc(Expr::Ternary {
cond: enable,
when_true: element,
when_false: value,
}),
});
}
if mem.mem.write_port.borrow().is_some() {
let address = expr_arena.alloc(Expr::Ref {
name: mem.write_address_name.clone(),
scope: Scope::Member,
});
let value = expr_arena.alloc(Expr::Ref {
name: mem.write_value_name.clone(),
scope: Scope::Member,
});
let enable = expr_arena.alloc(Expr::Ref {
name: mem.write_enable_name.clone(),
scope: Scope::Member,
});
let element = expr_arena.alloc(Expr::ArrayIndex {
target: expr_arena.alloc(Expr::Ref {
name: mem.mem_name.clone(),
scope: Scope::Member,
}),
index: address,
});
// TODO: Conditional assign statement instead of always writing ternary
posedge_clk_context.push(Assignment {
target: element,
expr: expr_arena.alloc(Expr::Ternary {
cond: enable,
when_true: value,
when_false: element,
}),
});
}
}
if !reset_context.is_empty() {
w.append_newline()?;
w.append_line("pub fn reset(&mut self) {")?;
w.indent();
reset_context.write(&mut w)?;
w.unindent();
w.append_line("}")?;
}
if !posedge_clk_context.is_empty() {
w.append_newline()?;
w.append_line("pub fn posedge_clk(&mut self) {")?;
w.indent();
posedge_clk_context.write(&mut w)?;
w.unindent();
w.append_line("}")?;
}
w.append_newline()?;
w.append_line("pub fn prop(&mut self) {")?;
w.indent();
prop_context.write(&mut w)?;
w.unindent();
w.append_line("}")?;
if options.tracing {
w.append_newline()?;
w.append_line("pub fn update_trace(&mut self, time_stamp: u64) -> std::io::Result<()> {")?;
w.indent();
w.append_line("self.__trace.update_time_stamp(time_stamp)?;")?;
w.append_newline()?;
for module_trace_signals in trace_signals.values() {
for trace_signal in module_trace_signals.iter() {
w.append_line(&format!("self.__trace.update_signal(&self.{}, kaze::runtime::tracing::TraceValue::{}(self.{}))?;", trace_signal.member_name, match trace_signal.type_ {
TraceValueType::Bool => "Bool",
TraceValueType::U32 => "U32",
TraceValueType::U64 => "U64",
TraceValueType::U128 => "U128",
}, trace_signal.value_name))?;
}
}
w.append_newline()?;
w.append_line("Ok(())")?;
w.unindent();
w.append_line("}")?;
}
w.unindent();
w.append_line("}")?;
w.append_newline()?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
#[test]
#[should_panic(
expected = "Cannot generate code for module \"A\" because module \"A\" contains an instance of module \"B\" called \"b\" whose input \"i\" is not driven."
)]
fn undriven_instance_input_error() {
let c = Context::new();
let a = c.module("a", "A");
let b = a.module("b", "B");
let _ = b.input("i", 1);
// Panic
generate(a, GenerationOptions::default(), Vec::new()).unwrap();
}
#[test]
#[should_panic(
expected = "Cannot generate code for module \"A\" because module \"A\" contains a register called \"r\" which is not driven."
)]
fn undriven_register_error1() {
let c = Context::new();
let a = c.module("a", "A");
let _ = a.reg("r", 1);
// Panic
generate(a, GenerationOptions::default(), Vec::new()).unwrap();
}
#[test]
#[should_panic(
expected = "Cannot generate code for module \"A\" because module \"B\" contains a register called \"r\" which is not driven."
)]
fn undriven_register_error2() {
let c = Context::new();
let a = c.module("a", "A");
let b = a.module("b", "B");
let _ = b.reg("r", 1);
// Panic
generate(a, GenerationOptions::default(), Vec::new()).unwrap();
}
#[test]
#[should_panic(
expected = "Cannot generate code for module \"A\" because module \"A\" contains a memory called \"m\" which doesn't have any read ports."
)]
fn mem_without_read_ports_error1() {
let c = Context::new();
let a = c.module("a", "A");
let _ = a.mem("m", 1, 1);
// Panic
generate(a, GenerationOptions::default(), Vec::new()).unwrap();
}
#[test]
#[should_panic(
expected = "Cannot generate code for module \"A\" because module \"B\" contains a memory called \"m\" which doesn't have any read ports."
)]
fn mem_without_read_ports_error2() {
let c = Context::new();
let a = c.module("a", "A");
let b = a.module("b", "B");
let _ = b.mem("m", 1, 1);
// Panic
generate(a, GenerationOptions::default(), Vec::new()).unwrap();
}
#[test]
#[should_panic(
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."
)]
fn mem_without_initial_contents_or_write_port_error1() {
let c = Context::new();
let a = c.module("a", "A");
let m = a.mem("m", 1, 1);
let _ = m.read_port(a.low(), a.low());
// Panic
generate(a, GenerationOptions::default(), Vec::new()).unwrap();
}
#[test]
#[should_panic(
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."
)]
fn mem_without_initial_contents_or_write_port_error2() {
let c = Context::new();
let a = c.module("a", "A");
let b = a.module("b", "B");
let m = b.mem("m", 1, 1);
let _ = m.read_port(b.low(), b.low());
// Panic
generate(a, GenerationOptions::default(), Vec::new()).unwrap();
}
#[test]
#[should_panic(
expected = "Cannot generate code for module \"b\" because module \"a\" contains an output called \"o\" which forms a combinational loop with itself."
)]
fn combinational_loop_error() {
let c = Context::new();
let b = c.module("b", "b");
let a = b.module("a", "a");
let a_i = a.input("i", 1);
let a_o = a.output("o", a_i);
a_i.drive(a_o);
// Panic
generate(b, GenerationOptions::default(), Vec::new()).unwrap();
}
}
================================================
FILE: kaze/src/state_elements.rs
================================================
use crate::graph;
use crate::graph::internal_signal;
use std::collections::HashMap;
pub(super) struct Register<'a> {
pub data: &'a graph::RegisterData<'a>,
pub value_name: String,
pub next_name: String,
}
pub(super) struct Mem<'a> {
pub mem: &'a graph::Mem<'a>,
pub mem_name: String,
pub read_signal_names: HashMap<
(
&'a internal_signal::InternalSignal<'a>,
&'a internal_signal::InternalSignal<'a>,
),
ReadSignalNames,
>,
pub write_address_name: String,
pub write_value_name: String,
pub write_enable_name: String,
}
pub struct ReadSignalNames {
pub address_name: String,
pub enable_name: String,
pub value_name: String,
}
// TODO: Move?
// TODO: Cover registers as well
#[derive(Clone, Copy)]
pub(super) enum IncludedPorts {
All,
ReachableFromTopLevelOutputs,
}
pub(super) struct StateElements<'a> {
pub mems: HashMap<&'a graph::Mem<'a>, Mem<'a>>,
pub regs: HashMap<&'a internal_signal::InternalSignal<'a>, Register<'a>>,
}
impl<'a> StateElements<'a> {
pub fn new(
m: &'a graph::Module<'a>,
// TODO: Cover registers as well
included_ports: IncludedPorts,
signal_reference_counts: &mut HashMap<&'a internal_signal::InternalSignal<'a>, u32>,
) -> StateElements<'a> {
let mut mems = HashMap::new();
let mut regs = HashMap::new();
visit_module(
m,
included_ports,
&mut mems,
&mut regs,
signal_reference_counts,
);
StateElements { mems, regs }
}
}
fn visit_module<'a>(
m: &'a graph::Module<'a>,
included_ports: IncludedPorts,
mems: &mut HashMap<&'a graph::Mem<'a>, Mem<'a>>,
regs: &mut HashMap<&'a internal_signal::InternalSignal<'a>, Register<'a>>,
signal_reference_counts: &mut HashMap<&'a internal_signal::InternalSignal<'a>, u32>,
) {
match included_ports {
// TODO: Match all of these with traces
// TODO: Test
IncludedPorts::All => {
for (_, &input) in m.inputs.borrow().iter() {
visit_signal(input.value, mems, regs, signal_reference_counts);
}
for (_, &output) in m.outputs.borrow().iter() {
visit_signal(output.data.source, mems, regs, signal_reference_counts);
}
for ®ister in m.registers.borrow().iter() {
match register.data {
internal_signal::SignalData::Reg { ref data } => {
visit_signal(
data.next.borrow().unwrap(),
mems,
regs,
signal_reference_counts,
);
}
_ => unreachable!(),
}
}
for &module in m.modules.borrow().iter() {
visit_module(module, included_ports, mems, regs, signal_reference_counts);
}
// TODO: Cover all mems as well
}
IncludedPorts::ReachableFromTopLevelOutputs => {
for (_, &output) in m.outputs.borrow().iter() {
visit_signal(output.data.source, mems, regs, signal_reference_counts);
}
}
}
}
// TODO: Move this to ctor and iterate over input module outputs there?
fn visit_signal<'a>(
signal: &'a internal_signal::InternalSignal<'a>,
mems: &mut HashMap<&'a graph::Mem<'a>, Mem<'a>>,
regs: &mut HashMap<&'a internal_signal::InternalSignal<'a>, Register<'a>>,
signal_reference_counts: &mut HashMap<&'a internal_signal::InternalSignal<'a>, u32>,
) {
// TODO: Do we even need this with just the one member?
struct Frame<'a> {
signal: &'a internal_signal::InternalSignal<'a>,
}
let mut frames = Vec::new();
frames.push(Frame { signal });
while let Some(frame) = frames.pop() {
let signal = frame.signal;
let reference_count = signal_reference_counts.entry(signal).or_insert(0);
*reference_count += 1;
if *reference_count > 1 {
continue;
}
match signal.data {
internal_signal::SignalData::Lit { .. } => (),
internal_signal::SignalData::Input { data } => {
if let Some(driven_value) = data.driven_value.borrow().clone() {
frames.push(Frame {
signal: driven_value,
});
}
}
internal_signal::SignalData::Output { data } => {
frames.push(Frame {
signal: data.source,
});
}
internal_signal::SignalData::Reg { data } => {
let key = signal;
let value_name = format!(
"__reg_{}_{}_{}",
signal.module_instance_name_prefix(),
data.name,
regs.len()
);
let next_name = format!("{}_next", value_name);
regs.insert(
key,
Register {
data,
value_name,
next_name,
},
);
frames.push(Frame {
signal: data.next.borrow().unwrap(),
});
}
internal_signal::SignalData::UnOp { source, .. } => {
frames.push(Frame { signal: source });
}
internal_signal::SignalData::SimpleBinOp { lhs, rhs, .. } => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::AdditiveBinOp { lhs, rhs, .. } => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::ComparisonBinOp { lhs, rhs, .. } => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::ShiftBinOp { lhs, rhs, .. } => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::Mul { lhs, rhs, .. } => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::MulSigned { lhs, rhs, .. } => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::Bits { source, .. } => {
frames.push(Frame { signal: source });
}
internal_signal::SignalData::Repeat { source, .. } => {
frames.push(Frame { signal: source });
}
internal_signal::SignalData::Concat { lhs, rhs, .. } => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::Mux {
cond,
when_true,
when_false,
..
} => {
frames.push(Frame { signal: cond });
frames.push(Frame { signal: when_true });
frames.push(Frame { signal: when_false });
}
internal_signal::SignalData::MemReadPortOutput { mem, .. } => {
let key = mem;
let mem_name = format!(
"__mem_{}_{}_{}",
signal.module_instance_name_prefix(),
mem.name,
mems.len()
);
// TODO: It might actually be too conservative to trace all read ports,
// as we only know that the write port and _this_ read port are reachable
// at this point, but we have to keep some extra state to know whether or
// not we've hit each read port otherwise.
let mut read_signal_names = HashMap::new();
for (index, (address, enable)) in mem.read_ports.borrow().iter().enumerate() {
let name_prefix = format!("{}_read_port_{}_", mem_name, index);
read_signal_names.insert(
(*address, *enable),
ReadSignalNames {
address_name: format!("{}address", name_prefix),
enable_name: format!("{}enable", name_prefix),
value_name: format!("{}value", name_prefix),
},
);
}
let name_prefix = format!("{}_write_port_", mem_name);
let write_address_name = format!("{}address", name_prefix);
let write_value_name = format!("{}value", name_prefix);
let write_enable_name = format!("{}enable", name_prefix);
mems.insert(
key,
Mem {
mem,
mem_name,
write_address_name,
write_value_name,
write_enable_name,
read_signal_names,
},
);
for (address, enable) in mem.read_ports.borrow().iter() {
frames.push(Frame { signal: address });
frames.push(Frame { signal: enable });
}
if let Some((address, value, enable)) = *mem.write_port.borrow() {
frames.push(Frame { signal: address });
frames.push(Frame { signal: value });
frames.push(Frame { signal: enable });
}
}
}
}
}
================================================
FILE: kaze/src/validation.rs
================================================
use crate::graph;
use crate::graph::internal_signal;
pub fn validate_module_hierarchy<'a>(m: &'a graph::Module<'a>) {
detect_undriven_registers_and_inputs(m, m);
detect_mem_errors(m, m);
detect_combinational_loops(m, m);
}
fn detect_undriven_registers_and_inputs<'a>(m: &graph::Module<'a>, root: &graph::Module<'a>) {
for register in m.registers.borrow().iter() {
match register.data {
internal_signal::SignalData::Reg { ref data } => {
if data.next.borrow().is_none() {
panic!("Cannot generate code for module \"{}\" because module \"{}\" contains a register called \"{}\" which is not driven.", root.name, m.name, data.name);
}
}
_ => unreachable!(),
}
}
for module in m.modules.borrow().iter() {
for (name, input) in module.inputs.borrow().iter() {
if input.data.driven_value.borrow().is_none() {
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);
}
}
detect_undriven_registers_and_inputs(module, root);
}
}
fn detect_mem_errors<'a>(m: &graph::Module<'a>, root: &graph::Module<'a>) {
for mem in m.mems.borrow().iter() {
if mem.read_ports.borrow().is_empty() {
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);
}
if mem.initial_contents.borrow().is_none() && mem.write_port.borrow().is_none() {
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);
}
}
for module in m.modules.borrow().iter() {
detect_mem_errors(module, root);
}
}
fn detect_combinational_loops<'a>(m: &graph::Module<'a>, root: &graph::Module<'a>) {
for module in m.modules.borrow().iter() {
for (_, output) in module.outputs.borrow().iter() {
trace_signal(output.data.source, output.data.source, root);
}
detect_combinational_loops(module, root);
}
}
fn trace_signal<'a>(
signal: &'a internal_signal::InternalSignal<'a>,
source_output: &'a internal_signal::InternalSignal<'a>,
root: &graph::Module<'a>,
) {
struct Frame<'a> {
signal: &'a internal_signal::InternalSignal<'a>,
}
let mut frames = Vec::new();
frames.push(Frame { signal });
while let Some(frame) = frames.pop() {
let signal = frame.signal;
match signal.data {
internal_signal::SignalData::Lit { .. } => (),
internal_signal::SignalData::Input { data } => {
if let Some(driven_value) = data.driven_value.borrow().clone() {
frames.push(Frame {
signal: driven_value,
});
}
}
internal_signal::SignalData::Output { data } => {
if data.source == source_output {
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);
}
frames.push(Frame {
signal: data.source,
});
}
internal_signal::SignalData::Reg { .. } => (),
internal_signal::SignalData::UnOp { ref source, .. } => {
frames.push(Frame { signal: source });
}
internal_signal::SignalData::SimpleBinOp {
ref lhs, ref rhs, ..
} => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::AdditiveBinOp {
ref lhs, ref rhs, ..
} => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::ComparisonBinOp {
ref lhs, ref rhs, ..
} => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::ShiftBinOp {
ref lhs, ref rhs, ..
} => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::Mul {
ref lhs, ref rhs, ..
} => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::MulSigned {
ref lhs, ref rhs, ..
} => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::Bits { ref source, .. } => {
frames.push(Frame { signal: source });
}
internal_signal::SignalData::Repeat { ref source, .. } => {
frames.push(Frame { signal: source });
}
internal_signal::SignalData::Concat {
ref lhs, ref rhs, ..
} => {
frames.push(Frame { signal: lhs });
frames.push(Frame { signal: rhs });
}
internal_signal::SignalData::Mux {
ref cond,
ref when_true,
ref when_false,
..
} => {
frames.push(Frame { signal: cond });
frames.push(Frame { signal: when_true });
frames.push(Frame { signal: when_false });
}
internal_signal::SignalData::MemReadPortOutput { .. } => (),
}
}
}
================================================
FILE: kaze/src/verilog/compiler.rs
================================================
use super::ir::*;
use crate::internal_signal;
use crate::state_elements::*;
use std::collections::HashMap;
pub(super) struct Compiler<'graph> {
signal_exprs: HashMap<&'graph internal_signal::InternalSignal<'graph>, Expr>,
}
impl<'graph, 'context> Compiler<'graph> {
pub fn new() -> Compiler<'graph> {
Compiler {
signal_exprs: HashMap::new(),
}
}
pub fn compile_signal(
&mut self,
signal: &'graph internal_signal::InternalSignal<'graph>,
state_elements: &'context StateElements<'graph>,
a: &mut AssignmentContext,
) -> Expr {
enum Frame<'graph> {
Enter(&'graph internal_signal::InternalSignal<'graph>),
Leave(&'graph internal_signal::InternalSignal<'graph>),
}
let mut frames = Vec::new();
frames.push(Frame::Enter(signal));
let mut results = Vec::new();
while let Some(frame) = frames.pop() {
if let Some(expr) = match frame {
Frame::Enter(signal) => {
if let Some(expr) = self.signal_exprs.get(&signal) {
results.push(expr.clone());
continue;
}
match signal.data {
internal_signal::SignalData::Lit {
ref value,
bit_width,
} => Some(Expr::from_constant(value, bit_width)),
internal_signal::SignalData::Input { data } => {
if let Some(driven_value) = data.driven_value.borrow().clone() {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(driven_value));
None
} else {
Some(Expr::Ref {
name: data.name.clone(),
})
}
}
internal_signal::SignalData::Output { data } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(data.source));
None
}
internal_signal::SignalData::Reg { .. } => Some(Expr::Ref {
name: state_elements.regs[&signal].value_name.clone(),
}),
internal_signal::SignalData::UnOp { source, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(source));
None
}
internal_signal::SignalData::SimpleBinOp { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::AdditiveBinOp { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::ComparisonBinOp { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::ShiftBinOp { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::Mul { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::MulSigned { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::Bits { source, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(source));
None
}
internal_signal::SignalData::Repeat { source, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(source));
None
}
internal_signal::SignalData::Concat { lhs, rhs, .. } => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(lhs));
frames.push(Frame::Enter(rhs));
None
}
internal_signal::SignalData::Mux {
cond,
when_true,
when_false,
..
} => {
frames.push(Frame::Leave(signal));
frames.push(Frame::Enter(cond));
frames.push(Frame::Enter(when_true));
frames.push(Frame::Enter(when_false));
None
}
internal_signal::SignalData::MemReadPortOutput {
mem,
address,
enable,
} => {
let mem = &state_elements.mems[&mem];
let read_signal_names = &mem.read_signal_names[&(address, enable)];
Some(Expr::Ref {
name: read_signal_names.value_name.clone(),
})
}
}
}
Frame::Leave(signal) => {
match signal.data {
internal_signal::SignalData::Lit { .. } => unreachable!(),
internal_signal::SignalData::Input { data } => {
if data.driven_value.borrow().is_none() {
unreachable!();
}
Some(a.gen_temp(
results.pop().unwrap(),
signal.bit_width(),
format!("{}_{}", signal.module_instance_name_prefix(), data.name),
))
}
internal_signal::SignalData::Output { data } => Some(a.gen_temp(
results.pop().unwrap(),
signal.bit_width(),
format!(
"{}_{}",
data.source.module_instance_name_prefix(),
data.name
),
)),
internal_signal::SignalData::Reg { .. } => unreachable!(),
internal_signal::SignalData::UnOp { op, bit_width, .. } => {
let source = results.pop().unwrap();
Some(a.gen_temp(
Expr::UnOp {
source: Box::new(source),
op: match op {
internal_signal::UnOp::Not => UnOp::Not,
},
},
bit_width,
signal.module_instance_name_prefix(),
))
}
internal_signal::SignalData::SimpleBinOp { op, bit_width, .. } => {
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
Some(a.gen_temp(
Expr::BinOp {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
op: match op {
internal_signal::SimpleBinOp::BitAnd => BinOp::BitAnd,
internal_signal::SimpleBinOp::BitOr => BinOp::BitOr,
internal_signal::SimpleBinOp::BitXor => BinOp::BitXor,
},
},
bit_width,
signal.module_instance_name_prefix(),
))
}
internal_signal::SignalData::AdditiveBinOp { op, bit_width, .. } => {
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
Some(a.gen_temp(
Expr::BinOp {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
op: match op {
internal_signal::AdditiveBinOp::Add => BinOp::Add,
internal_signal::AdditiveBinOp::Sub => BinOp::Sub,
},
},
bit_width,
signal.module_instance_name_prefix(),
))
}
internal_signal::SignalData::ComparisonBinOp { op, .. } => {
let bit_width = signal.bit_width();
let mut lhs = results.pop().unwrap();
let mut rhs = results.pop().unwrap();
match op {
internal_signal::ComparisonBinOp::GreaterThanEqualSigned
| internal_signal::ComparisonBinOp::GreaterThanSigned
| internal_signal::ComparisonBinOp::LessThanEqualSigned
| internal_signal::ComparisonBinOp::LessThanSigned => {
lhs = Expr::Signed {
source: Box::new(lhs),
};
rhs = Expr::Signed {
source: Box::new(rhs),
};
}
_ => (),
}
Some(a.gen_temp(
Expr::BinOp {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
op: match op {
internal_signal::ComparisonBinOp::Equal => BinOp::Equal,
internal_signal::ComparisonBinOp::NotEqual => BinOp::NotEqual,
internal_signal::ComparisonBinOp::LessThan
| internal_signal::ComparisonBinOp::LessThanSigned => BinOp::LessThan,
internal_signal::ComparisonBinOp::LessThanEqual
| internal_signal::ComparisonBinOp::LessThanEqualSigned => {
BinOp::LessThanEqual
}
internal_signal::ComparisonBinOp::GreaterThan
| internal_signal::ComparisonBinOp::GreaterThanSigned => {
BinOp::GreaterThan
}
internal_signal::ComparisonBinOp::GreaterThanEqual
| internal_signal::ComparisonBinOp::GreaterThanEqualSigned => {
BinOp::GreaterThanEqual
}
},
},
bit_width,
signal.module_instance_name_prefix(),
))
}
internal_signal::SignalData::ShiftBinOp { op, bit_width, .. } => {
let lhs = results.pop().unwrap();
let lhs = match op {
internal_signal::ShiftBinOp::Shl
| internal_signal::ShiftBinOp::Shr => lhs,
internal_signal::ShiftBinOp::ShrArithmetic => Expr::Signed {
source: Box::new(lhs),
},
};
let rhs = results.pop().unwrap();
Some(a.gen_temp(
Expr::BinOp {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
op: match op {
internal_signal::ShiftBinOp::Shl => BinOp::Shl,
internal_signal::ShiftBinOp::Shr => BinOp::Shr,
internal_signal::ShiftBinOp::ShrArithmetic => {
BinOp::ShrArithmetic
}
},
},
bit_width,
signal.module_instance_name_prefix(),
))
}
internal_signal::SignalData::Mul { bit_width, .. } => {
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
Some(a.gen_temp(
Expr::BinOp {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
op: BinOp::Mul,
},
bit_width,
signal.module_instance_name_prefix(),
))
}
internal_signal::SignalData::MulSigned { bit_width, .. } => {
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
let lhs = Expr::Signed {
source: Box::new(lhs),
};
let rhs = Expr::Signed {
source: Box::new(rhs),
};
Some(a.gen_temp(
Expr::BinOp {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
op: BinOp::Mul,
},
bit_width,
signal.module_instance_name_prefix(),
))
}
internal_signal::SignalData::Bits {
source,
range_high,
range_low,
} => {
let source_bit_width = source.bit_width();
let bit_width = signal.bit_width();
let source = results.pop().unwrap();
// Verilog doesn't allow indexing scalars
Some(if source_bit_width == 1 {
source
} else {
a.gen_temp(
Expr::Bits {
source: Box::new(source),
range_high,
range_low,
},
bit_width,
signal.module_instance_name_prefix(),
)
})
}
internal_signal::SignalData::Repeat {
count, bit_width, ..
} => {
let source = results.pop().unwrap();
Some(a.gen_temp(
Expr::Repeat {
source: Box::new(source),
count,
},
bit_width,
signal.module_instance_name_prefix(),
))
}
internal_signal::SignalData::Concat { bit_width, .. } => {
let lhs = results.pop().unwrap();
let rhs = results.pop().unwrap();
Some(a.gen_temp(
Expr::Concat {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
},
bit_width,
signal.module_instance_name_prefix(),
))
}
internal_signal::SignalData::Mux { bit_width, .. } => {
let cond = results.pop().unwrap();
let when_true = results.pop().unwrap();
let when_false = results.pop().unwrap();
Some(a.gen_temp(
Expr::Ternary {
cond: Box::new(cond),
when_true: Box::new(when_true),
when_false: Box::new(when_false),
},
bit_width,
signal.module_instance_name_prefix(),
))
}
internal_signal::SignalData::MemReadPortOutput { .. } => unreachable!(),
}
}
} {
self.signal_exprs.insert(signal, expr.clone());
results.push(expr);
}
}
results.pop().unwrap()
}
}
================================================
FILE: kaze/src/verilog/ir.rs
================================================
use crate::code_writer;
use crate::graph;
use std::io::{Result, Write};
pub struct NodeDecl {
pub net_type: NetType,
pub name: String,
pub bit_width: u32,
}
impl NodeDecl {
pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> {
w.append_indent()?;
self.net_type.write(w)?;
w.append(" ")?;
if self.bit_width > 1 {
w.append(&format!("[{}:{}] ", self.bit_width - 1, 0))?;
}
w.append(&format!("{};", self.name))?;
w.append_newline()?;
Ok(())
}
}
pub enum NetType {
Reg,
Wire,
}
impl NetType {
pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> {
w.append(match self {
NetType::Reg => "reg",
NetType::Wire => "wire",
})
}
}
pub struct AssignmentContext {
assignments: Vec,
local_decls: Vec,
}
impl AssignmentContext {
pub fn new() -> AssignmentContext {
AssignmentContext {
assignments: Vec::new(),
local_decls: Vec::new(),
}
}
pub fn gen_temp(&mut self, expr: Expr, bit_width: u32, name_prefix: String) -> Expr {
let name = format!("__temp_{}_{}", name_prefix, self.local_decls.len());
self.local_decls.push(NodeDecl {
net_type: NetType::Wire,
name: name.clone(),
bit_width,
});
self.assignments.push(Assignment {
target_name: name.clone(),
expr,
});
Expr::Ref { name }
}
pub fn is_empty(&self) -> bool {
self.assignments.is_empty()
}
pub fn push(&mut self, assignment: Assignment) {
self.assignments.push(assignment);
}
pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> {
if !self.local_decls.is_empty() {
for node_decl in self.local_decls.iter() {
node_decl.write(w)?;
}
w.append_newline()?;
}
for assignment in self.assignments.iter() {
assignment.write(w)?;
}
Ok(())
}
}
pub struct Assignment {
pub target_name: String,
pub expr: Expr,
}
impl Assignment {
fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> {
w.append_indent()?;
w.append(&format!("assign {}", self.target_name))?;
w.append(" = ")?;
self.expr.write(w)?;
w.append(";")?;
w.append_newline()?;
Ok(())
}
}
#[derive(Clone)]
pub enum Expr {
BinOp {
lhs: Box,
rhs: Box,
op: BinOp,
},
Bits {
source: Box,
range_high: u32,
range_low: u32,
},
Concat {
lhs: Box,
rhs: Box,
},
Constant {
bit_width: u32,
value: u128,
},
Ref {
name: String,
},
Repeat {
source: Box,
count: u32,
},
Signed {
source: Box,
},
Ternary {
cond: Box,
when_true: Box,
when_false: Box,
},
UnOp {
source: Box,
op: UnOp,
},
}
impl Expr {
pub fn from_constant(value: &graph::Constant, bit_width: u32) -> Expr {
Expr::Constant {
bit_width,
value: value.numeric_value(),
}
}
pub fn write(&self, w: &mut code_writer::CodeWriter) -> Result<()> {
match self {
Expr::BinOp { lhs, rhs, op } => {
lhs.write(w)?;
w.append(&format!(
" {} ",
match op {
BinOp::Add => "+",
BinOp::BitAnd => "&",
BinOp::BitOr => "|",
BinOp::BitXor => "^",
BinOp::Equal => "==",
BinOp::NotEqual => "!=",
BinOp::LessThan => "<",
BinOp::LessThanEqual => "<=",
BinOp::GreaterThan => ">",
BinOp::GreaterThanEqual => ">=",
BinOp::Shl => "<<",
BinOp::Shr => ">>",
BinOp::ShrArithmetic => ">>>",
BinOp::Sub => "-",
BinOp::Mul => "*",
}
))?;
rhs.write(w)?;
}
Expr::Bits {
source,
range_high,
range_low,
} => {
source.write(w)?;
if range_high != range_low {
w.append(&format!("[{}:{}]", range_high, range_low))?;
} else {
w.append(&format!("[{}]", range_high))?;
}
}
Expr::Concat { lhs, rhs } => {
w.append("{")?;
lhs.write(w)?;
w.append(", ")?;
rhs.write(w)?;
w.append("}")?;
}
Expr::Constant { bit_width, value } => {
w.append(&format!("{}'h{:x}", bit_width, value))?;
}
Expr::Ref { name } => {
w.append(name)?;
}
Expr::Repeat { source, count } => {
w.append(&format!("{{{}{{", count))?;
source.write(w)?;
w.append("}}")?;
}
Expr::Signed { source } => {
w.append("$signed(")?;
source.write(w)?;
w.append(")")?;
}
Expr::Ternary {
cond,
when_true,
when_false,
} => {
cond.write(w)?;
w.append(" ? ")?;
when_true.write(w)?;
w.append(" : ")?;
when_false.write(w)?;
}
Expr::UnOp { source, op } => {
w.append(match op {
UnOp::Not => "~",
})?;
source.write(w)?;
}
}
Ok(())
}
}
#[derive(Clone)]
pub enum BinOp {
Add,
BitAnd,
BitOr,
BitXor,
Equal,
NotEqual,
LessThan,
LessThanEqual,
GreaterThan,
GreaterThanEqual,
Shl,
Shr,
ShrArithmetic,
Sub,
Mul,
}
#[derive(Clone)]
pub enum UnOp {
Not,
}
================================================
FILE: kaze/src/verilog.rs
================================================
//! Verilog code generation.
mod compiler;
mod ir;
use compiler::*;
use ir::*;
use crate::code_writer;
use crate::graph;
use crate::state_elements::*;
use crate::validation::*;
use std::collections::HashMap;
use std::io::{Result, Write};
// TODO: Note that mutable writer reference can be passed, see https://rust-lang.github.io/api-guidelines/interoperability.html#c-rw-value
pub fn generate<'a, W: Write>(m: &'a graph::Module<'a>, w: W) -> Result<()> {
validate_module_hierarchy(m);
let mut signal_reference_counts = HashMap::new();
let state_elements = StateElements::new(
m,
IncludedPorts::ReachableFromTopLevelOutputs,
&mut signal_reference_counts,
);
let mut c = Compiler::new();
let mut assignments = AssignmentContext::new();
for (name, &output) in m.outputs.borrow().iter() {
let expr = c.compile_signal(output.data.source, &state_elements, &mut assignments);
assignments.push(Assignment {
target_name: name.clone(),
expr,
});
}
let mut node_decls = Vec::new();
for (mem, mem_decls) in state_elements.mems.iter() {
for ((address, enable), read_signal_names) in mem_decls.read_signal_names.iter() {
let expr = c.compile_signal(address, &state_elements, &mut assignments);
node_decls.push(NodeDecl {
net_type: NetType::Wire,
name: read_signal_names.address_name.clone(),
bit_width: address.bit_width(),
});
assignments.push(Assignment {
target_name: read_signal_names.address_name.clone(),
expr,
});
let expr = c.compile_signal(enable, &state_elements, &mut assignments);
node_decls.push(NodeDecl {
net_type: NetType::Wire,
name: read_signal_names.enable_name.clone(),
bit_width: enable.bit_width(),
});
assignments.push(Assignment {
target_name: read_signal_names.enable_name.clone(),
expr,
});
node_decls.push(NodeDecl {
net_type: NetType::Reg,
name: read_signal_names.value_name.clone(),
bit_width: mem.element_bit_width,
});
}
if let Some((address, value, enable)) = *mem.write_port.borrow() {
let expr = c.compile_signal(address, &state_elements, &mut assignments);
node_decls.push(NodeDecl {
net_type: NetType::Wire,
name: mem_decls.write_address_name.clone(),
bit_width: address.bit_width(),
});
assignments.push(Assignment {
target_name: mem_decls.write_address_name.clone(),
expr,
});
let expr = c.compile_signal(value, &state_elements, &mut assignments);
node_decls.push(NodeDecl {
net_type: NetType::Wire,
name: mem_decls.write_value_name.clone(),
bit_width: value.bit_width(),
});
assignments.push(Assignment {
target_name: mem_decls.write_value_name.clone(),
expr,
});
let expr = c.compile_signal(enable, &state_elements, &mut assignments);
node_decls.push(NodeDecl {
net_type: NetType::Wire,
name: mem_decls.write_enable_name.clone(),
bit_width: enable.bit_width(),
});
assignments.push(Assignment {
target_name: mem_decls.write_enable_name.clone(),
expr,
});
}
}
for reg in state_elements.regs.values() {
node_decls.push(NodeDecl {
net_type: NetType::Reg,
name: reg.value_name.clone(),
bit_width: reg.data.bit_width,
});
node_decls.push(NodeDecl {
net_type: NetType::Wire,
name: reg.next_name.clone(),
bit_width: reg.data.bit_width,
});
let expr = c.compile_signal(
reg.data.next.borrow().unwrap(),
&state_elements,
&mut assignments,
);
assignments.push(Assignment {
target_name: reg.next_name.clone(),
expr,
});
}
let mut w = code_writer::CodeWriter::new(w);
w.append_line(&format!("module {}(", m.name))?;
w.indent();
// TODO: Make conditional based on the presence of (resetable) state elements
w.append_line("input wire reset_n,")?;
w.append_indent()?;
w.append("input wire clk")?;
if !m.inputs.borrow().is_empty() || !m.outputs.borrow().is_empty() {
w.append(",")?;
w.append_newline()?;
}
w.append_newline()?;
let inputs = m.inputs.borrow();
let num_inputs = inputs.len();
for (i, (name, &input)) in inputs.iter().enumerate() {
w.append_indent()?;
w.append("input wire ")?;
if input.data.bit_width > 1 {
w.append(&format!("[{}:{}] ", input.data.bit_width - 1, 0))?;
}
w.append(name)?;
if !m.outputs.borrow().is_empty() || i < num_inputs - 1 {
w.append(",")?;
}
w.append_newline()?;
}
let outputs = m.outputs.borrow();
let num_outputs = outputs.len();
for (i, (name, &output)) in outputs.iter().enumerate() {
w.append_indent()?;
w.append("output wire ")?;
if output.data.bit_width > 1 {
w.append(&format!("[{}:{}] ", output.data.bit_width - 1, 0))?;
}
w.append(name)?;
if i < num_outputs - 1 {
w.append(",")?;
}
w.append_newline()?;
}
w.append_line(");")?;
w.append_newline()?;
if !node_decls.is_empty() {
for node_decl in node_decls {
node_decl.write(&mut w)?;
}
w.append_newline()?;
}
for (mem, mem_decls) in state_elements.mems.iter() {
w.append_indent()?;
w.append("reg ")?;
if mem.element_bit_width > 1 {
w.append(&format!("[{}:{}] ", mem.element_bit_width - 1, 0))?;
}
w.append(&format!(
"{}[{}:{}];",
mem_decls.mem_name,
0,
(1 << mem.address_bit_width) - 1
))?;
w.append_newline()?;
w.append_newline()?;
if let Some(ref initial_contents) = *mem.initial_contents.borrow() {
w.append_line("initial begin")?;
w.indent();
for (i, element) in initial_contents.iter().enumerate() {
w.append_line(&format!(
"{}[{}] = {}'h{:x};",
mem_decls.mem_name,
i,
mem.element_bit_width,
element.numeric_value()
))?;
}
w.unindent();
w.append_line("end")?;
w.append_newline()?;
}
if !mem_decls.read_signal_names.is_empty() || mem.write_port.borrow().is_some() {
w.append_line("always @(posedge clk) begin")?;
w.indent();
}
for (_, read_signal_names) in mem_decls.read_signal_names.iter() {
w.append_line(&format!("if ({}) begin", read_signal_names.enable_name))?;
w.indent();
w.append_line(&format!(
"{} <= {}[{}];",
read_signal_names.value_name, mem_decls.mem_name, read_signal_names.address_name
))?;
w.unindent();
w.append_line("end")?;
}
if mem.write_port.borrow().is_some() {
w.append_line(&format!("if ({}) begin", mem_decls.write_enable_name))?;
w.indent();
w.append_line(&format!(
"{}[{}] <= {};",
mem_decls.mem_name, mem_decls.write_address_name, mem_decls.write_value_name
))?;
w.unindent();
w.append_line("end")?;
}
if !mem_decls.read_signal_names.is_empty() || mem.write_port.borrow().is_some() {
w.unindent();
w.append_line("end")?;
w.append_newline()?;
}
}
for reg in state_elements.regs.values() {
w.append_indent()?;
w.append("always @(posedge clk")?;
if reg.data.initial_value.borrow().is_some() {
w.append(", negedge reset_n")?;
}
w.append(") begin")?;
w.append_newline()?;
w.indent();
if let Some(ref initial_value) = *reg.data.initial_value.borrow() {
w.append_line("if (~reset_n) begin")?;
w.indent();
w.append_line(&format!(
"{} <= {}'h{:x};",
reg.value_name,
reg.data.bit_width,
initial_value.numeric_value()
))?;
w.unindent();
w.append_line("end")?;
w.append_line("else begin")?;
w.indent();
}
w.append_line(&format!("{} <= {};", reg.value_name, reg.next_name))?;
if reg.data.initial_value.borrow().is_some() {
w.unindent();
w.append_line("end")?;
}
w.unindent();
w.append_line("end")?;
w.append_newline()?;
}
if !assignments.is_empty() {
assignments.write(&mut w)?;
w.append_newline()?;
}
w.unindent();
w.append_line("endmodule")?;
w.append_newline()?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
#[test]
#[should_panic(
expected = "Cannot generate code for module \"A\" because module \"A\" contains an instance of module \"B\" called \"b\" whose input \"i\" is not driven."
)]
fn undriven_instance_input_error() {
let c = Context::new();
let a = c.module("a", "A");
let b = a.module("b", "B");
let _ = b.input("i", 1);
// Panic
generate(a, Vec::new()).unwrap();
}
#[test]
#[should_panic(
expected = "Cannot generate code for module \"A\" because module \"A\" contains a register called \"r\" which is not driven."
)]
fn undriven_register_error1() {
let c = Context::new();
let a = c.module("a", "A");
let _ = a.reg("r", 1);
// Panic
generate(a, Vec::new()).unwrap();
}
#[test]
#[should_panic(
expected = "Cannot generate code for module \"A\" because module \"B\" contains a register called \"r\" which is not driven."
)]
fn undriven_register_error2() {
let c = Context::new();
let a = c.module("a", "A");
let b = a.module("b", "B");
let _ = b.reg("r", 1);
// Panic
generate(a, Vec::new()).unwrap();
}
#[test]
#[should_panic(
expected = "Cannot generate code for module \"A\" because module \"A\" contains a memory called \"m\" which doesn't have any read ports."
)]
fn mem_without_read_ports_error1() {
let c = Context::new();
let a = c.module("a", "A");
let _ = a.mem("m", 1, 1);
// Panic
generate(a, Vec::new()).unwrap();
}
#[test]
#[should_panic(
expected = "Cannot generate code for module \"A\" because module \"B\" contains a memory called \"m\" which doesn't have any read ports."
)]
fn mem_without_read_ports_error2() {
let c = Context::new();
let a = c.module("a", "A");
let b = a.module("b", "B");
let _ = b.mem("m", 1, 1);
// Panic
generate(a, Vec::new()).unwrap();
}
#[test]
#[should_panic(
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."
)]
fn mem_without_initial_contents_or_write_port_error1() {
let c = Context::new();
let a = c.module("a", "A");
let m = a.mem("m", 1, 1);
let _ = m.read_port(a.low(), a.low());
// Panic
generate(a, Vec::new()).unwrap();
}
#[test]
#[should_panic(
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."
)]
fn mem_without_initial_contents_or_write_port_error2() {
let c = Context::new();
let a = c.module("a", "A");
let b = a.module("b", "B");
let m = b.mem("m", 1, 1);
let _ = m.read_port(b.low(), b.low());
// Panic
generate(a, Vec::new()).unwrap();
}
#[test]
#[should_panic(
expected = "Cannot generate code for module \"b\" because module \"a\" contains an output called \"o\" which forms a combinational loop with itself."
)]
fn combinational_loop_error() {
let c = Context::new();
let b = c.module("b", "b");
let a = b.module("a", "a");
let a_i = a.input("i", 1);
let a_o = a.output("o", a_i);
a_i.drive(a_o);
// Panic
generate(b, Vec::new()).unwrap();
}
}
================================================
FILE: sim-tests/Cargo.toml
================================================
[package]
name = "sim-tests"
version = "0.1.0"
authors = ["Jake \"ferris\" Taylor "]
edition = "2018"
license = "MIT OR Apache-2.0"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
kaze = { path = "../kaze" }
[dependencies]
kaze = { path = "../kaze" }
================================================
FILE: sim-tests/build.rs
================================================
use kaze::*;
use std::env;
use std::fs::File;
use std::io::Result;
use std::path::Path;
fn main() -> Result<()> {
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("modules.rs");
let mut file = File::create(&dest_path).unwrap();
let p = Context::new();
sim::generate(
input_masking(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
widest_input(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
add_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
sub_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
mul_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
mul_signed_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
shl_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
shr_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
shr_arithmetic_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
bit_and_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
bit_or_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
bit_xor_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
not_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
reg_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
simple_reg_delay(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
bit_test_module_0(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
bit_test_module_1(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
bits_test_module_0(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
bits_test_module_1(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
repeat_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
concat_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
eq_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
ne_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
lt_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
le_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
gt_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
ge_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
lt_signed_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
le_signed_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
gt_signed_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
ge_signed_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
mux_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
reg_next_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
reg_next_with_default_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
instantiation_test_module_comb(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
instantiation_test_module_reg(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
nested_instantiation_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
mem_test_module_0(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
mem_test_module_1(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
mem_test_module_2(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
sim::generate(
trace_test_module_0(&p),
sim::GenerationOptions {
tracing: true,
..sim::GenerationOptions::default()
},
&mut file,
)?;
sim::generate(
trace_test_module_1(&p),
sim::GenerationOptions {
tracing: true,
..sim::GenerationOptions::default()
},
&mut file,
)?;
sim::generate(
trace_test_module_2(&p),
sim::GenerationOptions {
tracing: true,
..sim::GenerationOptions::default()
},
&mut file,
)?;
sim::generate(
trace_test_module_3(&p),
sim::GenerationOptions {
tracing: true,
..sim::GenerationOptions::default()
},
&mut file,
)?;
sim::generate(
deep_graph_test_module(&p),
sim::GenerationOptions::default(),
&mut file,
)?;
Ok(())
}
fn input_masking<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("input_masking", "InputMasking");
m.output("o", m.input("i", 27));
m
}
fn widest_input<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("widest_input", "WidestInput");
m.output("o", m.input("i", 128));
m
}
fn add_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("add_test_module", "AddTestModule");
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
m.output("o1", i1 + i2);
let i3 = m.input("i3", 16);
let i4 = m.input("i4", 16);
m.output("o2", i3 + i4);
let i5 = m.input("i5", 32);
let i6 = m.input("i6", 32);
m.output("o3", i5 + i6);
let i7 = m.input("i7", 64);
let i8_ = m.input("i8", 64);
m.output("o4", i7 + i8_);
let i9 = m.input("i9", 128);
let i10 = m.input("i10", 128);
m.output("o5", i9 + i10);
let i11 = m.input("i11", 7);
let i12 = m.input("i12", 7);
m.output("o6", i11 + i12);
m
}
fn sub_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("sub_test_module", "SubTestModule");
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
m.output("o1", i1 - i2);
let i3 = m.input("i3", 16);
let i4 = m.input("i4", 16);
m.output("o2", i3 - i4);
let i5 = m.input("i5", 32);
let i6 = m.input("i6", 32);
m.output("o3", i5 - i6);
let i7 = m.input("i7", 64);
let i8_ = m.input("i8", 64);
m.output("o4", i7 - i8_);
let i9 = m.input("i9", 128);
let i10 = m.input("i10", 128);
m.output("o5", i9 - i10);
let i11 = m.input("i11", 7);
let i12 = m.input("i12", 7);
m.output("o6", i11 - i12);
m
}
fn mul_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("mul_test_module", "MulTestModule");
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
m.output("o1", i1 * i2);
let i3 = m.input("i3", 3);
let i4 = m.input("i4", 4);
m.output("o2", i3 * i4);
let i5 = m.input("i5", 32);
let i6 = m.input("i6", 1);
m.output("o3", i5 * i6);
let i7 = m.input("i7", 32);
let i8_ = m.input("i8", 32);
m.output("o4", i7 * i8_);
let i9 = m.input("i9", 64);
let i10 = m.input("i10", 1);
m.output("o5", i9 * i10);
let i11 = m.input("i11", 64);
let i12 = m.input("i12", 64);
m.output("o6", i11 * i12);
let i13 = m.input("i13", 127);
let i14 = m.input("i14", 1);
m.output("o7", i13 * i14);
m
}
fn mul_signed_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("mul_signed_test_module", "MulSignedTestModule");
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
m.output("o1", i1.mul_signed(i2));
let i3 = m.input("i3", 3);
let i4 = m.input("i4", 4);
m.output("o2", i3.mul_signed(i4));
let i5 = m.input("i5", 32);
let i6 = m.input("i6", 1);
m.output("o3", i5.mul_signed(i6));
let i7 = m.input("i7", 32);
let i8_ = m.input("i8", 32);
m.output("o4", i7.mul_signed(i8_));
let i9 = m.input("i9", 64);
let i10 = m.input("i10", 1);
m.output("o5", i9.mul_signed(i10));
let i11 = m.input("i11", 64);
let i12 = m.input("i12", 64);
m.output("o6", i11.mul_signed(i12));
let i13 = m.input("i13", 127);
let i14 = m.input("i14", 1);
m.output("o7", i13.mul_signed(i14));
m
}
fn shl_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("shl_test_module", "ShlTestModule");
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
m.output("o1", i1 << i2);
let i3 = m.input("i3", 16);
let i4 = m.input("i4", 6);
m.output("o2", i3 << i4);
let i5 = m.input("i5", 32);
let i6 = m.input("i6", 32);
m.output("o3", i5 << i6);
let i7 = m.input("i7", 64);
let i8_ = m.input("i8", 64);
m.output("o4", i7 << i8_);
let i9 = m.input("i9", 128);
let i10 = m.input("i10", 128);
m.output("o5", i9 << i10);
let i11 = m.input("i11", 7);
let i12 = m.input("i12", 7);
m.output("o6", i11 << i12);
let i13 = m.input("i13", 32);
let i14 = m.input("i14", 1);
m.output("o7", i13 << i14);
let i15 = m.input("i15", 64);
let i16_ = m.input("i16", 1);
m.output("o8", i15 << i16_);
let i17 = m.input("i17", 128);
let i18 = m.input("i18", 1);
m.output("o9", i17 << i18);
m
}
fn shr_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("shr_test_module", "ShrTestModule");
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
m.output("o1", i1 >> i2);
let i3 = m.input("i3", 16);
let i4 = m.input("i4", 6);
m.output("o2", i3 >> i4);
let i5 = m.input("i5", 32);
let i6 = m.input("i6", 32);
m.output("o3", i5 >> i6);
let i7 = m.input("i7", 64);
let i8_ = m.input("i8", 64);
m.output("o4", i7 >> i8_);
let i9 = m.input("i9", 128);
let i10 = m.input("i10", 128);
m.output("o5", i9 >> i10);
let i11 = m.input("i11", 7);
let i12 = m.input("i12", 7);
m.output("o6", i11 >> i12);
let i13 = m.input("i13", 32);
let i14 = m.input("i14", 1);
m.output("o7", i13 >> i14);
let i15 = m.input("i15", 64);
let i16_ = m.input("i16", 1);
m.output("o8", i15 >> i16_);
let i17 = m.input("i17", 128);
let i18 = m.input("i18", 1);
m.output("o9", i17 >> i18);
m
}
fn shr_arithmetic_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("shr_arithmetic_test_module", "ShrArithmeticTestModule");
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
m.output("o1", i1.shr_arithmetic(i2));
let i3 = m.input("i3", 16);
let i4 = m.input("i4", 6);
m.output("o2", i3.shr_arithmetic(i4));
let i5 = m.input("i5", 32);
let i6 = m.input("i6", 32);
m.output("o3", i5.shr_arithmetic(i6));
let i7 = m.input("i7", 64);
let i8_ = m.input("i8", 64);
m.output("o4", i7.shr_arithmetic(i8_));
let i9 = m.input("i9", 128);
let i10 = m.input("i10", 128);
m.output("o5", i9.shr_arithmetic(i10));
let i11 = m.input("i11", 7);
let i12 = m.input("i12", 7);
m.output("o6", i11.shr_arithmetic(i12));
let i13 = m.input("i13", 32);
let i14 = m.input("i14", 1);
m.output("o7", i13.shr_arithmetic(i14));
let i15 = m.input("i15", 64);
let i16_ = m.input("i16", 1);
m.output("o8", i15.shr_arithmetic(i16_));
let i17 = m.input("i17", 128);
let i18 = m.input("i18", 1);
m.output("o9", i17.shr_arithmetic(i18));
m
}
fn bit_and_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("bit_and_test_module", "BitAndTestModule");
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
m.output("o", i1 & i2);
m
}
fn bit_or_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("bit_or_test_module", "BitOrTestModule");
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
m.output("o", i1 | i2);
m
}
fn bit_xor_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("bit_xor_test_module", "BitXorTestModule");
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
m.output("o", i1 ^ i2);
m
}
fn not_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("not_test_module", "NotTestModule");
let i = m.input("i", 4);
m.output("o", !i);
m
}
fn reg_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("reg_test_module", "RegTestModule");
let r1 = m.reg("r1", 32);
r1.default_value(0u32);
r1.drive_next(m.input("i1", 32));
m.output("o1", r1);
let r2 = m.reg("r2", 32);
r2.drive_next(m.input("i2", 32));
m.output("o2", r2);
m
}
fn simple_reg_delay<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("simple_reg_delay", "SimpleRegDelay");
let r1 = m.reg("r1", 100);
r1.default_value(0u32);
r1.drive_next(m.input("i", 100));
let r2 = m.reg("r2", 100);
r2.default_value(0u32);
r2.drive_next(r1);
let r3 = m.reg("r3", 100);
r3.default_value(0u32);
r3.drive_next(r2);
m.output("o", r3);
m
}
fn bit_test_module_0<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("bit_test_module_0", "BitTestModule0");
let i = m.input("i", 1);
m.output("o", i.bit(0));
m
}
fn bit_test_module_1<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("bit_test_module_1", "BitTestModule1");
let i = m.input("i", 4);
m.output("o0", i.bit(0));
m.output("o1", i.bit(1));
m.output("o2", i.bit(2).bit(0));
m.output("o3", i.bit(3));
m
}
fn bits_test_module_0<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("bits_test_module_0", "BitsTestModule0");
let i = m.input("i", 4);
m.output("o210", i.bits(2, 0));
m.output("o321", i.bits(3, 1).bits(2, 0));
m.output("o10", i.bits(1, 0).bits(1, 0));
m.output("o32", i.bits(3, 2));
m.output("o2", i.bits(2, 2));
m
}
fn bits_test_module_1<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("bits_test_module_1", "BitsTestModule1");
let i = m.input("i", 128);
m.output("o0", i.bits(127, 0));
m.output("o1", i.bits(126, 0));
m.output("o2", i.bits(127, 64));
m.output("o3", i.bits(63, 0));
m.output("o4", i.bits(127, 96));
m.output("o5", i.bits(95, 64));
m.output("o6", i.bits(63, 32).bits(31, 0));
m.output("o7", i.bits(31, 0));
m.output("o8", i.bits(123, 60));
m.output("o9", i.bits(99, 99).bits(0, 0).bits(0, 0));
m.output("o10", i.bits(63, 48));
m.output("o11", i.bits(63, 0).bits(31, 0).bits(15, 0).bits(0, 0));
m
}
fn repeat_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("repeat_test_module", "RepeatTestModule");
let i = m.input("i", 4);
m.output("o0", i.repeat(1));
m.output("o1", i.repeat(2));
m.output("o2", i.repeat(5));
m.output("o3", i.repeat(8));
m.output("o4", i.repeat(16));
m.output("o5", i.repeat(32));
m.output("o6", i.bit(0).repeat(3));
m.output("o7", i.bit(0).repeat(64));
m.output("o8", i.bit(0).repeat(128));
m
}
fn concat_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("concat_test_module", "ConcatTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
let i3 = m.input("i3", 32);
m.output("o0", i1.concat(i2));
m.output("o1", i2.concat(i1));
m.output("o2", m.low().concat(i1));
m.output("o3", m.high().concat(i1));
m.output("o4", i2.bit(0).concat(i1));
m.output("o5", i3.concat(i3));
m.output("o6", i3.concat(i3).concat(i3).concat(i3));
m
}
fn eq_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("eq_test_module", "EqTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
m.output("o1", i1.eq(i2));
m.output("o2", i1.bit(0).eq(i2.bit(0)));
m
}
fn ne_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("ne_test_module", "NeTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
m.output("o1", i1.ne(i2));
m.output("o2", i1.bit(0).ne(i2.bit(0)));
m
}
fn lt_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("lt_test_module", "LtTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
m.output("o1", i1.lt(i2));
m.output("o2", i1.bit(0).lt(i2.bit(0)));
m
}
fn le_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("le_test_module", "LeTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
m.output("o1", i1.le(i2));
m.output("o2", i1.bit(0).le(i2.bit(0)));
m
}
fn gt_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("gt_test_module", "GtTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
m.output("o1", i1.gt(i2));
m.output("o2", i1.bit(0).gt(i2.bit(0)));
m
}
fn ge_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("ge_test_module", "GeTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
m.output("o1", i1.ge(i2));
m.output("o2", i1.bit(0).ge(i2.bit(0)));
m
}
fn lt_signed_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("lt_signed_test_module", "LtSignedTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
m.output("o1", i1.lt_signed(i2));
m.output("o2", i1.bits(1, 0).lt_signed(i2.bits(1, 0)));
m
}
fn le_signed_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("le_signed_test_module", "LeSignedTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
m.output("o1", i1.le_signed(i2));
m.output("o2", i1.bits(1, 0).le_signed(i2.bits(1, 0)));
m
}
fn gt_signed_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("gt_signed_test_module", "GtSignedTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
m.output("o1", i1.gt_signed(i2));
m.output("o2", i1.bits(1, 0).gt_signed(i2.bits(1, 0)));
m
}
fn ge_signed_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("ge_signed_test_module", "GeSignedTestModule");
let i1 = m.input("i1", 4);
let i2 = m.input("i2", 4);
m.output("o1", i1.ge_signed(i2));
m.output("o2", i1.bits(1, 0).ge_signed(i2.bits(1, 0)));
m
}
fn mux_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("mux_test_module", "MuxTestModule");
let invert = m.input("invert", 1);
let i1 = m.input("i1", 1);
let i2 = m.input("i2", 1);
let (i1, i2) = if_(m.high(), {
let i1 = !i1;
let i1 = i1;
let i1 = !i1;
if_(invert, {
let i2 = i2;
if_(!m.low(), {
let i1 = !i1;
let i1 = i1;
let i2 = !i2;
(i1, i2)
})
.else_((i1, i2))
})
.else_((i1, i2))
})
.else_((m.low(), m.low()));
m.output("o1", i1);
m.output("o2", i2);
m
}
fn reg_next_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("reg_next_test_module", "RegNextTestModule");
let i = m.input("i", 1);
m.output("o1", i);
m.output("o2", i.reg_next("i_delayed"));
m
}
fn reg_next_with_default_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module(
"reg_next_with_default_test_module",
"RegNextWithDefaultTestModule",
);
let i = m.input("i", 1);
m.output("o1", i);
m.output("o2", i.reg_next_with_default("i_delayed", true));
m
}
fn instantiation_test_module_comb<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
// TODO: Do we want to restructure all of these test modules to follow this pattern?
struct InstantiationTestModuleCombInner<'a> {
i1: &'a Input<'a>,
i2: &'a Input<'a>,
o: &'a Output<'a>,
}
impl<'a> InstantiationTestModuleCombInner<'a> {
fn new(
instance_name: impl Into,
p: &'a impl ModuleParent<'a>,
) -> InstantiationTestModuleCombInner<'a> {
let m = p.module(instance_name, "InstantiationTestModuleCombInner");
let i1 = m.input("i1", 32);
let i2 = m.input("i2", 32);
let o = m.output("o", i1 & i2);
InstantiationTestModuleCombInner { i1, i2, o }
}
}
let m = p.module(
"instantiation_test_module_comb",
"InstantiationTestModuleComb",
);
let inner1 = InstantiationTestModuleCombInner::new("inner1", m);
inner1.i1.drive(m.input("i1", 32));
inner1.i2.drive(m.input("i2", 32));
let inner2 = InstantiationTestModuleCombInner::new("inner2", m);
inner2.i1.drive(m.input("i3", 32));
inner2.i2.drive(m.input("i4", 32));
let inner3 = InstantiationTestModuleCombInner::new("inner3", m);
inner3.i1.drive(inner1.o);
inner3.i2.drive(inner2.o);
m.output("o", inner3.o);
m
}
fn instantiation_test_module_reg<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
struct InstantiationTestModuleRegInner<'a> {
i1: &'a Input<'a>,
i2: &'a Input<'a>,
o: &'a Output<'a>,
}
impl<'a> InstantiationTestModuleRegInner<'a> {
fn new(
instance_name: impl Into,
p: &'a impl ModuleParent<'a>,
) -> InstantiationTestModuleRegInner<'a> {
let m = p.module(instance_name, "InstantiationTestModuleRegInner");
let i1 = m.input("i1", 32);
let i2 = m.input("i2", 32);
let r = m.reg("r", 32);
r.default_value(0u32);
r.drive_next(i1 & i2);
let o = m.output("o", r);
InstantiationTestModuleRegInner { i1, i2, o }
}
}
let m = p.module(
"instantiation_test_module_reg",
"InstantiationTestModuleReg",
);
let inner1 = InstantiationTestModuleRegInner::new("inner1", m);
inner1.i1.drive(m.input("i1", 32));
inner1.i2.drive(m.input("i2", 32));
let inner2 = InstantiationTestModuleRegInner::new("inner2", m);
inner2.i1.drive(m.input("i3", 32));
inner2.i2.drive(m.input("i4", 32));
let inner3 = InstantiationTestModuleRegInner::new("inner3", m);
inner3.i1.drive(inner1.o);
inner3.i2.drive(inner2.o);
m.output("o", inner3.o);
m
}
fn nested_instantiation_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
struct NestedInstantiationTestModuleInnerInner<'a> {
i: &'a Input<'a>,
o: &'a Output<'a>,
}
impl<'a> NestedInstantiationTestModuleInnerInner<'a> {
fn new(
instance_name: impl Into,
p: &'a impl ModuleParent<'a>,
) -> NestedInstantiationTestModuleInnerInner<'a> {
let m = p.module(instance_name, "NestedInstantiationTestModuleInnerInner");
let i = m.input("i", 32);
let o = m.output("o", i);
NestedInstantiationTestModuleInnerInner { i, o }
}
}
struct NestedInstantiationTestModuleInner<'a> {
i1: &'a Input<'a>,
i2: &'a Input<'a>,
o: &'a Output<'a>,
}
impl<'a> NestedInstantiationTestModuleInner<'a> {
fn new(
instance_name: impl Into,
p: &'a impl ModuleParent<'a>,
) -> NestedInstantiationTestModuleInner<'a> {
let m = p.module(instance_name, "NestedInstantiationTestModuleInner");
let inner = NestedInstantiationTestModuleInnerInner::new("inner", m);
let i1 = m.input("i1", 32);
let i2 = m.input("i2", 32);
inner.i.drive(i1 & i2);
let o = m.output("o", inner.o);
NestedInstantiationTestModuleInner { i1, i2, o }
}
}
let m = p.module(
"nested_instantiation_test_module",
"NestedInstantiationTestModule",
);
let inner1 = NestedInstantiationTestModuleInner::new("inner1", m);
inner1.i1.drive(m.input("i1", 32));
inner1.i2.drive(m.input("i2", 32));
let inner2 = NestedInstantiationTestModuleInner::new("inner2", m);
inner2.i1.drive(m.input("i3", 32));
inner2.i2.drive(m.input("i4", 32));
let inner3 = NestedInstantiationTestModuleInner::new("inner3", m);
inner3.i1.drive(inner1.o);
inner3.i2.drive(inner2.o);
m.output("o", inner3.o);
m
}
fn mem_test_module_0<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("mem_test_module_0", "MemTestModule0");
// No initial contents, single write port, single read port
let mem = m.mem("mem", 1, 4);
mem.write_port(
m.input("write_addr", 1),
m.input("write_value", 4),
m.input("write_enable", 1),
);
m.output(
"read_data",
mem.read_port(m.input("read_addr", 1), m.input("read_enable", 1)),
);
m
}
fn mem_test_module_1<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("mem_test_module_1", "MemTestModule1");
// Initial contents, no write ports, single read port
let mem = m.mem("mem", 2, 32);
mem.initial_contents(&[0xfadebabeu32, 0xdeadbeefu32, 0xabadcafeu32, 0xabad1deau32]);
m.output(
"read_data",
mem.read_port(m.input("read_addr", 2), m.input("read_enable", 1)),
);
m
}
fn mem_test_module_2<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("mem_test_module_2", "MemTestModule2");
// No initial contents, single write port, single read port
let mem = m.mem("mem", 1, 1);
mem.write_port(
m.input("write_addr", 1),
m.input("write_value", 1),
m.input("write_enable", 1),
);
m.output(
"read_data",
mem.read_port(m.input("read_addr", 1), m.input("read_enable", 1)),
);
m
}
fn trace_test_module_0<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("trace_test_module_0", "TraceTestModule0");
m.output("o0", m.input("i0", 1));
m.output("o1", m.input("i1", 2));
m.output("o2", m.input("i2", 32));
m.output("o3", m.input("i3", 64));
m.output("o4", m.input("i4", 128));
m
}
fn trace_test_module_1<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("trace_test_module_1", "TraceTestModule1");
let r1 = m.reg("r1", 32);
r1.default_value(0u32);
r1.drive_next(m.input("i1", 32));
m.output("o1", r1);
let r2 = m.reg("r2", 32);
r2.drive_next(m.input("i2", 32));
m.output("o2", r2);
m
}
fn trace_test_module_2<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
struct TraceTestModule2Inner<'a> {
i1: &'a Input<'a>,
i2: &'a Input<'a>,
o: &'a Output<'a>,
}
impl<'a> TraceTestModule2Inner<'a> {
fn new(
instance_name: impl Into,
p: &'a impl ModuleParent<'a>,
) -> TraceTestModule2Inner<'a> {
let m = p.module(instance_name, "TraceTestModule2Inner");
let i1 = m.input("i1", 32);
let i2 = m.input("i2", 32);
let r = m.reg("r", 32);
r.default_value(0u32);
r.drive_next(i1 & i2);
let o = m.output("o", r);
TraceTestModule2Inner { i1, i2, o }
}
}
let m = p.module("trace_test_module_2", "TraceTestModule2");
let inner1 = TraceTestModule2Inner::new("inner1", m);
inner1.i1.drive(m.input("i1", 32));
inner1.i2.drive(m.input("i2", 32));
let inner2 = TraceTestModule2Inner::new("inner2", m);
inner2.i1.drive(m.input("i3", 32));
inner2.i2.drive(m.input("i4", 32));
let inner3 = TraceTestModule2Inner::new("inner3", m);
inner3.i1.drive(inner1.o);
inner3.i2.drive(inner2.o);
m.output("o", inner3.o);
m
}
fn trace_test_module_3<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("trace_test_module_3", "TraceTestModule3");
// No initial contents, single write port, single read port
let mem = m.mem("mem", 1, 4);
mem.write_port(
m.input("write_addr", 1),
m.input("write_value", 4),
m.input("write_enable", 1),
);
m.output(
"read_data",
mem.read_port(m.input("read_addr", 1), m.input("read_enable", 1)),
);
m
}
fn deep_graph_test_module<'a>(p: &'a impl ModuleParent<'a>) -> &Module<'a> {
let m = p.module("deep_graph_test_module", "DeepGraphTestModule");
let mut x: &'a dyn Signal<'a> = m.input("i", 1);
for _ in 0..3001 {
x = !x;
}
m.output("o", x);
m
}
================================================
FILE: sim-tests/src/lib.rs
================================================
#[cfg(test)]
mod tests {
extern crate kaze;
mod modules {
include!(concat!(env!("OUT_DIR"), "/modules.rs"));
}
use modules::*;
use kaze::runtime::tracing::*;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::fmt;
use std::io;
use std::rc::Rc;
struct CodeWriter<'a, 'b> {
f: &'a mut fmt::Formatter<'b>,
indent_level: u32,
}
impl<'a, 'b> CodeWriter<'a, 'b> {
fn new(f: &'a mut fmt::Formatter<'b>) -> CodeWriter<'a, 'b> {
CodeWriter { f, indent_level: 0 }
}
fn indent(&mut self) {
self.indent_level += 1;
}
fn unindent(&mut self) {
if self.indent_level == 0 {
panic!("Indent level underflow");
}
self.indent_level -= 1;
}
fn append_indent(&mut self) -> fmt::Result {
for _ in 0..self.indent_level {
write!(self.f, " ")?;
}
Ok(())
}
fn append_newline(&mut self) -> fmt::Result {
writeln!(self.f, "")?;
Ok(())
}
fn append(&mut self, s: &str) -> fmt::Result {
write!(self.f, "{}", s)?;
Ok(())
}
fn append_line(&mut self, s: &str) -> fmt::Result {
self.append_indent()?;
self.append(s)?;
self.append_newline()?;
Ok(())
}
}
#[derive(Eq, PartialEq)]
struct Capture {
root: Option<(&'static str, CaptureModule)>,
}
impl fmt::Debug for Capture {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut w = CodeWriter::new(f);
let (name, module) = self.root.as_ref().unwrap();
fn print_module(w: &mut CodeWriter, name: &str, module: &CaptureModule) -> fmt::Result {
w.append_line(&format!("module {}:", name))?;
w.indent();
w.append_line(&format!("children:"))?;
w.indent();
for (name, module) in module.children.iter() {
print_module(w, name, module)?;
}
w.unindent();
w.append_line(&format!("signals:"))?;
w.indent();
for (name, signal) in module.signals.iter() {
w.append_line(&format!(
"{}: {} bit(s) ({:?})",
name, signal.bit_width, signal.type_
))?;
w.indent();
for (timestamp, value) in signal.values.borrow().iter() {
w.append_line(&format!("{}: {:?}", timestamp, value))?;
}
w.unindent();
}
w.unindent();
w.unindent();
Ok(())
}
print_module(&mut w, name, module)?;
Ok(())
}
}
impl Capture {
fn new() -> Capture {
Capture { root: None }
}
}
#[derive(Eq, PartialEq)]
struct CaptureModule {
children: BTreeMap<&'static str, CaptureModule>,
signals: BTreeMap<&'static str, Rc>,
}
#[derive(Debug, Eq, PartialEq)]
struct CaptureSignal {
bit_width: u32,
type_: TraceValueType,
values: RefCell>,
}
struct CaptureTrace<'a> {
capture: &'a mut Capture,
module_stack: Vec<(&'static str, CaptureModule)>,
time_stamp: u64,
}
impl<'a> CaptureTrace<'a> {
fn new(capture: &'a mut Capture) -> CaptureTrace<'a> {
CaptureTrace {
capture,
module_stack: Vec::new(),
time_stamp: 0,
}
}
}
impl<'a> Trace for CaptureTrace<'a> {
type SignalId = Rc;
fn push_module(&mut self, name: &'static str) -> io::Result<()> {
self.module_stack.push((
name,
CaptureModule {
children: BTreeMap::new(),
signals: BTreeMap::new(),
},
));
Ok(())
}
fn pop_module(&mut self) -> io::Result<()> {
let (name, current_module) = self.module_stack.pop().unwrap();
if self.module_stack.is_empty() {
self.capture.root = Some((name, current_module));
} else {
self.module_stack
.last_mut()
.unwrap()
.1
.children
.insert(name, current_module);
}
Ok(())
}
fn add_signal(
&mut self,
name: &'static str,
bit_width: u32,
type_: TraceValueType,
) -> io::Result {
let (_, current_module) = self.module_stack.last_mut().unwrap();
let ret = Rc::new(CaptureSignal {
bit_width,
type_,
values: RefCell::new(Vec::new()),
});
current_module.signals.insert(name, ret.clone());
Ok(ret)
}
fn update_time_stamp(&mut self, time_stamp: u64) -> io::Result<()> {
self.time_stamp = time_stamp;
Ok(())
}
fn update_signal(
&mut self,
signal_id: &Self::SignalId,
value: TraceValue,
) -> io::Result<()> {
signal_id.values.borrow_mut().push((self.time_stamp, value));
Ok(())
}
}
#[test]
fn input_masking() {
let mut m = InputMasking::new();
m.i = 0xffffffff;
m.prop();
assert_eq!(m.o, 0x07ffffff);
}
#[test]
fn widest_input() {
let mut m = WidestInput::new();
m.i = 0xfadebabedeadbeefabad1deabadc0de5;
m.prop();
assert_eq!(m.o, 0xfadebabedeadbeefabad1deabadc0de5);
}
#[test]
fn add_test_module() {
let mut m = AddTestModule::new();
m.i1 = false;
m.i2 = false;
m.prop();
assert_eq!(m.o1, false);
m.i1 = true;
m.i2 = false;
m.prop();
assert_eq!(m.o1, true);
m.i1 = false;
m.i2 = true;
m.prop();
assert_eq!(m.o1, true);
m.i1 = true;
m.i2 = true;
m.prop();
assert_eq!(m.o1, false);
m.i3 = 1;
m.i4 = 2;
m.prop();
assert_eq!(m.o2, 3);
m.i3 = 0xffffu32;
m.i4 = 0x0002u32;
m.prop();
assert_eq!(m.o2, 0x0001u32);
m.i5 = 0xfade0000u32;
m.i6 = 0x0000babeu32;
m.prop();
assert_eq!(m.o3, 0xfadebabeu32);
m.i5 = 0xffffffffu32;
m.i6 = 0x00000002u32;
m.prop();
assert_eq!(m.o3, 0x00000001u32);
m.i7 = 0xfade00000000beefu64;
m.i8 = 0x0000babedead0000u64;
m.prop();
assert_eq!(m.o4, 0xfadebabedeadbeefu64);
m.i7 = 0xffffffffffffffffu64;
m.i8 = 0x0000000000000002u64;
m.prop();
assert_eq!(m.o4, 0x0000000000000001u64);
m.i9 = 0xfade00000000beef0000000000000001u128;
m.i10 = 0x0000babedead00000000000000000002u128;
m.prop();
assert_eq!(m.o5, 0xfadebabedeadbeef0000000000000003u128);
m.i9 = 0xffffffffffffffffffffffffffffffffu128;
m.i10 = 0x00000000000000000000000000000002u128;
m.prop();
assert_eq!(m.o5, 0x00000000000000000000000000000001u128);
m.i11 = 127u32;
m.i12 = 2u32;
m.prop();
assert_eq!(m.o6, 1u32);
}
#[test]
fn sub_test_module() {
let mut m = SubTestModule::new();
m.i1 = false;
m.i2 = false;
m.prop();
assert_eq!(m.o1, false);
m.i1 = true;
m.i2 = false;
m.prop();
assert_eq!(m.o1, true);
m.i1 = false;
m.i2 = true;
m.prop();
assert_eq!(m.o1, true);
m.i1 = true;
m.i2 = true;
m.prop();
assert_eq!(m.o1, false);
m.i3 = 3;
m.i4 = 2;
m.prop();
assert_eq!(m.o2, 1);
m.i3 = 0x0001u32;
m.i4 = 0x0002u32;
m.prop();
assert_eq!(m.o2, 0xffffu32);
m.i5 = 0xfadebabeu32;
m.i6 = 0x0000babeu32;
m.prop();
assert_eq!(m.o3, 0xfade0000u32);
m.i5 = 0x00000001u32;
m.i6 = 0x00000002u32;
m.prop();
assert_eq!(m.o3, 0xffffffffu32);
m.i7 = 0xfadebabedeadbeefu64;
m.i8 = 0x0000babedead0000u64;
m.prop();
assert_eq!(m.o4, 0xfade00000000beefu64);
m.i7 = 0x0000000000000001u64;
m.i8 = 0x0000000000000002u64;
m.prop();
assert_eq!(m.o4, 0xffffffffffffffffu64);
m.i9 = 0xfadebabedeadbeef0000000000000003u128;
m.i10 = 0x0000babedead00000000000000000002u128;
m.prop();
assert_eq!(m.o5, 0xfade00000000beef0000000000000001u128);
m.i9 = 0x00000000000000000000000000000001u128;
m.i10 = 0x00000000000000000000000000000002u128;
m.prop();
assert_eq!(m.o5, 0xffffffffffffffffffffffffffffffffu128);
m.i11 = 1u32;
m.i12 = 2u32;
m.prop();
assert_eq!(m.o6, 127u32);
}
#[test]
fn mul_test_module() {
let mut m = MulTestModule::new();
m.i1 = false;
m.i2 = false;
m.prop();
assert_eq!(m.o1, 0);
m.i1 = true;
m.i2 = false;
m.prop();
assert_eq!(m.o1, 0);
m.i1 = false;
m.i2 = true;
m.prop();
assert_eq!(m.o1, 0);
m.i1 = true;
m.i2 = true;
m.prop();
assert_eq!(m.o1, 1);
m.i3 = 7;
m.i4 = 15;
m.prop();
assert_eq!(m.o2, 105);
m.i5 = 0xffffffff;
m.i6 = true;
m.prop();
assert_eq!(m.o3, 0xffffffff);
m.i7 = 0xffffffff;
m.i8 = 0xffffffff;
m.prop();
assert_eq!(m.o4, 0xfffffffe00000001);
m.i9 = 0xfadebabedeadbeef;
m.i10 = true;
m.prop();
assert_eq!(m.o5, 0xfadebabedeadbeef);
m.i11 = 0xfadebabedeadbeef;
m.i12 = 0xabad1deacafeb00b;
m.prop();
assert_eq!(m.o6, 0xa83c_6c93_0366_3080_b2ff_e1cd_0bdd_8445);
m.i13 = 0x7adebabedeadbeefabad1deacafeb00b;
m.i14 = false;
m.prop();
assert_eq!(m.o7, 0);
m.i14 = true;
m.prop();
assert_eq!(m.o7, 0x7adebabedeadbeefabad1deacafeb00b);
}
#[test]
fn mul_signed_test_module() {
let mut m = MulSignedTestModule::new();
m.i1 = false;
m.i2 = false;
m.prop();
assert_eq!(m.o1, 0);
m.i1 = true; // -1
m.i2 = false;
m.prop();
assert_eq!(m.o1, 0);
m.i1 = false;
m.i2 = true; // -1
m.prop();
assert_eq!(m.o1, 0);
m.i1 = true; // -1
m.i2 = true; // -1
m.prop();
assert_eq!(m.o1, 1);
m.i3 = 7; // -1
m.i4 = 15; // -1
m.prop();
assert_eq!(m.o2, 1);
m.i5 = 0xffffffff; // -1
m.i6 = true; // 1
m.prop();
assert_eq!(m.o3, 1);
m.i7 = 0xffffffff; // -1
m.i8 = 0x7fffffff;
m.prop();
assert_eq!(m.o4, 0xffffffff80000001);
m.i9 = 0xfadebabedeadbeef; // negative something
m.i10 = true; // -1
m.prop();
assert_eq!(m.o5, 0x0521454121524111);
m.i11 = 0xfadebabedeadbeef; // negative something
m.i12 = 0xabad1deacafeb00b; // negative something
m.prop();
assert_eq!(m.o6, 0x1b093e959b9c186b2ffe1cd0bdd8445);
m.i13 = 0x7adebabedeadbeefabad1deacafeb00b;
m.i14 = false;
m.prop();
assert_eq!(m.o7, 0);
m.i14 = true; // -1
m.prop();
assert_eq!(m.o7, 0x5214541215241105452e21535014ff5);
}
#[test]
fn shl_test_module() {
let mut m = ShlTestModule::new();
m.i1 = false;
m.i2 = false;
m.prop();
assert_eq!(m.o1, false);
m.i1 = true;
m.i2 = false;
m.prop();
assert_eq!(m.o1, true);
m.i1 = false;
m.i2 = true;
m.prop();
assert_eq!(m.o1, false);
m.i1 = true;
m.i2 = true;
m.prop();
assert_eq!(m.o1, false);
m.i3 = 0xffff;
m.i4 = 0;
m.prop();
assert_eq!(m.o2, 0xffff);
m.i3 = 0xffff;
m.i4 = 4;
m.prop();
assert_eq!(m.o2, 0xfff0);
m.i3 = 0xffff;
m.i4 = 8;
m.prop();
assert_eq!(m.o2, 0xff00);
m.i3 = 0xffff;
m.i4 = 16;
m.prop();
assert_eq!(m.o2, 0);
m.i3 = 0xffff;
m.i4 = 3;
m.prop();
assert_eq!(m.o2, 0xfff8);
m.i3 = 0xffff;
m.i4 = 31;
m.prop();
assert_eq!(m.o2, 0);
m.i3 = 0xffff;
m.i4 = 32;
m.prop();
assert_eq!(m.o2, 0);
m.i5 = 0xfadebabe;
m.i6 = 4;
m.prop();
assert_eq!(m.o3, 0xadebabe0);
m.i5 = 0xdeadbeef;
m.i6 = 16;
m.prop();
assert_eq!(m.o3, 0xbeef0000);
m.i5 = 0xdeadbeef;
m.i6 = std::u32::MAX;
m.prop();
assert_eq!(m.o3, 0);
m.i7 = 0xfadebabedeadbeef;
m.i8 = 4;
m.prop();
assert_eq!(m.o4, 0xadebabedeadbeef0);
m.i7 = 0xfadebabedeadbeef;
m.i8 = 16;
m.prop();
assert_eq!(m.o4, 0xbabedeadbeef0000);
m.i7 = 0xfadebabedeadbeef;
m.i8 = std::u64::MAX;
m.prop();
assert_eq!(m.o4, 0);
m.i9 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i10 = 4;
m.prop();
assert_eq!(m.o5, 0xaaaaaaa55555555fadebabedeadbeef0);
m.i9 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i10 = 16;
m.prop();
assert_eq!(m.o5, 0xaaaa55555555fadebabedeadbeef0000);
m.i9 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i10 = std::u128::MAX;
m.prop();
assert_eq!(m.o5, 0);
m.i11 = 0x7f;
m.i12 = 1;
m.prop();
assert_eq!(m.o6, 0x7e);
m.i11 = 0x7f;
m.i12 = 4;
m.prop();
assert_eq!(m.o6, 0x70);
m.i11 = 0x7f;
m.i12 = 6;
m.prop();
assert_eq!(m.o6, 0x40);
m.i11 = 0x7f;
m.i12 = 7;
m.prop();
assert_eq!(m.o6, 0);
m.i13 = 0xdeadbeef;
m.i14 = false;
m.prop();
assert_eq!(m.o7, 0xdeadbeef);
m.i13 = 0xdeadbeef;
m.i14 = true;
m.prop();
assert_eq!(m.o7, 0xbd5b7dde);
m.i15 = 0xfadebabedeadbeef;
m.i16 = false;
m.prop();
assert_eq!(m.o8, 0xfadebabedeadbeef);
m.i15 = 0xfadebabedeadbeef;
m.i16 = true;
m.prop();
assert_eq!(m.o8, 0xf5bd757dbd5b7dde);
m.i17 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i18 = false;
m.prop();
assert_eq!(m.o9, 0xaaaaaaaa55555555fadebabedeadbeef);
m.i17 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i18 = true;
m.prop();
assert_eq!(m.o9, 0x55555554aaaaaaabf5bd757dbd5b7dde);
}
#[test]
fn shr_test_module() {
let mut m = ShrTestModule::new();
m.i1 = false;
m.i2 = false;
m.prop();
assert_eq!(m.o1, false);
m.i1 = true;
m.i2 = false;
m.prop();
assert_eq!(m.o1, true);
m.i1 = false;
m.i2 = true;
m.prop();
assert_eq!(m.o1, false);
m.i1 = true;
m.i2 = true;
m.prop();
assert_eq!(m.o1, false);
m.i3 = 0xffff;
m.i4 = 0;
m.prop();
assert_eq!(m.o2, 0xffff);
m.i3 = 0xffff;
m.i4 = 4;
m.prop();
assert_eq!(m.o2, 0x0fff);
m.i3 = 0xffff;
m.i4 = 8;
m.prop();
assert_eq!(m.o2, 0x00ff);
m.i3 = 0xffff;
m.i4 = 16;
m.prop();
assert_eq!(m.o2, 0);
m.i3 = 0xffff;
m.i4 = 3;
m.prop();
assert_eq!(m.o2, 0x1fff);
m.i3 = 0xffff;
m.i4 = 31;
m.prop();
assert_eq!(m.o2, 0);
m.i3 = 0xffff;
m.i4 = 32;
m.prop();
assert_eq!(m.o2, 0);
m.i5 = 0xfadebabe;
m.i6 = 4;
m.prop();
assert_eq!(m.o3, 0x0fadebab);
m.i5 = 0xdeadbeef;
m.i6 = 16;
m.prop();
assert_eq!(m.o3, 0x0000dead);
m.i5 = 0xdeadbeef;
m.i6 = std::u32::MAX;
m.prop();
assert_eq!(m.o3, 0);
m.i7 = 0xfadebabedeadbeef;
m.i8 = 4;
m.prop();
assert_eq!(m.o4, 0x0fadebabedeadbee);
m.i7 = 0xfadebabedeadbeef;
m.i8 = 16;
m.prop();
assert_eq!(m.o4, 0x0000fadebabedead);
m.i7 = 0xfadebabedeadbeef;
m.i8 = std::u64::MAX;
m.prop();
assert_eq!(m.o4, 0);
m.i9 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i10 = 4;
m.prop();
assert_eq!(m.o5, 0x0aaaaaaaa55555555fadebabedeadbee);
m.i9 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i10 = 16;
m.prop();
assert_eq!(m.o5, 0x0000aaaaaaaa55555555fadebabedead);
m.i9 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i10 = std::u128::MAX;
m.prop();
assert_eq!(m.o5, 0);
m.i11 = 0x7f;
m.i12 = 1;
m.prop();
assert_eq!(m.o6, 0x3f);
m.i11 = 0x7f;
m.i12 = 4;
m.prop();
assert_eq!(m.o6, 0x07);
m.i11 = 0x7f;
m.i12 = 6;
m.prop();
assert_eq!(m.o6, 0x01);
m.i11 = 0x7f;
m.i12 = 7;
m.prop();
assert_eq!(m.o6, 0);
m.i13 = 0xdeadbeef;
m.i14 = false;
m.prop();
assert_eq!(m.o7, 0xdeadbeef);
m.i13 = 0xdeadbeef;
m.i14 = true;
m.prop();
assert_eq!(m.o7, 0x6f56df77);
m.i15 = 0xfadebabedeadbeef;
m.i16 = false;
m.prop();
assert_eq!(m.o8, 0xfadebabedeadbeef);
m.i15 = 0xfadebabedeadbeef;
m.i16 = true;
m.prop();
assert_eq!(m.o8, 0x7d6f5d5f6f56df77);
m.i17 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i18 = false;
m.prop();
assert_eq!(m.o9, 0xaaaaaaaa55555555fadebabedeadbeef);
m.i17 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i18 = true;
m.prop();
assert_eq!(m.o9, 0x555555552aaaaaaafd6f5d5f6f56df77);
}
#[test]
fn shr_arithmetic_test_module() {
let mut m = ShrArithmeticTestModule::new();
m.i1 = false;
m.i2 = false;
m.prop();
assert_eq!(m.o1, false);
m.i1 = true;
m.i2 = false;
m.prop();
assert_eq!(m.o1, true);
m.i1 = false;
m.i2 = true;
m.prop();
assert_eq!(m.o1, false);
m.i1 = true;
m.i2 = true;
m.prop();
assert_eq!(m.o1, true);
m.i3 = 0x7fff;
m.i4 = 0;
m.prop();
assert_eq!(m.o2, 0x7fff);
m.i3 = 0x7fff;
m.i4 = 4;
m.prop();
assert_eq!(m.o2, 0x07ff);
m.i3 = 0x7fff;
m.i4 = 8;
m.prop();
assert_eq!(m.o2, 0x007f);
m.i3 = 0x7fff;
m.i4 = 16;
m.prop();
assert_eq!(m.o2, 0x0000);
m.i3 = 0x7fff;
m.i4 = 3;
m.prop();
assert_eq!(m.o2, 0x0fff);
m.i3 = 0x7fff;
m.i4 = 31;
m.prop();
assert_eq!(m.o2, 0x0000);
m.i3 = 0x7fff;
m.i4 = 32;
m.prop();
assert_eq!(m.o2, 0x0000);
m.i3 = 0xffff;
m.i4 = 0;
m.prop();
assert_eq!(m.o2, 0xffff);
m.i3 = 0xffff;
m.i4 = 4;
m.prop();
assert_eq!(m.o2, 0xffff);
m.i3 = 0xffff;
m.i4 = 8;
m.prop();
assert_eq!(m.o2, 0xffff);
m.i3 = 0xffff;
m.i4 = 16;
m.prop();
assert_eq!(m.o2, 0xffff);
m.i3 = 0xffff;
m.i4 = 3;
m.prop();
assert_eq!(m.o2, 0xffff);
m.i3 = 0xffff;
m.i4 = 31;
m.prop();
assert_eq!(m.o2, 0xffff);
m.i3 = 0xffff;
m.i4 = 32;
m.prop();
assert_eq!(m.o2, 0xffff);
m.i5 = 0xfadebabe;
m.i6 = 4;
m.prop();
assert_eq!(m.o3, 0xffadebab);
m.i5 = 0xdeadbeef;
m.i6 = 16;
m.prop();
assert_eq!(m.o3, 0xffffdead);
m.i5 = 0xdeadbeef;
m.i6 = std::u32::MAX;
m.prop();
assert_eq!(m.o3, 0xffffffff);
m.i7 = 0xfadebabedeadbeef;
m.i8 = 4;
m.prop();
assert_eq!(m.o4, 0xffadebabedeadbee);
m.i7 = 0xfadebabedeadbeef;
m.i8 = 16;
m.prop();
assert_eq!(m.o4, 0xfffffadebabedead);
m.i7 = 0xfadebabedeadbeef;
m.i8 = std::u64::MAX;
m.prop();
assert_eq!(m.o4, 0xffffffffffffffff);
m.i9 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i10 = 4;
m.prop();
assert_eq!(m.o5, 0xfaaaaaaaa55555555fadebabedeadbee);
m.i9 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i10 = 16;
m.prop();
assert_eq!(m.o5, 0xffffaaaaaaaa55555555fadebabedead);
m.i9 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i10 = std::u128::MAX;
m.prop();
assert_eq!(m.o5, 0xffffffffffffffffffffffffffffffff);
m.i11 = 0x3f;
m.i12 = 1;
m.prop();
assert_eq!(m.o6, 0x1f);
m.i11 = 0x3f;
m.i12 = 4;
m.prop();
assert_eq!(m.o6, 0x03);
m.i11 = 0x3f;
m.i12 = 5;
m.prop();
assert_eq!(m.o6, 0x01);
m.i11 = 0x3f;
m.i12 = 6;
m.prop();
assert_eq!(m.o6, 0x00);
m.i11 = 0x7f;
m.i12 = 1;
m.prop();
assert_eq!(m.o6, 0x7f);
m.i11 = 0x7f;
m.i12 = 4;
m.prop();
assert_eq!(m.o6, 0x7f);
m.i11 = 0x7f;
m.i12 = 6;
m.prop();
assert_eq!(m.o6, 0x7f);
m.i11 = 0x7f;
m.i12 = 7;
m.prop();
assert_eq!(m.o6, 0x7f);
m.i13 = 0xdeadbeef;
m.i14 = false;
m.prop();
assert_eq!(m.o7, 0xdeadbeef);
m.i13 = 0xdeadbeef;
m.i14 = true;
m.prop();
assert_eq!(m.o7, 0xef56df77);
m.i15 = 0xfadebabedeadbeef;
m.i16 = false;
m.prop();
assert_eq!(m.o8, 0xfadebabedeadbeef);
m.i15 = 0xfadebabedeadbeef;
m.i16 = true;
m.prop();
assert_eq!(m.o8, 0xfd6f5d5f6f56df77);
m.i17 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i18 = false;
m.prop();
assert_eq!(m.o9, 0xaaaaaaaa55555555fadebabedeadbeef);
m.i17 = 0xaaaaaaaa55555555fadebabedeadbeef;
m.i18 = true;
m.prop();
assert_eq!(m.o9, 0xd55555552aaaaaaafd6f5d5f6f56df77);
}
#[test]
fn bit_and_test_module() {
let mut m = BitAndTestModule::new();
m.i1 = false;
m.i2 = false;
m.prop();
assert_eq!(m.o, false);
m.i1 = true;
m.i2 = false;
m.prop();
assert_eq!(m.o, false);
m.i1 = false;
m.i2 = true;
m.prop();
assert_eq!(m.o, false);
m.i1 = true;
m.i2 = true;
m.prop();
assert_eq!(m.o, true);
m.i1 = false;
m.i2 = false;
assert_eq!(m.o, true); // No propagation
m.prop();
assert_eq!(m.o, false);
}
#[test]
fn bit_or_test_module() {
let mut m = BitOrTestModule::new();
m.i1 = false;
m.i2 = false;
m.prop();
assert_eq!(m.o, false);
m.i1 = true;
m.i2 = false;
m.prop();
assert_eq!(m.o, true);
m.i1 = false;
m.i2 = true;
m.prop();
assert_eq!(m.o, true);
m.i1 = true;
m.i2 = true;
m.prop();
assert_eq!(m.o, true);
m.i1 = false;
m.i2 = false;
assert_eq!(m.o, true); // No propagation
m.prop();
assert_eq!(m.o, false);
}
#[test]
fn bit_xor_test_module() {
let mut m = BitXorTestModule::new();
m.i1 = false;
m.i2 = false;
m.prop();
assert_eq!(m.o, false);
m.i1 = true;
m.i2 = false;
m.prop();
assert_eq!(m.o, true);
m.i1 = false;
m.i2 = true;
m.prop();
assert_eq!(m.o, true);
m.i1 = true;
m.i2 = true;
m.prop();
assert_eq!(m.o, false);
m.i1 = true;
m.i2 = false;
assert_eq!(m.o, false); // No propagation
m.prop();
assert_eq!(m.o, true);
}
#[test]
fn not_test_module() {
let mut m = NotTestModule::new();
m.i = 0;
m.prop();
assert_eq!(m.o, 0xf);
m.i = 0xff;
m.prop();
assert_eq!(m.o, 0);
m.i = 0xa;
m.prop();
assert_eq!(m.o, 0x5);
m.i = 0x5;
m.prop();
assert_eq!(m.o, 0xa);
}
#[test]
fn reg_test_module() {
let mut m = RegTestModule::new();
// Check initial value
m.reset();
m.prop();
assert_eq!(m.o1, 0);
// Register value doesn't change without clock edge
m.i1 = 0xdeadbeef;
m.prop();
assert_eq!(m.o1, 0);
m.posedge_clk();
assert_eq!(m.o1, 0); // No propagation
m.prop();
assert_eq!(m.o1, 0xdeadbeef);
m.i1 = 0xfadebabe;
m.prop();
assert_eq!(m.o1, 0xdeadbeef);
// Clock in initial value (second reg explicitly doesn't have one!)
m.i2 = 0xfadebabe;
m.prop();
m.posedge_clk();
m.prop();
assert_eq!(m.o2, 0xfadebabe);
// Register with no default value doesn't change with reset
m.reset();
m.prop();
assert_eq!(m.o2, 0xfadebabe);
}
#[test]
fn simple_reg_delay() {
let mut m = SimpleRegDelay::new();
// Check initial value
m.reset();
m.prop();
assert_eq!(m.o, 0);
// The input propagates through 3 registers, so we won't see it output for 3 cycles
m.i = 0xffffffffffffffffffffffffffffffff;
m.prop(); // Propagate to first register input
assert_eq!(m.o, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.o, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.o, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.o, 0xfffffffffffffffffffffffff);
}
#[test]
fn bit_test_module_0() {
let mut m = BitTestModule0::new();
m.i = false;
m.prop();
assert_eq!(m.o, false);
m.i = true;
m.prop();
assert_eq!(m.o, true);
}
#[test]
fn bit_test_module_1() {
let mut m = BitTestModule1::new();
m.i = 0b0110;
m.prop();
assert_eq!(m.o0, false);
assert_eq!(m.o1, true);
assert_eq!(m.o2, true);
assert_eq!(m.o3, false);
}
#[test]
fn bits_test_module_0() {
let mut m = BitsTestModule0::new();
m.i = 0b0110;
m.prop();
assert_eq!(m.o210, 0b110);
assert_eq!(m.o321, 0b011);
assert_eq!(m.o10, 0b10);
assert_eq!(m.o32, 0b01);
assert_eq!(m.o2, true);
m.i = 0b1001;
m.prop();
assert_eq!(m.o210, 0b001);
assert_eq!(m.o321, 0b100);
assert_eq!(m.o10, 0b01);
assert_eq!(m.o32, 0b10);
assert_eq!(m.o2, false);
m.i = 0b1111;
m.prop();
assert_eq!(m.o210, 0b111);
assert_eq!(m.o321, 0b111);
assert_eq!(m.o10, 0b11);
assert_eq!(m.o32, 0b11);
assert_eq!(m.o2, true);
}
#[test]
fn bits_test_module_1() {
let mut m = BitsTestModule1::new();
m.i = 0xfadebabedeadbeefabad1deabadc0de5;
m.prop();
assert_eq!(m.o0, 0xfadebabedeadbeefabad1deabadc0de5u128);
assert_eq!(m.o1, 0x7adebabedeadbeefabad1deabadc0de5u128);
assert_eq!(m.o2, 0xfadebabedeadbeefu64);
assert_eq!(m.o3, 0xabad1deabadc0de5u64);
assert_eq!(m.o4, 0xfadebabeu32);
assert_eq!(m.o5, 0xdeadbeefu32);
assert_eq!(m.o6, 0xabad1deau32);
assert_eq!(m.o7, 0xbadc0de5u32);
assert_eq!(m.o8, 0xadebabedeadbeefau64);
assert_eq!(m.o9, true);
assert_eq!(m.o10, 0xabadu32);
assert_eq!(m.o11, true);
}
#[test]
fn repeat_test_module() {
let mut m = RepeatTestModule::new();
m.i = 0xa;
m.prop();
assert_eq!(m.o0, 0xau32);
assert_eq!(m.o1, 0xaau32);
assert_eq!(m.o2, 0xaaaaau32);
assert_eq!(m.o3, 0xaaaaaaaau32);
assert_eq!(m.o4, 0xaaaaaaaaaaaaaaaau64);
assert_eq!(m.o5, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau128);
assert_eq!(m.o6, 0b000u32);
assert_eq!(m.o7, 0x0u64);
assert_eq!(m.o8, 0x0u128);
m.i = 0x5;
m.prop();
assert_eq!(m.o0, 0x5u32);
assert_eq!(m.o1, 0x55u32);
assert_eq!(m.o2, 0x55555u32);
assert_eq!(m.o3, 0x55555555u32);
assert_eq!(m.o4, 0x5555555555555555u64);
assert_eq!(m.o5, 0x55555555555555555555555555555555u128);
assert_eq!(m.o6, 0b111u32);
assert_eq!(m.o7, 0xffffffffffffffffu64);
assert_eq!(m.o8, 0xffffffffffffffffffffffffffffffffu128);
}
#[test]
fn concat_test_module() {
let mut m = ConcatTestModule::new();
m.i1 = 0xa;
m.i2 = 0x5;
m.i3 = 0xfadebabe;
m.prop();
assert_eq!(m.o0, 0xa5u32);
assert_eq!(m.o1, 0x5au32);
assert_eq!(m.o2, 0xau32);
assert_eq!(m.o3, 0x1au32);
assert_eq!(m.o4, 0x1au32);
assert_eq!(m.o5, 0xfadebabefadebabeu64);
assert_eq!(m.o6, 0xfadebabefadebabefadebabefadebabeu128);
}
#[test]
fn eq_test_module() {
let mut m = EqTestModule::new();
m.i1 = 0xa;
m.i2 = 0xa;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, true);
m.i1 = 0b01;
m.i2 = 0b11;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, true);
}
#[test]
fn ne_test_module() {
let mut m = NeTestModule::new();
m.i1 = 0xa;
m.i2 = 0xa;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, false);
m.i1 = 0b01;
m.i2 = 0b11;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, false);
}
#[test]
fn lt_test_module() {
let mut m = LtTestModule::new();
m.i1 = 0xa;
m.i2 = 0xb;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, true);
m.i1 = 0b01;
m.i2 = 0b11;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, false);
}
#[test]
fn le_test_module() {
let mut m = LeTestModule::new();
m.i1 = 0xa;
m.i2 = 0xb;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, true);
m.i1 = 0b01;
m.i2 = 0b11;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, true);
}
#[test]
fn gt_test_module() {
let mut m = GtTestModule::new();
m.i1 = 0xa;
m.i2 = 0xb;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, false);
m.i1 = 0b01;
m.i2 = 0b11;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, false);
}
#[test]
fn ge_test_module() {
let mut m = GeTestModule::new();
m.i1 = 0xa;
m.i2 = 0xb;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, false);
m.i1 = 0b01;
m.i2 = 0b11;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, true);
}
#[test]
fn lt_signed_test_module() {
let mut m = LtSignedTestModule::new();
m.i1 = 0xa;
m.i2 = 0xb;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, true);
m.i1 = 0b01;
m.i2 = 0b11;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, false);
}
#[test]
fn le_signed_test_module() {
let mut m = LeSignedTestModule::new();
m.i1 = 0xa;
m.i2 = 0xb;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, true);
m.i1 = 0b01;
m.i2 = 0b11;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, false);
}
#[test]
fn gt_signed_test_module() {
let mut m = GtSignedTestModule::new();
m.i1 = 0xa;
m.i2 = 0xb;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, false);
m.i1 = 0b01;
m.i2 = 0b11;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, true);
}
#[test]
fn ge_signed_test_module() {
let mut m = GeSignedTestModule::new();
m.i1 = 0xa;
m.i2 = 0xb;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, false);
m.i1 = 0b01;
m.i2 = 0b11;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, true);
}
#[test]
fn mux_test_module() {
let mut m = MuxTestModule::new();
m.i1 = false;
m.invert = false;
m.prop();
assert_eq!(m.o1, false);
m.i1 = true;
m.invert = false;
m.prop();
assert_eq!(m.o1, true);
m.i1 = false;
m.invert = true;
m.prop();
assert_eq!(m.o1, true);
m.i1 = true;
m.invert = true;
m.prop();
assert_eq!(m.o1, false);
m.i2 = false;
m.invert = false;
m.prop();
assert_eq!(m.o2, false);
m.i2 = true;
m.invert = false;
m.prop();
assert_eq!(m.o2, true);
m.i2 = false;
m.invert = true;
m.prop();
assert_eq!(m.o2, true);
m.i2 = true;
m.invert = true;
m.prop();
assert_eq!(m.o2, false);
}
#[test]
fn reg_next_test_module() {
let mut m = RegNextTestModule::new();
// Check (undefined) initial value
m.prop();
assert_eq!(m.o2, false);
m.i = false;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, false);
m.i = true;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, false);
m.posedge_clk();
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, true);
}
#[test]
fn reg_next_with_default_test_module() {
let mut m = RegNextWithDefaultTestModule::new();
// Check initial value
m.reset();
m.prop();
assert_eq!(m.o2, true);
m.i = true;
m.prop();
assert_eq!(m.o1, true);
assert_eq!(m.o2, true);
m.i = false;
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, true);
m.posedge_clk();
m.prop();
assert_eq!(m.o1, false);
assert_eq!(m.o2, false);
}
#[test]
fn instantiation_test_module_comb() {
let mut m = InstantiationTestModuleComb::new();
m.i1 = 0xffffffff;
m.i2 = 0xffff0000;
m.i3 = 0x00ff0000;
m.i4 = 0x000f0000;
m.prop();
assert_eq!(m.o, 0x000f0000u32);
m.i1 = 0x00000f00;
m.i2 = 0xffffffff;
m.i3 = 0x0000ffff;
m.i4 = 0xffffffff;
m.prop();
assert_eq!(m.o, 0x00000f00u32);
}
#[test]
fn instantiation_test_module_reg() {
let mut m = InstantiationTestModuleReg::new();
// Check initial value
m.reset();
m.prop();
assert_eq!(m.o, 0);
// The inputs propagate through 2 registers, so we won't see proper output for 2 cycles
m.i1 = 0xffffffff;
m.i2 = 0xffff0000;
m.i3 = 0x00ff0000;
m.i4 = 0x000f0000;
m.prop(); // Propagate to first register inputs
assert_eq!(m.o, 0u32);
m.posedge_clk();
m.prop();
assert_eq!(m.o, 0u32);
m.posedge_clk();
m.prop();
assert_eq!(m.o, 0x000f0000u32);
}
#[test]
fn nested_instantiation_test_module() {
let mut m = NestedInstantiationTestModule::new();
m.i1 = 0xffffffff;
m.i2 = 0xffff0000;
m.i3 = 0x00ff0000;
m.i4 = 0x000f0000;
m.prop();
assert_eq!(m.o, 0x000f0000u32);
m.i1 = 0x00000f00;
m.i2 = 0xffffffff;
m.i3 = 0x0000ffff;
m.i4 = 0xffffffff;
m.prop();
assert_eq!(m.o, 0x00000f00u32);
}
#[test]
fn mem_test_module_0() {
let mut m = MemTestModule0::new();
// Initial state, no read/write
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = false;
m.read_enable = false;
m.prop();
assert_eq!(m.read_data, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0);
// Initial state, read from addr 0
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = false;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0);
// Initial state, read from addr 1
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0);
// Initial state, write to addr 0
m.write_addr = false;
m.write_value = 0x5;
m.write_enable = true;
m.read_addr = false;
m.read_enable = false;
m.prop();
assert_eq!(m.read_data, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0);
// Write to addr 1
m.write_addr = true;
m.write_value = 0xa;
m.write_enable = true;
m.read_addr = false;
m.read_enable = false;
m.prop();
assert_eq!(m.read_data, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0);
// Read from addr 0
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = false;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0x5);
// Read from addr 1
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0x5);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0xa);
// Write to/read from addr 0
m.write_addr = false;
m.write_value = 0xc;
m.write_enable = true;
m.read_addr = false;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0xa);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0x5);
// Write to/read from addr 1
m.write_addr = true;
m.write_value = 0x3;
m.write_enable = true;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0x5);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0xa);
// Read from addr 0
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = false;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0xa);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0xc);
// Read from addr 1
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0xc);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0x3);
// Write to addr 0/read from addr 1
m.write_addr = false;
m.write_value = 0x9;
m.write_enable = true;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0x3);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0x3);
// Write to addr 1/read from addr 0
m.write_addr = true;
m.write_value = 0x5;
m.write_enable = true;
m.read_addr = false;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0x3);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0x9);
// Read from addr 1
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0x9);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0x5);
}
#[test]
fn mem_test_module_1() {
let mut m = MemTestModule1::new();
// No read
m.read_addr = 0;
m.read_enable = false;
m.prop();
assert_eq!(m.read_data, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0);
// Read from addr 0
m.read_addr = 0;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0xfadebabe);
// Read from addr 1
m.read_addr = 1;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0xfadebabe);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0xdeadbeef);
// Read from addr 2
m.read_addr = 2;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0xdeadbeef);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0xabadcafe);
// Read from addr 3
m.read_addr = 3;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, 0xabadcafe);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0xabad1dea);
// No read
m.read_addr = 0;
m.read_enable = false;
m.prop();
assert_eq!(m.read_data, 0xabad1dea);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, 0xabad1dea);
}
#[test]
fn mem_test_module_2() {
let mut m = MemTestModule2::new();
// Initial state, no read/write
m.write_addr = false;
m.write_value = false;
m.write_enable = false;
m.read_addr = false;
m.read_enable = false;
m.prop();
assert_eq!(m.read_data, false);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, false);
// Initial state, read from addr 0
m.write_addr = false;
m.write_value = false;
m.write_enable = false;
m.read_addr = false;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, false);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, false);
// Initial state, read from addr 1
m.write_addr = false;
m.write_value = false;
m.write_enable = false;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, false);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, false);
// Initial state, write to addr 0
m.write_addr = false;
m.write_value = true;
m.write_enable = true;
m.read_addr = false;
m.read_enable = false;
m.prop();
assert_eq!(m.read_data, false);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, false);
// Write to addr 1
m.write_addr = true;
m.write_value = true;
m.write_enable = true;
m.read_addr = false;
m.read_enable = false;
m.prop();
assert_eq!(m.read_data, false);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, false);
// Read from addr 0
m.write_addr = false;
m.write_value = false;
m.write_enable = false;
m.read_addr = false;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, false);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, true);
// Read from addr 1
m.write_addr = false;
m.write_value = false;
m.write_enable = false;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, true);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, true);
// Write to/read from addr 0
m.write_addr = false;
m.write_value = false;
m.write_enable = true;
m.read_addr = false;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, true);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, true);
// Write to/read from addr 1
m.write_addr = true;
m.write_value = true;
m.write_enable = true;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, true);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, true);
// Read from addr 0
m.write_addr = false;
m.write_value = false;
m.write_enable = false;
m.read_addr = false;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, true);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, false);
// Read from addr 1
m.write_addr = false;
m.write_value = false;
m.write_enable = false;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, false);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, true);
// Write to addr 0/read from addr 1
m.write_addr = false;
m.write_value = true;
m.write_enable = true;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, true);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, true);
// Write to addr 1/read from addr 0
m.write_addr = true;
m.write_value = false;
m.write_enable = true;
m.read_addr = false;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, true);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, true);
// Read from addr 1
m.write_addr = false;
m.write_value = false;
m.write_enable = false;
m.read_addr = true;
m.read_enable = true;
m.prop();
assert_eq!(m.read_data, true);
m.posedge_clk();
m.prop();
assert_eq!(m.read_data, false);
}
#[test]
fn trace_test_module_0() -> io::Result<()> {
let mut capture = Capture::new();
let trace = CaptureTrace::new(&mut capture);
let mut m = TraceTestModule0::new(trace)?;
let mut time_stamp = 0;
m.prop();
m.update_trace(time_stamp)?;
time_stamp += 1;
m.i0 = true;
m.prop();
m.update_trace(time_stamp)?;
time_stamp += 50;
m.i1 = 0b11u32;
m.i2 = 0xfadebabeu32;
m.i3 = 0xdeadbeefcafed00du64;
m.i4 = 0xc0cac01adeadbeefabad1deabadc0de5u128;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(
capture,
Capture {
root: Some((
"trace_test_module_0",
CaptureModule {
children: BTreeMap::new(),
signals: vec![
(
"i0",
Rc::new(CaptureSignal {
bit_width: 1,
type_: TraceValueType::Bool,
values: RefCell::new(vec![
(0, TraceValue::Bool(false)),
(1, TraceValue::Bool(true)),
(51, TraceValue::Bool(true)),
]),
})
),
(
"i1",
Rc::new(CaptureSignal {
bit_width: 2,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(51, TraceValue::U32(0b11)),
]),
})
),
(
"i2",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(51, TraceValue::U32(0xfadebabe)),
]),
})
),
(
"i3",
Rc::new(CaptureSignal {
bit_width: 64,
type_: TraceValueType::U64,
values: RefCell::new(vec![
(0, TraceValue::U64(0)),
(1, TraceValue::U64(0)),
(51, TraceValue::U64(0xdeadbeefcafed00d)),
]),
})
),
(
"i4",
Rc::new(CaptureSignal {
bit_width: 128,
type_: TraceValueType::U128,
values: RefCell::new(vec![
(0, TraceValue::U128(0)),
(1, TraceValue::U128(0)),
(51, TraceValue::U128(0xc0cac01adeadbeefabad1deabadc0de5)),
]),
})
),
(
"o0",
Rc::new(CaptureSignal {
bit_width: 1,
type_: TraceValueType::Bool,
values: RefCell::new(vec![
(0, TraceValue::Bool(false)),
(1, TraceValue::Bool(true)),
(51, TraceValue::Bool(true)),
]),
})
),
(
"o1",
Rc::new(CaptureSignal {
bit_width: 2,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(51, TraceValue::U32(0b11)),
]),
})
),
(
"o2",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(51, TraceValue::U32(0xfadebabe)),
]),
})
),
(
"o3",
Rc::new(CaptureSignal {
bit_width: 64,
type_: TraceValueType::U64,
values: RefCell::new(vec![
(0, TraceValue::U64(0)),
(1, TraceValue::U64(0)),
(51, TraceValue::U64(0xdeadbeefcafed00d)),
]),
})
),
(
"o4",
Rc::new(CaptureSignal {
bit_width: 128,
type_: TraceValueType::U128,
values: RefCell::new(vec![
(0, TraceValue::U128(0)),
(1, TraceValue::U128(0)),
(51, TraceValue::U128(0xc0cac01adeadbeefabad1deabadc0de5)),
]),
})
),
]
.into_iter()
.collect(),
}
)),
}
);
Ok(())
}
#[test]
fn trace_test_module_1() -> io::Result<()> {
let mut capture = Capture::new();
let trace = CaptureTrace::new(&mut capture);
let mut m = TraceTestModule1::new(trace)?;
let mut time_stamp = 0;
// Check initial value
m.reset();
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.o1, 0);
m.i1 = 0xdeadbeef;
m.prop();
m.update_trace(time_stamp)?;
m.posedge_clk();
time_stamp += 1;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.o1, 0xdeadbeef);
m.i1 = 0xfadebabe;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.o1, 0xdeadbeef);
// Clock in initial value (second reg explicitly doesn't have one!)
m.i2 = 0xfadebabe;
m.prop();
m.update_trace(time_stamp)?;
m.posedge_clk();
time_stamp += 1;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.o2, 0xfadebabe);
assert_eq!(
capture,
Capture {
root: Some((
"trace_test_module_1",
CaptureModule {
children: BTreeMap::new(),
signals: vec![
(
"i1",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0xdeadbeef)),
(1, TraceValue::U32(0xdeadbeef)),
(1, TraceValue::U32(0xfadebabe)),
(1, TraceValue::U32(0xfadebabe)),
(2, TraceValue::U32(0xfadebabe)),
]),
})
),
(
"i2",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(1, TraceValue::U32(0xfadebabe)),
(2, TraceValue::U32(0xfadebabe)),
]),
})
),
(
"o1",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0xdeadbeef)),
(1, TraceValue::U32(0xdeadbeef)),
(1, TraceValue::U32(0xdeadbeef)),
(2, TraceValue::U32(0xfadebabe)),
]),
})
),
(
"o2",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(2, TraceValue::U32(0xfadebabe)),
]),
})
),
(
"r1",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0xdeadbeef)),
(1, TraceValue::U32(0xdeadbeef)),
(1, TraceValue::U32(0xdeadbeef)),
(2, TraceValue::U32(0xfadebabe)),
]),
})
),
(
"r2",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(2, TraceValue::U32(0xfadebabe)),
]),
})
),
]
.into_iter()
.collect(),
}
)),
}
);
Ok(())
}
#[test]
fn trace_test_module_2() -> io::Result<()> {
let mut capture = Capture::new();
let trace = CaptureTrace::new(&mut capture);
let mut m = TraceTestModule2::new(trace)?;
let mut time_stamp = 0;
// Check initial value
m.reset();
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.o, 0);
// The inputs propagate through 2 registers, so we won't see proper output for 2 cycles
m.i1 = 0xffffffff;
m.i2 = 0xffff0000;
m.i3 = 0x00ff0000;
m.i4 = 0x000f0000;
m.prop(); // Propagate to first register inputs
m.update_trace(time_stamp)?;
assert_eq!(m.o, 0u32);
m.posedge_clk();
time_stamp += 1;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.o, 0u32);
m.posedge_clk();
time_stamp += 1;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.o, 0x000f0000u32);
assert_eq!(
capture,
Capture {
root: Some((
"trace_test_module_2",
CaptureModule {
children: vec![
(
"inner1",
CaptureModule {
children: BTreeMap::new(),
signals: vec![(
"r",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0xffff0000)),
(2, TraceValue::U32(0xffff0000)),
]),
})
),]
.into_iter()
.collect(),
}
),
(
"inner2",
CaptureModule {
children: BTreeMap::new(),
signals: vec![(
"r",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0x000f0000)),
(2, TraceValue::U32(0x000f0000)),
]),
})
),]
.into_iter()
.collect(),
}
),
(
"inner3",
CaptureModule {
children: BTreeMap::new(),
signals: vec![(
"r",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(2, TraceValue::U32(0x000f0000)),
]),
})
),]
.into_iter()
.collect(),
}
),
]
.into_iter()
.collect(),
signals: vec![
(
"i1",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0xffffffff)),
(1, TraceValue::U32(0xffffffff)),
(2, TraceValue::U32(0xffffffff)),
]),
})
),
(
"i2",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0xffff0000)),
(1, TraceValue::U32(0xffff0000)),
(2, TraceValue::U32(0xffff0000)),
]),
})
),
(
"i3",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0x00ff0000)),
(1, TraceValue::U32(0x00ff0000)),
(2, TraceValue::U32(0x00ff0000)),
]),
})
),
(
"i4",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0x000f0000)),
(1, TraceValue::U32(0x000f0000)),
(2, TraceValue::U32(0x000f0000)),
]),
})
),
(
"o",
Rc::new(CaptureSignal {
bit_width: 32,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(2, TraceValue::U32(0x000f0000)),
]),
})
),
]
.into_iter()
.collect(),
}
)),
}
);
Ok(())
}
#[test]
fn trace_test_module_3() -> io::Result<()> {
let mut capture = Capture::new();
let trace = CaptureTrace::new(&mut capture);
let mut m = TraceTestModule3::new(trace)?;
let mut time_stamp = 0;
// Initial state, no read/write
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = false;
m.read_enable = false;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.read_data, 0);
m.posedge_clk();
time_stamp += 1;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.read_data, 0);
// Initial state, read from addr 0
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = false;
m.read_enable = true;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.read_data, 0);
m.posedge_clk();
time_stamp += 1;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.read_data, 0);
// Initial state, write to addr 0
m.write_addr = false;
m.write_value = 0x5;
m.write_enable = true;
m.read_addr = false;
m.read_enable = false;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.read_data, 0);
m.posedge_clk();
time_stamp += 1;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.read_data, 0);
// Read from addr 0
m.write_addr = false;
m.write_value = 0;
m.write_enable = false;
m.read_addr = false;
m.read_enable = true;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.read_data, 0);
m.posedge_clk();
time_stamp += 1;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.read_data, 0x5);
// Write to/read from addr 0
m.write_addr = false;
m.write_value = 0xc;
m.write_enable = true;
m.read_addr = false;
m.read_enable = true;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.read_data, 0x5);
m.posedge_clk();
time_stamp += 1;
m.prop();
m.update_trace(time_stamp)?;
assert_eq!(m.read_data, 0x5);
assert_eq!(
capture,
Capture {
root: Some((
"trace_test_module_3",
CaptureModule {
children: BTreeMap::new(),
signals: vec![
(
"mem_0_read_port_0_address",
Rc::new(CaptureSignal {
bit_width: 1,
type_: TraceValueType::Bool,
values: RefCell::new(vec![
(0, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(2, TraceValue::Bool(false)),
(2, TraceValue::Bool(false)),
(3, TraceValue::Bool(false)),
(3, TraceValue::Bool(false)),
(4, TraceValue::Bool(false)),
(4, TraceValue::Bool(false)),
(5, TraceValue::Bool(false)),
]),
})
),
(
"mem_0_read_port_0_enable",
Rc::new(CaptureSignal {
bit_width: 1,
type_: TraceValueType::Bool,
values: RefCell::new(vec![
(0, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(1, TraceValue::Bool(true)),
(2, TraceValue::Bool(true)),
(2, TraceValue::Bool(false)),
(3, TraceValue::Bool(false)),
(3, TraceValue::Bool(true)),
(4, TraceValue::Bool(true)),
(4, TraceValue::Bool(true)),
(5, TraceValue::Bool(true)),
]),
})
),
(
"mem_0_write_port_address",
Rc::new(CaptureSignal {
bit_width: 1,
type_: TraceValueType::Bool,
values: RefCell::new(vec![
(0, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(2, TraceValue::Bool(false)),
(2, TraceValue::Bool(false)),
(3, TraceValue::Bool(false)),
(3, TraceValue::Bool(false)),
(4, TraceValue::Bool(false)),
(4, TraceValue::Bool(false)),
(5, TraceValue::Bool(false)),
]),
})
),
(
"mem_0_write_port_enable",
Rc::new(CaptureSignal {
bit_width: 1,
type_: TraceValueType::Bool,
values: RefCell::new(vec![
(0, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(2, TraceValue::Bool(false)),
(2, TraceValue::Bool(true)),
(3, TraceValue::Bool(true)),
(3, TraceValue::Bool(false)),
(4, TraceValue::Bool(false)),
(4, TraceValue::Bool(true)),
(5, TraceValue::Bool(true)),
]),
})
),
(
"mem_0_write_port_value",
Rc::new(CaptureSignal {
bit_width: 4,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(2, TraceValue::U32(0)),
(2, TraceValue::U32(5)),
(3, TraceValue::U32(5)),
(3, TraceValue::U32(0)),
(4, TraceValue::U32(0)),
(4, TraceValue::U32(12)),
(5, TraceValue::U32(12)),
]),
})
),
(
"read_addr",
Rc::new(CaptureSignal {
bit_width: 1,
type_: TraceValueType::Bool,
values: RefCell::new(vec![
(0, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(2, TraceValue::Bool(false)),
(2, TraceValue::Bool(false)),
(3, TraceValue::Bool(false)),
(3, TraceValue::Bool(false)),
(4, TraceValue::Bool(false)),
(4, TraceValue::Bool(false)),
(5, TraceValue::Bool(false)),
]),
})
),
(
"read_data",
Rc::new(CaptureSignal {
bit_width: 4,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(2, TraceValue::U32(0)),
(2, TraceValue::U32(0)),
(3, TraceValue::U32(0)),
(3, TraceValue::U32(0)),
(4, TraceValue::U32(5)),
(4, TraceValue::U32(5)),
(5, TraceValue::U32(5)),
]),
})
),
(
"read_enable",
Rc::new(CaptureSignal {
bit_width: 1,
type_: TraceValueType::Bool,
values: RefCell::new(vec![
(0, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(1, TraceValue::Bool(true)),
(2, TraceValue::Bool(true)),
(2, TraceValue::Bool(false)),
(3, TraceValue::Bool(false)),
(3, TraceValue::Bool(true)),
(4, TraceValue::Bool(true)),
(4, TraceValue::Bool(true)),
(5, TraceValue::Bool(true)),
]),
})
),
(
"write_addr",
Rc::new(CaptureSignal {
bit_width: 1,
type_: TraceValueType::Bool,
values: RefCell::new(vec![
(0, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(2, TraceValue::Bool(false)),
(2, TraceValue::Bool(false)),
(3, TraceValue::Bool(false)),
(3, TraceValue::Bool(false)),
(4, TraceValue::Bool(false)),
(4, TraceValue::Bool(false)),
(5, TraceValue::Bool(false)),
]),
})
),
(
"write_enable",
Rc::new(CaptureSignal {
bit_width: 1,
type_: TraceValueType::Bool,
values: RefCell::new(vec![
(0, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(1, TraceValue::Bool(false)),
(2, TraceValue::Bool(false)),
(2, TraceValue::Bool(true)),
(3, TraceValue::Bool(true)),
(3, TraceValue::Bool(false)),
(4, TraceValue::Bool(false)),
(4, TraceValue::Bool(true)),
(5, TraceValue::Bool(true)),
]),
})
),
(
"write_value",
Rc::new(CaptureSignal {
bit_width: 4,
type_: TraceValueType::U32,
values: RefCell::new(vec![
(0, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(1, TraceValue::U32(0)),
(2, TraceValue::U32(0)),
(2, TraceValue::U32(5)),
(3, TraceValue::U32(5)),
(3, TraceValue::U32(0)),
(4, TraceValue::U32(0)),
(4, TraceValue::U32(12)),
(5, TraceValue::U32(12)),
]),
})
),
]
.into_iter()
.collect(),
}
)),
}
);
Ok(())
}
#[test]
fn deep_graph_test_module() {
let mut m = DeepGraphTestModule::new();
m.i = false;
m.prop();
assert_eq!(m.o, true);
m.i = true;
m.prop();
assert_eq!(m.o, false);
}
}