futilehdl/src/frontend/types.rs

144 lines
3.6 KiB
Rust

/// Alias for &TypeStruct to reduce repetition
/// and make futura migration to interning
/// easier
pub type Type<'ty> = &'ty TypeStruct<'ty>;
pub struct TypeStruct<'ty> {
kind: TypeKind<'ty>,
}
#[derive(Debug)]
enum TypeKind<'ty> {
/// Elaboration-time types
ElabType(ElabKind),
/// Signal/Wire of generic width
Logic(ElabData<'ty>),
/// UInt of generic width
UInt(ElabData<'ty>),
/// Callable
Callable,
}
#[derive(Debug)]
struct ElabData<'ty> {
typ: Type<'ty>,
value: ElabValue<'ty>,
}
#[derive(Debug)]
enum ElabValue<'ty> {
/// the value is not given and has to be inferred
Infer,
/// the value is given as some byte representation
Concrete(ElabValueData<'ty>),
}
#[derive(Debug)]
enum ElabValueData<'ty> {
U32(u32),
Bytes(&'ty [u8]),
}
/// Types that are only valid during Elaboration
#[derive(Debug)]
enum ElabKind {
/// general, unsized number type
Num,
}
/// Helper functions to create primitive types
impl<'ty> TypeStruct<'ty> {
/// a logic signal with inferred width
pub fn logic_infer() -> Self {
Self {
kind: TypeKind::Logic(ElabData {
typ: &TypeStruct {
kind: TypeKind::ElabType(ElabKind::Num),
},
value: ElabValue::Infer,
}),
}
}
/// a logic signal with known width
pub fn logic_width(width: u32) -> Self {
Self {
kind: TypeKind::Logic(ElabData::from_u32(width)),
}
}
/// return an elaboration number type
pub fn elab_num() -> Self {
Self {
kind: TypeKind::ElabType(ElabKind::Num),
}
}
pub fn bit_width(&self) -> Option<u32> {
match &self.kind {
// elab types are not representable in hardware
TypeKind::ElabType(_) => None,
TypeKind::Logic(data) => data.try_u32(),
TypeKind::UInt(_) => todo!(),
// callables are not representable in hardware
TypeKind::Callable => None,
}
}
pub fn genparam_count(&self) -> u32 {
match self.kind {
TypeKind::ElabType(_) => todo!(),
TypeKind::Logic(_) => 1,
TypeKind::UInt(_) => 1,
TypeKind::Callable => todo!(),
}
}
}
impl std::fmt::Debug for TypeStruct<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.kind {
TypeKind::Logic(width) => {
if let Some(num) = width.try_u32() {
write!(f, "Logic<{}>", num)
} else {
write!(f, "Logic<?>")
}
}
_ => f
.debug_struct("TypeStruct")
.field("kind", &self.kind)
.finish(),
}
}
}
/// Helper functions to create primitive elaboration values
impl<'ty> ElabData<'ty> {
/// an integer
pub fn from_u32(val: u32) -> Self {
Self {
typ: &TypeStruct {
kind: TypeKind::ElabType(ElabKind::Num),
},
value: ElabValue::Concrete(ElabValueData::U32(val)),
}
}
/// return Some(u32) if this is a number
pub fn try_u32(&self) -> Option<u32> {
// TODO: assert this is actually a number
match &self.value {
ElabValue::Infer => None,
ElabValue::Concrete(val) => match val {
ElabValueData::U32(num) => Some(*num),
ElabValueData::Bytes(_) => todo!(),
},
}
}
}
pub fn make_primitives() -> Vec<(String, TypeStruct<'static>)> {
vec![("Logic".to_string(), TypeStruct::logic_infer())]
}