#![allow(clippy::type_complexity)]
#![allow(clippy::multiple_bound_locations)]
use crate::{
annotations::{Annotation, IntoAnnotations, TargetedAnnotation},
array::{Array, ArrayType},
bundle::{Bundle, BundleType},
clock::Clock,
expr::{ops::BundleLiteral, repeat, Expr, Flow, ToExpr, ToLiteralBits},
hdl,
int::{Bool, DynSize, Size, UInt, UIntType},
intern::{Intern, Interned},
module::ScopedNameId,
source_location::SourceLocation,
ty::{AsMask, CanonicalType, Type},
util::DebugAsDisplay,
};
use bitvec::slice::BitSlice;
use std::{
cell::RefCell,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
num::NonZeroU32,
rc::Rc,
};
#[hdl]
pub struct ReadStruct<Element, AddrWidth: Size> {
pub addr: UIntType<AddrWidth>,
pub en: Bool,
pub clk: Clock,
#[hdl(flip)]
pub data: Element,
}
#[hdl]
pub struct WriteStruct<Element, AddrWidth: Size> {
pub addr: UIntType<AddrWidth>,
pub en: Bool,
pub clk: Clock,
pub data: Element,
pub mask: AsMask<Element>,
}
#[hdl]
pub struct ReadWriteStruct<Element, AddrWidth: Size> {
pub addr: UIntType<AddrWidth>,
pub en: Bool,
pub clk: Clock,
#[hdl(flip)]
pub rdata: Element,
pub wmode: Bool,
pub wdata: Element,
pub wmask: AsMask<Element>,
}
mod sealed {
pub trait Sealed {}
}
pub trait PortType:
sealed::Sealed + Clone + Eq + Hash + fmt::Debug + Send + Sync + 'static
{
type PortKindTy: Copy + Eq + Hash + fmt::Debug + Send + Sync + 'static;
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::Port;
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct DynPortType {
kind: PortKind,
}
impl sealed::Sealed for DynPortType {}
impl PortType for DynPortType {
type PortKindTy = PortKind;
type Port = Bundle;
fn port_kind(port_kind: Self::PortKindTy) -> PortKind {
port_kind
}
fn from_port_kind(port_kind: PortKind) -> Self::PortKindTy {
port_kind
}
fn port_ty(port: &MemPort<Self>) -> Self::Port {
Bundle::new(match port.port_kind {
PortKind::ReadOnly => {
MemPort::<ReadStruct<CanonicalType, DynSize>>::from_canonical(*port)
.ty()
.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: BundleType + sealed::Sealed + PortType<PortKindTy = (), Port = Self> {
type Element: Type;
const PORT_KIND: PortKind;
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<Bool>;
}
macro_rules! impl_port_struct {
(
impl<$Element:ident> _ for $Struct:ident {
fn port_ty($port_ty_port:ident: $port_ty_port_ty:ty) -> $port_ty_ret_ty:ty {
$($port_ty_body:tt)*
}
$($body:tt)*
}
) => {
impl<$Element: Type> sealed::Sealed for $Struct<$Element, DynSize> {}
impl<$Element: Type> PortType for $Struct<$Element, DynSize> {
type PortKindTy = ();
type Port = Self;
fn port_kind(_port_kind: Self::PortKindTy) -> PortKind {
Self::PORT_KIND
}
fn from_port_kind(port_kind: PortKind) -> Self::PortKindTy {
assert_eq!(port_kind, Self::PORT_KIND);
}
fn port_ty($port_ty_port: $port_ty_port_ty) -> $port_ty_ret_ty {
$($port_ty_body)*
}
}
impl<$Element: Type> PortStruct for $Struct<$Element, DynSize> {
type Element = $Element;
fn addr(this: Expr<Self>) -> Expr<UInt> {
this.addr
}
fn en(this: Expr<Self>) -> Expr<Bool> {
this.en
}
fn clk(this: Expr<Self>) -> Expr<Clock> {
this.clk
}
$($body)*
}
};
}
impl_port_struct! {
impl<Element> _ for ReadStruct {
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;
fn rdata(this: Expr<Self>) -> Option<Expr<Self::Element>> {
Some(this.data)
}
fn wdata(_this: Expr<Self>) -> Option<Expr<Self::Element>> {
None
}
fn wmask(_this: Expr<Self>) -> Option<Expr<AsMask<Self::Element>>> {
None
}
fn wmode(_this: Expr<Self>) -> Expr<Bool> {
false.to_expr()
}
}
}
impl_port_struct! {
impl<Element> _ for WriteStruct {
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;
fn rdata(_this: Expr<Self>) -> Option<Expr<Self::Element>> {
None
}
fn wdata(this: Expr<Self>) -> Option<Expr<Self::Element>> {
Some(this.data)
}
fn wmask(this: Expr<Self>) -> Option<Expr<AsMask<Self::Element>>> {
Some(this.mask)
}
fn wmode(_this: Expr<Self>) -> Expr<Bool> {
true.to_expr()
}
}
}
impl_port_struct! {
impl<Element> _ for ReadWriteStruct {
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;
fn rdata(this: Expr<Self>) -> Option<Expr<Self::Element>> {
Some(this.rdata)
}
fn wdata(this: Expr<Self>) -> Option<Expr<Self::Element>> {
Some(this.wdata)
}
fn wmask(this: Expr<Self>) -> Option<Expr<AsMask<Self::Element>>> {
Some(this.wmask)
}
fn wmode(this: Expr<Self>) -> Expr<Bool> {
this.wmode
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum PortKind {
ReadOnly,
WriteOnly,
ReadWrite,
}
impl PortKind {
pub const fn can_read(self) -> bool {
match self {
PortKind::ReadOnly => true,
PortKind::WriteOnly => false,
PortKind::ReadWrite => true,
}
}
pub const fn can_write(self) -> bool {
match self {
PortKind::ReadOnly => false,
PortKind::WriteOnly => true,
PortKind::ReadWrite => true,
}
}
pub const fn is_read_only(self) -> bool {
matches!(self, PortKind::ReadOnly)
}
pub const fn is_write_only(self) -> bool {
matches!(self, PortKind::WriteOnly)
}
pub const fn is_read_write(self) -> bool {
matches!(self, PortKind::ReadWrite)
}
pub const fn port_name_prefix(self) -> &'static str {
match self {
PortKind::ReadOnly => "r",
PortKind::WriteOnly => "w",
PortKind::ReadWrite => "rw",
}
}
pub const fn rdata_name(self) -> Option<&'static str> {
match self {
PortKind::ReadOnly => Some("data"),
PortKind::WriteOnly => None,
PortKind::ReadWrite => Some("rdata"),
}
}
pub const fn wdata_name(self) -> Option<&'static str> {
match self {
PortKind::ReadOnly => None,
PortKind::WriteOnly => Some("data"),
PortKind::ReadWrite => Some("wdata"),
}
}
pub const fn wmask_name(self) -> Option<&'static str> {
match self {
PortKind::ReadOnly => None,
PortKind::WriteOnly => Some("mask"),
PortKind::ReadWrite => Some("wmask"),
}
}
pub const fn wmode_name(self) -> Option<&'static str> {
match self {
PortKind::ReadOnly => None,
PortKind::WriteOnly => None,
PortKind::ReadWrite => Some("wmode"),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct PortName {
pub kind: PortKind,
pub index: usize,
}
impl fmt::Display for PortName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.kind.port_name_prefix(), self.index)
}
}
impl fmt::Debug for PortName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct MemPort<T: PortType> {
mem_name: ScopedNameId,
source_location: SourceLocation,
port_kind: T::PortKindTy,
port_index: usize,
addr_type: UInt,
mem_element_type: CanonicalType,
}
impl<T: PortType> fmt::Debug for MemPort<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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
}
pub fn port_kind(&self) -> PortKind {
T::port_kind(self.port_kind)
}
pub fn mem_name(&self) -> ScopedNameId {
self.mem_name
}
pub fn port_index(&self) -> usize {
self.port_index
}
pub fn port_name(&self) -> PortName {
PortName {
kind: self.port_kind(),
index: self.port_index,
}
}
pub fn mem_element_type(&self) -> CanonicalType {
self.mem_element_type
}
pub fn addr_type(&self) -> UInt {
self.addr_type
}
pub fn canonical(&self) -> MemPort<DynPortType> {
let Self {
mem_name,
source_location,
port_kind,
port_index,
addr_type,
mem_element_type,
} = *self;
MemPort::<DynPortType> {
mem_name,
source_location,
port_kind: T::port_kind(port_kind),
port_index,
addr_type,
mem_element_type,
}
}
pub fn from_canonical(port: MemPort<DynPortType>) -> Self
where
T: PortStruct,
{
let MemPort {
mem_name,
source_location,
port_kind,
port_index,
addr_type,
mem_element_type,
} = port;
assert_eq!(port_kind, T::PORT_KIND, "port kind mismatch");
Self {
mem_name,
source_location,
port_kind: (),
port_index,
addr_type,
mem_element_type,
}
}
#[track_caller]
pub fn new_unchecked(
mem_name: ScopedNameId,
source_location: SourceLocation,
port_name: PortName,
addr_type: UInt,
mem_element_type: CanonicalType,
) -> Self {
assert!(
mem_element_type.is_storable(),
"memory element type must be a storable type"
);
Self {
mem_name,
source_location,
port_kind: T::from_port_kind(port_name.kind),
port_index: port_name.index,
addr_type,
mem_element_type,
}
}
pub fn flow(&self) -> Flow {
Flow::Sink
}
pub fn must_connect_to(&self) -> bool {
true
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum ReadUnderWrite {
Old,
New,
Undefined,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct MemImpl<Element: Type, Len: Size, P> {
scoped_name: ScopedNameId,
source_location: SourceLocation,
array_type: ArrayType<Element, Len>,
initial_value: Option<Interned<BitSlice>>,
ports: P,
read_latency: usize,
write_latency: NonZeroU32,
read_under_write: ReadUnderWrite,
port_annotations: Interned<[TargetedAnnotation]>,
mem_annotations: Interned<[Annotation]>,
}
pub struct Mem<Element: Type = CanonicalType, Len: Size = DynSize>(
Interned<MemImpl<Element, Len, Interned<[MemPort<DynPortType>]>>>,
);
struct PortsDebug<'a>(&'a [MemPort<DynPortType>]);
impl fmt::Debug for PortsDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map()
.entries(
self.0
.iter()
.map(|p| (p.port_name(), DebugAsDisplay("..."))),
)
.finish()
}
}
impl<Element: Type, Len: Size> fmt::Debug for Mem<Element, Len> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let MemImpl {
scoped_name,
source_location: _,
array_type,
initial_value,
ports,
read_latency,
write_latency,
read_under_write,
port_annotations,
mem_annotations,
} = &*self.0;
f.debug_struct("Mem")
.field("name", scoped_name)
.field("array_type", array_type)
.field("initial_value", initial_value)
.field("read_latency", read_latency)
.field("write_latency", write_latency)
.field("read_under_write", read_under_write)
.field("ports", &PortsDebug(ports))
.field("port_annotations", port_annotations)
.field("mem_annotations", mem_annotations)
.finish_non_exhaustive()
}
}
impl<Element: Type, Len: Size> Hash for Mem<Element, Len> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<Element: Type, Len: Size> Eq for Mem<Element, Len> {}
impl<Element: Type, Len: Size> PartialEq for Mem<Element, Len> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<Element: Type, Len: Size> Copy for Mem<Element, Len> {}
impl<Element: Type, Len: Size> Clone for Mem<Element, Len> {
fn clone(&self) -> Self {
*self
}
}
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<Element, Len>,
initial_value: Option<Interned<BitSlice>>,
ports: Interned<[MemPort<DynPortType>]>,
read_latency: usize,
write_latency: NonZeroU32,
read_under_write: ReadUnderWrite,
port_annotations: Interned<[TargetedAnnotation]>,
mem_annotations: Interned<[Annotation]>,
) -> Self {
if let Some(initial_value) = initial_value {
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();
assert!(
expected_mem_element_type.is_storable(),
"memory element type must be a storable type"
);
for (index, port) in ports.iter().enumerate() {
let MemPort {
mem_name,
source_location: _,
port_kind: _,
port_index,
addr_type,
mem_element_type,
} = *port;
assert_eq!(mem_name, scoped_name, "memory name must match with ports");
assert_eq!(
port_index, index,
"port index must match position in ports list"
);
assert_eq!(
addr_type.width, addr_width,
"memory address width must match with ports"
);
assert_eq!(
mem_element_type, expected_mem_element_type,
"memory element type must match with ports"
);
}
for port_annotation in port_annotations {
let base = port_annotation.target().base();
let Some(port) = base.mem_port() else {
panic!("port annotation must have a memory port as its target base");
};
assert_eq!(
Some(port),
ports.get(port.port_index),
"port on memory must match annotation's target base"
);
}
Self(Intern::intern_sized(MemImpl {
scoped_name,
source_location,
array_type,
initial_value,
ports,
read_latency,
write_latency,
read_under_write,
port_annotations,
mem_annotations,
}))
}
pub fn scoped_name(self) -> ScopedNameId {
self.0.scoped_name
}
pub fn source_location(self) -> SourceLocation {
self.0.source_location
}
pub fn array_type(self) -> ArrayType<Element, Len> {
self.0.array_type
}
pub fn initial_value(self) -> Option<Interned<BitSlice>> {
self.0.initial_value
}
pub fn ports(self) -> Interned<[MemPort<DynPortType>]> {
self.0.ports
}
pub fn read_latency(self) -> usize {
self.0.read_latency
}
pub fn write_latency(self) -> NonZeroU32 {
self.0.write_latency
}
pub fn read_under_write(self) -> ReadUnderWrite {
self.0.read_under_write
}
pub fn port_annotations(self) -> Interned<[TargetedAnnotation]> {
self.0.port_annotations
}
pub fn mem_annotations(self) -> Interned<[Annotation]> {
self.0.mem_annotations
}
pub fn canonical(self) -> Mem {
let MemImpl {
scoped_name,
source_location,
ref array_type,
initial_value,
ports,
read_latency,
write_latency,
read_under_write,
port_annotations,
mem_annotations,
} = *self.0;
let array_type = array_type.as_dyn_array();
Mem(Intern::intern_sized(MemImpl {
scoped_name,
source_location,
array_type,
initial_value,
ports,
read_latency,
write_latency,
read_under_write,
port_annotations,
mem_annotations,
}))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
struct MaybeSpecified<T>(Option<T>);
impl<T: fmt::Debug> fmt::Debug for MaybeSpecified<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(v) = &self.0 {
v.fmt(f)
} else {
f.write_str("<not yet specified>")
}
}
}
pub(crate) struct MemBuilderTarget {
pub(crate) scoped_name: ScopedNameId,
pub(crate) source_location: SourceLocation,
pub(crate) mem_element_type: CanonicalType,
pub(crate) depth: Option<usize>,
pub(crate) initial_value: Option<Interned<BitSlice>>,
pub(crate) ports: Vec<MemPort<DynPortType>>,
pub(crate) read_latency: usize,
pub(crate) write_latency: NonZeroU32,
pub(crate) read_under_write: ReadUnderWrite,
pub(crate) port_annotations: Vec<TargetedAnnotation>,
pub(crate) mem_annotations: Vec<Annotation>,
}
impl MemBuilderTarget {
pub(crate) fn make_memory(&self) -> Option<Mem> {
Some(Mem::new_unchecked(
self.scoped_name,
self.source_location,
ArrayType::new_dyn(self.mem_element_type, self.depth?),
self.initial_value,
Intern::intern(&self.ports),
self.read_latency,
self.write_latency,
self.read_under_write,
Intern::intern(&self.port_annotations),
Intern::intern(&self.mem_annotations),
))
}
fn debug_fmt(
&self,
struct_name: &str,
mem_element_type: &dyn fmt::Debug,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let Self {
scoped_name,
source_location: _,
mem_element_type: _,
depth,
initial_value,
ports,
read_latency,
write_latency,
read_under_write,
port_annotations,
mem_annotations,
} = self;
f.debug_struct(struct_name)
.field("name", scoped_name)
.field("mem_element_type", mem_element_type)
.field("depth", &MaybeSpecified(*depth))
.field("initial_value", initial_value)
.field("read_latency", read_latency)
.field("write_latency", write_latency)
.field("read_under_write", read_under_write)
.field("ports", ports)
.field("port_annotations", port_annotations)
.field("mem_annotations", mem_annotations)
.finish_non_exhaustive()
}
}
impl fmt::Debug for MemBuilderTarget {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.debug_fmt("MemBuilderTarget", &self.mem_element_type, f)
}
}
pub struct MemBuilder<Element: Type, Len: Size = DynSize> {
mem_element_type: Element,
target: Rc<RefCell<MemBuilderTarget>>,
_phantom: PhantomData<Len>,
}
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)
}
}
pub fn memory_addr_width(depth: usize) -> usize {
depth
.checked_next_power_of_two()
.map_or(usize::BITS, usize::ilog2) as usize
}
impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
#[track_caller]
fn check_initial_value_bit_slice(
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(element_bit_width).expect(
"memory must be small enough that its initializer bit length fits in usize",
);
assert_eq!(
expected_len,
initial_value.len(),
"Mem's initializer bit length doesn't match the expected value",
);
}
assert!(
initial_value
.len()
.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",
);
initial_value
}
#[track_caller]
fn check_initial_value_expr(
mem_element_type: &Element,
depth: Option<usize>,
initial_value: Expr<Array>,
) -> Interned<BitSlice> {
let initial_value_ty = Expr::ty(initial_value);
assert_eq!(
*mem_element_type,
Element::from_canonical(initial_value_ty.element()),
"Mem's element type must match initializer's element type",
);
if let Some(depth) = depth {
assert_eq!(
depth,
initial_value_ty.len(),
"Mem's depth must match initializer's length"
);
}
let Ok(retval) = initial_value.to_literal_bits() else {
panic!("Mem's initializer must be convertible to literal bits");
};
debug_assert_eq!(
retval.len(),
initial_value_ty.type_properties().bit_width,
"initial value produced wrong literal bits length"
);
retval
}
#[track_caller]
pub(crate) fn new(
scoped_name: ScopedNameId,
source_location: SourceLocation,
mem_element_type: Element,
) -> (Self, Rc<RefCell<MemBuilderTarget>>) {
let canonical_mem_element_type = mem_element_type.canonical();
assert!(
canonical_mem_element_type.is_storable(),
"memory element type must be a storable type"
);
let target = Rc::new(RefCell::new(MemBuilderTarget {
scoped_name,
source_location,
mem_element_type: canonical_mem_element_type,
depth: Len::KNOWN_VALUE,
initial_value: None,
ports: vec![],
read_latency: 0,
write_latency: NonZeroU32::new(1).unwrap(),
read_under_write: ReadUnderWrite::Old,
port_annotations: vec![],
mem_annotations: vec![],
}));
(
Self {
mem_element_type,
target: Rc::clone(&target),
_phantom: PhantomData,
},
target,
)
}
#[track_caller]
fn new_port_impl(
&mut self,
source_location: SourceLocation,
port_kind: PortKind,
) -> 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 = MemPort {
mem_name: target.scoped_name,
source_location,
port_kind,
port_index: target.ports.len(),
addr_type: UInt::new(memory_addr_width(depth)),
mem_element_type: target.mem_element_type,
};
target.ports.push(port);
port
}
#[track_caller]
pub fn new_port_with_loc(
&mut self,
source_location: SourceLocation,
kind: PortKind,
) -> Expr<Bundle> {
self.new_port_impl(source_location, kind).to_expr()
}
#[track_caller]
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<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<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<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<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<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<Element, DynSize>> {
self.new_rw_port_with_loc(SourceLocation::caller())
}
pub fn scoped_name(&self) -> ScopedNameId {
self.target.borrow().scoped_name
}
pub fn source_location(&self) -> SourceLocation {
self.target.borrow().source_location
}
pub fn get_mem_element_type(&self) -> &Element {
&self.mem_element_type
}
#[allow(clippy::result_unit_err)]
pub fn get_depth(&self) -> Result<usize, ()> {
self.target.borrow().depth.ok_or(())
}
#[track_caller]
pub fn depth(&mut self, depth: usize) {
let mut target = self.target.borrow_mut();
assert!(
target.ports.is_empty(),
"Mem depth can only be set before adding ports"
);
if let Some(expected) = target.depth {
assert_eq!(
expected, depth,
"Mem depth doesn't match previously specified value"
);
}
target.depth = Some(depth);
}
#[allow(clippy::result_unit_err)]
pub fn get_array_type(&self) -> Result<ArrayType<Element, Len>, ()> {
Ok(ArrayType::new(
self.mem_element_type,
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<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 = 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(Expr::ty(initial_value).len());
}
#[track_caller]
pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) {
let mut target = self.target.borrow_mut();
if target.initial_value.is_some() {
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,
target.depth,
initial_value,
));
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);
}
}
pub fn get_read_latency(&self) -> usize {
self.target.borrow().read_latency
}
pub fn read_latency(&mut self, read_latency: usize) {
self.target.borrow_mut().read_latency = read_latency;
}
pub fn get_write_latency(&self) -> NonZeroU32 {
self.target.borrow().write_latency
}
pub fn write_latency(&mut self, write_latency: NonZeroU32) {
self.target.borrow_mut().write_latency = write_latency;
}
pub fn get_read_under_write(&self) -> ReadUnderWrite {
self.target.borrow().read_under_write
}
pub fn read_under_write(&mut self, read_under_write: ReadUnderWrite) {
self.target.borrow_mut().read_under_write = read_under_write;
}
#[track_caller]
pub fn annotate(&mut self, annotations: impl IntoAnnotations) {
self.target
.borrow_mut()
.mem_annotations
.extend(annotations.into_annotations());
}
}
pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> {
let canonical_ty = ty.canonical();
match canonical_ty {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::Enum(_) => Expr::from_canonical(Expr::canonical(value)),
CanonicalType::Array(array) => Expr::from_canonical(Expr::canonical(repeat(
splat_mask(array.element(), value),
array.len(),
))),
CanonicalType::Bundle(bundle) => Expr::from_canonical(Expr::canonical(
BundleLiteral::new(
bundle.mask_type(),
bundle
.fields()
.iter()
.map(|field| splat_mask(field.ty, value))
.collect(),
)
.to_expr(),
)),
}
}