forked from libre-chip/fayalite
WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
This commit is contained in:
parent
cd99dbc849
commit
5835b995a9
63 changed files with 13500 additions and 13210 deletions
|
@ -4,6 +4,7 @@ use fayalite_visit_gen::parse_and_generate;
|
|||
use std::{env, fs, path::Path};
|
||||
|
||||
fn main() {
|
||||
println!("cargo::rustc-check-cfg=cfg(todo)");
|
||||
let path = "visit_types.json";
|
||||
println!("cargo::rerun-if-changed={path}");
|
||||
println!("cargo::rerun-if-changed=build.rs");
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
use clap::Parser;
|
||||
use fayalite::{
|
||||
cli,
|
||||
clock::{Clock, ClockDomain},
|
||||
hdl_module,
|
||||
int::{DynUInt, DynUIntType, IntCmp, IntTypeTrait, UInt},
|
||||
reset::{SyncReset, ToReset},
|
||||
};
|
||||
use fayalite::{cli, prelude::*};
|
||||
|
||||
#[hdl_module]
|
||||
fn blinky(clock_frequency: u64) {
|
||||
|
@ -19,21 +13,21 @@ fn blinky(clock_frequency: u64) {
|
|||
rst: rst.to_reset(),
|
||||
};
|
||||
let max_value = clock_frequency / 2 - 1;
|
||||
let int_ty = DynUIntType::range_inclusive(0..=max_value);
|
||||
let int_ty = UInt::range_inclusive(0..=max_value);
|
||||
#[hdl]
|
||||
let counter: DynUInt = m.reg_builder().clock_domain(cd).reset(int_ty.literal(0));
|
||||
let counter: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty));
|
||||
#[hdl]
|
||||
let output_reg: UInt<1> = m.reg_builder().clock_domain(cd).reset_default();
|
||||
let output_reg: Bool = reg_builder().clock_domain(cd).reset(false);
|
||||
#[hdl]
|
||||
if counter.cmp_eq(max_value) {
|
||||
m.connect_any(counter, 0u8);
|
||||
m.connect(output_reg, !output_reg);
|
||||
connect_any(counter, 0u8);
|
||||
connect(output_reg, !output_reg);
|
||||
} else {
|
||||
m.connect_any(counter, counter + 1_hdl_u1);
|
||||
connect_any(counter, counter + 1_hdl_u1);
|
||||
}
|
||||
#[hdl]
|
||||
let led: UInt<1> = m.output();
|
||||
m.connect(led, output_reg);
|
||||
let led: Bool = m.output();
|
||||
connect(led, output_reg);
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
//! function to add inputs/outputs and other components to that module.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! #
|
||||
//! #[hdl_module]
|
||||
//! pub fn example_module() {
|
||||
|
@ -18,7 +18,7 @@
|
|||
//! let an_input: UInt<10> = m.input(); // create an input that is a 10-bit unsigned integer
|
||||
//! #[hdl]
|
||||
//! let some_output: UInt<10> = m.output();
|
||||
//! m.connect(some_output, an_input); // assigns the value of `an_input` to `some_output`
|
||||
//! connect(some_output, an_input); // assigns the value of `an_input` to `some_output`
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
//! * [`parameter_raw_verilog()`][`ModuleBuilder::parameter_raw_verilog`]
|
||||
//! * [`parameter()`][`ModuleBuilder::parameter`]
|
||||
//!
|
||||
//! These use the [`ExternModule`][`crate::module::ExternModule`] tag type.
|
||||
//!
|
||||
//! [inputs/outputs]: crate::_docs::modules::module_bodies::hdl_let_statements::inputs_outputs
|
||||
|
||||
#[allow(unused)]
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
//! # `#[hdl]` Array Expressions
|
||||
//!
|
||||
//! `#[hdl]` can be used on Array Expressions to construct an [`Array<[T; N]>`][Array] expression:
|
||||
//! `#[hdl]` can be used on Array Expressions to construct an [`Array<[T; N]>`][type@Array] expression:
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt, array::Array};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let v: UInt<8> = m.input();
|
||||
//! #[hdl]
|
||||
//! let w: Array<[UInt<8>; 4]> = m.wire();
|
||||
//! m.connect(
|
||||
//! let w: Array<UInt<8>, 4> = wire();
|
||||
//! connect(
|
||||
//! w,
|
||||
//! #[hdl]
|
||||
//! [4_hdl_u8, v, 3_hdl_u8, (v + 7_hdl_u8).cast()] // you can make an array like this
|
||||
//! [4_hdl_u8, v, 3_hdl_u8, (v + 7_hdl_u8).cast_to_static()] // you can make an array like this
|
||||
//! );
|
||||
//! m.connect(
|
||||
//! connect(
|
||||
//! w,
|
||||
//! #[hdl]
|
||||
//! [(v + 1_hdl_u8).cast(); 4] // or you can make an array repeat like this
|
||||
//! [(v + 1_hdl_u8).cast_to_static(); 4] // or you can make an array repeat like this
|
||||
//! );
|
||||
//! # }
|
||||
//! ```
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
//! `#[hdl] if` statements behave similarly to Rust `if` statements, except they end up as muxes
|
||||
//! and stuff in the final hardware instead of being run when the fayalite module is being created.
|
||||
//!
|
||||
//! The condition of an `#[hdl] if` statement must have type [`UInt<1>`] or [`DynUInt`] with
|
||||
//! `width() == 1` or be an [expression][Expr] of one of those types.
|
||||
//! The condition of an `#[hdl] if` statement must have type [`Expr<Bool>`][Bool].
|
||||
//!
|
||||
//! `#[hdl] if` statements' bodies must evaluate to type `()` for now.
|
||||
//!
|
||||
|
@ -14,7 +13,4 @@
|
|||
//! [match]: super::hdl_match_statements
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::{
|
||||
expr::Expr,
|
||||
int::{DynUInt, UInt},
|
||||
};
|
||||
use crate::int::Bool;
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
//! so you should read it.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt, expr::Expr, array::Array};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let my_input: UInt<10> = m.input();
|
||||
//! let _: Expr<UInt<10>> = my_input; // my_input has type Expr<UInt<10>>
|
||||
//! #[hdl]
|
||||
//! let my_output: Array<[UInt<10>; 3]> = m.output();
|
||||
//! let my_output: Array<UInt<10>, 3> = m.output();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
//! you can create them like so:
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt, array::Array};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let my_instance = m.instance(some_module());
|
||||
//! let my_instance = instance(some_module());
|
||||
//! // now you can use `my_instance`'s inputs/outputs like so:
|
||||
//! #[hdl]
|
||||
//! let v: UInt<3> = m.input();
|
||||
//! m.connect(my_instance.a, v);
|
||||
//! connect(my_instance.a, v);
|
||||
//! #[hdl_module]
|
||||
//! fn some_module() {
|
||||
//! #[hdl]
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
//!
|
||||
//! There are several different ways to create a memory:
|
||||
//!
|
||||
//! ## using [`ModuleBuilder::memory()`]
|
||||
//! ## using [`memory()`]
|
||||
//!
|
||||
//! This way you have to set the [`depth`][`MemBuilder::depth`] separately.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt, clock::ClockDomain};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! // first, we need some IO
|
||||
|
@ -25,45 +25,45 @@
|
|||
//!
|
||||
//! // now create the memory
|
||||
//! #[hdl]
|
||||
//! let mut my_memory = m.memory();
|
||||
//! let mut my_memory = memory();
|
||||
//! my_memory.depth(256); // the memory has 256 elements
|
||||
//!
|
||||
//! let read_port = my_memory.new_read_port();
|
||||
//!
|
||||
//! // connect up the read port
|
||||
//! m.connect_any(read_port.addr, read_addr);
|
||||
//! m.connect(read_port.en, 1_hdl_u1);
|
||||
//! m.connect(read_port.clk, cd.clk);
|
||||
//! m.connect(read_data, read_port.data);
|
||||
//! connect_any(read_port.addr, read_addr);
|
||||
//! connect(read_port.en, true);
|
||||
//! connect(read_port.clk, cd.clk);
|
||||
//! connect(read_data, read_port.data);
|
||||
//!
|
||||
//! // we need more IO for the write port
|
||||
//! #[hdl]
|
||||
//! let write_addr: UInt<8> = m.input();
|
||||
//! #[hdl]
|
||||
//! let do_write: UInt<1> = m.input();
|
||||
//! let do_write: Bool = m.input();
|
||||
//! #[hdl]
|
||||
//! let write_data: UInt<8> = m.input();
|
||||
//!
|
||||
//! let write_port = my_memory.new_write_port();
|
||||
//!
|
||||
//! m.connect_any(write_port.addr, write_addr);
|
||||
//! m.connect(write_port.en, do_write);
|
||||
//! m.connect(write_port.clk, cd.clk);
|
||||
//! m.connect(write_port.data, write_port.data);
|
||||
//! m.connect(write_port.mask, 1_hdl_u1);
|
||||
//! connect_any(write_port.addr, write_addr);
|
||||
//! connect(write_port.en, do_write);
|
||||
//! connect(write_port.clk, cd.clk);
|
||||
//! connect(write_port.data, write_port.data);
|
||||
//! connect(write_port.mask, true);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## using [`ModuleBuilder::memory_array()`]
|
||||
//! ## using [`memory_array()`]
|
||||
//!
|
||||
//! this allows you to specify the memory's underlying array type directly.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt, memory::MemBuilder};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let mut my_memory: MemBuilder<[UInt<8>; 256]> = m.memory_array();
|
||||
//! let mut my_memory: MemBuilder<UInt<8>, ConstUsize<256>> = memory_array();
|
||||
//!
|
||||
//! let read_port = my_memory.new_read_port();
|
||||
//! // ...
|
||||
|
@ -72,25 +72,22 @@
|
|||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## using [`ModuleBuilder::memory_with_init()`]
|
||||
//! ## using [`memory_with_init()`]
|
||||
//!
|
||||
//! This allows you to deduce the memory's array type from the data used to initialize the memory.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! # #[hdl]
|
||||
//! # let read_addr: UInt<2> = m.input();
|
||||
//! #[hdl]
|
||||
//! let mut my_memory = m.memory_with_init(
|
||||
//! #[hdl]
|
||||
//! [0x12_hdl_u8, 0x34_hdl_u8, 0x56_hdl_u8, 0x78_hdl_u8],
|
||||
//! );
|
||||
//! let mut my_memory = memory_with_init([0x12_hdl_u8, 0x34_hdl_u8, 0x56_hdl_u8, 0x78_hdl_u8]);
|
||||
//!
|
||||
//! let read_port = my_memory.new_read_port();
|
||||
//! // note that `read_addr` is `UInt<2>` since the memory only has 4 elements
|
||||
//! m.connect_any(read_port.addr, read_addr);
|
||||
//! connect_any(read_port.addr, read_addr);
|
||||
//! // ...
|
||||
//! let write_port = my_memory.new_write_port();
|
||||
//! // ...
|
||||
|
@ -98,4 +95,4 @@
|
|||
//! ```
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::{memory::MemBuilder, module::ModuleBuilder};
|
||||
use crate::prelude::*;
|
||||
|
|
|
@ -8,19 +8,19 @@
|
|||
//! Registers follow [connection semantics], which are unlike assignments in software, so you should read it.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt, array::Array, clock::ClockDomain};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! # #[hdl]
|
||||
//! # let v: UInt<1> = m.input();
|
||||
//! # let v: Bool = m.input();
|
||||
//! #[hdl]
|
||||
//! let cd: ClockDomain = m.input();
|
||||
//! #[hdl]
|
||||
//! let my_register: UInt<8> = m.reg_builder().clock_domain(cd).reset(8_hdl_u8);
|
||||
//! let my_register: UInt<8> = reg_builder().clock_domain(cd).reset(8_hdl_u8);
|
||||
//! #[hdl]
|
||||
//! if v {
|
||||
//! // my_register is only changed when both `v` is set and `cd`'s clock edge occurs.
|
||||
//! m.connect(my_register, 0x45_hdl_u8);
|
||||
//! connect(my_register, 0x45_hdl_u8);
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
|
|
|
@ -2,26 +2,26 @@
|
|||
//!
|
||||
//! Wires are kinda like variables, but unlike registers,
|
||||
//! they have no memory (they're combinatorial).
|
||||
//! You must [connect][`ModuleBuilder::connect`] to all wires, so they have a defined value.
|
||||
//! You must [connect] to all wires, so they have a defined value.
|
||||
//!
|
||||
//! Wires create a Rust variable with type [`Expr<T>`] where `T` is the type of the wire.
|
||||
//!
|
||||
//! Wires follow [connection semantics], which are unlike assignments in software, so you should read it.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt, array::Array, clock::ClockDomain};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! # #[hdl]
|
||||
//! # let v: UInt<1> = m.input();
|
||||
//! # let v: Bool = m.input();
|
||||
//! #[hdl]
|
||||
//! let my_wire: UInt<8> = m.wire();
|
||||
//! let my_wire: UInt<8> = wire();
|
||||
//! #[hdl]
|
||||
//! if v {
|
||||
//! m.connect(my_wire, 0x45_hdl_u8);
|
||||
//! connect(my_wire, 0x45_hdl_u8);
|
||||
//! } else {
|
||||
//! // wires must be connected to under all conditions
|
||||
//! m.connect(my_wire, 0x23_hdl_u8);
|
||||
//! connect(my_wire, 0x23_hdl_u8);
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
|
@ -29,4 +29,4 @@
|
|||
//! [connection semantics]: crate::_docs::semantics::connection_semantics
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::{expr::Expr, module::ModuleBuilder};
|
||||
use crate::prelude::*;
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
//!
|
||||
//! You can have integer literals with an arbitrary number of bits like so:
|
||||
//!
|
||||
//! `_hdl`-suffixed literals have type [`Expr<UInt<N>>`] or [`Expr<SInt<N>>`]
|
||||
//! ... which are basically just [`UInt<N>`] or [`SInt<N>`] converted to an expression.
|
||||
//! `_hdl`-suffixed literals have type [`Expr<UInt<N>>`] or [`Expr<SInt<N>>`].
|
||||
//!
|
||||
//! ```
|
||||
//! # #[fayalite::hdl_module]
|
||||
|
|
|
@ -2,27 +2,24 @@
|
|||
//!
|
||||
//! Note: Structs are also known as [Bundles] when used in Fayalite, the Bundle name comes from [FIRRTL].
|
||||
//!
|
||||
//! [Bundles]: crate::bundle::BundleValue
|
||||
//! [Bundles]: crate::bundle::BundleType
|
||||
//! [FIRRTL]: https://github.com/chipsalliance/firrtl-spec
|
||||
//!
|
||||
//! `#[hdl]` can be used on Struct/Variant Expressions to construct a value of that
|
||||
//! struct/variant's type. They can also be used on tuples.
|
||||
//! `#[hdl]` can be used on Struct Expressions to construct a value of that
|
||||
//! struct's type. They can also be used on tuples.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt, array::Array, ty::Value};
|
||||
//! #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
//! #[hdl(static)]
|
||||
//! # use fayalite::prelude::*;
|
||||
//! #[hdl]
|
||||
//! pub struct MyStruct {
|
||||
//! pub a: UInt<8>,
|
||||
//! pub b: UInt<16>,
|
||||
//! }
|
||||
//!
|
||||
//! #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
//! #[hdl]
|
||||
//! pub enum MyEnum {
|
||||
//! A,
|
||||
//! B {
|
||||
//! v: UInt<32>,
|
||||
//! },
|
||||
//! B(UInt<32>),
|
||||
//! }
|
||||
//!
|
||||
//! # #[hdl_module]
|
||||
|
@ -30,8 +27,8 @@
|
|||
//! #[hdl]
|
||||
//! let v: UInt<8> = m.input();
|
||||
//! #[hdl]
|
||||
//! let my_struct: MyStruct = m.wire();
|
||||
//! m.connect(
|
||||
//! let my_struct: MyStruct = wire();
|
||||
//! connect(
|
||||
//! my_struct,
|
||||
//! #[hdl]
|
||||
//! MyStruct {
|
||||
|
@ -40,15 +37,14 @@
|
|||
//! },
|
||||
//! );
|
||||
//! #[hdl]
|
||||
//! let my_enum: MyEnum = m.wire();
|
||||
//! m.connect(
|
||||
//! let my_enum: MyEnum = wire();
|
||||
//! connect(
|
||||
//! my_enum,
|
||||
//! #[hdl]
|
||||
//! MyEnum::B { v: 12345678_hdl_u32 },
|
||||
//! MyEnum.B(12345678_hdl_u32),
|
||||
//! );
|
||||
//! #[hdl]
|
||||
//! let some_tuple: (UInt<4>, UInt<12>) = m.wire();
|
||||
//! m.connect(
|
||||
//! let some_tuple: (UInt<4>, UInt<12>) = wire();
|
||||
//! connect(
|
||||
//! some_tuple,
|
||||
//! #[hdl]
|
||||
//! (12_hdl_u4, 3421_hdl_u12),
|
||||
|
@ -57,4 +53,4 @@
|
|||
//! ```
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::array::Array;
|
||||
use crate::prelude::*;
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
//! # Normal Modules
|
||||
//!
|
||||
//! These use the [`NormalModule`][`crate::module::NormalModule`] tag type.
|
||||
//!
|
||||
//! See also: [Extern Modules][`super::extern_module`]
|
||||
//! See also: [Module Bodies][`super::module_bodies`]
|
||||
|
|
|
@ -20,62 +20,60 @@
|
|||
//! Connection Semantics Example:
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let a: UInt<8> = m.wire();
|
||||
//! let a: UInt<8> = wire();
|
||||
//! #[hdl]
|
||||
//! let b: UInt<8> = m.output();
|
||||
//!
|
||||
//! // doesn't actually affect anything, since `a` is completely overwritten later
|
||||
//! m.connect(a, 5_hdl_u8);
|
||||
//! connect(a, 5_hdl_u8);
|
||||
//!
|
||||
//! // here `a` has value `7` since the last connection assigns
|
||||
//! // `7` to `a`, so `b` has value `7` too.
|
||||
//! m.connect(b, a);
|
||||
//! connect(b, a);
|
||||
//!
|
||||
//! // this is the last `connect` to `a`, so this `connect` determines `a`'s value
|
||||
//! m.connect(a, 7_hdl_u8);
|
||||
//! connect(a, 7_hdl_u8);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Conditional Connection Semantics
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::{hdl_module, int::UInt};
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let cond: UInt<1> = m.input();
|
||||
//! let cond: Bool = m.input();
|
||||
//! #[hdl]
|
||||
//! let a: UInt<8> = m.wire();
|
||||
//! let a: UInt<8> = wire();
|
||||
//! #[hdl]
|
||||
//! let b: UInt<8> = m.output();
|
||||
//!
|
||||
//! // this is the last `connect` to `a` when `cond` is `0`
|
||||
//! m.connect(a, 5_hdl_u8);
|
||||
//! connect(a, 5_hdl_u8);
|
||||
//!
|
||||
//! // here `a` has value `7` if `cond` is `1` since the last connection assigns
|
||||
//! // `7` to `a`, so `b` has value `7` too, otherwise `a` (and therefore `b`)
|
||||
//! // have value `5` since then the connection assigning `7` is in a
|
||||
//! // conditional block where the condition doesn't hold.
|
||||
//! m.connect(b, a);
|
||||
//! connect(b, a);
|
||||
//!
|
||||
//! #[hdl]
|
||||
//! if cond {
|
||||
//! // this is the last `connect` to `a` when `cond` is `1`
|
||||
//! m.connect(a, 7_hdl_u8);
|
||||
//! connect(a, 7_hdl_u8);
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! [conditional block]: self#conditional-connection-semantics
|
||||
//! [`connect()`]: ModuleBuilder::connect
|
||||
//! [`connect_any()`]: ModuleBuilder::connect_any
|
||||
//! [wire]: crate::_docs::modules::module_bodies::hdl_let_statements::wires
|
||||
//! [if]: crate::_docs::modules::module_bodies::hdl_if_statements
|
||||
//! [FIRRTL]: https://github.com/chipsalliance/firrtl-spec
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::module::ModuleBuilder;
|
||||
use crate::prelude::*;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
expr::Target,
|
||||
expr::target::Target,
|
||||
intern::{Intern, Interned},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,671 +1,234 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
bundle::{BundleType, BundleValue},
|
||||
expr::{
|
||||
ops::{ArrayIndex, ArrayLiteral, ExprIndex},
|
||||
Expr, ToExpr,
|
||||
},
|
||||
intern::{Intern, Interned, InternedCompare, Memoize},
|
||||
module::{
|
||||
transform::visit::{Fold, Folder, Visit, Visitor},
|
||||
ModuleBuilder, NormalModule,
|
||||
},
|
||||
expr::{ops::ArrayIndex, Expr, ToExpr},
|
||||
int::{DynSize, KnownSize, Size},
|
||||
intern::{Intern, Interned, LazyInterned},
|
||||
module::transform::visit::{Fold, Folder, Visit, Visitor},
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
|
||||
DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType,
|
||||
StaticValue, Type, TypeEnum, Value, ValueEnum,
|
||||
CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref,
|
||||
},
|
||||
util::{ConstBool, GenericConstBool, MakeMutSlice},
|
||||
};
|
||||
use bitvec::{slice::BitSlice, vec::BitVec};
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::{Borrow, BorrowMut},
|
||||
fmt,
|
||||
hash::Hash,
|
||||
marker::PhantomData,
|
||||
ops::IndexMut,
|
||||
sync::Arc,
|
||||
util::ConstUsize,
|
||||
};
|
||||
use std::ops::Index;
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
|
||||
element: LazyInterned<T>,
|
||||
len: Len::SizeType,
|
||||
type_properties: TypeProperties,
|
||||
}
|
||||
|
||||
pub trait ValueArrayOrSlice:
|
||||
sealed::Sealed
|
||||
+ BorrowMut<[<Self as ValueArrayOrSlice>::Element]>
|
||||
+ AsRef<[<Self as ValueArrayOrSlice>::Element]>
|
||||
+ AsMut<[<Self as ValueArrayOrSlice>::Element]>
|
||||
+ Hash
|
||||
+ fmt::Debug
|
||||
+ Eq
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ IndexMut<usize, Output = <Self as ValueArrayOrSlice>::Element>
|
||||
+ ToOwned
|
||||
+ InternedCompare
|
||||
{
|
||||
type Element: Value<Type = <Self as ValueArrayOrSlice>::ElementType>;
|
||||
type ElementType: Type<Value = <Self as ValueArrayOrSlice>::Element>;
|
||||
type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync;
|
||||
type Match: 'static
|
||||
+ Clone
|
||||
+ Eq
|
||||
+ fmt::Debug
|
||||
+ Hash
|
||||
+ Send
|
||||
+ Sync
|
||||
+ BorrowMut<[Expr<Self::Element>]>;
|
||||
type MaskVA: ValueArrayOrSlice<
|
||||
Element = <Self::ElementType as Type>::MaskValue,
|
||||
ElementType = <Self::ElementType as Type>::MaskType,
|
||||
LenType = Self::LenType,
|
||||
MaskVA = Self::MaskVA,
|
||||
> + ?Sized;
|
||||
type IsStaticLen: GenericConstBool;
|
||||
const FIXED_LEN_TYPE: Option<Self::LenType>;
|
||||
fn make_match(array: Expr<Array<Self>>) -> Self::Match;
|
||||
fn len_from_len_type(v: Self::LenType) -> usize;
|
||||
#[allow(clippy::result_unit_err)]
|
||||
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()>;
|
||||
fn len_type(&self) -> Self::LenType;
|
||||
fn len(&self) -> usize;
|
||||
fn is_empty(&self) -> bool;
|
||||
fn iter(&self) -> std::slice::Iter<Self::Element> {
|
||||
Borrow::<[_]>::borrow(self).iter()
|
||||
}
|
||||
fn clone_to_arc(&self) -> Arc<Self>;
|
||||
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self;
|
||||
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]>;
|
||||
}
|
||||
|
||||
impl<T> sealed::Sealed for [T] {}
|
||||
|
||||
impl<V: Value> ValueArrayOrSlice for [V]
|
||||
where
|
||||
V::Type: Type<Value = V>,
|
||||
{
|
||||
type Element = V;
|
||||
type ElementType = V::Type;
|
||||
type LenType = usize;
|
||||
type Match = Box<[Expr<V>]>;
|
||||
type MaskVA = [<Self::ElementType as Type>::MaskValue];
|
||||
type IsStaticLen = ConstBool<false>;
|
||||
const FIXED_LEN_TYPE: Option<Self::LenType> = None;
|
||||
|
||||
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
|
||||
(0..array.canonical_type().len())
|
||||
.map(|index| ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn len_from_len_type(v: Self::LenType) -> usize {
|
||||
v
|
||||
}
|
||||
|
||||
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn len_type(&self) -> Self::LenType {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
<[_]>::len(self)
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
<[_]>::is_empty(self)
|
||||
}
|
||||
|
||||
fn clone_to_arc(&self) -> Arc<Self> {
|
||||
Arc::from(self)
|
||||
}
|
||||
|
||||
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
|
||||
MakeMutSlice::make_mut_slice(v)
|
||||
}
|
||||
|
||||
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
|
||||
self
|
||||
impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Array<{:?}, {}>", self.element, self.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> sealed::Sealed for [T; N] {}
|
||||
pub type Array<
|
||||
T = CanonicalType,
|
||||
const LEN: usize = { <DynSize as crate::util::GenericConstUsize>::VALUE },
|
||||
> = ArrayType<T, ConstUsize<LEN>>;
|
||||
|
||||
impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N]
|
||||
where
|
||||
V::Type: Type<Value = V>,
|
||||
{
|
||||
type Element = V;
|
||||
type ElementType = V::Type;
|
||||
type LenType = ();
|
||||
type Match = [Expr<V>; N];
|
||||
type MaskVA = [<Self::ElementType as Type>::MaskValue; N];
|
||||
type IsStaticLen = ConstBool<true>;
|
||||
const FIXED_LEN_TYPE: Option<Self::LenType> = Some(());
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics;
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const ArrayType: ArrayWithoutGenerics = ArrayWithoutGenerics;
|
||||
|
||||
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
|
||||
std::array::from_fn(|index| {
|
||||
ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr()
|
||||
})
|
||||
}
|
||||
|
||||
fn len_from_len_type(_v: Self::LenType) -> usize {
|
||||
N
|
||||
}
|
||||
|
||||
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
|
||||
if v == N {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn len_type(&self) -> Self::LenType {}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
N
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
N == 0
|
||||
}
|
||||
|
||||
fn clone_to_arc(&self) -> Arc<Self> {
|
||||
Arc::new(self.clone())
|
||||
}
|
||||
|
||||
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
|
||||
Arc::make_mut(v)
|
||||
}
|
||||
|
||||
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> {
|
||||
element: VA::ElementType,
|
||||
len: VA::LenType,
|
||||
bit_width: usize,
|
||||
}
|
||||
|
||||
pub trait ArrayTypeTrait:
|
||||
Type<
|
||||
CanonicalType = ArrayType<[DynCanonicalValue]>,
|
||||
Value = Array<<Self as ArrayTypeTrait>::ValueArrayOrSlice>,
|
||||
CanonicalValue = Array<[DynCanonicalValue]>,
|
||||
MaskType = ArrayType<
|
||||
<<Self as ArrayTypeTrait>::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA,
|
||||
>,
|
||||
> + From<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
|
||||
+ Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
|
||||
+ BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
|
||||
+ sealed::Sealed
|
||||
+ Connect<Self>
|
||||
{
|
||||
type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType>
|
||||
+ ?Sized;
|
||||
type Element: Value<Type = Self::ElementType>;
|
||||
type ElementType: Type<Value = Self::Element>;
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> {
|
||||
type ValueArrayOrSlice = VA;
|
||||
type Element = VA::Element;
|
||||
type ElementType = VA::ElementType;
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayType<VA> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
element: self.element.clone(),
|
||||
len: self.len,
|
||||
bit_width: self.bit_width,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayType<VA> where VA::ElementType: Copy {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
|
||||
pub fn element(&self) -> &VA::ElementType {
|
||||
&self.element
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
VA::len_from_len_type(self.len)
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn bit_width(&self) -> usize {
|
||||
self.bit_width
|
||||
}
|
||||
pub fn into_slice_type(self) -> ArrayType<[VA::Element]> {
|
||||
ArrayType {
|
||||
len: self.len(),
|
||||
element: self.element,
|
||||
bit_width: self.bit_width,
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_with_len(element: VA::ElementType, len: usize) -> Self {
|
||||
Self::new_with_len_type(
|
||||
element,
|
||||
VA::try_len_type_from_len(len).expect("length should match"),
|
||||
)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self {
|
||||
let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else {
|
||||
panic!("array is too big: bit-width overflowed");
|
||||
impl<T: Type, Len: Size> ArrayType<T, Len> {
|
||||
const fn make_type_properties(element: TypeProperties, len: usize) -> TypeProperties {
|
||||
let TypeProperties {
|
||||
is_passive,
|
||||
is_storable,
|
||||
is_castable_from_bits,
|
||||
bit_width,
|
||||
} = element;
|
||||
let Some(bit_width) = bit_width.checked_mul(len) else {
|
||||
panic!("array too big");
|
||||
};
|
||||
ArrayType {
|
||||
element,
|
||||
len,
|
||||
TypeProperties {
|
||||
is_passive,
|
||||
is_storable,
|
||||
is_castable_from_bits,
|
||||
bit_width,
|
||||
}
|
||||
}
|
||||
pub fn new(element: T, len: Len::SizeType) -> Self {
|
||||
let type_properties =
|
||||
Self::make_type_properties(element.canonical().type_properties(), Len::as_usize(len));
|
||||
Self {
|
||||
element: LazyInterned::Interned(element.intern_sized()),
|
||||
len,
|
||||
type_properties,
|
||||
}
|
||||
}
|
||||
pub fn element(&self) -> T {
|
||||
*self.element
|
||||
}
|
||||
pub fn len(self) -> usize {
|
||||
Len::as_usize(self.len)
|
||||
}
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn type_properties(self) -> TypeProperties {
|
||||
self.type_properties
|
||||
}
|
||||
pub fn as_dyn_array(self) -> Array {
|
||||
Array::new_dyn(self.element().canonical(), self.len())
|
||||
}
|
||||
pub fn can_connect<T2: Type, Len2: Size>(self, rhs: ArrayType<T2, Len2>) -> bool {
|
||||
self.len() == rhs.len()
|
||||
&& self
|
||||
.element()
|
||||
.canonical()
|
||||
.can_connect(rhs.element().canonical())
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA>
|
||||
where
|
||||
VA::ElementType: Fold<State>,
|
||||
{
|
||||
impl<T: Type, Len: KnownSize> ArrayType<T, Len> {
|
||||
pub fn new_static(element: T) -> Self {
|
||||
Self::new(element, Len::SizeType::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
|
||||
const TYPE: Self = Self {
|
||||
element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()),
|
||||
len: Len::SIZE,
|
||||
type_properties: Self::TYPE_PROPERTIES,
|
||||
};
|
||||
const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> {
|
||||
element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()),
|
||||
len: Len::SIZE,
|
||||
type_properties: Self::MASK_TYPE_PROPERTIES,
|
||||
};
|
||||
const TYPE_PROPERTIES: TypeProperties =
|
||||
Self::make_type_properties(T::TYPE_PROPERTIES, Len::VALUE);
|
||||
const MASK_TYPE_PROPERTIES: TypeProperties =
|
||||
Self::make_type_properties(T::MASK_TYPE_PROPERTIES, Len::VALUE);
|
||||
}
|
||||
|
||||
impl<T: Type> Array<T> {
|
||||
pub fn new_dyn(element: T, len: usize) -> Self {
|
||||
Self::new(element, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type + Fold<State>, Len: Size, State: Folder + ?Sized> Fold<State> for ArrayType<T, Len> {
|
||||
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
|
||||
state.fold_array_type(self)
|
||||
}
|
||||
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
|
||||
Ok(Self::new_with_len_type(self.element.fold(state)?, self.len))
|
||||
|
||||
fn default_fold(self, state: &mut State) -> Result<Self, <State as Folder>::Error> {
|
||||
Ok(ArrayType::new(self.element().fold(state)?, self.len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA>
|
||||
where
|
||||
VA::ElementType: Visit<State>,
|
||||
impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State>
|
||||
for ArrayType<T, Len>
|
||||
{
|
||||
fn visit(&self, state: &mut State) -> Result<(), State::Error> {
|
||||
state.visit_array_type(self)
|
||||
}
|
||||
|
||||
fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
|
||||
self.element.visit(state)
|
||||
self.element().visit(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> {
|
||||
pub fn new_array(element: V::Type) -> Self {
|
||||
ArrayType::new_with_len_type(element, ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> {
|
||||
fn static_type() -> Self {
|
||||
Self::new_array(StaticType::static_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Value<Type: Type<Value = V>>> ArrayType<[V]> {
|
||||
pub fn new_slice(element: V::Type, len: usize) -> Self {
|
||||
ArrayType::new_with_len_type(element, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Type for ArrayType<VA> {
|
||||
type CanonicalType = ArrayType<[DynCanonicalValue]>;
|
||||
type Value = Array<VA>;
|
||||
type CanonicalValue = Array<[DynCanonicalValue]>;
|
||||
type MaskType = ArrayType<VA::MaskVA>;
|
||||
type MaskValue = Array<VA::MaskVA>;
|
||||
type MatchVariant = VA::Match;
|
||||
impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
||||
type BaseType = Array;
|
||||
type MaskType = ArrayType<T::MaskType, Len>;
|
||||
type MatchVariant = Len::ArrayMatch<T>;
|
||||
type MatchActiveScope = ();
|
||||
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>;
|
||||
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>;
|
||||
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
|
||||
|
||||
fn match_variants<IO: BundleValue>(
|
||||
this: Expr<Self::Value>,
|
||||
module_builder: &mut ModuleBuilder<IO, NormalModule>,
|
||||
fn match_variants(
|
||||
this: Expr<Self>,
|
||||
source_location: SourceLocation,
|
||||
) -> Self::MatchVariantsIter
|
||||
where
|
||||
IO::Type: BundleType<Value = IO>,
|
||||
{
|
||||
let _ = module_builder;
|
||||
) -> Self::MatchVariantsIter {
|
||||
let base = Expr::as_dyn_array(this);
|
||||
let base_ty = Expr::ty(base);
|
||||
let _ = source_location;
|
||||
std::iter::once(MatchVariantWithoutScope(VA::make_match(this)))
|
||||
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
|
||||
std::iter::once(MatchVariantWithoutScope(
|
||||
Len::ArrayMatch::<T>::try_from(retval)
|
||||
.ok()
|
||||
.expect("unreachable"),
|
||||
))
|
||||
}
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
struct ArrayMaskTypeMemoize<T: ArrayTypeTrait>(PhantomData<T>);
|
||||
impl<T: ArrayTypeTrait> Copy for ArrayMaskTypeMemoize<T> {}
|
||||
impl<T: ArrayTypeTrait> Memoize for ArrayMaskTypeMemoize<T> {
|
||||
type Input = ArrayType<T::ValueArrayOrSlice>;
|
||||
type InputOwned = ArrayType<T::ValueArrayOrSlice>;
|
||||
type Output = <ArrayType<T::ValueArrayOrSlice> as Type>::MaskType;
|
||||
|
||||
fn inner(self, input: &Self::Input) -> Self::Output {
|
||||
ArrayType::new_with_len_type(input.element.mask_type(), input.len)
|
||||
}
|
||||
}
|
||||
ArrayMaskTypeMemoize::<Self>(PhantomData).get(self)
|
||||
ArrayType::new(self.element().mask_type(), self.len)
|
||||
}
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
ArrayType {
|
||||
element: self.element.canonical_dyn(),
|
||||
len: self.len(),
|
||||
bit_width: self.bit_width,
|
||||
}
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
CanonicalType::Array(Array::new_dyn(self.element().canonical(), self.len()))
|
||||
}
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
#[track_caller]
|
||||
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||
let CanonicalType::Array(array) = canonical_type else {
|
||||
panic!("expected array");
|
||||
};
|
||||
Self::new(
|
||||
T::from_canonical(array.element()),
|
||||
Len::from_usize(array.len()),
|
||||
)
|
||||
}
|
||||
fn source_location() -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
}
|
||||
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::ArrayType(self.canonical())
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
Self {
|
||||
element: VA::ElementType::from_dyn_canonical_type(t.element),
|
||||
len: VA::try_len_type_from_len(t.len).expect("length should match"),
|
||||
bit_width: t.bit_width,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>(
|
||||
this,
|
||||
)?)
|
||||
impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
|
||||
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
|
||||
let base = Expr::as_dyn_array(*this);
|
||||
let base_ty = Expr::ty(base);
|
||||
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
|
||||
Interned::<_>::into_inner(Intern::intern_sized(
|
||||
Len::ArrayMatch::<T>::try_from(retval)
|
||||
.ok()
|
||||
.expect("unreachable"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>>
|
||||
for ArrayType<Lhs>
|
||||
{
|
||||
}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
|
||||
pub struct ArrayWithoutGenerics;
|
||||
|
||||
impl CanonicalType for ArrayType<[DynCanonicalValue]> {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType;
|
||||
}
|
||||
impl<T: Type> Index<T> for ArrayWithoutGenerics {
|
||||
type Output = ArrayWithoutLen<T>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Array<VA: ValueArrayOrSlice + ?Sized> {
|
||||
element_ty: VA::ElementType,
|
||||
value: Arc<VA>,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for Array<VA> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
element_ty: self.element_ty.clone(),
|
||||
value: self.value.clone(),
|
||||
}
|
||||
fn index(&self, element: T) -> &Self::Output {
|
||||
Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element }))
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> {
|
||||
type Type = ArrayType<VA>;
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ArrayWithoutLen<T: Type> {
|
||||
element: T,
|
||||
}
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type())
|
||||
}
|
||||
impl<T: Type> Index<usize> for ArrayWithoutLen<T> {
|
||||
type Output = Array<T>;
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::from_value(self)
|
||||
fn index(&self, len: usize) -> &Self::Output {
|
||||
Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
Array {
|
||||
element_ty: self.element_ty.canonical_dyn(),
|
||||
value: AsRef::<[_]>::as_ref(&*self.value)
|
||||
.iter()
|
||||
.map(|v| v.to_canonical_dyn())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>);
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayToBitsMemoize<VA> {}
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> {
|
||||
type Input = Array<VA>;
|
||||
type InputOwned = Array<VA>;
|
||||
type Output = Interned<BitSlice>;
|
||||
|
||||
fn inner(self, input: &Self::Input) -> Self::Output {
|
||||
let mut bits = BitVec::with_capacity(input.ty().bit_width());
|
||||
for element in AsRef::<[_]>::as_ref(&*input.value).iter() {
|
||||
bits.extend_from_bitslice(&element.to_bits());
|
||||
}
|
||||
Intern::intern_owned(bits)
|
||||
}
|
||||
}
|
||||
ArrayToBitsMemoize::<VA>(PhantomData).get(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for Array<[DynCanonicalValue]> {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::Array(this.clone())
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
Value::to_bits_impl(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Array<VA> {
|
||||
pub fn element_ty(&self) -> &VA::ElementType {
|
||||
&self.element_ty
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
VA::len_from_len_type(self.value.len_type())
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn value(&self) -> &Arc<VA> {
|
||||
&self.value
|
||||
}
|
||||
pub fn set_element(&mut self, index: usize, element: VA::Element) {
|
||||
assert_eq!(self.element_ty, element.ty());
|
||||
VA::arc_make_mut(&mut self.value)[index] = element;
|
||||
}
|
||||
pub fn new(element_ty: VA::ElementType, value: Arc<VA>) -> Self {
|
||||
for element in value.iter() {
|
||||
assert_eq!(element_ty, element.ty());
|
||||
}
|
||||
Self { element_ty, value }
|
||||
}
|
||||
pub fn into_slice(self) -> Array<[VA::Element]> {
|
||||
Array {
|
||||
element_ty: self.element_ty,
|
||||
value: self.value.arc_to_arc_slice(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized, T: Into<Arc<VA>>> From<T> for Array<VA>
|
||||
impl<T: Type, const LEN: usize> Index<ConstUsize<LEN>> for ArrayWithoutLen<T>
|
||||
where
|
||||
VA::Element: StaticValue,
|
||||
ConstUsize<LEN>: KnownSize,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
Self::new(StaticType::static_type(), value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for [E] {
|
||||
type Type = ArrayType<[T::Value]>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
ArrayType::new_with_len_type(StaticType::static_type(), self.len())
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
let elements = Intern::intern_owned(Vec::from_iter(
|
||||
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
|
||||
));
|
||||
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for Vec<E> {
|
||||
type Type = ArrayType<[T::Value]>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
<[E]>::ty(self)
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
<[E]>::to_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>, const N: usize> ToExpr for [E; N] {
|
||||
type Type = ArrayType<[T::Value; N]>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
ArrayType::new_with_len_type(StaticType::static_type(), ())
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
let elements = Intern::intern_owned(Vec::from_iter(
|
||||
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
|
||||
));
|
||||
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArrayIntoIter<VA: ValueArrayOrSlice> {
|
||||
array: Arc<VA>,
|
||||
indexes: std::ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> {
|
||||
type Item = VA::Element;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(self.array[self.indexes.next()?].clone())
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.indexes.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
Some(self.array[self.indexes.next_back()?].clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Array<VA> {
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> {
|
||||
self.value.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> {
|
||||
type Item = &'a VA::Element;
|
||||
type IntoIter = std::slice::Iter<'a, VA::Element>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.value.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> {
|
||||
type Item = VA::Element;
|
||||
type IntoIter = ArrayIntoIter<VA>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
ArrayIntoIter {
|
||||
indexes: 0..self.len(),
|
||||
array: self.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArrayExprIter<VA: ValueArrayOrSlice> {
|
||||
array: Expr<Array<VA>>,
|
||||
indexes: std::ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> {
|
||||
type Item = Expr<VA::Element>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(ExprIndex::expr_index(self.array, self.indexes.next()?))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.indexes.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> {
|
||||
type Item = Expr<VA::Element>;
|
||||
type IntoIter = ArrayExprIter<VA>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> {
|
||||
type Item = Expr<VA::Element>;
|
||||
type IntoIter = ArrayExprIter<VA>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Expr<Array<VA>> {
|
||||
pub fn len(self) -> usize {
|
||||
self.canonical_type().len()
|
||||
}
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.canonical_type().is_empty()
|
||||
}
|
||||
pub fn iter(self) -> ArrayExprIter<VA> {
|
||||
ArrayExprIter {
|
||||
indexes: 0..self.len(),
|
||||
array: self,
|
||||
}
|
||||
type Output = Array<T, LEN>;
|
||||
|
||||
fn index(&self, len: ConstUsize<LEN>) -> &Self::Output {
|
||||
Interned::<_>::into_inner(Intern::intern_sized(Array::new(self.element, len)))
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
bundle::{BundleType, BundleValue, DynBundle},
|
||||
bundle::{Bundle, BundleType},
|
||||
firrtl,
|
||||
intern::Interned,
|
||||
module::Module,
|
||||
|
@ -82,7 +82,7 @@ impl FirrtlOutput {
|
|||
}
|
||||
|
||||
impl FirrtlArgs {
|
||||
fn run_impl(&self, top_module: Module<DynBundle>) -> Result<FirrtlOutput> {
|
||||
fn run_impl(&self, top_module: Module<Bundle>) -> Result<FirrtlOutput> {
|
||||
let firrtl::FileBackend {
|
||||
top_fir_file_stem, ..
|
||||
} = firrtl::export(self.base.to_firrtl_file_backend(), &top_module)?;
|
||||
|
@ -94,20 +94,14 @@ impl FirrtlArgs {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: BundleValue> RunPhase<Module<T>> for FirrtlArgs
|
||||
where
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
impl<T: BundleType> RunPhase<Module<T>> for FirrtlArgs {
|
||||
type Output = FirrtlOutput;
|
||||
fn run(&self, top_module: Module<T>) -> Result<Self::Output> {
|
||||
self.run_impl(top_module.canonical())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BundleValue> RunPhase<Interned<Module<T>>> for FirrtlArgs
|
||||
where
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
impl<T: BundleType> RunPhase<Interned<Module<T>>> for FirrtlArgs {
|
||||
type Output = FirrtlOutput;
|
||||
fn run(&self, top_module: Interned<Module<T>>) -> Result<Self::Output> {
|
||||
self.run(*top_module)
|
||||
|
@ -228,7 +222,7 @@ enum CliCommand {
|
|||
/// Use like:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use fayalite::hdl_module;
|
||||
/// # use fayalite::prelude::*;
|
||||
/// # #[hdl_module]
|
||||
/// # fn my_module() {}
|
||||
/// use fayalite::cli;
|
||||
|
@ -241,7 +235,7 @@ enum CliCommand {
|
|||
/// You can also use it with a larger [`clap`]-based CLI like so:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use fayalite::hdl_module;
|
||||
/// # use fayalite::prelude::*;
|
||||
/// # #[hdl_module]
|
||||
/// # fn my_module() {}
|
||||
/// use clap::{Subcommand, Parser};
|
||||
|
|
|
@ -2,111 +2,61 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
expr::{Expr, ToExpr},
|
||||
int::{UInt, UIntType},
|
||||
intern::Interned,
|
||||
hdl,
|
||||
int::Bool,
|
||||
reset::Reset,
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
|
||||
DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum,
|
||||
},
|
||||
util::interned_bit,
|
||||
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
|
||||
};
|
||||
use bitvec::slice::BitSlice;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||
pub struct ClockType;
|
||||
pub struct Clock;
|
||||
|
||||
impl ClockType {
|
||||
pub const fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
impl Type for Clock {
|
||||
type BaseType = Clock;
|
||||
type MaskType = Bool;
|
||||
|
||||
impl Type for ClockType {
|
||||
type Value = Clock;
|
||||
type CanonicalType = ClockType;
|
||||
type CanonicalValue = Clock;
|
||||
type MaskType = UIntType<1>;
|
||||
type MaskValue = UInt<1>;
|
||||
|
||||
impl_match_values_as_self!();
|
||||
impl_match_variant_as_self!();
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
UIntType::new()
|
||||
Bool
|
||||
}
|
||||
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::Clock(*self)
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
CanonicalType::Clock(*self)
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
t
|
||||
}
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
*self
|
||||
}
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
fn source_location() -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(this)
|
||||
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||
let CanonicalType::Clock(retval) = canonical_type else {
|
||||
panic!("expected Clock");
|
||||
};
|
||||
retval
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect<Self> for ClockType {}
|
||||
|
||||
impl CanonicalType for ClockType {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Clock;
|
||||
}
|
||||
|
||||
impl StaticType for ClockType {
|
||||
fn static_type() -> Self {
|
||||
Self
|
||||
impl Clock {
|
||||
pub fn type_properties(self) -> TypeProperties {
|
||||
Self::TYPE_PROPERTIES
|
||||
}
|
||||
pub fn can_connect(self, _rhs: Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct Clock(pub bool);
|
||||
|
||||
impl ToExpr for Clock {
|
||||
type Type = ClockType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
ClockType
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<Self> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for Clock {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
*self
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for Clock {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::Clock(*this)
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Value)]
|
||||
#[hdl(static, outline_generated)]
|
||||
pub struct ClockDomain {
|
||||
pub clk: Clock,
|
||||
pub rst: Reset,
|
||||
impl StaticType for Clock {
|
||||
const TYPE: Self = Self;
|
||||
const MASK_TYPE: Self::MaskType = Bool;
|
||||
const TYPE_PROPERTIES: TypeProperties = TypeProperties {
|
||||
is_passive: true,
|
||||
is_storable: false,
|
||||
is_castable_from_bits: true,
|
||||
bit_width: 1,
|
||||
};
|
||||
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
|
||||
}
|
||||
|
||||
pub trait ToClock {
|
||||
|
@ -137,10 +87,10 @@ impl ToClock for Expr<Clock> {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToClock for Clock {
|
||||
fn to_clock(&self) -> Expr<Clock> {
|
||||
self.to_expr()
|
||||
}
|
||||
#[hdl]
|
||||
pub struct ClockDomain {
|
||||
pub clk: Clock,
|
||||
pub rst: Reset,
|
||||
}
|
||||
|
||||
impl ToClock for bool {
|
||||
|
@ -148,9 +98,3 @@ impl ToClock for bool {
|
|||
self.to_expr().to_clock()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToClock for UInt<1> {
|
||||
fn to_clock(&self) -> Expr<Clock> {
|
||||
self.to_expr().to_clock()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +1,39 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
use crate::{
|
||||
bundle::{BundleValue, TypeHintTrait},
|
||||
expr::{ops::VariantAccess, Expr, ToExpr},
|
||||
int::{UInt, UIntType},
|
||||
intern::{Intern, Interned, MemoizeGeneric},
|
||||
hdl,
|
||||
int::Bool,
|
||||
intern::{Intern, Interned},
|
||||
module::{
|
||||
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder,
|
||||
NormalModule, Scope,
|
||||
enum_match_variants_helper, EnumMatchVariantAndInactiveScopeImpl,
|
||||
EnumMatchVariantsIterImpl, Scope,
|
||||
},
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
|
||||
DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum,
|
||||
},
|
||||
ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties},
|
||||
};
|
||||
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
|
||||
use hashbrown::HashMap;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
iter::FusedIterator,
|
||||
marker::PhantomData,
|
||||
};
|
||||
use std::{fmt, iter::FusedIterator};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct VariantType<T> {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct EnumVariant {
|
||||
pub name: Interned<str>,
|
||||
pub ty: Option<T>,
|
||||
pub ty: Option<CanonicalType>,
|
||||
}
|
||||
|
||||
pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>);
|
||||
impl EnumVariant {
|
||||
pub fn fmt_debug_in_enum(self) -> FmtDebugInEnum {
|
||||
FmtDebugInEnum(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> {
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct FmtDebugInEnum(EnumVariant);
|
||||
|
||||
impl fmt::Debug for FmtDebugInEnum {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let VariantType { name, ref ty } = *self.0;
|
||||
let EnumVariant { name, ty } = self.0;
|
||||
if let Some(ty) = ty {
|
||||
write!(f, "{name}({ty:?})")
|
||||
} else {
|
||||
|
@ -45,76 +42,22 @@ impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> {
|
||||
impl fmt::Display for FmtDebugInEnum {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> VariantType<T> {
|
||||
pub fn map_opt_ty<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> VariantType<U> {
|
||||
let Self { name, ty } = self;
|
||||
VariantType { name, ty: f(ty) }
|
||||
}
|
||||
pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> VariantType<U> {
|
||||
let Self { name, ty } = self;
|
||||
VariantType {
|
||||
name,
|
||||
ty: ty.map(f),
|
||||
}
|
||||
}
|
||||
pub fn as_ref_ty(&self) -> VariantType<&T> {
|
||||
VariantType {
|
||||
name: self.name,
|
||||
ty: self.ty.as_ref(),
|
||||
}
|
||||
}
|
||||
pub fn fmt_debug_in_enum(&self) -> FmtDebugInEnum<T> {
|
||||
FmtDebugInEnum(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> VariantType<T> {
|
||||
pub fn canonical(&self) -> VariantType<T::CanonicalType> {
|
||||
self.as_ref_ty().map_ty(T::canonical)
|
||||
}
|
||||
pub fn to_dyn(&self) -> VariantType<Interned<dyn DynType>> {
|
||||
self.as_ref_ty().map_ty(T::to_dyn)
|
||||
}
|
||||
pub fn canonical_dyn(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
|
||||
self.as_ref_ty().map_ty(T::canonical_dyn)
|
||||
}
|
||||
}
|
||||
|
||||
impl VariantType<Interned<dyn DynCanonicalType>> {
|
||||
pub fn from_canonical_type_helper_has_value<T: Type>(self, expected_name: &str) -> T {
|
||||
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
|
||||
let Some(ty) = self.ty else {
|
||||
panic!("variant {expected_name} has no value but a value is expected");
|
||||
};
|
||||
T::from_dyn_canonical_type(ty)
|
||||
}
|
||||
pub fn from_canonical_type_helper_no_value(self, expected_name: &str) {
|
||||
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
|
||||
assert!(
|
||||
self.ty.is_none(),
|
||||
"variant {expected_name} has a value but is expected to have no value"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq)]
|
||||
struct DynEnumTypeImpl {
|
||||
variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>,
|
||||
struct EnumImpl {
|
||||
variants: Interned<[EnumVariant]>,
|
||||
name_indexes: HashMap<Interned<str>, usize>,
|
||||
bit_width: usize,
|
||||
is_storable: bool,
|
||||
is_castable_from_bits: bool,
|
||||
type_properties: TypeProperties,
|
||||
}
|
||||
|
||||
impl fmt::Debug for DynEnumTypeImpl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "DynEnumType ")?;
|
||||
impl std::fmt::Debug for EnumImpl {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Enum ")?;
|
||||
f.debug_set()
|
||||
.entries(
|
||||
self.variants
|
||||
|
@ -125,66 +68,123 @@ impl fmt::Debug for DynEnumTypeImpl {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq for DynEnumTypeImpl {
|
||||
impl std::hash::Hash for EnumImpl {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.variants.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for EnumImpl {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.variants == other.variants
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for DynEnumTypeImpl {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.variants.hash(state);
|
||||
}
|
||||
}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Enum(Interned<EnumImpl>);
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct DynEnumType(Interned<DynEnumTypeImpl>);
|
||||
|
||||
impl fmt::Debug for DynEnumType {
|
||||
impl fmt::Debug for Enum {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
fn discriminant_bit_width_impl(variant_count: usize) -> usize {
|
||||
variant_count
|
||||
.next_power_of_two()
|
||||
.checked_ilog2()
|
||||
.unwrap_or(0) as usize
|
||||
const fn discriminant_bit_width_impl(variant_count: usize) -> usize {
|
||||
match variant_count.next_power_of_two().checked_ilog2() {
|
||||
Some(x) => x as usize,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
impl DynEnumType {
|
||||
#[derive(Clone)]
|
||||
pub struct EnumTypePropertiesBuilder {
|
||||
type_properties: TypeProperties,
|
||||
variant_count: usize,
|
||||
}
|
||||
|
||||
impl EnumTypePropertiesBuilder {
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
type_properties: TypeProperties {
|
||||
is_passive: true,
|
||||
is_storable: true,
|
||||
is_castable_from_bits: true,
|
||||
bit_width: 0,
|
||||
},
|
||||
variant_count: 0,
|
||||
}
|
||||
}
|
||||
pub const fn clone(&self) -> Self {
|
||||
Self { ..*self }
|
||||
}
|
||||
#[must_use]
|
||||
pub const fn variant(self, field_props: Option<TypeProperties>) -> Self {
|
||||
let Self {
|
||||
mut type_properties,
|
||||
variant_count,
|
||||
} = self;
|
||||
if let Some(TypeProperties {
|
||||
is_passive,
|
||||
is_storable,
|
||||
is_castable_from_bits,
|
||||
bit_width,
|
||||
}) = field_props
|
||||
{
|
||||
assert!(is_passive, "variant type must be a passive type");
|
||||
type_properties = TypeProperties {
|
||||
is_passive: true,
|
||||
is_storable: type_properties.is_storable & is_storable,
|
||||
is_castable_from_bits: type_properties.is_castable_from_bits
|
||||
& is_castable_from_bits,
|
||||
bit_width: if type_properties.bit_width < bit_width {
|
||||
bit_width
|
||||
} else {
|
||||
type_properties.bit_width
|
||||
},
|
||||
};
|
||||
}
|
||||
Self {
|
||||
type_properties,
|
||||
variant_count: variant_count + 1,
|
||||
}
|
||||
}
|
||||
pub const fn finish(self) -> TypeProperties {
|
||||
assert!(
|
||||
self.variant_count != 0,
|
||||
"zero-variant enums aren't yet supported: \
|
||||
https://github.com/chipsalliance/firrtl-spec/issues/208",
|
||||
);
|
||||
let Some(bit_width) = self
|
||||
.type_properties
|
||||
.bit_width
|
||||
.checked_add(discriminant_bit_width_impl(self.variant_count))
|
||||
else {
|
||||
panic!("enum is too big: bit-width overflowed");
|
||||
};
|
||||
TypeProperties {
|
||||
bit_width,
|
||||
..self.type_properties
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Enum {
|
||||
#[track_caller]
|
||||
pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self {
|
||||
assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208");
|
||||
pub fn new(variants: Interned<[EnumVariant]>) -> Self {
|
||||
let mut name_indexes = HashMap::with_capacity(variants.len());
|
||||
let mut body_bit_width = 0usize;
|
||||
let mut is_storable = true;
|
||||
let mut is_castable_from_bits = true;
|
||||
for (index, &VariantType { name, ty }) in variants.iter().enumerate() {
|
||||
if let Some(old_index) = name_indexes.insert(name, index) {
|
||||
let mut type_props_builder = EnumTypePropertiesBuilder::new();
|
||||
for (index, EnumVariant { name, ty }) in variants.iter().enumerate() {
|
||||
if let Some(old_index) = name_indexes.insert(*name, index) {
|
||||
panic!("duplicate variant name {name:?}: at both index {old_index} and {index}");
|
||||
}
|
||||
if let Some(ty) = ty {
|
||||
assert!(
|
||||
ty.is_passive(),
|
||||
"variant type must be a passive type: {ty:?}"
|
||||
);
|
||||
body_bit_width = body_bit_width.max(ty.bit_width());
|
||||
is_storable &= ty.is_storable();
|
||||
is_castable_from_bits &= ty.is_castable_from_bits();
|
||||
}
|
||||
type_props_builder = type_props_builder.variant(ty.map(CanonicalType::type_properties));
|
||||
}
|
||||
let bit_width = body_bit_width
|
||||
.checked_add(discriminant_bit_width_impl(variants.len()))
|
||||
.unwrap_or_else(|| panic!("enum is too big: bit-width overflowed"));
|
||||
Self(
|
||||
DynEnumTypeImpl {
|
||||
EnumImpl {
|
||||
variants,
|
||||
name_indexes,
|
||||
bit_width,
|
||||
is_storable,
|
||||
is_castable_from_bits,
|
||||
type_properties: type_props_builder.finish(),
|
||||
}
|
||||
.intern_sized(),
|
||||
)
|
||||
|
@ -192,233 +192,62 @@ impl DynEnumType {
|
|||
pub fn discriminant_bit_width(self) -> usize {
|
||||
discriminant_bit_width_impl(self.variants().len())
|
||||
}
|
||||
pub fn is_passive(self) -> bool {
|
||||
true
|
||||
}
|
||||
pub fn is_storable(self) -> bool {
|
||||
self.0.is_storable
|
||||
}
|
||||
pub fn is_castable_from_bits(self) -> bool {
|
||||
self.0.is_castable_from_bits
|
||||
}
|
||||
pub fn bit_width(self) -> usize {
|
||||
self.0.bit_width
|
||||
pub fn type_properties(self) -> TypeProperties {
|
||||
self.0.type_properties
|
||||
}
|
||||
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
|
||||
&self.0.name_indexes
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct DynEnum {
|
||||
ty: DynEnumType,
|
||||
variant_index: usize,
|
||||
variant_value: Option<DynCanonicalValue>,
|
||||
}
|
||||
|
||||
impl DynEnum {
|
||||
#[track_caller]
|
||||
pub fn new_by_index(
|
||||
ty: DynEnumType,
|
||||
variant_index: usize,
|
||||
variant_value: Option<DynCanonicalValue>,
|
||||
) -> Self {
|
||||
let variant = ty.variants()[variant_index];
|
||||
assert_eq!(
|
||||
variant_value.as_ref().map(|v| v.ty()),
|
||||
variant.ty,
|
||||
"variant value doesn't match type"
|
||||
);
|
||||
Self {
|
||||
ty,
|
||||
variant_index,
|
||||
variant_value,
|
||||
pub fn can_connect(self, rhs: Self) -> bool {
|
||||
if self.0.variants.len() != rhs.0.variants.len() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_by_name(
|
||||
ty: DynEnumType,
|
||||
variant_name: Interned<str>,
|
||||
variant_value: Option<DynCanonicalValue>,
|
||||
) -> Self {
|
||||
let variant_index = ty.name_indexes()[&variant_name];
|
||||
Self::new_by_index(ty, variant_index, variant_value)
|
||||
}
|
||||
pub fn variant_index(&self) -> usize {
|
||||
self.variant_index
|
||||
}
|
||||
pub fn variant_value(&self) -> &Option<DynCanonicalValue> {
|
||||
&self.variant_value
|
||||
}
|
||||
pub fn variant_with_type(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
|
||||
self.ty.variants()[self.variant_index]
|
||||
}
|
||||
pub fn variant_name(&self) -> Interned<str> {
|
||||
self.variant_with_type().name
|
||||
}
|
||||
pub fn variant_type(&self) -> Option<Interned<dyn DynCanonicalType>> {
|
||||
self.variant_with_type().ty
|
||||
}
|
||||
pub fn variant_with_value(&self) -> VariantType<&DynCanonicalValue> {
|
||||
self.variant_with_type()
|
||||
.map_opt_ty(|_| self.variant_value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct VariantsHint {
|
||||
pub known_variants: Interned<[VariantType<Interned<dyn TypeHintTrait>>]>,
|
||||
pub more_variants: bool,
|
||||
}
|
||||
|
||||
impl VariantsHint {
|
||||
pub fn new(
|
||||
known_variants: impl IntoIterator<Item = VariantType<Interned<dyn TypeHintTrait>>>,
|
||||
more_variants: bool,
|
||||
) -> Self {
|
||||
let known_variants = Intern::intern_owned(Vec::from_iter(known_variants));
|
||||
Self {
|
||||
known_variants,
|
||||
more_variants,
|
||||
}
|
||||
}
|
||||
pub fn check_variant(
|
||||
self,
|
||||
index: usize,
|
||||
variant: VariantType<&dyn DynType>,
|
||||
) -> Result<(), String> {
|
||||
let Some(&known_variant) = self.known_variants.get(index) else {
|
||||
return if self.more_variants {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"too many variants: name={:?} index={index}",
|
||||
variant.name
|
||||
))
|
||||
};
|
||||
};
|
||||
let VariantType {
|
||||
name: known_name,
|
||||
ty: type_hint,
|
||||
} = known_variant;
|
||||
let VariantType { name, ty } = variant;
|
||||
if name != known_name {
|
||||
Err(format!(
|
||||
"wrong variant name {name:?}, expected {known_name:?}"
|
||||
))
|
||||
} else {
|
||||
match (ty, type_hint) {
|
||||
(Some(ty), Some(type_hint)) => type_hint.matches(ty),
|
||||
(None, None) => Ok(()),
|
||||
(None, Some(_)) => Err(format!(
|
||||
"expected variant {name:?} to have type, no type provided"
|
||||
)),
|
||||
(Some(_), None) => Err(format!(
|
||||
"expected variant {name:?} to have no type, but a type was provided"
|
||||
)),
|
||||
for (
|
||||
&EnumVariant {
|
||||
name: lhs_name,
|
||||
ty: lhs_ty,
|
||||
},
|
||||
&EnumVariant {
|
||||
name: rhs_name,
|
||||
ty: rhs_ty,
|
||||
},
|
||||
) in self.0.variants.iter().zip(rhs.0.variants.iter())
|
||||
{
|
||||
if lhs_name != rhs_name {
|
||||
return false;
|
||||
}
|
||||
match (lhs_ty, rhs_ty) {
|
||||
(None, None) => {}
|
||||
(None, Some(_)) | (Some(_), None) => return false,
|
||||
(Some(lhs_ty), Some(rhs_ty)) => {
|
||||
if !lhs_ty.can_connect(rhs_ty) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EnumType:
|
||||
Type<
|
||||
CanonicalType = DynEnumType,
|
||||
CanonicalValue = DynEnum,
|
||||
MaskType = UIntType<1>,
|
||||
MaskValue = UInt<1>,
|
||||
MatchActiveScope = Scope,
|
||||
MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
|
||||
MatchVariantsIter = EnumMatchVariantsIter<Self>,
|
||||
> + Connect<Self>
|
||||
where
|
||||
Self::Value: EnumValue + ToExpr<Type = Self>,
|
||||
BaseType = Enum,
|
||||
MaskType = Bool,
|
||||
MatchActiveScope = Scope,
|
||||
MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
|
||||
MatchVariantsIter = EnumMatchVariantsIter<Self>,
|
||||
>
|
||||
{
|
||||
type Builder;
|
||||
fn variants(&self) -> Interned<[EnumVariant]>;
|
||||
fn match_activate_scope(
|
||||
v: Self::MatchVariantAndInactiveScope,
|
||||
) -> (Self::MatchVariant, Self::MatchActiveScope);
|
||||
fn builder() -> Self::Builder;
|
||||
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]>;
|
||||
fn variants_hint() -> VariantsHint;
|
||||
#[allow(clippy::result_unit_err)]
|
||||
fn variant_to_bits<VariantValue: ToExpr + Eq + Hash + Send + Sync + 'static + Clone>(
|
||||
&self,
|
||||
variant_index: usize,
|
||||
variant_value: Option<&VariantValue>,
|
||||
) -> Result<Interned<BitSlice>, ()> {
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>);
|
||||
impl<E, V> Clone for VariantToBitsMemoize<E, V> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<E, V> Copy for VariantToBitsMemoize<E, V> {}
|
||||
impl<
|
||||
E: EnumType<Value: EnumValue<Type = E>>,
|
||||
V: ToExpr + Eq + Hash + Send + Sync + 'static + Clone,
|
||||
> MemoizeGeneric for VariantToBitsMemoize<E, V>
|
||||
{
|
||||
type InputRef<'a> = (&'a E, usize, Option<&'a V>);
|
||||
type InputOwned = (E, usize, Option<V>);
|
||||
type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>);
|
||||
type Output = Result<Interned<BitSlice>, ()>;
|
||||
|
||||
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
|
||||
(&input.0, input.1, input.2.as_ref())
|
||||
}
|
||||
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
|
||||
a == b
|
||||
}
|
||||
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
|
||||
(input.0.into_owned(), input.1, input.2.map(Cow::into_owned))
|
||||
}
|
||||
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
|
||||
(&input.0, input.1, input.2.as_deref())
|
||||
}
|
||||
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
|
||||
(Cow::Owned(input.0), input.1, input.2.map(Cow::Owned))
|
||||
}
|
||||
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
|
||||
(Cow::Borrowed(input.0), input.1, input.2.map(Cow::Borrowed))
|
||||
}
|
||||
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
|
||||
let (ty, variant_index, variant_value) = input;
|
||||
let ty = ty.canonical();
|
||||
let mut bits = BitVec::with_capacity(ty.bit_width());
|
||||
bits.extend_from_bitslice(
|
||||
&variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()],
|
||||
);
|
||||
if let Some(variant_value) = variant_value {
|
||||
bits.extend_from_bitslice(&variant_value.to_expr().to_literal_bits()?);
|
||||
}
|
||||
bits.resize(ty.bit_width(), false);
|
||||
Ok(Intern::intern_owned(bits))
|
||||
}
|
||||
}
|
||||
VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get((
|
||||
self,
|
||||
variant_index,
|
||||
variant_value,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EnumValue: Value
|
||||
where
|
||||
<Self as ToExpr>::Type: EnumType<Value = Self>,
|
||||
{
|
||||
}
|
||||
pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>);
|
||||
|
||||
pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>)
|
||||
where
|
||||
T::Value: EnumValue<Type = T>;
|
||||
|
||||
impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T> {
|
||||
type MatchVariant = T::MatchVariant;
|
||||
type MatchActiveScope = Scope;
|
||||
|
||||
|
@ -427,36 +256,22 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> {
|
||||
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> {
|
||||
pub fn variant_access(&self) -> VariantAccess {
|
||||
self.0.variant_access()
|
||||
}
|
||||
pub fn activate(
|
||||
self,
|
||||
) -> (
|
||||
Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>,
|
||||
Scope,
|
||||
) {
|
||||
pub fn activate(self) -> (VariantAccess, Scope) {
|
||||
self.0.activate()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EnumMatchVariantsIter<T: EnumType>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
pub struct EnumMatchVariantsIter<T: EnumType> {
|
||||
pub(crate) inner: EnumMatchVariantsIterImpl<T>,
|
||||
pub(crate) variant_index: std::ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<T: EnumType> Iterator for EnumMatchVariantsIter<T>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> {
|
||||
type Item = EnumMatchVariantAndInactiveScope<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
@ -470,21 +285,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> {
|
||||
fn len(&self) -> usize {
|
||||
self.variant_index.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> where T::Value: EnumValue<Type = T> {}
|
||||
impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> {}
|
||||
|
||||
impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.variant_index.next_back().map(|variant_index| {
|
||||
EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index))
|
||||
|
@ -492,129 +301,66 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl Type for DynEnumType {
|
||||
type CanonicalType = DynEnumType;
|
||||
type Value = DynEnum;
|
||||
type CanonicalValue = DynEnum;
|
||||
type MaskType = UIntType<1>;
|
||||
type MaskValue = UInt<1>;
|
||||
type MatchVariant = Option<Expr<DynCanonicalValue>>;
|
||||
type MatchActiveScope = Scope;
|
||||
type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
|
||||
type MatchVariantsIter = EnumMatchVariantsIter<Self>;
|
||||
|
||||
fn match_variants<IO: BundleValue>(
|
||||
this: Expr<Self::Value>,
|
||||
module_builder: &mut ModuleBuilder<IO, NormalModule>,
|
||||
source_location: SourceLocation,
|
||||
) -> Self::MatchVariantsIter
|
||||
where
|
||||
IO::Type: crate::bundle::BundleType<Value = IO>,
|
||||
{
|
||||
module_builder.enum_match_variants_helper(this, source_location)
|
||||
}
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
UIntType::new()
|
||||
}
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
*self
|
||||
}
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::EnumType(*self)
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
t
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect<Self> for DynEnumType {}
|
||||
|
||||
pub struct NoBuilder;
|
||||
|
||||
impl EnumType for DynEnumType {
|
||||
type Builder = NoBuilder;
|
||||
|
||||
impl EnumType for Enum {
|
||||
fn match_activate_scope(
|
||||
v: Self::MatchVariantAndInactiveScope,
|
||||
) -> (Self::MatchVariant, Self::MatchActiveScope) {
|
||||
let (expr, scope) = v.0.activate();
|
||||
(expr.variant_type().ty.map(|_| expr.to_expr()), scope)
|
||||
(expr.variant_type().map(|_| expr.to_expr()), scope)
|
||||
}
|
||||
|
||||
fn builder() -> Self::Builder {
|
||||
NoBuilder
|
||||
}
|
||||
|
||||
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]> {
|
||||
fn variants(&self) -> Interned<[EnumVariant]> {
|
||||
self.0.variants
|
||||
}
|
||||
}
|
||||
|
||||
fn variants_hint() -> VariantsHint {
|
||||
VariantsHint {
|
||||
known_variants: [][..].intern(),
|
||||
more_variants: true,
|
||||
}
|
||||
impl Type for Enum {
|
||||
type BaseType = Enum;
|
||||
type MaskType = Bool;
|
||||
type MatchVariant = Option<Expr<CanonicalType>>;
|
||||
type MatchActiveScope = Scope;
|
||||
type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
|
||||
type MatchVariantsIter = EnumMatchVariantsIter<Self>;
|
||||
|
||||
fn match_variants(
|
||||
this: Expr<Self>,
|
||||
source_location: SourceLocation,
|
||||
) -> Self::MatchVariantsIter {
|
||||
enum_match_variants_helper(this, source_location)
|
||||
}
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
Bool
|
||||
}
|
||||
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
CanonicalType::Enum(*self)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||
let CanonicalType::Enum(retval) = canonical_type else {
|
||||
panic!("expected enum");
|
||||
};
|
||||
retval
|
||||
}
|
||||
fn source_location() -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalType for DynEnumType {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::EnumType;
|
||||
#[hdl]
|
||||
pub enum HdlOption<T: Type> {
|
||||
HdlNone,
|
||||
HdlSome(T),
|
||||
}
|
||||
|
||||
impl ToExpr for DynEnum {
|
||||
type Type = DynEnumType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
|
||||
HdlOption[T::TYPE].HdlNone()
|
||||
}
|
||||
|
||||
impl Value for DynEnum {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
self.clone()
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
this.ty
|
||||
.variant_to_bits(this.variant_index, this.variant_value.as_ref())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumValue for DynEnum {}
|
||||
|
||||
impl CanonicalValue for DynEnum {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::Enum(this.clone())
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
this.ty
|
||||
.variant_to_bits(this.variant_index, this.variant_value.as_ref())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
mod impl_option {
|
||||
#[allow(dead_code)]
|
||||
#[derive(crate::ty::Value)]
|
||||
#[hdl(target(std::option::Option), connect_inexact, outline_generated)]
|
||||
pub enum Option<T> {
|
||||
None,
|
||||
Some(T),
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
pub fn HdlSome<T: Type>(value: impl ToExpr<Type = T>) -> Expr<HdlOption<T>> {
|
||||
let value = value.to_expr();
|
||||
HdlOption[Expr::ty(value)].HdlSome(value)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
432
crates/fayalite/src/expr/target.rs
Normal file
432
crates/fayalite/src/expr/target.rs
Normal file
|
@ -0,0 +1,432 @@
|
|||
use crate::{
|
||||
array::Array,
|
||||
bundle::{Bundle, BundleField},
|
||||
expr::Flow,
|
||||
intern::{Intern, Interned},
|
||||
memory::{DynPortType, MemPort},
|
||||
module::{Instance, ModuleIO, TargetName},
|
||||
reg::Reg,
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, Type},
|
||||
wire::Wire,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TargetPathBundleField {
|
||||
pub name: Interned<str>,
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetPathBundleField {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, ".{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TargetPathArrayElement {
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetPathArrayElement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[{}]", self.index)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TargetPathDynArrayElement {}
|
||||
|
||||
impl fmt::Display for TargetPathDynArrayElement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[<dyn>]")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum TargetPathElement {
|
||||
BundleField(TargetPathBundleField),
|
||||
ArrayElement(TargetPathArrayElement),
|
||||
DynArrayElement(TargetPathDynArrayElement),
|
||||
}
|
||||
|
||||
impl From<TargetPathBundleField> for TargetPathElement {
|
||||
fn from(value: TargetPathBundleField) -> Self {
|
||||
Self::BundleField(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TargetPathArrayElement> for TargetPathElement {
|
||||
fn from(value: TargetPathArrayElement) -> Self {
|
||||
Self::ArrayElement(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TargetPathDynArrayElement> for TargetPathElement {
|
||||
fn from(value: TargetPathDynArrayElement) -> Self {
|
||||
Self::DynArrayElement(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetPathElement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::BundleField(v) => v.fmt(f),
|
||||
Self::ArrayElement(v) => v.fmt(f),
|
||||
Self::DynArrayElement(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetPathElement {
|
||||
pub fn canonical_ty(&self, parent: Interned<Target>) -> CanonicalType {
|
||||
match self {
|
||||
&Self::BundleField(TargetPathBundleField { name }) => {
|
||||
let parent_ty = Bundle::from_canonical(parent.canonical_ty());
|
||||
let field = parent_ty
|
||||
.field_by_name(name)
|
||||
.expect("field name is known to be a valid field of parent type");
|
||||
field.ty
|
||||
}
|
||||
&Self::ArrayElement(TargetPathArrayElement { index }) => {
|
||||
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
|
||||
assert!(index < parent_ty.len());
|
||||
parent_ty.element()
|
||||
}
|
||||
Self::DynArrayElement(_) => {
|
||||
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
|
||||
parent_ty.element()
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn flow(&self, parent: Interned<Target>) -> Flow {
|
||||
match self {
|
||||
Self::BundleField(v) => {
|
||||
let parent_ty = Bundle::from_canonical(parent.canonical_ty());
|
||||
let field = parent_ty
|
||||
.field_by_name(v.name)
|
||||
.expect("field name is known to be a valid field of parent type");
|
||||
parent.flow().flip_if(field.flipped)
|
||||
}
|
||||
Self::ArrayElement(_) => parent.flow(),
|
||||
Self::DynArrayElement(_) => parent.flow(),
|
||||
}
|
||||
}
|
||||
pub fn is_static(&self) -> bool {
|
||||
match self {
|
||||
Self::BundleField(_) | Self::ArrayElement(_) => true,
|
||||
Self::DynArrayElement(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_target_base {
|
||||
(
|
||||
$(#[$enum_meta:meta])*
|
||||
$enum_vis:vis enum $TargetBase:ident {
|
||||
$(
|
||||
#[is = $is_fn:ident]
|
||||
#[to = $to_fn:ident]
|
||||
$(#[$variant_meta:meta])*
|
||||
$Variant:ident($VariantTy:ty),
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
$(#[$enum_meta])*
|
||||
$enum_vis enum $TargetBase {
|
||||
$(
|
||||
$(#[$variant_meta])*
|
||||
$Variant($VariantTy),
|
||||
)*
|
||||
}
|
||||
|
||||
impl fmt::Debug for $TargetBase {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.fmt(f),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl From<$VariantTy> for $TargetBase {
|
||||
fn from(value: $VariantTy) -> Self {
|
||||
Self::$Variant(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$VariantTy> for Target {
|
||||
fn from(value: $VariantTy) -> Self {
|
||||
$TargetBase::$Variant(value).into()
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
impl $TargetBase {
|
||||
$(
|
||||
$enum_vis fn $is_fn(&self) -> bool {
|
||||
self.$to_fn().is_some()
|
||||
}
|
||||
$enum_vis fn $to_fn(&self) -> Option<&$VariantTy> {
|
||||
if let Self::$Variant(retval) = self {
|
||||
Some(retval)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
)*
|
||||
$enum_vis fn must_connect_to(&self) -> bool {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.must_connect_to(),)*
|
||||
}
|
||||
}
|
||||
$enum_vis fn flow(&self) -> Flow {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.flow(),)*
|
||||
}
|
||||
}
|
||||
$enum_vis fn source_location(&self) -> SourceLocation {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.source_location(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_target_base! {
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub enum TargetBase {
|
||||
#[is = is_module_io]
|
||||
#[to = module_io]
|
||||
ModuleIO(ModuleIO<CanonicalType>),
|
||||
#[is = is_mem_port]
|
||||
#[to = mem_port]
|
||||
MemPort(MemPort<DynPortType>),
|
||||
#[is = is_reg]
|
||||
#[to = reg]
|
||||
Reg(Reg<CanonicalType>),
|
||||
#[is = is_wire]
|
||||
#[to = wire]
|
||||
Wire(Wire<CanonicalType>),
|
||||
#[is = is_instance]
|
||||
#[to = instance]
|
||||
Instance(Instance<Bundle>),
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetBase {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self.target_name())
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetBase {
|
||||
pub fn target_name(&self) -> TargetName {
|
||||
match self {
|
||||
TargetBase::ModuleIO(v) => TargetName(v.scoped_name(), None),
|
||||
TargetBase::MemPort(v) => TargetName(v.mem_name(), Some(v.port_name())),
|
||||
TargetBase::Reg(v) => TargetName(v.scoped_name(), None),
|
||||
TargetBase::Wire(v) => TargetName(v.scoped_name(), None),
|
||||
TargetBase::Instance(v) => TargetName(v.scoped_name(), None),
|
||||
}
|
||||
}
|
||||
pub fn canonical_ty(&self) -> CanonicalType {
|
||||
match self {
|
||||
TargetBase::ModuleIO(v) => v.ty(),
|
||||
TargetBase::MemPort(v) => v.ty().canonical(),
|
||||
TargetBase::Reg(v) => v.ty(),
|
||||
TargetBase::Wire(v) => v.ty(),
|
||||
TargetBase::Instance(v) => v.ty().canonical(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TargetChild {
|
||||
parent: Interned<Target>,
|
||||
path_element: Interned<TargetPathElement>,
|
||||
canonical_ty: CanonicalType,
|
||||
flow: Flow,
|
||||
}
|
||||
|
||||
impl fmt::Debug for TargetChild {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
parent,
|
||||
path_element,
|
||||
canonical_ty: _,
|
||||
flow: _,
|
||||
} = self;
|
||||
parent.fmt(f)?;
|
||||
fmt::Display::fmt(path_element, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetChild {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
parent,
|
||||
path_element,
|
||||
canonical_ty: _,
|
||||
flow: _,
|
||||
} = self;
|
||||
parent.fmt(f)?;
|
||||
path_element.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetChild {
|
||||
pub fn new(parent: Interned<Target>, path_element: Interned<TargetPathElement>) -> Self {
|
||||
Self {
|
||||
parent,
|
||||
path_element,
|
||||
canonical_ty: path_element.canonical_ty(parent),
|
||||
flow: path_element.flow(parent),
|
||||
}
|
||||
}
|
||||
pub fn parent(self) -> Interned<Target> {
|
||||
self.parent
|
||||
}
|
||||
pub fn path_element(self) -> Interned<TargetPathElement> {
|
||||
self.path_element
|
||||
}
|
||||
pub fn canonical_ty(self) -> CanonicalType {
|
||||
self.canonical_ty
|
||||
}
|
||||
pub fn flow(self) -> Flow {
|
||||
self.flow
|
||||
}
|
||||
pub fn bundle_field(self) -> Option<BundleField> {
|
||||
if let TargetPathElement::BundleField(TargetPathBundleField { name }) = *self.path_element {
|
||||
let parent_ty = Bundle::from_canonical(self.parent.canonical_ty());
|
||||
Some(
|
||||
parent_ty
|
||||
.field_by_name(name)
|
||||
.expect("field name known to be a valid field of parent"),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Target {
|
||||
Base(Interned<TargetBase>),
|
||||
Child(TargetChild),
|
||||
}
|
||||
|
||||
impl From<TargetBase> for Target {
|
||||
fn from(value: TargetBase) -> Self {
|
||||
Self::Base(Intern::intern_sized(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TargetChild> for Target {
|
||||
fn from(value: TargetChild) -> Self {
|
||||
Self::Child(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Interned<TargetBase>> for Target {
|
||||
fn from(value: Interned<TargetBase>) -> Self {
|
||||
Self::Base(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub fn base(&self) -> Interned<TargetBase> {
|
||||
let mut target = self;
|
||||
loop {
|
||||
match target {
|
||||
Self::Base(v) => break *v,
|
||||
Self::Child(v) => target = &v.parent,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn child(&self) -> Option<TargetChild> {
|
||||
match *self {
|
||||
Target::Base(_) => None,
|
||||
Target::Child(v) => Some(v),
|
||||
}
|
||||
}
|
||||
pub fn is_static(&self) -> bool {
|
||||
let mut target = self;
|
||||
loop {
|
||||
match target {
|
||||
Self::Base(_) => return true,
|
||||
Self::Child(v) if !v.path_element().is_static() => return false,
|
||||
Self::Child(v) => target = &v.parent,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
pub fn join(&self, path_element: Interned<TargetPathElement>) -> Self {
|
||||
TargetChild::new(self.intern(), path_element).into()
|
||||
}
|
||||
pub fn flow(&self) -> Flow {
|
||||
match self {
|
||||
Self::Base(v) => v.flow(),
|
||||
Self::Child(v) => v.flow(),
|
||||
}
|
||||
}
|
||||
pub fn canonical_ty(&self) -> CanonicalType {
|
||||
match self {
|
||||
Target::Base(v) => v.canonical_ty(),
|
||||
Target::Child(v) => v.canonical_ty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Target {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Base(v) => v.fmt(f),
|
||||
Self::Child(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Target {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Base(v) => v.fmt(f),
|
||||
Self::Child(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GetTarget {
|
||||
fn target(&self) -> Option<Interned<Target>>;
|
||||
}
|
||||
|
||||
impl GetTarget for bool {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetTarget + Send + Sync + 'static> GetTarget for Interned<T> {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
T::target(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetTarget> GetTarget for &'_ T {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
T::target(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetTarget> GetTarget for &'_ mut T {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
T::target(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetTarget> GetTarget for Box<T> {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
T::target(self)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -22,6 +22,179 @@ use std::{
|
|||
|
||||
pub mod type_map;
|
||||
|
||||
pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any {
|
||||
fn get(&self) -> Interned<T>;
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Send + Sync + 'static, F: ?Sized + Fn() -> Interned<T> + Send + Sync + Any>
|
||||
LazyInternedTrait<T> for F
|
||||
{
|
||||
fn get(&self) -> Interned<T> {
|
||||
self()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct LazyInternedFn<T: ?Sized + Send + Sync + 'static>(pub &'static dyn LazyInternedTrait<T>);
|
||||
|
||||
impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedFn<T> {}
|
||||
|
||||
impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedFn<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedFn<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.get_ptr_eq_with_type_id().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedFn<T> {}
|
||||
|
||||
impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedFn<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.get_ptr_eq_with_type_id() == other.0.get_ptr_eq_with_type_id()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LazyInterned<T: ?Sized + Send + Sync + 'static> {
|
||||
Interned(Interned<T>),
|
||||
Lazy(LazyInternedFn<T>),
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> {
|
||||
pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
|
||||
Self::Lazy(LazyInternedFn(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Sync + Send + 'static> Clone for LazyInterned<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {}
|
||||
|
||||
impl<T: ?Sized + Sync + Send + 'static + Intern> Deref for LazyInterned<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
Interned::into_inner(self.interned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Sync + Send + 'static + Intern> Eq for LazyInterned<T> where Interned<T>: Eq {}
|
||||
|
||||
impl<T: ?Sized + Sync + Send + 'static + Intern> PartialEq for LazyInterned<T>
|
||||
where
|
||||
Interned<T>: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.interned() == other.interned()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Sync + Send + 'static + Intern> Ord for LazyInterned<T>
|
||||
where
|
||||
Interned<T>: Ord,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.interned().cmp(&other.interned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Sync + Send + 'static + Intern> PartialOrd for LazyInterned<T>
|
||||
where
|
||||
Interned<T>: PartialOrd,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.interned().partial_cmp(&other.interned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Sync + Send + 'static + Intern> Hash for LazyInterned<T>
|
||||
where
|
||||
Interned<T>: Hash,
|
||||
{
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.interned().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Sync + Send + 'static> LazyInterned<T> {
|
||||
pub fn interned(self) -> Interned<T>
|
||||
where
|
||||
T: Intern,
|
||||
{
|
||||
struct MemoizeInterned<T: ?Sized + Intern>(PhantomData<T>);
|
||||
|
||||
impl<T: ?Sized + Intern> Hash for MemoizeInterned<T> {
|
||||
fn hash<H: Hasher>(&self, _state: &mut H) {}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Intern> PartialEq for MemoizeInterned<T> {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Intern> Eq for MemoizeInterned<T> {}
|
||||
|
||||
impl<T: ?Sized + Intern> Clone for MemoizeInterned<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Intern> Copy for MemoizeInterned<T> {}
|
||||
|
||||
impl<T: ?Sized + Intern> MemoizeGeneric for MemoizeInterned<T> {
|
||||
type InputRef<'a> = LazyInternedFn<T>;
|
||||
|
||||
type InputOwned = LazyInternedFn<T>;
|
||||
|
||||
type InputCow<'a> = LazyInternedFn<T>;
|
||||
|
||||
type Output = Interned<T>;
|
||||
|
||||
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
|
||||
a == b
|
||||
}
|
||||
|
||||
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
|
||||
*input
|
||||
}
|
||||
|
||||
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
|
||||
input
|
||||
}
|
||||
|
||||
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
|
||||
*input
|
||||
}
|
||||
|
||||
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
|
||||
input
|
||||
}
|
||||
|
||||
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
|
||||
input
|
||||
}
|
||||
|
||||
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
|
||||
input.0.get()
|
||||
}
|
||||
}
|
||||
match self {
|
||||
Self::Interned(retval) => retval,
|
||||
Self::Lazy(retval) => MemoizeInterned(PhantomData).get(retval),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InternedCompare {
|
||||
type InternedCompareKey: Ord + Hash;
|
||||
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey;
|
||||
|
@ -471,6 +644,12 @@ pub struct Interned<T: ?Sized + 'static + Send + Sync, C: InternContext = Global
|
|||
|
||||
macro_rules! forward_fmt_trait {
|
||||
($Tr:ident) => {
|
||||
impl<T: ?Sized + Intern + fmt::$Tr> fmt::$Tr for LazyInterned<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
T::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + 'static + Send + Sync + fmt::$Tr, C: InternContext> fmt::$Tr
|
||||
for Interned<T, C>
|
||||
{
|
||||
|
|
|
@ -12,7 +12,6 @@ extern crate self as fayalite;
|
|||
pub use std as __std;
|
||||
|
||||
#[doc(inline)]
|
||||
#[doc(alias = "hdl")]
|
||||
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
|
||||
/// a [`Module`][`::fayalite::module::Module`] when called.
|
||||
/// In the function body it will implicitly create a
|
||||
|
@ -21,9 +20,17 @@ pub use std as __std;
|
|||
/// See [Fayalite Modules][crate::_docs::modules]
|
||||
pub use fayalite_proc_macros::hdl_module;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use fayalite_proc_macros::hdl;
|
||||
|
||||
/// struct used as a placeholder when applying defaults
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct __;
|
||||
|
||||
#[cfg(feature = "unstable-doc")]
|
||||
pub mod _docs;
|
||||
|
||||
// FIXME: finish
|
||||
pub mod annotations;
|
||||
pub mod array;
|
||||
pub mod bundle;
|
||||
|
@ -41,5 +48,6 @@ pub mod reset;
|
|||
pub mod source_location;
|
||||
pub mod ty;
|
||||
pub mod util;
|
||||
pub mod valueless;
|
||||
//pub mod valueless;
|
||||
pub mod prelude;
|
||||
pub mod wire;
|
||||
|
|
|
@ -4,15 +4,16 @@
|
|||
#![allow(clippy::multiple_bound_locations)]
|
||||
use crate::{
|
||||
annotations::{Annotation, IntoAnnotations, TargetedAnnotation},
|
||||
array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice},
|
||||
bundle::{BundleType, BundleValue, DynBundle, DynBundleType},
|
||||
clock::{Clock, ClockType},
|
||||
expr::{Expr, ExprEnum, ExprTrait, Flow, ToExpr},
|
||||
int::{DynUInt, DynUIntType, UInt, UIntType},
|
||||
array::{Array, ArrayType},
|
||||
bundle::{Bundle, BundleType},
|
||||
clock::Clock,
|
||||
expr::{Expr, Flow, ToExpr, ToLiteralBits},
|
||||
hdl,
|
||||
int::{Bool, DynSize, Size, UInt, UIntType},
|
||||
intern::{Intern, Interned},
|
||||
module::ScopedNameId,
|
||||
source_location::SourceLocation,
|
||||
ty::{AsMask, DynCanonicalType, DynCanonicalValue, DynType, Type, Value},
|
||||
ty::{AsMask, CanonicalType, Type},
|
||||
util::DebugAsDisplay,
|
||||
};
|
||||
use bitvec::slice::BitSlice;
|
||||
|
@ -20,37 +21,37 @@ use std::{
|
|||
cell::RefCell,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
num::NonZeroU32,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
#[derive(Value, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ReadStruct<Element> {
|
||||
pub addr: DynUInt,
|
||||
pub en: UInt<1>,
|
||||
#[hdl]
|
||||
pub struct ReadStruct<Element, AddrWidth: Size> {
|
||||
pub addr: UIntType<AddrWidth>,
|
||||
pub en: Bool,
|
||||
pub clk: Clock,
|
||||
#[hdl(flip)]
|
||||
pub data: Element,
|
||||
}
|
||||
|
||||
#[derive(Value, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct WriteStruct<Element: Value> {
|
||||
pub addr: DynUInt,
|
||||
pub en: UInt<1>,
|
||||
#[hdl]
|
||||
pub struct WriteStruct<Element, AddrWidth: Size> {
|
||||
pub addr: UIntType<AddrWidth>,
|
||||
pub en: Bool,
|
||||
pub clk: Clock,
|
||||
pub data: Element,
|
||||
pub mask: AsMask<Element>,
|
||||
}
|
||||
|
||||
#[allow(clippy::multiple_bound_locations)]
|
||||
#[derive(Value, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ReadWriteStruct<Element: Value> {
|
||||
pub addr: DynUInt,
|
||||
pub en: UInt<1>,
|
||||
#[hdl]
|
||||
pub struct ReadWriteStruct<Element, AddrWidth: Size> {
|
||||
pub addr: UIntType<AddrWidth>,
|
||||
pub en: Bool,
|
||||
pub clk: Clock,
|
||||
#[hdl(flip)]
|
||||
pub rdata: Element,
|
||||
pub wmode: UInt<1>,
|
||||
pub wmode: Bool,
|
||||
pub wdata: Element,
|
||||
pub wmask: AsMask<Element>,
|
||||
}
|
||||
|
@ -63,11 +64,10 @@ pub trait PortType:
|
|||
sealed::Sealed + Clone + Eq + Hash + fmt::Debug + Send + Sync + 'static
|
||||
{
|
||||
type PortKindTy: Copy + Eq + Hash + fmt::Debug + Send + Sync + 'static;
|
||||
type PortType: BundleType<Value = Self::PortValue>;
|
||||
type PortValue: BundleValue<Type = Self::PortType>;
|
||||
type Port: BundleType;
|
||||
fn port_kind(port_kind: Self::PortKindTy) -> PortKind;
|
||||
fn from_port_kind(port_kind: PortKind) -> Self::PortKindTy;
|
||||
fn port_ty(port: &MemPort<Self>) -> Self::PortType;
|
||||
fn port_ty(port: &MemPort<Self>) -> Self::Port;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
@ -79,8 +79,7 @@ impl sealed::Sealed for DynPortType {}
|
|||
|
||||
impl PortType for DynPortType {
|
||||
type PortKindTy = PortKind;
|
||||
type PortType = DynBundleType;
|
||||
type PortValue = DynBundle;
|
||||
type Port = Bundle;
|
||||
|
||||
fn port_kind(port_kind: Self::PortKindTy) -> PortKind {
|
||||
port_kind
|
||||
|
@ -90,41 +89,38 @@ impl PortType for DynPortType {
|
|||
port_kind
|
||||
}
|
||||
|
||||
fn port_ty(port: &MemPort<Self>) -> Self::PortType {
|
||||
match port.port_kind {
|
||||
PortKind::ReadOnly => MemPort::<ReadStruct<DynCanonicalValue>>::from_canonical(*port)
|
||||
.ty()
|
||||
.canonical(),
|
||||
PortKind::WriteOnly => MemPort::<WriteStruct<DynCanonicalValue>>::from_canonical(*port)
|
||||
.ty()
|
||||
.canonical(),
|
||||
PortKind::ReadWrite => {
|
||||
MemPort::<ReadWriteStruct<DynCanonicalValue>>::from_canonical(*port)
|
||||
fn port_ty(port: &MemPort<Self>) -> Self::Port {
|
||||
Bundle::new(match port.port_kind {
|
||||
PortKind::ReadOnly => {
|
||||
MemPort::<ReadStruct<CanonicalType, DynSize>>::from_canonical(*port)
|
||||
.ty()
|
||||
.canonical()
|
||||
.fields()
|
||||
}
|
||||
}
|
||||
PortKind::WriteOnly => {
|
||||
MemPort::<WriteStruct<CanonicalType, DynSize>>::from_canonical(*port)
|
||||
.ty()
|
||||
.fields()
|
||||
}
|
||||
PortKind::ReadWrite => {
|
||||
MemPort::<ReadWriteStruct<CanonicalType, DynSize>>::from_canonical(*port)
|
||||
.ty()
|
||||
.fields()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PortStruct:
|
||||
BundleValue
|
||||
+ sealed::Sealed
|
||||
+ PortType<PortKindTy = (), PortType = <Self as ToExpr>::Type, PortValue = Self>
|
||||
where
|
||||
Self::Type: BundleType<Value = Self>,
|
||||
{
|
||||
type Element: Value<Type = Self::ElementType>;
|
||||
type ElementType: Type<Value = Self::Element>;
|
||||
pub trait PortStruct: BundleType + sealed::Sealed + PortType<PortKindTy = (), Port = Self> {
|
||||
type Element: Type;
|
||||
const PORT_KIND: PortKind;
|
||||
|
||||
fn addr(this: Expr<Self>) -> Expr<DynUInt>;
|
||||
fn en(this: Expr<Self>) -> Expr<UInt<1>>;
|
||||
fn addr(this: Expr<Self>) -> Expr<UInt>;
|
||||
fn en(this: Expr<Self>) -> Expr<Bool>;
|
||||
fn clk(this: Expr<Self>) -> Expr<Clock>;
|
||||
fn rdata(this: Expr<Self>) -> Option<Expr<Self::Element>>;
|
||||
fn wdata(this: Expr<Self>) -> Option<Expr<Self::Element>>;
|
||||
fn wmask(this: Expr<Self>) -> Option<Expr<AsMask<Self::Element>>>;
|
||||
fn wmode(this: Expr<Self>) -> Expr<UInt<1>>;
|
||||
fn wmode(this: Expr<Self>) -> Expr<Bool>;
|
||||
}
|
||||
|
||||
macro_rules! impl_port_struct {
|
||||
|
@ -137,19 +133,11 @@ macro_rules! impl_port_struct {
|
|||
$($body:tt)*
|
||||
}
|
||||
) => {
|
||||
impl<$Element: Value> sealed::Sealed for $Struct<$Element>
|
||||
where
|
||||
$Element::Type: Type<Value = $Element>,
|
||||
{
|
||||
}
|
||||
impl<$Element: Type> sealed::Sealed for $Struct<$Element, DynSize> {}
|
||||
|
||||
impl<$Element: Value> PortType for $Struct<$Element>
|
||||
where
|
||||
$Element::Type: Type<Value = $Element>,
|
||||
{
|
||||
impl<$Element: Type> PortType for $Struct<$Element, DynSize> {
|
||||
type PortKindTy = ();
|
||||
type PortType = <Self as ToExpr>::Type;
|
||||
type PortValue = Self;
|
||||
type Port = Self;
|
||||
|
||||
fn port_kind(_port_kind: Self::PortKindTy) -> PortKind {
|
||||
Self::PORT_KIND
|
||||
|
@ -164,18 +152,14 @@ macro_rules! impl_port_struct {
|
|||
}
|
||||
}
|
||||
|
||||
impl<$Element: Value> PortStruct for $Struct<$Element>
|
||||
where
|
||||
$Element::Type: Type<Value = $Element>,
|
||||
{
|
||||
impl<$Element: Type> PortStruct for $Struct<$Element, DynSize> {
|
||||
type Element = $Element;
|
||||
type ElementType = $Element::Type;
|
||||
|
||||
fn addr(this: Expr<Self>) -> Expr<DynUInt> {
|
||||
fn addr(this: Expr<Self>) -> Expr<UInt> {
|
||||
this.addr
|
||||
}
|
||||
|
||||
fn en(this: Expr<Self>) -> Expr<UInt<1>> {
|
||||
fn en(this: Expr<Self>) -> Expr<Bool> {
|
||||
this.en
|
||||
}
|
||||
|
||||
|
@ -190,14 +174,9 @@ macro_rules! impl_port_struct {
|
|||
|
||||
impl_port_struct! {
|
||||
impl<Element> _ for ReadStruct {
|
||||
fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type {
|
||||
type T<V> = <V as ToExpr>::Type;
|
||||
T::<Self> {
|
||||
addr: port.addr_type,
|
||||
en: UIntType::new(),
|
||||
clk: ClockType,
|
||||
data: Element::Type::from_dyn_canonical_type(port.mem_element_type),
|
||||
}
|
||||
fn port_ty(port: &MemPort<Self>) -> Self {
|
||||
let element = Element::from_canonical(port.mem_element_type);
|
||||
ReadStruct[element][port.addr_type.width()]
|
||||
}
|
||||
|
||||
const PORT_KIND: PortKind = PortKind::ReadOnly;
|
||||
|
@ -214,7 +193,7 @@ impl_port_struct! {
|
|||
None
|
||||
}
|
||||
|
||||
fn wmode(_this: Expr<Self>) -> Expr<UInt<1>> {
|
||||
fn wmode(_this: Expr<Self>) -> Expr<Bool> {
|
||||
false.to_expr()
|
||||
}
|
||||
}
|
||||
|
@ -222,16 +201,9 @@ impl_port_struct! {
|
|||
|
||||
impl_port_struct! {
|
||||
impl<Element> _ for WriteStruct {
|
||||
fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type {
|
||||
type T<V> = <V as ToExpr>::Type;
|
||||
let element_ty = Element::Type::from_dyn_canonical_type(port.mem_element_type);
|
||||
T::<Self> {
|
||||
addr: port.addr_type,
|
||||
en: UIntType::new(),
|
||||
clk: ClockType,
|
||||
mask: element_ty.mask_type(),
|
||||
data: element_ty,
|
||||
}
|
||||
fn port_ty(port: &MemPort<Self>) -> Self {
|
||||
let element = Element::from_canonical(port.mem_element_type);
|
||||
WriteStruct[element][port.addr_type.width()]
|
||||
}
|
||||
|
||||
const PORT_KIND: PortKind = PortKind::WriteOnly;
|
||||
|
@ -248,7 +220,7 @@ impl_port_struct! {
|
|||
Some(this.mask)
|
||||
}
|
||||
|
||||
fn wmode(_this: Expr<Self>) -> Expr<UInt<1>> {
|
||||
fn wmode(_this: Expr<Self>) -> Expr<Bool> {
|
||||
true.to_expr()
|
||||
}
|
||||
}
|
||||
|
@ -256,18 +228,9 @@ impl_port_struct! {
|
|||
|
||||
impl_port_struct! {
|
||||
impl<Element> _ for ReadWriteStruct {
|
||||
fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type {
|
||||
type T<V> = <V as ToExpr>::Type;
|
||||
let element_ty = Element::Type::from_dyn_canonical_type(port.mem_element_type);
|
||||
T::<Self> {
|
||||
addr: port.addr_type,
|
||||
en: UIntType::new(),
|
||||
clk: ClockType,
|
||||
rdata: element_ty.clone(),
|
||||
wmode: UIntType::new(),
|
||||
wmask: element_ty.mask_type(),
|
||||
wdata: element_ty,
|
||||
}
|
||||
fn port_ty(port: &MemPort<Self>) -> Self {
|
||||
let element = Element::from_canonical(port.mem_element_type);
|
||||
ReadWriteStruct[element][port.addr_type.width()]
|
||||
}
|
||||
|
||||
const PORT_KIND: PortKind = PortKind::ReadWrite;
|
||||
|
@ -284,7 +247,7 @@ impl_port_struct! {
|
|||
Some(this.wmask)
|
||||
}
|
||||
|
||||
fn wmode(this: Expr<Self>) -> Expr<UInt<1>> {
|
||||
fn wmode(this: Expr<Self>) -> Expr<Bool> {
|
||||
this.wmode
|
||||
}
|
||||
}
|
||||
|
@ -382,42 +345,31 @@ pub struct MemPort<T: PortType> {
|
|||
source_location: SourceLocation,
|
||||
port_kind: T::PortKindTy,
|
||||
port_index: usize,
|
||||
addr_type: DynUIntType,
|
||||
mem_element_type: Interned<dyn DynCanonicalType>,
|
||||
addr_type: UInt,
|
||||
mem_element_type: CanonicalType,
|
||||
}
|
||||
|
||||
impl<T: PortType> fmt::Debug for MemPort<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
mem_name,
|
||||
source_location: _,
|
||||
port_kind: _,
|
||||
port_index: _,
|
||||
addr_type,
|
||||
mem_element_type,
|
||||
} = self;
|
||||
f.debug_struct("MemPort")
|
||||
.field("mem_name", mem_name)
|
||||
.field("port_name", &self.port_name())
|
||||
.field("addr_type", addr_type)
|
||||
.field("mem_element_type", mem_element_type)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PortType> ToExpr for MemPort<T> {
|
||||
type Type = T::PortType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
T::port_ty(self)
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::new_unchecked(self.expr_enum())
|
||||
f.write_str("MemPort(")?;
|
||||
self.mem_name.fmt(f)?;
|
||||
f.write_str(".")?;
|
||||
self.port_name().fmt(f)?;
|
||||
f.write_str(": ")?;
|
||||
match self.port_kind() {
|
||||
PortKind::ReadOnly => f.write_str("ReadStruct<")?,
|
||||
PortKind::WriteOnly => f.write_str("WriteStruct<")?,
|
||||
PortKind::ReadWrite => f.write_str("ReadWriteStruct<")?,
|
||||
}
|
||||
self.mem_element_type.fmt(f)?;
|
||||
f.write_str(">)")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PortType> MemPort<T> {
|
||||
pub fn ty(&self) -> T::Port {
|
||||
T::port_ty(self)
|
||||
}
|
||||
pub fn source_location(&self) -> SourceLocation {
|
||||
self.source_location
|
||||
}
|
||||
|
@ -436,10 +388,10 @@ impl<T: PortType> MemPort<T> {
|
|||
index: self.port_index,
|
||||
}
|
||||
}
|
||||
pub fn mem_element_type(&self) -> Interned<dyn DynCanonicalType> {
|
||||
pub fn mem_element_type(&self) -> CanonicalType {
|
||||
self.mem_element_type
|
||||
}
|
||||
pub fn addr_type(&self) -> DynUIntType {
|
||||
pub fn addr_type(&self) -> UInt {
|
||||
self.addr_type
|
||||
}
|
||||
pub fn canonical(&self) -> MemPort<DynPortType> {
|
||||
|
@ -463,7 +415,6 @@ impl<T: PortType> MemPort<T> {
|
|||
pub fn from_canonical(port: MemPort<DynPortType>) -> Self
|
||||
where
|
||||
T: PortStruct,
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
let MemPort {
|
||||
mem_name,
|
||||
|
@ -488,8 +439,8 @@ impl<T: PortType> MemPort<T> {
|
|||
mem_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
port_name: PortName,
|
||||
addr_type: DynUIntType,
|
||||
mem_element_type: Interned<dyn DynCanonicalType>,
|
||||
addr_type: UInt,
|
||||
mem_element_type: CanonicalType,
|
||||
) -> Self {
|
||||
assert!(
|
||||
mem_element_type.is_storable(),
|
||||
|
@ -520,10 +471,10 @@ pub enum ReadUnderWrite {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct MemImpl<T: ArrayTypeTrait, P> {
|
||||
struct MemImpl<Element: Type, Len: Size, P> {
|
||||
scoped_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
array_type: T,
|
||||
array_type: ArrayType<Element, Len>,
|
||||
initial_value: Option<Interned<BitSlice>>,
|
||||
ports: P,
|
||||
read_latency: usize,
|
||||
|
@ -533,11 +484,11 @@ struct MemImpl<T: ArrayTypeTrait, P> {
|
|||
mem_annotations: Interned<[Annotation]>,
|
||||
}
|
||||
|
||||
pub struct Mem<VA: ValueArrayOrSlice + ?Sized>(
|
||||
Interned<MemImpl<ArrayType<VA>, Interned<[Interned<MemPort<DynPortType>>]>>>,
|
||||
pub struct Mem<Element: Type = CanonicalType, Len: Size = DynSize>(
|
||||
Interned<MemImpl<Element, Len, Interned<[MemPort<DynPortType>]>>>,
|
||||
);
|
||||
|
||||
struct PortsDebug<'a>(&'a [Interned<MemPort<DynPortType>>]);
|
||||
struct PortsDebug<'a>(&'a [MemPort<DynPortType>]);
|
||||
|
||||
impl fmt::Debug for PortsDebug<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
@ -551,7 +502,7 @@ impl fmt::Debug for PortsDebug<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for Mem<VA> {
|
||||
impl<Element: Type, Len: Size> fmt::Debug for Mem<Element, Len> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let MemImpl {
|
||||
scoped_name,
|
||||
|
@ -579,37 +530,37 @@ impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for Mem<VA> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Hash for Mem<VA> {
|
||||
impl<Element: Type, Len: Size> Hash for Mem<Element, Len> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Eq for Mem<VA> {}
|
||||
impl<Element: Type, Len: Size> Eq for Mem<Element, Len> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> PartialEq for Mem<VA> {
|
||||
impl<Element: Type, Len: Size> PartialEq for Mem<Element, Len> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Copy for Mem<VA> {}
|
||||
impl<Element: Type, Len: Size> Copy for Mem<Element, Len> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for Mem<VA> {
|
||||
impl<Element: Type, Len: Size> Clone for Mem<Element, Len> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
|
||||
impl<Element: Type, Len: Size> Mem<Element, Len> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[track_caller]
|
||||
pub fn new_unchecked(
|
||||
scoped_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
array_type: ArrayType<VA>,
|
||||
array_type: ArrayType<Element, Len>,
|
||||
initial_value: Option<Interned<BitSlice>>,
|
||||
ports: Interned<[Interned<MemPort<DynPortType>>]>,
|
||||
ports: Interned<[MemPort<DynPortType>]>,
|
||||
read_latency: usize,
|
||||
write_latency: NonZeroU32,
|
||||
read_under_write: ReadUnderWrite,
|
||||
|
@ -617,14 +568,14 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
|
|||
mem_annotations: Interned<[Annotation]>,
|
||||
) -> Self {
|
||||
if let Some(initial_value) = initial_value {
|
||||
MemBuilder::<VA>::check_initial_value_bit_slice(
|
||||
MemBuilder::<Element, Len>::check_initial_value_bit_slice(
|
||||
array_type.element(),
|
||||
Some(array_type.len()),
|
||||
initial_value,
|
||||
);
|
||||
}
|
||||
let addr_width = memory_addr_width(array_type.len());
|
||||
let expected_mem_element_type = array_type.element().canonical_dyn();
|
||||
let expected_mem_element_type = array_type.element().canonical();
|
||||
assert!(
|
||||
expected_mem_element_type.is_storable(),
|
||||
"memory element type must be a storable type"
|
||||
|
@ -637,7 +588,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
|
|||
port_index,
|
||||
addr_type,
|
||||
mem_element_type,
|
||||
} = **port;
|
||||
} = *port;
|
||||
assert_eq!(mem_name, scoped_name, "memory name must match with ports");
|
||||
assert_eq!(
|
||||
port_index, index,
|
||||
|
@ -659,7 +610,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
|
|||
};
|
||||
assert_eq!(
|
||||
Some(port),
|
||||
ports.get(port.port_index).map(|v| &**v),
|
||||
ports.get(port.port_index),
|
||||
"port on memory must match annotation's target base"
|
||||
);
|
||||
}
|
||||
|
@ -682,13 +633,13 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
|
|||
pub fn source_location(self) -> SourceLocation {
|
||||
self.0.source_location
|
||||
}
|
||||
pub fn array_type(self) -> ArrayType<VA> {
|
||||
pub fn array_type(self) -> ArrayType<Element, Len> {
|
||||
self.0.array_type.clone()
|
||||
}
|
||||
pub fn initial_value(self) -> Option<Interned<BitSlice>> {
|
||||
self.0.initial_value
|
||||
}
|
||||
pub fn ports(self) -> Interned<[Interned<MemPort<DynPortType>>]> {
|
||||
pub fn ports(self) -> Interned<[MemPort<DynPortType>]> {
|
||||
self.0.ports
|
||||
}
|
||||
pub fn read_latency(self) -> usize {
|
||||
|
@ -706,7 +657,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
|
|||
pub fn mem_annotations(self) -> Interned<[Annotation]> {
|
||||
self.0.mem_annotations
|
||||
}
|
||||
pub fn canonical(self) -> Mem<[DynCanonicalValue]> {
|
||||
pub fn canonical(self) -> Mem {
|
||||
let MemImpl {
|
||||
scoped_name,
|
||||
source_location,
|
||||
|
@ -719,7 +670,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
|
|||
port_annotations,
|
||||
mem_annotations,
|
||||
} = *self.0;
|
||||
let array_type = array_type.canonical();
|
||||
let array_type = array_type.as_dyn_array();
|
||||
Mem(Intern::intern_sized(MemImpl {
|
||||
scoped_name,
|
||||
source_location,
|
||||
|
@ -751,10 +702,10 @@ impl<T: fmt::Debug> fmt::Debug for MaybeSpecified<T> {
|
|||
pub(crate) struct MemBuilderTarget {
|
||||
pub(crate) scoped_name: ScopedNameId,
|
||||
pub(crate) source_location: SourceLocation,
|
||||
pub(crate) mem_element_type: Interned<dyn DynCanonicalType>,
|
||||
pub(crate) mem_element_type: CanonicalType,
|
||||
pub(crate) depth: Option<usize>,
|
||||
pub(crate) initial_value: Option<Interned<BitSlice>>,
|
||||
pub(crate) ports: Vec<Interned<MemPort<DynPortType>>>,
|
||||
pub(crate) ports: Vec<MemPort<DynPortType>>,
|
||||
pub(crate) read_latency: usize,
|
||||
pub(crate) write_latency: NonZeroU32,
|
||||
pub(crate) read_under_write: ReadUnderWrite,
|
||||
|
@ -763,11 +714,11 @@ pub(crate) struct MemBuilderTarget {
|
|||
}
|
||||
|
||||
impl MemBuilderTarget {
|
||||
pub(crate) fn make_memory(&self) -> Option<Mem<[DynCanonicalValue]>> {
|
||||
pub(crate) fn make_memory(&self) -> Option<Mem> {
|
||||
Some(Mem::new_unchecked(
|
||||
self.scoped_name,
|
||||
self.source_location,
|
||||
ArrayType::new_slice(self.mem_element_type, self.depth?),
|
||||
ArrayType::new_dyn(self.mem_element_type, self.depth?),
|
||||
self.initial_value,
|
||||
Intern::intern(&self.ports),
|
||||
self.read_latency,
|
||||
|
@ -817,16 +768,18 @@ impl fmt::Debug for MemBuilderTarget {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct MemBuilder<VA: ValueArrayOrSlice + ?Sized> {
|
||||
mem_element_type: VA::ElementType,
|
||||
pub struct MemBuilder<Element: Type, Len: Size = DynSize> {
|
||||
mem_element_type: Element,
|
||||
target: Rc<RefCell<MemBuilderTarget>>,
|
||||
_phantom: PhantomData<Len>,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for MemBuilder<VA> {
|
||||
impl<Element: Type, Len: Size> fmt::Debug for MemBuilder<Element, Len> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
mem_element_type,
|
||||
target,
|
||||
_phantom: _,
|
||||
} = &self;
|
||||
target.borrow().debug_fmt("MemBuilder", mem_element_type, f)
|
||||
}
|
||||
|
@ -838,15 +791,16 @@ pub fn memory_addr_width(depth: usize) -> usize {
|
|||
.map_or(usize::BITS, usize::ilog2) as usize
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
||||
impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
||||
#[track_caller]
|
||||
fn check_initial_value_bit_slice(
|
||||
mem_element_type: &VA::ElementType,
|
||||
mem_element_type: Element,
|
||||
depth: Option<usize>,
|
||||
initial_value: Interned<BitSlice>,
|
||||
) -> Interned<BitSlice> {
|
||||
let element_bit_width = mem_element_type.canonical().bit_width();
|
||||
if let Some(depth) = depth {
|
||||
let expected_len = depth.checked_mul(mem_element_type.bit_width()).expect(
|
||||
let expected_len = depth.checked_mul(element_bit_width).expect(
|
||||
"memory must be small enough that its initializer bit length fits in usize",
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -858,7 +812,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
assert!(
|
||||
initial_value
|
||||
.len()
|
||||
.checked_rem(mem_element_type.bit_width())
|
||||
.checked_rem(element_bit_width)
|
||||
.unwrap_or(initial_value.len())
|
||||
== 0,
|
||||
"Mem's initializer bit length must be a multiple of the element type's bit width",
|
||||
|
@ -867,14 +821,14 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
}
|
||||
#[track_caller]
|
||||
fn check_initial_value_expr(
|
||||
mem_element_type: &VA::ElementType,
|
||||
mem_element_type: &Element,
|
||||
depth: Option<usize>,
|
||||
initial_value: Expr<Array<[DynCanonicalValue]>>,
|
||||
initial_value: Expr<Array>,
|
||||
) -> Interned<BitSlice> {
|
||||
let initial_value_ty = initial_value.canonical_type();
|
||||
let initial_value_ty = Expr::ty(initial_value);
|
||||
assert_eq!(
|
||||
*mem_element_type,
|
||||
<VA::ElementType as DynType>::from_dyn_canonical_type(*initial_value_ty.element()),
|
||||
Element::from_canonical(initial_value_ty.element()),
|
||||
"Mem's element type must match initializer's element type",
|
||||
);
|
||||
if let Some(depth) = depth {
|
||||
|
@ -889,7 +843,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
};
|
||||
debug_assert_eq!(
|
||||
retval.len(),
|
||||
initial_value_ty.bit_width(),
|
||||
initial_value_ty.type_properties().bit_width,
|
||||
"initial value produced wrong literal bits length"
|
||||
);
|
||||
retval
|
||||
|
@ -898,9 +852,9 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
pub(crate) fn new(
|
||||
scoped_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
mem_element_type: VA::ElementType,
|
||||
mem_element_type: Element,
|
||||
) -> (Self, Rc<RefCell<MemBuilderTarget>>) {
|
||||
let canonical_mem_element_type = mem_element_type.canonical_dyn();
|
||||
let canonical_mem_element_type = mem_element_type.canonical();
|
||||
assert!(
|
||||
canonical_mem_element_type.is_storable(),
|
||||
"memory element type must be a storable type"
|
||||
|
@ -909,7 +863,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
scoped_name,
|
||||
source_location,
|
||||
mem_element_type: canonical_mem_element_type,
|
||||
depth: VA::FIXED_LEN_TYPE.map(VA::len_from_len_type),
|
||||
depth: Len::KNOWN_VALUE,
|
||||
initial_value: None,
|
||||
ports: vec![],
|
||||
read_latency: 0,
|
||||
|
@ -922,6 +876,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
Self {
|
||||
mem_element_type,
|
||||
target: Rc::clone(&target),
|
||||
_phantom: PhantomData,
|
||||
},
|
||||
target,
|
||||
)
|
||||
|
@ -931,19 +886,19 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
&mut self,
|
||||
source_location: SourceLocation,
|
||||
port_kind: PortKind,
|
||||
) -> Interned<MemPort<DynPortType>> {
|
||||
) -> MemPort<DynPortType> {
|
||||
let mut target = self.target.borrow_mut();
|
||||
let Some(depth) = target.depth else {
|
||||
panic!("MemBuilder::depth must be called before adding ports");
|
||||
};
|
||||
let port = Intern::intern_sized(MemPort {
|
||||
let port = MemPort {
|
||||
mem_name: target.scoped_name,
|
||||
source_location,
|
||||
port_kind,
|
||||
port_index: target.ports.len(),
|
||||
addr_type: DynUIntType::new(memory_addr_width(depth)),
|
||||
addr_type: UInt::new(memory_addr_width(depth)),
|
||||
mem_element_type: target.mem_element_type,
|
||||
});
|
||||
};
|
||||
target.ports.push(port);
|
||||
port
|
||||
}
|
||||
|
@ -952,50 +907,53 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
&mut self,
|
||||
source_location: SourceLocation,
|
||||
kind: PortKind,
|
||||
) -> Expr<DynBundle> {
|
||||
Expr::new_unchecked(ExprEnum::MemPort(self.new_port_impl(source_location, kind)))
|
||||
) -> Expr<Bundle> {
|
||||
self.new_port_impl(source_location, kind).to_expr()
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_port(&mut self, kind: PortKind) -> Expr<DynBundle> {
|
||||
pub fn new_port(&mut self, kind: PortKind) -> Expr<Bundle> {
|
||||
self.new_port_with_loc(SourceLocation::caller(), kind)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_read_port_with_loc(
|
||||
&mut self,
|
||||
source_location: SourceLocation,
|
||||
) -> Expr<ReadStruct<VA::Element>> {
|
||||
Expr::new_unchecked(ExprEnum::MemPort(
|
||||
self.new_port_impl(source_location, PortKind::ReadOnly),
|
||||
))
|
||||
) -> Expr<ReadStruct<Element, DynSize>> {
|
||||
Expr::from_bundle(
|
||||
self.new_port_impl(source_location, PortKind::ReadOnly)
|
||||
.to_expr(),
|
||||
)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_read_port(&mut self) -> Expr<ReadStruct<VA::Element>> {
|
||||
pub fn new_read_port(&mut self) -> Expr<ReadStruct<Element, DynSize>> {
|
||||
self.new_read_port_with_loc(SourceLocation::caller())
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_write_port_with_loc(
|
||||
&mut self,
|
||||
source_location: SourceLocation,
|
||||
) -> Expr<WriteStruct<VA::Element>> {
|
||||
Expr::new_unchecked(ExprEnum::MemPort(
|
||||
self.new_port_impl(source_location, PortKind::WriteOnly),
|
||||
))
|
||||
) -> Expr<WriteStruct<Element, DynSize>> {
|
||||
Expr::from_bundle(
|
||||
self.new_port_impl(source_location, PortKind::WriteOnly)
|
||||
.to_expr(),
|
||||
)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_write_port(&mut self) -> Expr<WriteStruct<VA::Element>> {
|
||||
pub fn new_write_port(&mut self) -> Expr<WriteStruct<Element, DynSize>> {
|
||||
self.new_write_port_with_loc(SourceLocation::caller())
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_rw_port_with_loc(
|
||||
&mut self,
|
||||
source_location: SourceLocation,
|
||||
) -> Expr<ReadWriteStruct<VA::Element>> {
|
||||
Expr::new_unchecked(ExprEnum::MemPort(
|
||||
self.new_port_impl(source_location, PortKind::ReadWrite),
|
||||
))
|
||||
) -> Expr<ReadWriteStruct<Element, DynSize>> {
|
||||
Expr::from_bundle(
|
||||
self.new_port_impl(source_location, PortKind::ReadWrite)
|
||||
.to_expr(),
|
||||
)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_rw_port(&mut self) -> Expr<ReadWriteStruct<VA::Element>> {
|
||||
pub fn new_rw_port(&mut self) -> Expr<ReadWriteStruct<Element, DynSize>> {
|
||||
self.new_rw_port_with_loc(SourceLocation::caller())
|
||||
}
|
||||
pub fn scoped_name(&self) -> ScopedNameId {
|
||||
|
@ -1004,7 +962,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
pub fn source_location(&self) -> SourceLocation {
|
||||
self.target.borrow().source_location
|
||||
}
|
||||
pub fn get_mem_element_type(&self) -> &VA::ElementType {
|
||||
pub fn get_mem_element_type(&self) -> &Element {
|
||||
&self.mem_element_type
|
||||
}
|
||||
#[allow(clippy::result_unit_err)]
|
||||
|
@ -1027,28 +985,28 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
target.depth = Some(depth);
|
||||
}
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn get_array_type(&self) -> Result<ArrayType<VA>, ()> {
|
||||
Ok(ArrayType::new_with_len(
|
||||
pub fn get_array_type(&self) -> Result<ArrayType<Element, Len>, ()> {
|
||||
Ok(ArrayType::new(
|
||||
self.mem_element_type.clone(),
|
||||
self.get_depth()?,
|
||||
Len::from_usize(self.get_depth()?),
|
||||
))
|
||||
}
|
||||
pub fn get_initial_value(&self) -> Option<Interned<BitSlice>> {
|
||||
self.target.borrow().initial_value
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn initial_value(&mut self, initial_value: impl ToExpr<Type = ArrayType<VA>>) {
|
||||
pub fn initial_value(&mut self, initial_value: impl ToExpr<Type = ArrayType<Element, Len>>) {
|
||||
let mut target = self.target.borrow_mut();
|
||||
if target.initial_value.is_some() {
|
||||
panic!("can't set Mem's initial value more than once");
|
||||
}
|
||||
let initial_value = initial_value.to_expr().canonical();
|
||||
let initial_value = Expr::as_dyn_array(initial_value.to_expr());
|
||||
target.initial_value = Some(Self::check_initial_value_expr(
|
||||
&self.mem_element_type,
|
||||
target.depth,
|
||||
initial_value,
|
||||
));
|
||||
target.depth = Some(initial_value.ty().len());
|
||||
target.depth = Some(Expr::ty(initial_value).len());
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) {
|
||||
|
@ -1057,11 +1015,11 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
|||
panic!("can't set Mem's initial value more than once");
|
||||
}
|
||||
target.initial_value = Some(Self::check_initial_value_bit_slice(
|
||||
&self.mem_element_type,
|
||||
self.mem_element_type,
|
||||
target.depth,
|
||||
initial_value,
|
||||
));
|
||||
let element_bit_width = self.mem_element_type.bit_width();
|
||||
let element_bit_width = self.mem_element_type.canonical().bit_width();
|
||||
if element_bit_width != 0 {
|
||||
target.depth = Some(initial_value.len() / element_bit_width);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,10 +2,14 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
array::{Array, ArrayType},
|
||||
bundle::{BundleType, BundleValue, DynBundle, DynBundleType},
|
||||
enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType},
|
||||
expr::{ops, Expr, ExprEnum, ToExpr},
|
||||
int::{DynUInt, DynUIntType, IntCmp},
|
||||
bundle::{Bundle, BundleType},
|
||||
enum_::{Enum, EnumType, EnumVariant},
|
||||
expr::{
|
||||
ops::{self, EnumLiteral},
|
||||
CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr,
|
||||
},
|
||||
hdl,
|
||||
int::{DynSize, IntCmp, Size, UInt, UIntType},
|
||||
intern::{Intern, Interned},
|
||||
memory::{DynPortType, Mem, MemPort},
|
||||
module::{
|
||||
|
@ -13,7 +17,7 @@ use crate::{
|
|||
Block, Module, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
|
||||
},
|
||||
source_location::SourceLocation,
|
||||
ty::{DynCanonicalType, DynCanonicalValue, Type, TypeEnum, Value, ValueEnum},
|
||||
ty::{CanonicalType, Type},
|
||||
wire::Wire,
|
||||
};
|
||||
use core::fmt;
|
||||
|
@ -21,7 +25,7 @@ use hashbrown::HashMap;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum SimplifyEnumsError {
|
||||
EnumIsNotCastableFromBits { enum_type: DynEnumType },
|
||||
EnumIsNotCastableFromBits { enum_type: Enum },
|
||||
}
|
||||
|
||||
impl fmt::Display for SimplifyEnumsError {
|
||||
|
@ -37,26 +41,23 @@ impl fmt::Display for SimplifyEnumsError {
|
|||
|
||||
impl std::error::Error for SimplifyEnumsError {}
|
||||
|
||||
#[derive(Value, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
struct TagAndBody<T> {
|
||||
#[hdl]
|
||||
struct TagAndBody<T, BodyWidth: Size> {
|
||||
tag: T,
|
||||
body: DynUInt,
|
||||
body: UIntType<BodyWidth>,
|
||||
}
|
||||
|
||||
type TagAndBodyType<T> = <TagAndBody<T> as ToExpr>::Type;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum EnumTypeState {
|
||||
TagEnumAndBody(TagAndBodyType<DynEnum>),
|
||||
TagUIntAndBody(TagAndBodyType<DynUInt>),
|
||||
UInt(DynUIntType),
|
||||
TagEnumAndBody(TagAndBody<Enum, DynSize>),
|
||||
TagUIntAndBody(TagAndBody<UInt, DynSize>),
|
||||
UInt(UInt),
|
||||
Unchanged,
|
||||
}
|
||||
|
||||
struct State {
|
||||
enum_types: HashMap<DynEnumType, EnumTypeState>,
|
||||
replacement_mem_ports:
|
||||
HashMap<Interned<MemPort<DynPortType>>, Interned<Wire<Interned<dyn DynCanonicalType>>>>,
|
||||
enum_types: HashMap<Enum, EnumTypeState>,
|
||||
replacement_mem_ports: HashMap<MemPort<DynPortType>, Wire<CanonicalType>>,
|
||||
kind: SimplifyEnumsKind,
|
||||
name_id_gen: NameIdGen,
|
||||
}
|
||||
|
@ -64,12 +65,12 @@ struct State {
|
|||
impl State {
|
||||
fn get_or_make_enum_type_state(
|
||||
&mut self,
|
||||
enum_type: DynEnumType,
|
||||
enum_type: Enum,
|
||||
) -> Result<EnumTypeState, SimplifyEnumsError> {
|
||||
if let Some(retval) = self.enum_types.get(&enum_type) {
|
||||
return Ok(retval.clone());
|
||||
}
|
||||
if !enum_type.is_castable_from_bits() {
|
||||
if !enum_type.type_properties().is_castable_from_bits {
|
||||
return Err(SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type });
|
||||
}
|
||||
let has_body = enum_type
|
||||
|
@ -78,29 +79,29 @@ impl State {
|
|||
.any(|variant| variant.ty.is_some());
|
||||
let retval = match (self.kind, has_body) {
|
||||
(SimplifyEnumsKind::SimplifyToEnumsWithNoBody, true) => {
|
||||
EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> {
|
||||
tag: DynEnumType::new(Interned::from_iter(enum_type.variants().iter().map(
|
||||
|v| VariantType {
|
||||
EnumTypeState::TagEnumAndBody(TagAndBody {
|
||||
tag: Enum::new(Interned::from_iter(enum_type.variants().iter().map(|v| {
|
||||
EnumVariant {
|
||||
name: v.name,
|
||||
ty: None,
|
||||
},
|
||||
))),
|
||||
body: DynUIntType::new(
|
||||
enum_type.bit_width() - enum_type.discriminant_bit_width(),
|
||||
}
|
||||
}))),
|
||||
body: UInt::new_dyn(
|
||||
enum_type.type_properties().bit_width - enum_type.discriminant_bit_width(),
|
||||
),
|
||||
})
|
||||
}
|
||||
(SimplifyEnumsKind::SimplifyToEnumsWithNoBody, false) => EnumTypeState::Unchanged,
|
||||
(SimplifyEnumsKind::ReplaceWithBundleOfUInts, _) => {
|
||||
EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> {
|
||||
tag: DynUIntType::new(enum_type.discriminant_bit_width()),
|
||||
body: DynUIntType::new(
|
||||
enum_type.bit_width() - enum_type.discriminant_bit_width(),
|
||||
EnumTypeState::TagUIntAndBody(TagAndBody {
|
||||
tag: UInt::new_dyn(enum_type.discriminant_bit_width()),
|
||||
body: UInt::new_dyn(
|
||||
enum_type.type_properties().bit_width - enum_type.discriminant_bit_width(),
|
||||
),
|
||||
})
|
||||
}
|
||||
(SimplifyEnumsKind::ReplaceWithUInt, _) => {
|
||||
EnumTypeState::UInt(DynUIntType::new(enum_type.bit_width()))
|
||||
EnumTypeState::UInt(UInt::new_dyn(enum_type.type_properties().bit_width))
|
||||
}
|
||||
};
|
||||
self.enum_types.insert(enum_type, retval.clone());
|
||||
|
@ -108,21 +109,14 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
fn value_to_uint<T: Value>(value: Option<&T>, target_ty: DynUIntType) -> DynUInt {
|
||||
let Some(value) = value else {
|
||||
return DynUInt::with_type(target_ty, 0u8);
|
||||
};
|
||||
DynUInt::from_bit_slice(&value.to_bits())
|
||||
}
|
||||
|
||||
fn connect_port(
|
||||
stmts: &mut Vec<Stmt>,
|
||||
lhs: Expr<DynCanonicalValue>,
|
||||
rhs: Expr<DynCanonicalValue>,
|
||||
lhs: Expr<CanonicalType>,
|
||||
rhs: Expr<CanonicalType>,
|
||||
source_location: SourceLocation,
|
||||
) {
|
||||
println!("connect_port: lhs={lhs:?} rhs={rhs:?}");
|
||||
if lhs.canonical_type() == rhs.canonical_type() {
|
||||
println!("connect_port:\nlhs={lhs:?}\nrhs={rhs:?}");
|
||||
if Expr::ty(lhs) == Expr::ty(rhs) {
|
||||
stmts.push(
|
||||
dbg!(StmtConnect {
|
||||
lhs,
|
||||
|
@ -133,55 +127,53 @@ fn connect_port(
|
|||
);
|
||||
return;
|
||||
}
|
||||
match (
|
||||
lhs.canonical_type().type_enum(),
|
||||
rhs.canonical_type().type_enum(),
|
||||
) {
|
||||
(TypeEnum::BundleType(lhs_type), TypeEnum::UInt(_)) => {
|
||||
let lhs = lhs.with_type::<DynBundle>();
|
||||
match (Expr::ty(lhs), Expr::ty(rhs)) {
|
||||
(CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_) | CanonicalType::Bool(_)) => {
|
||||
let lhs = Expr::<Bundle>::from_canonical(lhs);
|
||||
for field in lhs_type.fields() {
|
||||
assert!(!field.flipped);
|
||||
connect_port(stmts, lhs.field(&field.name), rhs, source_location);
|
||||
connect_port(stmts, Expr::field(lhs, &field.name), rhs, source_location);
|
||||
}
|
||||
}
|
||||
(TypeEnum::UInt(_), TypeEnum::BundleType(rhs_type)) => {
|
||||
let rhs = rhs.with_type::<DynBundle>();
|
||||
(CanonicalType::UInt(_) | CanonicalType::Bool(_), CanonicalType::Bundle(rhs_type)) => {
|
||||
let rhs = Expr::<Bundle>::from_canonical(rhs);
|
||||
for field in rhs_type.fields() {
|
||||
assert!(!field.flipped);
|
||||
connect_port(stmts, lhs, rhs.field(&field.name), source_location);
|
||||
connect_port(stmts, lhs, Expr::field(rhs, &field.name), source_location);
|
||||
}
|
||||
}
|
||||
(TypeEnum::BundleType(lhs_type), TypeEnum::BundleType(_)) => {
|
||||
let lhs = lhs.with_type::<DynBundle>();
|
||||
let rhs = rhs.with_type::<DynBundle>();
|
||||
(CanonicalType::Bundle(lhs_type), CanonicalType::Bundle(_)) => {
|
||||
let lhs = Expr::<Bundle>::from_canonical(lhs);
|
||||
let rhs = Expr::<Bundle>::from_canonical(rhs);
|
||||
for field in lhs_type.fields() {
|
||||
let (lhs_field, rhs_field) = if field.flipped {
|
||||
(rhs.field(&field.name), lhs.field(&field.name))
|
||||
(Expr::field(rhs, &field.name), Expr::field(lhs, &field.name))
|
||||
} else {
|
||||
(lhs.field(&field.name), rhs.field(&field.name))
|
||||
(Expr::field(lhs, &field.name), Expr::field(rhs, &field.name))
|
||||
};
|
||||
connect_port(stmts, lhs_field, rhs_field, source_location);
|
||||
}
|
||||
}
|
||||
(TypeEnum::ArrayType(lhs_type), TypeEnum::ArrayType(_)) => {
|
||||
let lhs = lhs.with_type::<Array<[DynCanonicalValue]>>();
|
||||
let rhs = rhs.with_type::<Array<[DynCanonicalValue]>>();
|
||||
(CanonicalType::Array(lhs_type), CanonicalType::Array(_)) => {
|
||||
let lhs = Expr::<Array>::from_canonical(lhs);
|
||||
let rhs = Expr::<Array>::from_canonical(rhs);
|
||||
for index in 0..lhs_type.len() {
|
||||
connect_port(stmts, lhs[index], rhs[index], source_location);
|
||||
}
|
||||
}
|
||||
(TypeEnum::BundleType(_), _)
|
||||
| (TypeEnum::EnumType(_), _)
|
||||
| (TypeEnum::ArrayType(_), _)
|
||||
| (TypeEnum::UInt(_), _)
|
||||
| (TypeEnum::SInt(_), _)
|
||||
| (TypeEnum::Clock(_), _)
|
||||
| (TypeEnum::AsyncReset(_), _)
|
||||
| (TypeEnum::SyncReset(_), _)
|
||||
| (TypeEnum::Reset(_), _) => unreachable!(
|
||||
(CanonicalType::Bundle(_), _)
|
||||
| (CanonicalType::Enum(_), _)
|
||||
| (CanonicalType::Array(_), _)
|
||||
| (CanonicalType::UInt(_), _)
|
||||
| (CanonicalType::SInt(_), _)
|
||||
| (CanonicalType::Bool(_), _)
|
||||
| (CanonicalType::Clock(_), _)
|
||||
| (CanonicalType::AsyncReset(_), _)
|
||||
| (CanonicalType::SyncReset(_), _)
|
||||
| (CanonicalType::Reset(_), _) => unreachable!(
|
||||
"trying to connect memory ports:\n{:?}\n{:?}",
|
||||
lhs.canonical_type().type_enum(),
|
||||
rhs.canonical_type().type_enum(),
|
||||
Expr::ty(lhs),
|
||||
Expr::ty(rhs),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -189,18 +181,11 @@ fn connect_port(
|
|||
impl Folder for State {
|
||||
type Error = SimplifyEnumsError;
|
||||
|
||||
fn fold_dyn_enum(&mut self, _v: DynEnum) -> Result<DynEnum, Self::Error> {
|
||||
fn fold_enum(&mut self, _v: Enum) -> Result<Enum, Self::Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn fold_dyn_enum_type(&mut self, _v: DynEnumType) -> Result<DynEnumType, Self::Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error>
|
||||
where
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
fn fold_module<T: BundleType>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> {
|
||||
let old_name_id_gen =
|
||||
std::mem::replace(&mut self.name_id_gen, NameIdGen::for_module(v.canonical()));
|
||||
let retval = Fold::default_fold(v, self);
|
||||
|
@ -211,98 +196,86 @@ impl Folder for State {
|
|||
fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> {
|
||||
match op {
|
||||
ExprEnum::EnumLiteral(op) => Ok(match self.get_or_make_enum_type_state(op.ty())? {
|
||||
EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> { tag, body }) => {
|
||||
TagAndBodyType::<DynEnum>::builder()
|
||||
.field_tag(DynEnum::new_by_index(tag, op.variant_index(), None))
|
||||
EnumTypeState::TagEnumAndBody(TagAndBody { tag, body }) => *Expr::expr_enum(
|
||||
<TagAndBody<Enum, DynSize> as BundleType>::Builder::default()
|
||||
.field_tag(EnumLiteral::new_by_index(tag, op.variant_index(), None))
|
||||
.field_body(match op.variant_value() {
|
||||
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
|
||||
None => DynUInt::with_type(body, 0u8).to_expr(),
|
||||
None => body.zero().to_expr(),
|
||||
})
|
||||
.build()
|
||||
.expr_enum()
|
||||
}
|
||||
EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> { tag, body }) => {
|
||||
TagAndBodyType::<DynUInt>::builder()
|
||||
.field_tag(DynUInt::with_type(tag, op.variant_index()))
|
||||
.field_body(match op.variant_value() {
|
||||
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
|
||||
None => DynUInt::with_type(body, 0u8).to_expr(),
|
||||
})
|
||||
.build()
|
||||
.expr_enum()
|
||||
}
|
||||
EnumTypeState::UInt(_) => TagAndBodyType::<DynUInt>::builder()
|
||||
.field_tag(DynUInt::with_type(
|
||||
DynUIntType::new(op.ty().discriminant_bit_width()),
|
||||
op.variant_index(),
|
||||
))
|
||||
.field_body(match op.variant_value() {
|
||||
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
|
||||
None => DynUInt::with_type(
|
||||
DynUIntType::new(
|
||||
op.ty().bit_width() - op.ty().discriminant_bit_width(),
|
||||
),
|
||||
0u8,
|
||||
)
|
||||
.to_expr(),
|
||||
})
|
||||
.build()
|
||||
.cast_to_bits()
|
||||
.expr_enum(),
|
||||
EnumTypeState::Unchanged => ExprEnum::EnumLiteral(
|
||||
ops::EnumLiteral::new_unchecked(
|
||||
op.variant_value().map(|v| v.fold(self)).transpose()?,
|
||||
op.variant_index(),
|
||||
op.ty(),
|
||||
)
|
||||
.intern_sized(),
|
||||
),
|
||||
EnumTypeState::TagUIntAndBody(TagAndBody { tag, body }) => *Expr::expr_enum(
|
||||
<TagAndBody<UInt, DynSize> as BundleType>::Builder::default()
|
||||
.field_tag(tag.from_int_wrapping(op.variant_index()))
|
||||
.field_body(match op.variant_value() {
|
||||
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
|
||||
None => body.zero().to_expr(),
|
||||
})
|
||||
.to_expr(),
|
||||
),
|
||||
EnumTypeState::UInt(_) => *Expr::expr_enum(
|
||||
<TagAndBody<UInt, DynSize> as BundleType>::Builder::default()
|
||||
.field_tag(
|
||||
UIntType::new(op.ty().discriminant_bit_width())
|
||||
.from_int_wrapping(op.variant_index()),
|
||||
)
|
||||
.field_body(match op.variant_value() {
|
||||
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
|
||||
None => UIntType::new(
|
||||
op.ty().type_properties().bit_width
|
||||
- op.ty().discriminant_bit_width(),
|
||||
)
|
||||
.zero()
|
||||
.to_expr(),
|
||||
})
|
||||
.cast_to_bits(),
|
||||
),
|
||||
EnumTypeState::Unchanged => ExprEnum::EnumLiteral(ops::EnumLiteral::new_by_index(
|
||||
op.ty(),
|
||||
op.variant_index(),
|
||||
op.variant_value().map(|v| v.fold(self)).transpose()?,
|
||||
)),
|
||||
}),
|
||||
ExprEnum::VariantAccess(op) => {
|
||||
Ok(match self.get_or_make_enum_type_state(op.base().ty())? {
|
||||
ExprEnum::VariantAccess(op) => Ok(
|
||||
match self.get_or_make_enum_type_state(Expr::ty(op.base()))? {
|
||||
EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => {
|
||||
match op.variant_type().ty {
|
||||
Some(_) => op
|
||||
.base()
|
||||
.expr_enum()
|
||||
.fold(self)?
|
||||
.to_expr()
|
||||
.with_type::<TagAndBody<DynCanonicalValue>>()
|
||||
.body[..op.ty().bit_width()]
|
||||
.cast_bits_to::<DynCanonicalValue>(op.ty())
|
||||
.expr_enum(),
|
||||
None => ().to_expr().expr_enum(),
|
||||
match op.variant_type() {
|
||||
Some(variant_type) => *Expr::expr_enum(
|
||||
Expr::<TagAndBody<CanonicalType, DynSize>>::from_canonical(
|
||||
(*Expr::expr_enum(op.base())).fold(self)?.to_expr(),
|
||||
)
|
||||
.body[..variant_type.bit_width()]
|
||||
.cast_bits_to(variant_type),
|
||||
),
|
||||
None => *Expr::expr_enum(().to_expr()),
|
||||
}
|
||||
}
|
||||
EnumTypeState::UInt(_) => match op.variant_type().ty {
|
||||
Some(_) => {
|
||||
let base_int = op
|
||||
.base()
|
||||
.expr_enum()
|
||||
.fold(self)?
|
||||
.to_expr()
|
||||
.with_type::<DynUInt>();
|
||||
EnumTypeState::UInt(_) => match op.variant_type() {
|
||||
Some(variant_type) => {
|
||||
let base_int = Expr::<UInt>::from_canonical(
|
||||
(*Expr::expr_enum(op.base())).fold(self)?.to_expr(),
|
||||
);
|
||||
dbg!(base_int);
|
||||
let base_ty = op.base().ty();
|
||||
let ty_bit_width = op.ty().bit_width();
|
||||
base_int[base_ty.discriminant_bit_width()..][..ty_bit_width]
|
||||
.cast_bits_to::<DynCanonicalValue>(op.ty())
|
||||
.expr_enum()
|
||||
}
|
||||
None => ().to_expr().expr_enum(),
|
||||
},
|
||||
EnumTypeState::Unchanged => match op.variant_type().ty {
|
||||
Some(_) => ExprEnum::VariantAccess(
|
||||
ops::VariantAccess::new_unchecked(
|
||||
op.base().fold(self)?,
|
||||
op.variant_index(),
|
||||
let base_ty = Expr::ty(op.base());
|
||||
let variant_type_bit_width = variant_type.bit_width();
|
||||
*Expr::expr_enum(
|
||||
base_int[base_ty.discriminant_bit_width()..]
|
||||
[..variant_type_bit_width]
|
||||
.cast_bits_to(variant_type),
|
||||
)
|
||||
.intern_sized(),
|
||||
),
|
||||
None => ().to_expr().expr_enum(),
|
||||
}
|
||||
None => *Expr::expr_enum(().to_expr()),
|
||||
},
|
||||
})
|
||||
}
|
||||
EnumTypeState::Unchanged => match op.variant_type() {
|
||||
Some(_) => ExprEnum::VariantAccess(ops::VariantAccess::new_by_index(
|
||||
op.base().fold(self)?,
|
||||
op.variant_index(),
|
||||
)),
|
||||
None => *Expr::expr_enum(().to_expr()),
|
||||
},
|
||||
},
|
||||
),
|
||||
ExprEnum::MemPort(mem_port) => Ok(
|
||||
if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) {
|
||||
ExprEnum::Wire(wire)
|
||||
|
@ -310,24 +283,34 @@ impl Folder for State {
|
|||
ExprEnum::MemPort(mem_port.fold(self)?)
|
||||
},
|
||||
),
|
||||
ExprEnum::Literal(_)
|
||||
| ExprEnum::ArrayLiteral(_)
|
||||
ExprEnum::UIntLiteral(_)
|
||||
| ExprEnum::SIntLiteral(_)
|
||||
| ExprEnum::BoolLiteral(_)
|
||||
| ExprEnum::BundleLiteral(_)
|
||||
| ExprEnum::ArrayLiteral(_)
|
||||
| ExprEnum::NotU(_)
|
||||
| ExprEnum::NotS(_)
|
||||
| ExprEnum::NotB(_)
|
||||
| ExprEnum::Neg(_)
|
||||
| ExprEnum::BitAndU(_)
|
||||
| ExprEnum::BitAndS(_)
|
||||
| ExprEnum::BitAndB(_)
|
||||
| ExprEnum::BitOrU(_)
|
||||
| ExprEnum::BitOrS(_)
|
||||
| ExprEnum::BitOrB(_)
|
||||
| ExprEnum::BitXorU(_)
|
||||
| ExprEnum::BitXorS(_)
|
||||
| ExprEnum::BitXorB(_)
|
||||
| ExprEnum::AddU(_)
|
||||
| ExprEnum::AddS(_)
|
||||
| ExprEnum::SubU(_)
|
||||
| ExprEnum::SubS(_)
|
||||
| ExprEnum::MulU(_)
|
||||
| ExprEnum::MulS(_)
|
||||
| ExprEnum::DivU(_)
|
||||
| ExprEnum::DivS(_)
|
||||
| ExprEnum::RemU(_)
|
||||
| ExprEnum::RemS(_)
|
||||
| ExprEnum::DynShlU(_)
|
||||
| ExprEnum::DynShlS(_)
|
||||
| ExprEnum::DynShrU(_)
|
||||
|
@ -336,41 +319,68 @@ impl Folder for State {
|
|||
| ExprEnum::FixedShlS(_)
|
||||
| ExprEnum::FixedShrU(_)
|
||||
| ExprEnum::FixedShrS(_)
|
||||
| ExprEnum::CmpLtB(_)
|
||||
| ExprEnum::CmpLeB(_)
|
||||
| ExprEnum::CmpGtB(_)
|
||||
| ExprEnum::CmpGeB(_)
|
||||
| ExprEnum::CmpEqB(_)
|
||||
| ExprEnum::CmpNeB(_)
|
||||
| ExprEnum::CmpLtU(_)
|
||||
| ExprEnum::CmpLtS(_)
|
||||
| ExprEnum::CmpLeU(_)
|
||||
| ExprEnum::CmpLeS(_)
|
||||
| ExprEnum::CmpGtU(_)
|
||||
| ExprEnum::CmpGtS(_)
|
||||
| ExprEnum::CmpGeU(_)
|
||||
| ExprEnum::CmpGeS(_)
|
||||
| ExprEnum::CmpEqU(_)
|
||||
| ExprEnum::CmpEqS(_)
|
||||
| ExprEnum::CmpNeU(_)
|
||||
| ExprEnum::CmpLtS(_)
|
||||
| ExprEnum::CmpLeS(_)
|
||||
| ExprEnum::CmpGtS(_)
|
||||
| ExprEnum::CmpGeS(_)
|
||||
| ExprEnum::CmpEqS(_)
|
||||
| ExprEnum::CmpNeS(_)
|
||||
| ExprEnum::CastUIntToUInt(_)
|
||||
| ExprEnum::CastUIntToSInt(_)
|
||||
| ExprEnum::CastSIntToUInt(_)
|
||||
| ExprEnum::CastSIntToSInt(_)
|
||||
| ExprEnum::SliceUInt(_)
|
||||
| ExprEnum::SliceSInt(_)
|
||||
| ExprEnum::ReduceBitAnd(_)
|
||||
| ExprEnum::ReduceBitOr(_)
|
||||
| ExprEnum::ReduceBitXor(_)
|
||||
| ExprEnum::CastBoolToUInt(_)
|
||||
| ExprEnum::CastBoolToSInt(_)
|
||||
| ExprEnum::CastUIntToBool(_)
|
||||
| ExprEnum::CastSIntToBool(_)
|
||||
| ExprEnum::CastBoolToSyncReset(_)
|
||||
| ExprEnum::CastUIntToSyncReset(_)
|
||||
| ExprEnum::CastSIntToSyncReset(_)
|
||||
| ExprEnum::CastBoolToAsyncReset(_)
|
||||
| ExprEnum::CastUIntToAsyncReset(_)
|
||||
| ExprEnum::CastSIntToAsyncReset(_)
|
||||
| ExprEnum::CastSyncResetToBool(_)
|
||||
| ExprEnum::CastSyncResetToUInt(_)
|
||||
| ExprEnum::CastSyncResetToSInt(_)
|
||||
| ExprEnum::CastSyncResetToReset(_)
|
||||
| ExprEnum::CastAsyncResetToBool(_)
|
||||
| ExprEnum::CastAsyncResetToUInt(_)
|
||||
| ExprEnum::CastAsyncResetToSInt(_)
|
||||
| ExprEnum::CastAsyncResetToReset(_)
|
||||
| ExprEnum::CastResetToBool(_)
|
||||
| ExprEnum::CastResetToUInt(_)
|
||||
| ExprEnum::CastResetToSInt(_)
|
||||
| ExprEnum::CastBoolToClock(_)
|
||||
| ExprEnum::CastUIntToClock(_)
|
||||
| ExprEnum::CastSIntToClock(_)
|
||||
| ExprEnum::CastClockToBool(_)
|
||||
| ExprEnum::CastClockToUInt(_)
|
||||
| ExprEnum::CastClockToSInt(_)
|
||||
| ExprEnum::FieldAccess(_)
|
||||
| ExprEnum::ArrayIndex(_)
|
||||
| ExprEnum::DynArrayIndex(_)
|
||||
| ExprEnum::ReduceBitAndU(_)
|
||||
| ExprEnum::ReduceBitAndS(_)
|
||||
| ExprEnum::ReduceBitOrU(_)
|
||||
| ExprEnum::ReduceBitOrS(_)
|
||||
| ExprEnum::ReduceBitXorU(_)
|
||||
| ExprEnum::ReduceBitXorS(_)
|
||||
| ExprEnum::SliceUInt(_)
|
||||
| ExprEnum::SliceSInt(_)
|
||||
| ExprEnum::CastToBits(_)
|
||||
| ExprEnum::CastBitsTo(_)
|
||||
| ExprEnum::CastBitToClock(_)
|
||||
| ExprEnum::CastBitToSyncReset(_)
|
||||
| ExprEnum::CastBitToAsyncReset(_)
|
||||
| ExprEnum::CastSyncResetToReset(_)
|
||||
| ExprEnum::CastAsyncResetToReset(_)
|
||||
| ExprEnum::CastClockToBit(_)
|
||||
| ExprEnum::CastSyncResetToBit(_)
|
||||
| ExprEnum::CastAsyncResetToBit(_)
|
||||
| ExprEnum::CastResetToBit(_)
|
||||
| ExprEnum::ModuleIO(_)
|
||||
| ExprEnum::Instance(_)
|
||||
| ExprEnum::Wire(_)
|
||||
|
@ -382,7 +392,7 @@ impl Folder for State {
|
|||
let mut memories = vec![];
|
||||
let mut stmts = vec![];
|
||||
for memory in block.memories {
|
||||
let old_element_ty = *memory.array_type().element();
|
||||
let old_element_ty = memory.array_type().element();
|
||||
let new_element_ty = memory.array_type().element().fold(self)?;
|
||||
if new_element_ty != old_element_ty {
|
||||
let mut new_ports = vec![];
|
||||
|
@ -394,7 +404,7 @@ impl Folder for State {
|
|||
port.addr_type(),
|
||||
new_element_ty,
|
||||
);
|
||||
new_ports.push(new_port.intern());
|
||||
new_ports.push(new_port);
|
||||
let new_port_ty = new_port.ty();
|
||||
let mut wire_ty_fields = Vec::from_iter(new_port_ty.fields());
|
||||
if let Some(wmask_name) = new_port.port_kind().wmask_name() {
|
||||
|
@ -404,7 +414,7 @@ impl Folder for State {
|
|||
.unwrap();
|
||||
wire_ty_fields[index].ty = port.ty().fields()[index].ty;
|
||||
}
|
||||
let wire_ty = DynBundleType::new(Intern::intern_owned(wire_ty_fields));
|
||||
let wire_ty = Bundle::new(Intern::intern_owned(wire_ty_fields));
|
||||
if wire_ty == new_port_ty {
|
||||
continue;
|
||||
}
|
||||
|
@ -419,23 +429,22 @@ impl Folder for State {
|
|||
stmts.push(
|
||||
StmtWire {
|
||||
annotations: Default::default(),
|
||||
wire: wire.to_dyn_canonical_wire(),
|
||||
wire: wire.canonical(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
connect_port(
|
||||
&mut stmts,
|
||||
new_port.to_expr().to_canonical_dyn(),
|
||||
wire.to_expr().to_canonical_dyn(),
|
||||
Expr::canonical(new_port.to_expr()),
|
||||
Expr::canonical(wire.to_expr()),
|
||||
port.source_location(),
|
||||
);
|
||||
self.replacement_mem_ports
|
||||
.insert(port, wire.to_dyn_canonical_wire().intern_sized());
|
||||
self.replacement_mem_ports.insert(port, wire.canonical());
|
||||
}
|
||||
memories.push(Mem::new_unchecked(
|
||||
memory.scoped_name(),
|
||||
memory.source_location(),
|
||||
ArrayType::new_slice(new_element_ty, memory.array_type().len()),
|
||||
ArrayType::new_dyn(new_element_ty, memory.array_type().len()),
|
||||
memory.initial_value(),
|
||||
Intern::intern_owned(new_ports),
|
||||
memory.read_latency(),
|
||||
|
@ -458,7 +467,7 @@ impl Folder for State {
|
|||
fn fold_stmt(&mut self, stmt: Stmt) -> Result<Stmt, Self::Error> {
|
||||
fn match_int_tag(
|
||||
state: &mut State,
|
||||
int_tag_expr: Expr<DynUInt>,
|
||||
int_tag_expr: Expr<UInt>,
|
||||
source_location: SourceLocation,
|
||||
blocks: Interned<[Block]>,
|
||||
) -> Result<StmtIf, SimplifyEnumsError> {
|
||||
|
@ -473,16 +482,15 @@ impl Folder for State {
|
|||
});
|
||||
};
|
||||
let mut retval = StmtIf {
|
||||
cond: int_tag_expr.cmp_eq(DynUInt::with_type(
|
||||
int_tag_expr.ty(),
|
||||
next_to_last_variant_index,
|
||||
)),
|
||||
cond: int_tag_expr
|
||||
.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)),
|
||||
source_location,
|
||||
blocks: [next_to_last_block.fold(state)?, last_block.fold(state)?],
|
||||
};
|
||||
for (variant_index, block) in blocks_iter.rev() {
|
||||
retval = StmtIf {
|
||||
cond: int_tag_expr.cmp_eq(DynUInt::with_type(int_tag_expr.ty(), variant_index)),
|
||||
cond: int_tag_expr
|
||||
.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)),
|
||||
source_location,
|
||||
blocks: [
|
||||
block.fold(state)?,
|
||||
|
@ -500,33 +508,27 @@ impl Folder for State {
|
|||
expr,
|
||||
source_location,
|
||||
blocks,
|
||||
}) => match self.get_or_make_enum_type_state(expr.ty())? {
|
||||
}) => match self.get_or_make_enum_type_state(Expr::ty(expr))? {
|
||||
EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch {
|
||||
expr: expr
|
||||
.expr_enum()
|
||||
.fold(self)?
|
||||
.to_expr()
|
||||
.with_type::<TagAndBody<DynEnum>>()
|
||||
.tag,
|
||||
expr: Expr::<TagAndBody<Enum, DynSize>>::from_canonical(
|
||||
Expr::canonical(expr).fold(self)?,
|
||||
)
|
||||
.tag,
|
||||
source_location,
|
||||
blocks: blocks.fold(self)?,
|
||||
}
|
||||
.into()),
|
||||
EnumTypeState::TagUIntAndBody(_) => {
|
||||
let int_tag_expr = expr
|
||||
.expr_enum()
|
||||
.fold(self)?
|
||||
.to_expr()
|
||||
.with_type::<TagAndBody<DynUInt>>()
|
||||
.tag;
|
||||
let int_tag_expr = Expr::<TagAndBody<UInt, DynSize>>::from_canonical(
|
||||
Expr::canonical(expr).fold(self)?,
|
||||
)
|
||||
.tag;
|
||||
Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into())
|
||||
}
|
||||
EnumTypeState::UInt(_) => {
|
||||
let int_tag_expr = expr
|
||||
.expr_enum()
|
||||
.fold(self)?
|
||||
.to_expr()
|
||||
.with_type::<DynUInt>()[..expr.ty().discriminant_bit_width()];
|
||||
let int_tag_expr =
|
||||
Expr::<UInt>::from_canonical(Expr::canonical(expr).fold(self)?)
|
||||
[..Expr::ty(expr).discriminant_bit_width()];
|
||||
Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into())
|
||||
}
|
||||
EnumTypeState::Unchanged => Ok(StmtMatch {
|
||||
|
@ -544,92 +546,49 @@ impl Folder for State {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
fn fold_type_enum(&mut self, type_enum: TypeEnum) -> Result<TypeEnum, Self::Error> {
|
||||
match type_enum {
|
||||
TypeEnum::EnumType(enum_type) => {
|
||||
fn fold_canonical_type(
|
||||
&mut self,
|
||||
canonical_type: CanonicalType,
|
||||
) -> Result<CanonicalType, Self::Error> {
|
||||
match canonical_type {
|
||||
CanonicalType::Enum(enum_type) => {
|
||||
Ok(match self.get_or_make_enum_type_state(enum_type)? {
|
||||
EnumTypeState::TagEnumAndBody(ty) => TypeEnum::BundleType(ty.canonical()),
|
||||
EnumTypeState::TagUIntAndBody(ty) => TypeEnum::BundleType(ty.canonical()),
|
||||
EnumTypeState::UInt(ty) => TypeEnum::UInt(ty),
|
||||
EnumTypeState::Unchanged => TypeEnum::EnumType(enum_type),
|
||||
EnumTypeState::TagEnumAndBody(ty) => ty.canonical(),
|
||||
EnumTypeState::TagUIntAndBody(ty) => ty.canonical(),
|
||||
EnumTypeState::UInt(ty) => ty.canonical(),
|
||||
EnumTypeState::Unchanged => enum_type.canonical(),
|
||||
})
|
||||
}
|
||||
TypeEnum::BundleType(_)
|
||||
| TypeEnum::ArrayType(_)
|
||||
| TypeEnum::UInt(_)
|
||||
| TypeEnum::SInt(_)
|
||||
| TypeEnum::Clock(_)
|
||||
| TypeEnum::AsyncReset(_)
|
||||
| TypeEnum::SyncReset(_)
|
||||
| TypeEnum::Reset(_) => type_enum.default_fold(self),
|
||||
CanonicalType::Bundle(_)
|
||||
| CanonicalType::Array(_)
|
||||
| CanonicalType::UInt(_)
|
||||
| CanonicalType::SInt(_)
|
||||
| CanonicalType::Bool(_)
|
||||
| CanonicalType::Clock(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_) => canonical_type.default_fold(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_value_enum(&mut self, value_enum: ValueEnum) -> Result<ValueEnum, Self::Error> {
|
||||
match value_enum {
|
||||
ValueEnum::Enum(enum_value) => {
|
||||
Ok(match self.get_or_make_enum_type_state(enum_value.ty())? {
|
||||
EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> {
|
||||
tag: tag_ty,
|
||||
body: body_ty,
|
||||
}) => ValueEnum::Bundle(
|
||||
TagAndBody {
|
||||
tag: DynEnum::new_by_index(tag_ty, enum_value.variant_index(), None),
|
||||
body: value_to_uint(enum_value.variant_value().as_ref(), body_ty),
|
||||
}
|
||||
.to_canonical(),
|
||||
),
|
||||
EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> {
|
||||
tag: tag_ty,
|
||||
body: body_ty,
|
||||
}) => ValueEnum::Bundle(
|
||||
TagAndBody {
|
||||
tag: DynUInt::with_type(tag_ty, enum_value.variant_index()),
|
||||
body: value_to_uint(enum_value.variant_value().as_ref(), body_ty),
|
||||
}
|
||||
.to_canonical(),
|
||||
),
|
||||
EnumTypeState::UInt(target_ty) => {
|
||||
ValueEnum::UInt(value_to_uint(Some(&enum_value), target_ty))
|
||||
}
|
||||
EnumTypeState::Unchanged => ValueEnum::Enum(enum_value),
|
||||
})
|
||||
}
|
||||
ValueEnum::Bundle(_)
|
||||
| ValueEnum::Array(_)
|
||||
| ValueEnum::UInt(_)
|
||||
| ValueEnum::SInt(_)
|
||||
| ValueEnum::Clock(_)
|
||||
| ValueEnum::AsyncReset(_)
|
||||
| ValueEnum::SyncReset(_)
|
||||
| ValueEnum::Reset(_) => value_enum.default_fold(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_variant_type<T>(&mut self, _v: VariantType<T>) -> Result<VariantType<T>, Self::Error> {
|
||||
fn fold_enum_variant(&mut self, _v: EnumVariant) -> Result<EnumVariant, Self::Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn fold_enum_literal<EnumTy>(
|
||||
fn fold_enum_literal<T: EnumType>(
|
||||
&mut self,
|
||||
_v: ops::EnumLiteral<EnumTy>,
|
||||
) -> Result<ops::EnumLiteral<EnumTy>, Self::Error>
|
||||
_v: ops::EnumLiteral<T>,
|
||||
) -> Result<ops::EnumLiteral<T>, Self::Error>
|
||||
where
|
||||
EnumTy: EnumType,
|
||||
EnumTy::Value: EnumValue<Type = EnumTy>,
|
||||
T: Fold<Self>,
|
||||
{
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn fold_variant_access<T, VariantTy>(
|
||||
fn fold_variant_access<VariantType: Type>(
|
||||
&mut self,
|
||||
_v: ops::VariantAccess<T, VariantTy>,
|
||||
) -> Result<ops::VariantAccess<T, VariantTy>, Self::Error>
|
||||
where
|
||||
T: EnumType,
|
||||
T::Value: EnumValue,
|
||||
VariantTy: Type,
|
||||
{
|
||||
_v: ops::VariantAccess<VariantType>,
|
||||
) -> Result<ops::VariantAccess<VariantType>, Self::Error> {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -642,9 +601,9 @@ pub enum SimplifyEnumsKind {
|
|||
}
|
||||
|
||||
pub fn simplify_enums(
|
||||
module: Interned<Module<DynBundle>>,
|
||||
module: Interned<Module<Bundle>>,
|
||||
kind: SimplifyEnumsKind,
|
||||
) -> Result<Interned<Module<DynBundle>>, SimplifyEnumsError> {
|
||||
) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
|
||||
module.fold(&mut State {
|
||||
enum_types: HashMap::new(),
|
||||
replacement_mem_ports: HashMap::new(),
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
annotations::TargetedAnnotation,
|
||||
array::{Array, ArrayType, ValueArrayOrSlice},
|
||||
bundle::{BundleType, BundleValue, DynBundle},
|
||||
expr::{Expr, ExprEnum, ToExpr},
|
||||
int::{DynSInt, DynSIntType, DynUInt, DynUIntType},
|
||||
array::Array,
|
||||
bundle::{Bundle, BundleType},
|
||||
expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr},
|
||||
int::{Bool, SInt, Size, UInt},
|
||||
intern::{Intern, Interned},
|
||||
memory::{Mem, MemPort, PortType},
|
||||
module::{
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||
Block, Module, NameId, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtWire,
|
||||
},
|
||||
source_location::SourceLocation,
|
||||
ty::{DynCanonicalValue, DynType, Type, TypeEnum},
|
||||
ty::{CanonicalType, Type},
|
||||
util::MakeMutSlice,
|
||||
wire::Wire,
|
||||
};
|
||||
|
@ -28,26 +28,31 @@ use std::{
|
|||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
enum SingleType {
|
||||
UInt(DynUIntType),
|
||||
SInt(DynSIntType),
|
||||
UIntArray(ArrayType<[DynUInt]>),
|
||||
SIntArray(ArrayType<[DynSInt]>),
|
||||
UInt(UInt),
|
||||
SInt(SInt),
|
||||
Bool(Bool),
|
||||
UIntArray(Array<UInt>),
|
||||
SIntArray(Array<SInt>),
|
||||
BoolArray(Array<Bool>),
|
||||
}
|
||||
|
||||
impl SingleType {
|
||||
fn is_array_type(self, array_type: ArrayType<[DynCanonicalValue]>) -> bool {
|
||||
fn is_array_type(self, array_type: Array) -> bool {
|
||||
match self {
|
||||
SingleType::UInt(_) | SingleType::SInt(_) => false,
|
||||
SingleType::UIntArray(ty) => ty.canonical() == array_type,
|
||||
SingleType::SIntArray(ty) => ty.canonical() == array_type,
|
||||
SingleType::UInt(_) | SingleType::SInt(_) | SingleType::Bool(_) => false,
|
||||
SingleType::UIntArray(ty) => ty.as_dyn_array() == array_type,
|
||||
SingleType::SIntArray(ty) => ty.as_dyn_array() == array_type,
|
||||
SingleType::BoolArray(ty) => ty.as_dyn_array() == array_type,
|
||||
}
|
||||
}
|
||||
fn array_len(self) -> usize {
|
||||
match self {
|
||||
SingleType::UInt(_ty) => 1,
|
||||
SingleType::SInt(_ty) => 1,
|
||||
SingleType::Bool(_ty) => 1,
|
||||
SingleType::UIntArray(ty) => ty.len(),
|
||||
SingleType::SIntArray(ty) => ty.len(),
|
||||
SingleType::BoolArray(ty) => ty.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +63,7 @@ enum MemSplit {
|
|||
fields: Rc<[MemSplit]>,
|
||||
},
|
||||
Single {
|
||||
output_mem: Option<Mem<[DynCanonicalValue]>>,
|
||||
output_mem: Option<Mem>,
|
||||
element_type: SingleType,
|
||||
unchanged_element_type: bool,
|
||||
},
|
||||
|
@ -83,17 +88,17 @@ impl MemSplit {
|
|||
MemSplit::Array { elements: _ } => self,
|
||||
}
|
||||
}
|
||||
fn new(element_type: TypeEnum) -> Self {
|
||||
fn new(element_type: CanonicalType) -> Self {
|
||||
match element_type {
|
||||
TypeEnum::BundleType(bundle_ty) => MemSplit::Bundle {
|
||||
CanonicalType::Bundle(bundle_ty) => MemSplit::Bundle {
|
||||
fields: bundle_ty
|
||||
.fields()
|
||||
.into_iter()
|
||||
.map(|field| Self::new(field.ty.type_enum()).mark_changed_element_type())
|
||||
.map(|field| Self::new(field.ty).mark_changed_element_type())
|
||||
.collect(),
|
||||
},
|
||||
TypeEnum::ArrayType(ty) => {
|
||||
let element = MemSplit::new(ty.element().type_enum());
|
||||
CanonicalType::Array(ty) => {
|
||||
let element = MemSplit::new(ty.element());
|
||||
if let Self::Single {
|
||||
output_mem: _,
|
||||
element_type,
|
||||
|
@ -103,7 +108,7 @@ impl MemSplit {
|
|||
match element_type {
|
||||
SingleType::UInt(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::UIntArray(ArrayType::new_slice(
|
||||
element_type: SingleType::UIntArray(Array::new_dyn(
|
||||
element_type,
|
||||
ty.len(),
|
||||
)),
|
||||
|
@ -111,7 +116,15 @@ impl MemSplit {
|
|||
},
|
||||
SingleType::SInt(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::SIntArray(ArrayType::new_slice(
|
||||
element_type: SingleType::SIntArray(Array::new_dyn(
|
||||
element_type,
|
||||
ty.len(),
|
||||
)),
|
||||
unchanged_element_type,
|
||||
},
|
||||
SingleType::Bool(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::BoolArray(Array::new_dyn(
|
||||
element_type,
|
||||
ty.len(),
|
||||
)),
|
||||
|
@ -119,8 +132,8 @@ impl MemSplit {
|
|||
},
|
||||
SingleType::UIntArray(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::UIntArray(ArrayType::new_slice(
|
||||
*element_type.element(),
|
||||
element_type: SingleType::UIntArray(Array::new_dyn(
|
||||
element_type.element(),
|
||||
ty.len()
|
||||
.checked_mul(element_type.len())
|
||||
.expect("memory element type can't be too big"),
|
||||
|
@ -129,8 +142,18 @@ impl MemSplit {
|
|||
},
|
||||
SingleType::SIntArray(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::SIntArray(ArrayType::new_slice(
|
||||
*element_type.element(),
|
||||
element_type: SingleType::SIntArray(Array::new_dyn(
|
||||
element_type.element(),
|
||||
ty.len()
|
||||
.checked_mul(element_type.len())
|
||||
.expect("memory element type can't be too big"),
|
||||
)),
|
||||
unchanged_element_type: false,
|
||||
},
|
||||
SingleType::BoolArray(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::BoolArray(Array::new_dyn(
|
||||
element_type.element(),
|
||||
ty.len()
|
||||
.checked_mul(element_type.len())
|
||||
.expect("memory element type can't be too big"),
|
||||
|
@ -145,25 +168,30 @@ impl MemSplit {
|
|||
}
|
||||
}
|
||||
}
|
||||
TypeEnum::UInt(ty) => Self::Single {
|
||||
CanonicalType::UInt(ty) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::UInt(ty),
|
||||
unchanged_element_type: true,
|
||||
},
|
||||
TypeEnum::SInt(ty) => Self::Single {
|
||||
CanonicalType::SInt(ty) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::SInt(ty),
|
||||
unchanged_element_type: true,
|
||||
},
|
||||
TypeEnum::EnumType(ty) => Self::Single {
|
||||
CanonicalType::Bool(ty) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::UInt(DynUIntType::new(ty.bit_width())),
|
||||
element_type: SingleType::Bool(ty),
|
||||
unchanged_element_type: true,
|
||||
},
|
||||
CanonicalType::Enum(ty) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::UInt(UInt::new_dyn(ty.type_properties().bit_width)),
|
||||
unchanged_element_type: false,
|
||||
},
|
||||
TypeEnum::Clock(_)
|
||||
| TypeEnum::AsyncReset(_)
|
||||
| TypeEnum::SyncReset(_)
|
||||
| TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"),
|
||||
CanonicalType::Clock(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,9 +201,9 @@ struct MemState {
|
|||
}
|
||||
|
||||
struct SplitState<'a> {
|
||||
wire_rdata: Box<[Option<Expr<DynCanonicalValue>>]>,
|
||||
wire_wdata: Box<[Option<Expr<DynCanonicalValue>>]>,
|
||||
wire_wmask: Box<[Option<Expr<DynCanonicalValue>>]>,
|
||||
wire_rdata: Box<[Option<Expr<CanonicalType>>]>,
|
||||
wire_wdata: Box<[Option<Expr<CanonicalType>>]>,
|
||||
wire_wmask: Box<[Option<Expr<CanonicalType>>]>,
|
||||
initial_value: Option<Box<[&'a BitSlice]>>,
|
||||
}
|
||||
|
||||
|
@ -223,7 +251,7 @@ impl<'a> SplitStateStack<'a> {
|
|||
}
|
||||
fn push_map(
|
||||
&mut self,
|
||||
mut wire_map: impl FnMut(Expr<DynCanonicalValue>) -> Expr<DynCanonicalValue>,
|
||||
mut wire_map: impl FnMut(Expr<CanonicalType>) -> Expr<CanonicalType>,
|
||||
mut initial_value_element_map: impl FnMut(&BitSlice) -> &BitSlice,
|
||||
) {
|
||||
let top_index = self.top_index + 1;
|
||||
|
@ -260,10 +288,10 @@ impl<'a> SplitStateStack<'a> {
|
|||
|
||||
struct SplitMemState<'a, 'b> {
|
||||
module_state: &'a mut ModuleState,
|
||||
input_mem: Mem<[DynCanonicalValue]>,
|
||||
output_mems: &'a mut Vec<Mem<[DynCanonicalValue]>>,
|
||||
input_mem: Mem,
|
||||
output_mems: &'a mut Vec<Mem>,
|
||||
output_stmts: &'a mut Vec<Stmt>,
|
||||
element_type: TypeEnum,
|
||||
element_type: CanonicalType,
|
||||
split: &'a mut MemSplit,
|
||||
mem_name_path: &'a mut String,
|
||||
split_state_stack: &'a mut SplitStateStack<'b>,
|
||||
|
@ -275,7 +303,7 @@ impl SplitMemState<'_, '_> {
|
|||
let outer_mem_name_path_len = self.mem_name_path.len();
|
||||
match self.split {
|
||||
MemSplit::Bundle { fields } => {
|
||||
let TypeEnum::BundleType(bundle_type) = self.element_type else {
|
||||
let CanonicalType::Bundle(bundle_type) = self.element_type else {
|
||||
unreachable!();
|
||||
};
|
||||
for ((field, field_offset), split) in bundle_type
|
||||
|
@ -289,7 +317,9 @@ impl SplitMemState<'_, '_> {
|
|||
self.mem_name_path.push_str(&field.name);
|
||||
let field_ty_bit_width = field.ty.bit_width();
|
||||
self.split_state_stack.push_map(
|
||||
|e: Expr<DynCanonicalValue>| e.with_type::<DynBundle>().field(&field.name),
|
||||
|e: Expr<CanonicalType>| {
|
||||
Expr::field(Expr::<Bundle>::from_canonical(e), &field.name)
|
||||
},
|
||||
|initial_value_element| {
|
||||
&initial_value_element[field_offset..][..field_ty_bit_width]
|
||||
},
|
||||
|
@ -299,7 +329,7 @@ impl SplitMemState<'_, '_> {
|
|||
input_mem: self.input_mem,
|
||||
output_mems: self.output_mems,
|
||||
output_stmts: self.output_stmts,
|
||||
element_type: field.ty.type_enum(),
|
||||
element_type: field.ty,
|
||||
split,
|
||||
mem_name_path: self.mem_name_path,
|
||||
split_state_stack: self.split_state_stack,
|
||||
|
@ -328,7 +358,7 @@ impl SplitMemState<'_, '_> {
|
|||
.zip(self.mem_state.replacement_ports.iter())
|
||||
{
|
||||
let port_expr = port.to_expr();
|
||||
let wire_expr = Expr::<DynBundle>::new_unchecked(*wire);
|
||||
let wire_expr = Expr::<Bundle>::from_canonical(wire.to_expr());
|
||||
for name in [
|
||||
Some("addr"),
|
||||
Some("clk"),
|
||||
|
@ -340,8 +370,8 @@ impl SplitMemState<'_, '_> {
|
|||
};
|
||||
self.output_stmts.push(
|
||||
StmtConnect {
|
||||
lhs: port_expr.field(name),
|
||||
rhs: wire_expr.field(name),
|
||||
lhs: Expr::field(port_expr, name),
|
||||
rhs: Expr::field(wire_expr, name),
|
||||
source_location: port.source_location(),
|
||||
}
|
||||
.into(),
|
||||
|
@ -352,18 +382,16 @@ impl SplitMemState<'_, '_> {
|
|||
self.output_mems.push(new_mem);
|
||||
}
|
||||
MemSplit::Array { elements } => {
|
||||
let TypeEnum::ArrayType(array_type) = self.element_type else {
|
||||
let CanonicalType::Array(array_type) = self.element_type else {
|
||||
unreachable!();
|
||||
};
|
||||
let element_type = array_type.element().type_enum();
|
||||
let element_bit_width = array_type.element().bit_width();
|
||||
let element_type = array_type.element();
|
||||
let element_bit_width = element_type.bit_width();
|
||||
for (index, split) in elements.make_mut_slice().iter_mut().enumerate() {
|
||||
self.mem_name_path.truncate(outer_mem_name_path_len);
|
||||
write!(self.mem_name_path, "_{index}").unwrap();
|
||||
self.split_state_stack.push_map(
|
||||
|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()[index]
|
||||
},
|
||||
|e| Expr::<Array>::from_canonical(e)[index],
|
||||
|initial_value_element| {
|
||||
&initial_value_element[index * element_bit_width..][..element_bit_width]
|
||||
},
|
||||
|
@ -388,7 +416,7 @@ impl SplitMemState<'_, '_> {
|
|||
}
|
||||
|
||||
struct ModuleState {
|
||||
output_module: Option<Interned<Module<DynBundle>>>,
|
||||
output_module: Option<Interned<Module<Bundle>>>,
|
||||
name_id_gen: NameIdGen,
|
||||
memories: HashMap<ScopedNameId, MemState>,
|
||||
}
|
||||
|
@ -397,18 +425,18 @@ impl ModuleState {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
fn connect_split_mem_port_arrays(
|
||||
output_stmts: &mut Vec<Stmt>,
|
||||
input_array_types: &[ArrayType<[DynCanonicalValue]>],
|
||||
input_array_types: &[Array],
|
||||
memory_element_array_range_start: usize,
|
||||
memory_element_array_range_len: usize,
|
||||
wire_rdata: Option<Expr<DynCanonicalValue>>,
|
||||
wire_wdata: Option<Expr<DynCanonicalValue>>,
|
||||
wire_wmask: Option<Expr<DynCanonicalValue>>,
|
||||
port_rdata: Option<Expr<Array<[DynCanonicalValue]>>>,
|
||||
port_wdata: Option<Expr<Array<[DynCanonicalValue]>>>,
|
||||
port_wmask: Option<Expr<Array<[DynCanonicalValue]>>>,
|
||||
connect_rdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
|
||||
connect_wdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
|
||||
connect_wmask: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
|
||||
wire_rdata: Option<Expr<CanonicalType>>,
|
||||
wire_wdata: Option<Expr<CanonicalType>>,
|
||||
wire_wmask: Option<Expr<CanonicalType>>,
|
||||
port_rdata: Option<Expr<Array>>,
|
||||
port_wdata: Option<Expr<Array>>,
|
||||
port_wmask: Option<Expr<Array>>,
|
||||
connect_rdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<CanonicalType>, Expr<CanonicalType>),
|
||||
connect_wdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<CanonicalType>, Expr<CanonicalType>),
|
||||
connect_wmask: impl Copy + Fn(&mut Vec<Stmt>, Expr<CanonicalType>, Expr<CanonicalType>),
|
||||
) {
|
||||
let Some((input_array_type, input_array_types_rest)) = input_array_types.split_first()
|
||||
else {
|
||||
|
@ -430,8 +458,7 @@ impl ModuleState {
|
|||
assert_eq!(memory_element_array_range_len % input_array_type.len(), 0);
|
||||
let chunk_size = memory_element_array_range_len / input_array_type.len();
|
||||
for index in 0..input_array_type.len() {
|
||||
let map =
|
||||
|e: Expr<DynCanonicalValue>| e.with_type::<Array<[DynCanonicalValue]>>()[index];
|
||||
let map = |e| Expr::<Array>::from_canonical(e)[index];
|
||||
let wire_rdata = wire_rdata.map(map);
|
||||
let wire_wdata = wire_wdata.map(map);
|
||||
let wire_wmask = wire_wmask.map(map);
|
||||
|
@ -456,20 +483,20 @@ impl ModuleState {
|
|||
fn connect_split_mem_port(
|
||||
&mut self,
|
||||
output_stmts: &mut Vec<Stmt>,
|
||||
mut input_element_type: TypeEnum,
|
||||
mut input_element_type: CanonicalType,
|
||||
single_type: SingleType,
|
||||
source_location: SourceLocation,
|
||||
wire_rdata: Option<Expr<DynCanonicalValue>>,
|
||||
wire_wdata: Option<Expr<DynCanonicalValue>>,
|
||||
wire_wmask: Option<Expr<DynCanonicalValue>>,
|
||||
port_rdata: Option<Expr<DynCanonicalValue>>,
|
||||
port_wdata: Option<Expr<DynCanonicalValue>>,
|
||||
port_wmask: Option<Expr<DynCanonicalValue>>,
|
||||
wire_rdata: Option<Expr<CanonicalType>>,
|
||||
wire_wdata: Option<Expr<CanonicalType>>,
|
||||
wire_wmask: Option<Expr<CanonicalType>>,
|
||||
port_rdata: Option<Expr<CanonicalType>>,
|
||||
port_wdata: Option<Expr<CanonicalType>>,
|
||||
port_wmask: Option<Expr<CanonicalType>>,
|
||||
) {
|
||||
let mut input_array_types = vec![];
|
||||
let connect_read = |output_stmts: &mut Vec<Stmt>,
|
||||
wire_read: Expr<DynCanonicalValue>,
|
||||
port_read: Expr<DynCanonicalValue>| {
|
||||
wire_read: Expr<CanonicalType>,
|
||||
port_read: Expr<CanonicalType>| {
|
||||
output_stmts.push(
|
||||
StmtConnect {
|
||||
lhs: wire_read,
|
||||
|
@ -480,8 +507,8 @@ impl ModuleState {
|
|||
);
|
||||
};
|
||||
let connect_write = |output_stmts: &mut Vec<Stmt>,
|
||||
wire_write: Expr<DynCanonicalValue>,
|
||||
port_write: Expr<DynCanonicalValue>| {
|
||||
wire_write: Expr<CanonicalType>,
|
||||
port_write: Expr<CanonicalType>| {
|
||||
output_stmts.push(
|
||||
StmtConnect {
|
||||
lhs: port_write,
|
||||
|
@ -491,32 +518,29 @@ impl ModuleState {
|
|||
.into(),
|
||||
);
|
||||
};
|
||||
let connect_read_enum =
|
||||
|output_stmts: &mut Vec<Stmt>,
|
||||
wire_read: Expr<DynCanonicalValue>,
|
||||
port_read: Expr<DynCanonicalValue>| {
|
||||
connect_read(
|
||||
output_stmts,
|
||||
wire_read,
|
||||
port_read
|
||||
.with_type::<DynUInt>()
|
||||
.cast_bits_to(wire_read.ty()),
|
||||
);
|
||||
};
|
||||
let connect_read_enum = |output_stmts: &mut Vec<Stmt>,
|
||||
wire_read: Expr<CanonicalType>,
|
||||
port_read: Expr<CanonicalType>| {
|
||||
connect_read(
|
||||
output_stmts,
|
||||
wire_read,
|
||||
Expr::<UInt>::from_canonical(port_read).cast_bits_to(Expr::ty(wire_read)),
|
||||
);
|
||||
};
|
||||
let connect_write_enum =
|
||||
|output_stmts: &mut Vec<Stmt>,
|
||||
wire_write: Expr<DynCanonicalValue>,
|
||||
port_write: Expr<DynCanonicalValue>| {
|
||||
wire_write: Expr<CanonicalType>,
|
||||
port_write: Expr<CanonicalType>| {
|
||||
connect_write(
|
||||
output_stmts,
|
||||
wire_write.cast_to_bits().to_canonical_dyn(),
|
||||
Expr::canonical(wire_write.cast_to_bits()),
|
||||
port_write,
|
||||
);
|
||||
};
|
||||
loop {
|
||||
match input_element_type {
|
||||
TypeEnum::BundleType(_) => unreachable!("bundle types are always split"),
|
||||
TypeEnum::EnumType(_)
|
||||
CanonicalType::Bundle(_) => unreachable!("bundle types are always split"),
|
||||
CanonicalType::Enum(_)
|
||||
if input_array_types
|
||||
.first()
|
||||
.map(|&v| single_type.is_array_type(v))
|
||||
|
@ -532,7 +556,7 @@ impl ModuleState {
|
|||
connect_write(output_stmts, wire_wmask, port_wmask);
|
||||
}
|
||||
}
|
||||
TypeEnum::EnumType(_) => Self::connect_split_mem_port_arrays(
|
||||
CanonicalType::Enum(_) => Self::connect_split_mem_port_arrays(
|
||||
output_stmts,
|
||||
&input_array_types,
|
||||
0,
|
||||
|
@ -540,25 +564,19 @@ impl ModuleState {
|
|||
wire_rdata,
|
||||
wire_wdata,
|
||||
wire_wmask,
|
||||
port_rdata.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
port_wdata.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
port_wmask.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
port_rdata.map(Expr::from_canonical),
|
||||
port_wdata.map(Expr::from_canonical),
|
||||
port_wmask.map(Expr::from_canonical),
|
||||
connect_read_enum,
|
||||
connect_write_enum,
|
||||
connect_write_enum,
|
||||
),
|
||||
TypeEnum::ArrayType(array_type) => {
|
||||
CanonicalType::Array(array_type) => {
|
||||
input_array_types.push(array_type);
|
||||
input_element_type = array_type.element().type_enum();
|
||||
input_element_type = array_type.element();
|
||||
continue;
|
||||
}
|
||||
TypeEnum::UInt(_) | TypeEnum::SInt(_)
|
||||
CanonicalType::UInt(_) | CanonicalType::SInt(_) | CanonicalType::Bool(_)
|
||||
if input_array_types
|
||||
.first()
|
||||
.map(|&v| single_type.is_array_type(v))
|
||||
|
@ -574,59 +592,56 @@ impl ModuleState {
|
|||
connect_write(output_stmts, wire_wmask, port_wmask);
|
||||
}
|
||||
}
|
||||
TypeEnum::UInt(_) | TypeEnum::SInt(_) => Self::connect_split_mem_port_arrays(
|
||||
output_stmts,
|
||||
&input_array_types,
|
||||
0,
|
||||
single_type.array_len(),
|
||||
wire_rdata,
|
||||
wire_wdata,
|
||||
wire_wmask,
|
||||
port_rdata.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
port_wdata.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
port_wmask.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
connect_read,
|
||||
connect_write,
|
||||
connect_write,
|
||||
),
|
||||
TypeEnum::Clock(_)
|
||||
| TypeEnum::AsyncReset(_)
|
||||
| TypeEnum::SyncReset(_)
|
||||
| TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"),
|
||||
CanonicalType::UInt(_) | CanonicalType::SInt(_) | CanonicalType::Bool(_) => {
|
||||
Self::connect_split_mem_port_arrays(
|
||||
output_stmts,
|
||||
&input_array_types,
|
||||
0,
|
||||
single_type.array_len(),
|
||||
wire_rdata,
|
||||
wire_wdata,
|
||||
wire_wmask,
|
||||
port_rdata.map(Expr::from_canonical),
|
||||
port_wdata.map(Expr::from_canonical),
|
||||
port_wmask.map(Expr::from_canonical),
|
||||
connect_read,
|
||||
connect_write,
|
||||
connect_write,
|
||||
)
|
||||
}
|
||||
CanonicalType::Clock(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fn create_split_mem(
|
||||
&mut self,
|
||||
input_mem: Mem<[DynCanonicalValue]>,
|
||||
input_mem: Mem,
|
||||
output_stmts: &mut Vec<Stmt>,
|
||||
input_element_type: TypeEnum,
|
||||
input_element_type: CanonicalType,
|
||||
single_type: SingleType,
|
||||
mem_name_path: &str,
|
||||
split_state: &SplitState<'_>,
|
||||
) -> Mem<[DynCanonicalValue]> {
|
||||
) -> Mem {
|
||||
let mem_name = self.name_id_gen.gen(Intern::intern_owned(format!(
|
||||
"{}{mem_name_path}",
|
||||
input_mem.scoped_name().1 .0
|
||||
)));
|
||||
let mem_name = ScopedNameId(input_mem.scoped_name().0, mem_name);
|
||||
let output_element_type = match single_type {
|
||||
SingleType::UInt(ty) => ty.canonical_dyn(),
|
||||
SingleType::SInt(ty) => ty.canonical_dyn(),
|
||||
SingleType::UIntArray(ty) => ty.canonical_dyn(),
|
||||
SingleType::SIntArray(ty) => ty.canonical_dyn(),
|
||||
SingleType::UInt(ty) => ty.canonical(),
|
||||
SingleType::SInt(ty) => ty.canonical(),
|
||||
SingleType::Bool(ty) => ty.canonical(),
|
||||
SingleType::UIntArray(ty) => ty.canonical(),
|
||||
SingleType::SIntArray(ty) => ty.canonical(),
|
||||
SingleType::BoolArray(ty) => ty.canonical(),
|
||||
};
|
||||
let output_array_type =
|
||||
ArrayType::new_slice(output_element_type, input_mem.array_type().len());
|
||||
let output_array_type = Array::new_dyn(output_element_type, input_mem.array_type().len());
|
||||
let initial_value = split_state.initial_value.as_ref().map(|initial_value| {
|
||||
let mut bits = BitVec::with_capacity(output_array_type.bit_width());
|
||||
let mut bits = BitVec::with_capacity(output_array_type.type_properties().bit_width);
|
||||
for element in initial_value.iter() {
|
||||
bits.extend_from_bitslice(element);
|
||||
}
|
||||
|
@ -643,7 +658,6 @@ impl ModuleState {
|
|||
port.addr_type(),
|
||||
output_element_type,
|
||||
)
|
||||
.intern_sized()
|
||||
})
|
||||
.collect();
|
||||
let output_mem = Mem::new_unchecked(
|
||||
|
@ -676,15 +690,15 @@ impl ModuleState {
|
|||
let port_rdata = port
|
||||
.port_kind()
|
||||
.rdata_name()
|
||||
.map(|name| port_expr.field(name));
|
||||
.map(|name| Expr::field(port_expr, name));
|
||||
let port_wdata = port
|
||||
.port_kind()
|
||||
.wdata_name()
|
||||
.map(|name| port_expr.field(name));
|
||||
.map(|name| Expr::field(port_expr, name));
|
||||
let port_wmask = port
|
||||
.port_kind()
|
||||
.wmask_name()
|
||||
.map(|name| port_expr.field(name));
|
||||
.map(|name| Expr::field(port_expr, name));
|
||||
self.connect_split_mem_port(
|
||||
output_stmts,
|
||||
input_element_type,
|
||||
|
@ -702,11 +716,11 @@ impl ModuleState {
|
|||
}
|
||||
fn process_mem(
|
||||
&mut self,
|
||||
input_mem: Mem<[DynCanonicalValue]>,
|
||||
output_mems: &mut Vec<Mem<[DynCanonicalValue]>>,
|
||||
input_mem: Mem,
|
||||
output_mems: &mut Vec<Mem>,
|
||||
output_stmts: &mut Vec<Stmt>,
|
||||
) {
|
||||
let element_type = input_mem.array_type().element().type_enum();
|
||||
let element_type = input_mem.array_type().element();
|
||||
let mut split = MemSplit::new(element_type);
|
||||
let mem_state = match split {
|
||||
MemSplit::Single {
|
||||
|
@ -748,7 +762,7 @@ impl ModuleState {
|
|||
port_ty,
|
||||
);
|
||||
let wire_expr = wire.to_expr();
|
||||
let canonical_wire = wire.to_dyn_canonical_wire();
|
||||
let canonical_wire = wire.canonical();
|
||||
output_stmts.push(
|
||||
StmtWire {
|
||||
annotations: Default::default(),
|
||||
|
@ -756,21 +770,21 @@ impl ModuleState {
|
|||
}
|
||||
.into(),
|
||||
);
|
||||
replacement_ports.push(ExprEnum::Wire(canonical_wire.intern_sized()));
|
||||
replacement_ports.push(ExprEnum::Wire(canonical_wire));
|
||||
wire_port_rdata.push(
|
||||
port.port_kind()
|
||||
.rdata_name()
|
||||
.map(|name| wire_expr.field(name)),
|
||||
.map(|name| Expr::field(wire_expr, name)),
|
||||
);
|
||||
wire_port_wdata.push(
|
||||
port.port_kind()
|
||||
.wdata_name()
|
||||
.map(|name| wire_expr.field(name)),
|
||||
.map(|name| Expr::field(wire_expr, name)),
|
||||
);
|
||||
wire_port_wmask.push(
|
||||
port.port_kind()
|
||||
.wmask_name()
|
||||
.map(|name| wire_expr.field(name)),
|
||||
.map(|name| Expr::field(wire_expr, name)),
|
||||
);
|
||||
}
|
||||
let mem_state = MemState {
|
||||
|
@ -815,8 +829,8 @@ impl ModuleState {
|
|||
|
||||
#[derive(Default)]
|
||||
struct State {
|
||||
modules: HashMap<Interned<Module<DynBundle>>, ModuleState>,
|
||||
current_module: Option<Interned<Module<DynBundle>>>,
|
||||
modules: HashMap<Interned<Module<Bundle>>, ModuleState>,
|
||||
current_module: Option<Interned<Module<Bundle>>>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
@ -828,11 +842,11 @@ impl State {
|
|||
|
||||
struct PushedState<'a> {
|
||||
state: &'a mut State,
|
||||
old_module: Option<Interned<Module<DynBundle>>>,
|
||||
old_module: Option<Interned<Module<Bundle>>>,
|
||||
}
|
||||
|
||||
impl<'a> PushedState<'a> {
|
||||
fn push_module(state: &'a mut State, module: Interned<Module<DynBundle>>) -> Self {
|
||||
fn push_module(state: &'a mut State, module: Interned<Module<Bundle>>) -> Self {
|
||||
let old_module = state.current_module.replace(module);
|
||||
Self { state, old_module }
|
||||
}
|
||||
|
@ -860,10 +874,7 @@ impl DerefMut for PushedState<'_> {
|
|||
impl Folder for State {
|
||||
type Error = Infallible;
|
||||
|
||||
fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error>
|
||||
where
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
fn fold_module<T: BundleType>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> {
|
||||
let module: Interned<_> = v.canonical().intern_sized();
|
||||
if let Some(module_state) = self.modules.get(&module) {
|
||||
return Ok(Module::from_canonical(
|
||||
|
@ -886,10 +897,10 @@ impl Folder for State {
|
|||
Ok(Module::from_canonical(*module))
|
||||
}
|
||||
|
||||
fn fold_mem<VA: ValueArrayOrSlice + ?Sized>(
|
||||
fn fold_mem<Element: Type, Len: Size>(
|
||||
&mut self,
|
||||
_v: Mem<VA>,
|
||||
) -> Result<Mem<VA>, Self::Error> {
|
||||
_v: Mem<Element, Len>,
|
||||
) -> Result<Mem<Element, Len>, Self::Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
|
@ -933,7 +944,7 @@ impl Folder for State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn simplify_memories(module: Interned<Module<DynBundle>>) -> Interned<Module<DynBundle>> {
|
||||
pub fn simplify_memories(module: Interned<Module<Bundle>>) -> Interned<Module<Bundle>> {
|
||||
module
|
||||
.fold(&mut State::default())
|
||||
.unwrap_or_else(|v| match v {})
|
||||
|
|
|
@ -3,18 +3,19 @@
|
|||
#![allow(clippy::multiple_bound_locations)]
|
||||
use crate::{
|
||||
annotations::{Annotation, CustomFirrtlAnnotation, TargetedAnnotation},
|
||||
array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice},
|
||||
bundle::{BundleType, BundleValue, DynBundle, DynBundleType, FieldType},
|
||||
clock::{Clock, ClockType},
|
||||
enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType},
|
||||
array::ArrayType,
|
||||
bundle::{Bundle, BundleField, BundleType},
|
||||
clock::Clock,
|
||||
enum_::{Enum, EnumType, EnumVariant},
|
||||
expr::{
|
||||
ops, Expr, ExprEnum, Literal, Target, TargetBase, TargetChild, TargetPathArrayElement,
|
||||
TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, ToExpr,
|
||||
},
|
||||
int::{
|
||||
DynInt, DynIntType, DynSInt, DynSIntType, DynUInt, DynUIntType, StaticOrDynIntType, IntType,
|
||||
IntTypeTrait,
|
||||
ops,
|
||||
target::{
|
||||
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
|
||||
TargetPathDynArrayElement, TargetPathElement,
|
||||
},
|
||||
Expr, ExprEnum,
|
||||
},
|
||||
int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue},
|
||||
intern::{Intern, Interned},
|
||||
memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite},
|
||||
module::{
|
||||
|
@ -24,10 +25,9 @@ use crate::{
|
|||
StmtMatch, StmtReg, StmtWire,
|
||||
},
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType},
|
||||
reset::{AsyncReset, Reset, SyncReset},
|
||||
source_location::SourceLocation,
|
||||
ty::{DynCanonicalType, DynCanonicalValue, DynType, Type, TypeEnum, Value, ValueEnum},
|
||||
util::{ConstBool, GenericConstBool},
|
||||
ty::{CanonicalType, Type},
|
||||
wire::Wire,
|
||||
};
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
|
@ -473,7 +473,4 @@ impl<T: ?Sized + Visit<State>, State: ?Sized + Visitor> Visit<State> for &'_ mut
|
|||
}
|
||||
}
|
||||
|
||||
type InternedDynType = Interned<dyn DynType>;
|
||||
type InternedDynCanonicalType = Interned<dyn DynCanonicalType>;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/visit.rs"));
|
||||
|
|
22
crates/fayalite/src/prelude.rs
Normal file
22
crates/fayalite/src/prelude.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
pub use crate::{
|
||||
annotations::Annotation,
|
||||
array::{Array, ArrayType},
|
||||
cli::Cli,
|
||||
clock::{Clock, ClockDomain, ToClock},
|
||||
enum_::{HdlNone, HdlOption, HdlSome},
|
||||
expr::{CastBitsTo, CastTo, CastToBits, Expr, ReduceBits, ToExpr},
|
||||
hdl, hdl_module,
|
||||
int::{Bool, DynSize, IntCmp, KnownSize, SInt, SIntType, Size, UInt, UIntType},
|
||||
memory::{Mem, MemBuilder, ReadUnderWrite},
|
||||
module::{
|
||||
annotate, connect, connect_any, instance, memory, memory_array, memory_with_init,
|
||||
reg_builder, wire, Instance, Module, ModuleBuilder,
|
||||
},
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
|
||||
source_location::SourceLocation,
|
||||
ty::{AsMask, CanonicalType, Type},
|
||||
util::{ConstUsize, GenericConstUsize},
|
||||
wire::Wire,
|
||||
__,
|
||||
};
|
|
@ -2,21 +2,21 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
clock::ClockDomain,
|
||||
expr::{Expr, ExprTrait, Flow, ToExpr},
|
||||
expr::{Expr, Flow},
|
||||
intern::Interned,
|
||||
module::{NameId, ScopedNameId},
|
||||
source_location::SourceLocation,
|
||||
ty::{DynCanonicalType, DynType, Type},
|
||||
ty::{CanonicalType, Type},
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Reg<T: Type> {
|
||||
name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
ty: T,
|
||||
clock_domain: Expr<ClockDomain>,
|
||||
init: Option<Expr<T::Value>>,
|
||||
init: Option<Expr<T>>,
|
||||
}
|
||||
|
||||
impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> {
|
||||
|
@ -37,20 +37,8 @@ impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Type> ToExpr for Reg<T> {
|
||||
type Type = T;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
self.ty.clone()
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::new_unchecked(self.expr_enum())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> Reg<T> {
|
||||
pub fn canonical(&self) -> Reg<T::CanonicalType> {
|
||||
pub fn canonical(&self) -> Reg<CanonicalType> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
|
@ -66,49 +54,20 @@ impl<T: Type> Reg<T> {
|
|||
init: init.map(Expr::canonical),
|
||||
}
|
||||
}
|
||||
pub fn to_dyn_reg(&self) -> Reg<Interned<dyn DynType>> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
ref ty,
|
||||
clock_domain,
|
||||
init,
|
||||
} = *self;
|
||||
Reg {
|
||||
name,
|
||||
source_location,
|
||||
ty: ty.to_dyn(),
|
||||
clock_domain,
|
||||
init: init.map(Expr::to_dyn),
|
||||
}
|
||||
}
|
||||
pub fn to_dyn_canonical_reg(&self) -> Reg<Interned<dyn DynCanonicalType>> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
ref ty,
|
||||
clock_domain,
|
||||
init,
|
||||
} = *self;
|
||||
Reg {
|
||||
name,
|
||||
source_location,
|
||||
ty: ty.canonical_dyn(),
|
||||
clock_domain,
|
||||
init: init.map(Expr::to_canonical_dyn),
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_unchecked(
|
||||
scoped_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
ty: T,
|
||||
clock_domain: Expr<ClockDomain>,
|
||||
init: Option<Expr<T::Value>>,
|
||||
init: Option<Expr<T>>,
|
||||
) -> Self {
|
||||
assert!(ty.is_storable(), "register type must be a storable type");
|
||||
assert!(
|
||||
ty.canonical().is_storable(),
|
||||
"register type must be a storable type"
|
||||
);
|
||||
if let Some(init) = init {
|
||||
assert_eq!(ty, init.ty(), "register's type must match init type");
|
||||
assert_eq!(ty, Expr::ty(init), "register's type must match init type");
|
||||
}
|
||||
Self {
|
||||
name: scoped_name,
|
||||
|
@ -118,6 +77,9 @@ impl<T: Type> Reg<T> {
|
|||
init,
|
||||
}
|
||||
}
|
||||
pub fn ty(&self) -> T {
|
||||
self.ty
|
||||
}
|
||||
pub fn source_location(&self) -> SourceLocation {
|
||||
self.source_location
|
||||
}
|
||||
|
@ -139,7 +101,7 @@ impl<T: Type> Reg<T> {
|
|||
pub fn clock_domain(&self) -> Expr<ClockDomain> {
|
||||
self.clock_domain
|
||||
}
|
||||
pub fn init(&self) -> Option<Expr<T::Value>> {
|
||||
pub fn init(&self) -> Option<Expr<T>> {
|
||||
self.init
|
||||
}
|
||||
pub fn flow(&self) -> Flow {
|
||||
|
|
|
@ -2,358 +2,119 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
expr::{Expr, ToExpr},
|
||||
int::{UInt, UIntType},
|
||||
intern::Interned,
|
||||
int::Bool,
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
|
||||
DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum,
|
||||
},
|
||||
util::interned_bit,
|
||||
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
|
||||
};
|
||||
use bitvec::slice::BitSlice;
|
||||
|
||||
pub trait ResetTypeTrait: CanonicalType + StaticType<MaskType = UIntType<1>> {}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||
pub struct AsyncResetType;
|
||||
|
||||
impl AsyncResetType {
|
||||
pub const fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
mod sealed {
|
||||
pub trait ResetTypeSealed {}
|
||||
}
|
||||
|
||||
impl Type for AsyncResetType {
|
||||
type Value = AsyncReset;
|
||||
type CanonicalType = AsyncResetType;
|
||||
type CanonicalValue = AsyncReset;
|
||||
type MaskType = UIntType<1>;
|
||||
type MaskValue = UInt<1>;
|
||||
pub trait ResetType: StaticType<MaskType = Bool> + sealed::ResetTypeSealed {}
|
||||
|
||||
impl_match_values_as_self!();
|
||||
macro_rules! reset_type {
|
||||
($name:ident, $Trait:ident::$trait_fn:ident, $is_castable_from_bits:literal) => {
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||
pub struct $name;
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
UIntType::new()
|
||||
}
|
||||
impl Type for $name {
|
||||
type BaseType = $name;
|
||||
type MaskType = Bool;
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
*self
|
||||
}
|
||||
impl_match_variant_as_self!();
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
Bool
|
||||
}
|
||||
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::AsyncReset(*self)
|
||||
}
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
CanonicalType::$name(*self)
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
t
|
||||
}
|
||||
fn source_location() -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect<Self> for AsyncResetType {}
|
||||
|
||||
impl CanonicalType for AsyncResetType {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::AsyncReset;
|
||||
}
|
||||
|
||||
impl StaticType for AsyncResetType {
|
||||
fn static_type() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetTypeTrait for AsyncResetType {}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct AsyncReset(pub bool);
|
||||
|
||||
impl ToExpr for AsyncReset {
|
||||
type Type = AsyncResetType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
AsyncResetType
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<Self> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for AsyncReset {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
*self
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for AsyncReset {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::AsyncReset(*this)
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||
pub struct SyncResetType;
|
||||
|
||||
impl SyncResetType {
|
||||
pub const fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Type for SyncResetType {
|
||||
type CanonicalType = SyncResetType;
|
||||
type Value = SyncReset;
|
||||
type CanonicalValue = SyncReset;
|
||||
type MaskType = UIntType<1>;
|
||||
type MaskValue = UInt<1>;
|
||||
|
||||
impl_match_values_as_self!();
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
UIntType::new()
|
||||
}
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
*self
|
||||
}
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::SyncReset(*self)
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
t
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect<Self> for SyncResetType {}
|
||||
|
||||
impl CanonicalType for SyncResetType {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::SyncReset;
|
||||
}
|
||||
|
||||
impl StaticType for SyncResetType {
|
||||
fn static_type() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetTypeTrait for SyncResetType {}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||
pub struct SyncReset(pub bool);
|
||||
|
||||
impl ToExpr for SyncReset {
|
||||
type Type = SyncResetType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
SyncResetType
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<Self> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for SyncReset {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
*self
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for SyncReset {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::SyncReset(*this)
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
|
||||
pub struct ResetType;
|
||||
|
||||
impl ResetType {
|
||||
pub const fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Type for ResetType {
|
||||
type Value = Reset;
|
||||
type CanonicalType = ResetType;
|
||||
type CanonicalValue = Reset;
|
||||
type MaskType = UIntType<1>;
|
||||
type MaskValue = UInt<1>;
|
||||
|
||||
impl_match_values_as_self!();
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
UIntType::new()
|
||||
}
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
*self
|
||||
}
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::Reset(*self)
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
t
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect<Self> for ResetType {}
|
||||
|
||||
impl CanonicalType for ResetType {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Reset;
|
||||
}
|
||||
|
||||
impl StaticType for ResetType {
|
||||
fn static_type() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetTypeTrait for ResetType {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum Reset {}
|
||||
|
||||
impl ToExpr for Reset {
|
||||
type Type = ResetType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
match *self {}
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<Self> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for Reset {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
*self
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
match *this {}
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for Reset {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::Reset(*this)
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
match *this {}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! make_to_reset {
|
||||
(
|
||||
$(#[from_value($from_value_ty:ty)])*
|
||||
$vis:vis trait $Trait:ident {
|
||||
fn $fn:ident(&self) -> Expr<$T:ty>;
|
||||
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||
let CanonicalType::$name(retval) = canonical_type else {
|
||||
panic!("expected {}", stringify!($name));
|
||||
};
|
||||
retval
|
||||
}
|
||||
}
|
||||
) => {
|
||||
$vis trait $Trait {
|
||||
fn $fn(&self) -> Expr<$T>;
|
||||
|
||||
impl $name {
|
||||
pub fn type_properties(self) -> TypeProperties {
|
||||
Self::TYPE_PROPERTIES
|
||||
}
|
||||
pub fn can_connect(self, _rhs: Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticType for $name {
|
||||
const TYPE: Self = Self;
|
||||
const MASK_TYPE: Self::MaskType = Bool;
|
||||
const TYPE_PROPERTIES: TypeProperties = TypeProperties {
|
||||
is_passive: true,
|
||||
is_storable: false,
|
||||
is_castable_from_bits: $is_castable_from_bits,
|
||||
bit_width: 1,
|
||||
};
|
||||
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
|
||||
}
|
||||
|
||||
impl sealed::ResetTypeSealed for $name {}
|
||||
|
||||
impl ResetType for $name {}
|
||||
|
||||
pub trait $Trait {
|
||||
fn $trait_fn(&self) -> Expr<$name>;
|
||||
}
|
||||
|
||||
impl<T: ?Sized + $Trait> $Trait for &'_ T {
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
(**self).$fn()
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
(**self).$trait_fn()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + $Trait> $Trait for &'_ mut T {
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
(**self).$fn()
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
(**self).$trait_fn()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + $Trait> $Trait for Box<T> {
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
(**self).$fn()
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
(**self).$trait_fn()
|
||||
}
|
||||
}
|
||||
|
||||
impl $Trait for Expr<$T> {
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
impl $Trait for Expr<$name> {
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl $Trait for $T {
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
self.to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
$(impl $Trait for $from_value_ty {
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
self.to_expr().$fn()
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
make_to_reset! {
|
||||
#[from_value(SyncReset)]
|
||||
#[from_value(AsyncReset)]
|
||||
pub trait ToReset {
|
||||
fn to_reset(&self) -> Expr<Reset>;
|
||||
reset_type!(AsyncReset, ToAsyncReset::to_async_reset, true);
|
||||
reset_type!(SyncReset, ToSyncReset::to_sync_reset, true);
|
||||
reset_type!(
|
||||
Reset,
|
||||
ToReset::to_reset,
|
||||
false // Reset is not castable from bits because we don't know if it's async or sync
|
||||
);
|
||||
|
||||
impl ToSyncReset for bool {
|
||||
fn to_sync_reset(&self) -> Expr<SyncReset> {
|
||||
self.to_expr().to_sync_reset()
|
||||
}
|
||||
}
|
||||
|
||||
make_to_reset! {
|
||||
#[from_value(bool)]
|
||||
#[from_value(UInt<1>)]
|
||||
pub trait ToAsyncReset {
|
||||
fn to_async_reset(&self) -> Expr<AsyncReset>;
|
||||
}
|
||||
}
|
||||
|
||||
make_to_reset! {
|
||||
#[from_value(bool)]
|
||||
#[from_value(UInt<1>)]
|
||||
pub trait ToSyncReset {
|
||||
fn to_sync_reset(&self) -> Expr<SyncReset>;
|
||||
impl ToAsyncReset for bool {
|
||||
fn to_async_reset(&self) -> Expr<AsyncReset> {
|
||||
self.to_expr().to_async_reset()
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,10 +3,14 @@
|
|||
|
||||
mod const_bool;
|
||||
mod const_cmp;
|
||||
mod const_usize;
|
||||
mod misc;
|
||||
mod scoped_ref;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
|
||||
#[doc(inline)]
|
||||
pub use const_usize::{ConstUsize, GenericConstUsize};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use const_cmp::{
|
||||
|
@ -14,6 +18,9 @@ pub use const_cmp::{
|
|||
const_usize_cmp,
|
||||
};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use scoped_ref::ScopedRef;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use misc::{
|
||||
interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice,
|
||||
|
|
29
crates/fayalite/src/util/const_usize.rs
Normal file
29
crates/fayalite/src/util/const_usize.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use std::{fmt::Debug, hash::Hash};
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
/// the only implementation is `ConstUsize<Self::VALUE>`
|
||||
pub trait GenericConstUsize:
|
||||
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
|
||||
{
|
||||
const VALUE: usize;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct ConstUsize<const VALUE: usize>;
|
||||
|
||||
impl<const VALUE: usize> Debug for ConstUsize<VALUE> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("ConstUsize").field(&Self::VALUE).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {}
|
||||
|
||||
impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> {
|
||||
const VALUE: usize = VALUE;
|
||||
}
|
106
crates/fayalite/src/util/scoped_ref.rs
Normal file
106
crates/fayalite/src/util/scoped_ref.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
mod safety_boundary {
|
||||
use std::{cell::Cell, ptr::NonNull};
|
||||
|
||||
pub(super) struct Impl<T: ?Sized>(Cell<Option<NonNull<T>>>);
|
||||
|
||||
impl<T: ?Sized> Impl<T> {
|
||||
#[inline]
|
||||
pub(super) const fn new() -> Self {
|
||||
Self(Cell::new(None))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) const fn opt_static_ref(v: Option<&'static T>) -> Self {
|
||||
Self(Cell::new(if let Some(v) = v {
|
||||
// SAFETY: v is a valid reference for lifetime 'static
|
||||
unsafe { Some(NonNull::new_unchecked(v as *const T as *mut T)) }
|
||||
} else {
|
||||
None
|
||||
}))
|
||||
}
|
||||
/// set `self` to `value` for the duration of the `f()` call.
|
||||
#[inline]
|
||||
pub(super) fn set_opt<F: FnOnce() -> R, R>(&self, value: Option<&T>, f: F) -> R {
|
||||
struct ResetOnDrop<'a, T: ?Sized> {
|
||||
this: &'a Impl<T>,
|
||||
old: Option<NonNull<T>>,
|
||||
}
|
||||
impl<T: ?Sized> Drop for ResetOnDrop<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.this.0.set(self.old);
|
||||
}
|
||||
}
|
||||
// reset to old value before exiting this function ensuring `self`
|
||||
// is not set to `value` when its lifetime is expired
|
||||
let _reset_on_drop = ResetOnDrop {
|
||||
this: self,
|
||||
old: self.0.replace(value.map(NonNull::from)),
|
||||
};
|
||||
f()
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn get_ptr(&self) -> Option<NonNull<T>> {
|
||||
self.0.get()
|
||||
}
|
||||
/// get the reference in `self` for the duration of the `f(...)` call.
|
||||
#[inline]
|
||||
pub(super) fn with_opt<F: for<'a> FnOnce(Option<&'a T>) -> R, R>(&self, f: F) -> R {
|
||||
// SAFETY:
|
||||
// `self.0` is only `Some` when inside some `set_opt` call or when set
|
||||
// to some `&'static T`, which ensures that the pointer is live and valid.
|
||||
//
|
||||
// the reference we give away has its lifetime scoped to this
|
||||
// function call which ensures that it won't escape
|
||||
unsafe { f(self.0.get().map(|v| &*v.as_ptr())) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// holds a `Cell<Option<&'scoped T>>` where `'scoped` is erased. This is useful for holding references in TLS.
|
||||
pub struct ScopedRef<T: ?Sized>(safety_boundary::Impl<T>);
|
||||
|
||||
impl<T: ?Sized> ScopedRef<T> {
|
||||
/// create a new empty [`ScopedRef`]
|
||||
#[inline]
|
||||
pub const fn new() -> Self {
|
||||
Self(safety_boundary::Impl::new())
|
||||
}
|
||||
#[inline]
|
||||
pub const fn opt_static_ref(v: Option<&'static T>) -> Self {
|
||||
Self(safety_boundary::Impl::opt_static_ref(v))
|
||||
}
|
||||
#[inline]
|
||||
pub const fn static_ref(v: &'static T) -> Self {
|
||||
Self::opt_static_ref(Some(v))
|
||||
}
|
||||
/// set `self` to `value` for the duration of the `f()` call.
|
||||
#[inline]
|
||||
pub fn set_opt<F: FnOnce() -> R, R>(&self, value: Option<&T>, f: F) -> R {
|
||||
self.0.set_opt(value, f)
|
||||
}
|
||||
/// set `self` to `value` for the duration of the `f()` call.
|
||||
#[inline]
|
||||
pub fn set<F: FnOnce() -> R, R>(&self, value: &T, f: F) -> R {
|
||||
self.0.set_opt(Some(value), f)
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_some(&self) -> bool {
|
||||
self.0.get_ptr().is_some()
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_none(&self) -> bool {
|
||||
self.0.get_ptr().is_none()
|
||||
}
|
||||
/// get the reference in `self` for the duration of the `f(...)` call. panics if no reference is set.
|
||||
#[inline]
|
||||
pub fn with<F: FnOnce(&T) -> R, R>(&self, f: F) -> R {
|
||||
self.0.with_opt(
|
||||
#[inline]
|
||||
|v| f(v.expect("called ScopedRef::with on an empty ScopedRef")),
|
||||
)
|
||||
}
|
||||
/// get the reference in `self` for the duration of the `f(...)` call.
|
||||
#[inline]
|
||||
pub fn with_opt<F: FnOnce(Option<&T>) -> R, R>(&self, f: F) -> R {
|
||||
self.0.with_opt(f)
|
||||
}
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
expr::{Expr, ExprTrait, Flow, ToExpr},
|
||||
expr::Flow,
|
||||
intern::Interned,
|
||||
module::{NameId, ScopedNameId},
|
||||
source_location::SourceLocation,
|
||||
ty::{DynCanonicalType, DynType, Type},
|
||||
ty::{CanonicalType, Type},
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Wire<T: Type> {
|
||||
name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
|
@ -18,27 +18,14 @@ pub struct Wire<T: Type> {
|
|||
|
||||
impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Wire")
|
||||
.field("name", &self.name)
|
||||
.field("ty", &self.ty)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> ToExpr for Wire<T> {
|
||||
type Type = T;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
self.ty.clone()
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::new_unchecked(self.expr_enum())
|
||||
write!(f, "Wire({:?}: ", self.name)?;
|
||||
self.ty.fmt(f)?;
|
||||
f.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> Wire<T> {
|
||||
pub fn canonical(&self) -> Wire<T::CanonicalType> {
|
||||
pub fn canonical(&self) -> Wire<CanonicalType> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
|
@ -50,29 +37,8 @@ impl<T: Type> Wire<T> {
|
|||
ty: ty.canonical(),
|
||||
}
|
||||
}
|
||||
pub fn to_dyn_wire(&self) -> Wire<Interned<dyn DynType>> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
ref ty,
|
||||
} = *self;
|
||||
Wire {
|
||||
name,
|
||||
source_location,
|
||||
ty: ty.to_dyn(),
|
||||
}
|
||||
}
|
||||
pub fn to_dyn_canonical_wire(&self) -> Wire<Interned<dyn DynCanonicalType>> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
ref ty,
|
||||
} = *self;
|
||||
Wire {
|
||||
name,
|
||||
source_location,
|
||||
ty: ty.canonical_dyn(),
|
||||
}
|
||||
pub fn ty(&self) -> T {
|
||||
self.ty
|
||||
}
|
||||
pub fn new_unchecked(
|
||||
scoped_name: ScopedNameId,
|
||||
|
|
26
crates/fayalite/tests/hdl_types.rs
Normal file
26
crates/fayalite/tests/hdl_types.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use fayalite::{
|
||||
array::ArrayType,
|
||||
hdl,
|
||||
int::{IntType, Size, UInt},
|
||||
};
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
pub struct S<T: IntType, Len: Size> {
|
||||
pub a: T,
|
||||
b: UInt<3>,
|
||||
pub(crate) c: ArrayType<UInt<1>, Len>,
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
pub enum E<T> {
|
||||
A,
|
||||
B(UInt<3>),
|
||||
C(T),
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
pub struct S2<T = ()> {
|
||||
pub v: E<T>,
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,8 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use fayalite::ty::Value;
|
||||
use fayalite::hdl;
|
||||
|
||||
#[derive(Value)]
|
||||
#[hdl]
|
||||
union U {
|
||||
a: (),
|
||||
}
|
7
crates/fayalite/tests/ui/hdl_types.stderr
Normal file
7
crates/fayalite/tests/ui/hdl_types.stderr
Normal file
|
@ -0,0 +1,7 @@
|
|||
error: top-level #[hdl] can only be used on structs or enums
|
||||
--> tests/ui/hdl_types.rs:5:1
|
||||
|
|
||||
5 | #[hdl]
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: this error originates in the attribute macro `hdl` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -1,7 +0,0 @@
|
|||
error: derive(Value) can only be used on structs or enums
|
||||
--> tests/ui/value_derive.rs:5:10
|
||||
|
|
||||
5 | #[derive(Value)]
|
||||
| ^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `Value` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -1,32 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use fayalite::{
|
||||
int::UInt,
|
||||
ty::{StaticValue, Value},
|
||||
};
|
||||
|
||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
#[hdl(outline_generated)]
|
||||
pub struct S<T> {
|
||||
pub a: T,
|
||||
b: UInt<3>,
|
||||
}
|
||||
|
||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
#[hdl(outline_generated)]
|
||||
pub enum E<T> {
|
||||
A,
|
||||
B {},
|
||||
C(),
|
||||
D(UInt<3>),
|
||||
E { a: UInt<3> },
|
||||
F(UInt<3>, UInt<3>),
|
||||
G(T),
|
||||
H(T, UInt<1>),
|
||||
}
|
||||
|
||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
#[hdl(outline_generated, static, where(T: StaticValue))]
|
||||
pub struct S2<T> {
|
||||
pub v: E<T>,
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue