Skip to content

Commit

Permalink
Implement compile-time port width verification
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanuppal committed Nov 20, 2024
1 parent cfe7916 commit 4893af0
Show file tree
Hide file tree
Showing 7 changed files with 622 additions and 89 deletions.
8 changes: 4 additions & 4 deletions tools/calyx-ffi/src/backend/cider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,22 +88,22 @@ macro_rules! cider_ffi_backend {
// println!("cider_ffi_backend tick");
let cider = unsafe { $dut.user_data.assume_init_mut() };
$(
cider.write_port(stringify!($input), &$dut.$input);
cider.write_port(stringify!($input), &$dut.$input.inner);
)*
cider.step();
$(
$dut.$output = cider.read_port(stringify!($output));
$dut.$output.inner = cider.read_port(stringify!($output));
)*
};
(@go $dut:ident; $($input:ident),*; $($output:ident),*) => {
// println!("cider_ffi_backend go");
let cider = unsafe { $dut.user_data.assume_init_mut() };
$(
cider.write_port(stringify!($input), &$dut.$input);
cider.write_port(stringify!($input), &$dut.$input.inner);
)*
cider.go();
$(
$dut.$output = cider.read_port(stringify!($output));
$dut.$output.inner = cider.read_port(stringify!($output));
)*
};
}
73 changes: 73 additions & 0 deletions tools/calyx-ffi/src/interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use calyx_ir::Context;
use std::{
any, cell::RefCell, collections::HashMap, env, path::PathBuf, rc::Rc,
};

/// A non-combinational calyx component.
pub trait CalyxFFIComponent: any::Any {
/// The path to the component source file. Must be a constant expression.
fn path(&self) -> &'static str;

/// The in-source name of this component. Must be a constant expression.
fn name(&self) -> &'static str;

/// Internal initialization routine. Do not call!
fn init(&mut self, context: &Context);

/// Resets this component.
fn reset(&mut self);

/// Whether this component's backend supports ticking.
fn can_tick(&self) -> bool;

/// Advances this component by one clock cycle. May not always be available, so check [`has_tick`]([CalyxFFIComponent::has_tick]).
fn tick(&mut self);

/// Calls this component, blocking until it is done executing.
fn go(&mut self);
}

pub type CalyxFFIComponentRef = Rc<RefCell<dyn CalyxFFIComponent>>;

fn box_calyx_ffi_component<T: CalyxFFIComponent>(
comp: T,
) -> CalyxFFIComponentRef {
Rc::new(RefCell::new(comp))
}

#[derive(Default)]
pub struct CalyxFFI {
contexts: HashMap<&'static str, Context>,
}

impl CalyxFFI {
pub fn new() -> Self {
Self::default()
}

/// Constructs a new calyx component of the given type.
///
/// The `path` implementation for `CalyxFFIComponent` must be a constant
/// expression and should derived via the `calyx_ffi` procedural macro.
pub fn new_comp<T: CalyxFFIComponent + Default>(
&mut self,
) -> CalyxFFIComponentRef {
let mut comp = T::default();
let path = comp.path();
let context = self.contexts.entry(path).or_insert_with_key(|path| {
// there has to be a better way to find lib
let home_dir = env::var("HOME").expect("user home not set");
let mut lib_path = PathBuf::from(home_dir);
lib_path.push(".calyx");
let ws = calyx_frontend::Workspace::construct(
&Some(path.into()),
&lib_path,
)
.expect("couldn't parse calyx");
calyx_ir::from_ast::ast_to_ir(ws)
.expect("couldn't construct calyx ir")
});
comp.init(context);
box_calyx_ffi_component(comp)
}
}
82 changes: 2 additions & 80 deletions tools/calyx-ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,82 +1,4 @@
use calyx_ir::Context;
use std::{
any, cell::RefCell, collections::HashMap, env, path::PathBuf, rc::Rc,
};

pub mod backend;
pub mod interface;
pub mod prelude;

/// A non-combinational calyx component.
pub trait CalyxFFIComponent: any::Any {
/// The path to the component source file. Must be a constant expression.
fn path(&self) -> &'static str;

/// The in-source name of this component. Must be a constant expression.
fn name(&self) -> &'static str;

/// Internal initialization routine. Do not call!
fn init(&mut self, context: &Context);

/// Resets this component.
fn reset(&mut self);

/// Whether this component's backend supports ticking.
fn can_tick(&self) -> bool;

/// Advances this component by one clock cycle. May not always be available, so check [`has_tick`]([CalyxFFIComponent::has_tick]).
fn tick(&mut self);

/// Calls this component, blocking until it is done executing.
fn go(&mut self);
}

pub type CalyxFFIComponentRef = Rc<RefCell<dyn CalyxFFIComponent>>;

fn box_calyx_ffi_component<T: CalyxFFIComponent>(
comp: T,
) -> CalyxFFIComponentRef {
Rc::new(RefCell::new(comp))
}

#[derive(Default)]
pub struct CalyxFFI {
contexts: HashMap<&'static str, Context>,
}

impl CalyxFFI {
pub fn new() -> Self {
Self::default()
}

/// Constructs a new calyx component of the given type.
///
/// The `path` implementation for `CalyxFFIComponent` must be a constant
/// expression and should derived via the `calyx_ffi` procedural macro.
pub fn new_comp<T: CalyxFFIComponent + Default>(
&mut self,
) -> CalyxFFIComponentRef {
let mut comp = T::default();
let path = comp.path();
let context = self.contexts.entry(path).or_insert_with_key(|path| {
// there has to be a better way to find lib
let home_dir = env::var("HOME").expect("user home not set");
let mut lib_path = PathBuf::from(home_dir);
lib_path.push(".calyx");
let ws = calyx_frontend::Workspace::construct(
&Some(path.into()),
&lib_path,
)
.expect("couldn't parse calyx");
calyx_ir::from_ast::ast_to_ir(ws)
.expect("couldn't construct calyx ir")
});
comp.init(context);
box_calyx_ffi_component(comp)
}
}

pub type Value<const N: u64> = interp::BitVecValue;

pub fn value_from_u64<const N: u64>(value: u64) -> Value<N> {
Value::from_u64(value, N as u32)
}
pub mod value;
7 changes: 4 additions & 3 deletions tools/calyx-ffi/src/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub use super::{
value_from_u64, CalyxFFI, CalyxFFIComponent, CalyxFFIComponentRef, Value,
interface::{CalyxFFI, CalyxFFIComponent, CalyxFFIComponentRef},
value::Value,
};
pub use calyx_ffi_macro::{calyx_ffi, calyx_ffi_test, calyx_ffi_tests};
pub use calyx_ir;
Expand All @@ -20,12 +21,12 @@ macro_rules! declare_interface {
calyx_ffi::prelude::paste::paste! {
pub trait $name: CalyxFFIComponent {
$(
fn [<$input _bits>](&mut self) -> &mut calyx_ffi::Value<$input_width>;
fn [<$input _bits>](&mut self) -> &mut calyx_ffi::value::Value<$input_width>;

fn [<set_ $input>](&mut self, value: u64);
)*
$(
fn [<$output _bits>](&self) -> &calyx_ffi::Value<$output_width>;
fn [<$output _bits>](&self) -> &calyx_ffi::value::Value<$output_width>;

fn $output(&self) -> u64;
)*
Expand Down
58 changes: 58 additions & 0 deletions tools/calyx-ffi/src/value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use std::{error, fmt};

pub use interp::WidthInt;

Check failure on line 3 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

unresolved import `interp::WidthInt`

Check failure on line 3 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

unresolved import `interp::WidthInt`

#[derive(Debug)]
pub enum ValueConversionError {
WidthTooLarge(interp::WidthInt),

Check failure on line 7 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`

Check failure on line 7 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`
}

impl fmt::Display for ValueConversionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ValueConversionError::WidthTooLarge(width) => {
write!(
f,
"Failed to convert bitvector of width `{}` into `u64`",
width
)
}
}
}
}

impl error::Error for ValueConversionError {}

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
pub struct Value<const N: interp::WidthInt> {

Check failure on line 27 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`

Check failure on line 27 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`
pub inner: interp::BitVecValue,
}

impl<const N: interp::WidthInt> From<u64> for Value<N> {

Check failure on line 31 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`

Check failure on line 31 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`
fn from(value: u64) -> Self {
Self {
inner: interp::BitVecValue::from_u64(value, N),
}
}
}

impl<const N: interp::WidthInt> TryInto<u64> for &Value<N> {

Check failure on line 39 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`

Check failure on line 39 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`
type Error = ValueConversionError;

fn try_into(self) -> Result<u64, Self::Error> {
use interp::BitVecOps;
self.inner
.to_u64()
.ok_or(Self::Error::WidthTooLarge(self.inner.width()))
}
}

impl<const N: interp::WidthInt> interp::BitVecOps for Value<N> {

Check failure on line 50 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`

Check failure on line 50 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`
fn width(&self) -> interp::WidthInt {

Check failure on line 51 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`

Check failure on line 51 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `WidthInt` in crate `interp`
N
}

fn words(&self) -> &[interp::Word] {

Check failure on line 55 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `Word` in crate `interp`

Check failure on line 55 in tools/calyx-ffi/src/value.rs

View workflow job for this annotation

GitHub Actions / Check Formatting

cannot find type `Word` in crate `interp`
self.inner.words()
}
}
Loading

0 comments on commit 4893af0

Please sign in to comment.