use super::context::*;
use super::module::*;
use super::signal::*;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::hash::{Hash, Hasher};
use std::ptr;
#[must_use]
pub struct Instance<'a> {
pub(super) context: &'a Context<'a>,
pub(super) module: &'a Module<'a>,
pub(crate) instantiated_module: &'a Module<'a>,
pub(crate) name: String,
pub(crate) driven_inputs: RefCell<BTreeMap<String, &'a Signal<'a>>>,
}
impl<'a> Instance<'a> {
pub fn drive_input<S: Into<String>>(&'a self, name: S, i: &'a Signal<'a>) {
let name = name.into();
let mut driven_inputs = self.driven_inputs.borrow_mut();
if !ptr::eq(self.module, i.module) {
panic!("Attempted to drive an instance input with a signal from a different module.");
}
if !self.instantiated_module.inputs.borrow().contains_key(&name) {
panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but no such input with this name exists on this module.", name, self.instantiated_module.name);
}
if driven_inputs.contains_key(&name) {
panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but this input is already driven for this instance.", name, self.instantiated_module.name);
}
let input_bit_width = self.instantiated_module.inputs.borrow()[&name].bit_width();
if input_bit_width != i.bit_width() {
panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but this input and the provided signal have different bit widths ({} and {}, respectively).", name, self.instantiated_module.name, input_bit_width, i.bit_width());
}
driven_inputs.insert(name, i);
}
pub fn output<S: Into<String>>(&'a self, name: S) -> &Signal<'a> {
let name = name.into();
let outputs = self.instantiated_module.outputs.borrow();
match outputs.get(&name) {
Some(output) => self.context.signal_arena.alloc(Signal {
context: self.context,
module: self.module,
data: SignalData::InstanceOutput {
instance: self,
name,
bit_width: output.bit_width(),
},
}),
_ => panic!("Attempted to create a signal for an output called \"{}\" on an instance of \"{}\", but no such output with this name exists on this module.", name, self.instantiated_module.name)
}
}
}
impl<'a> Eq for &'a Instance<'a> {}
impl<'a> Hash for &'a Instance<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_usize(*self as *const _ as usize)
}
}
impl<'a> PartialEq for &'a Instance<'a> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(*self, *other)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic(
expected = "Attempted to drive an instance input with a signal from a different module."
)]
fn drive_input_different_module_error() {
let c = Context::new();
let inner = c.module("Inner");
let _ = inner.input("a", 1);
let m1 = c.module("A");
let i1 = m1.input("a", 1);
let m2 = c.module("B");
let inner_inst = m2.instance("inner_inst", "Inner");
inner_inst.drive_input("a", i1);
}
#[test]
#[should_panic(
expected = "Attempted to drive an input called \"a\" on an instance of \"Inner\", but no such input with this name exists on this module."
)]
fn drive_input_nonexistent_input_error() {
let c = Context::new();
let _ = c.module("Inner");
let m = c.module("A");
let inner_inst = m.instance("inner_inst", "Inner");
inner_inst.drive_input("a", m.input("i", 1));
}
#[test]
#[should_panic(
expected = "Attempted to drive an input called \"a\" on an instance of \"Inner\", but this input is already driven for this instance."
)]
fn drive_input_already_driven_error() {
let c = Context::new();
let inner = c.module("Inner");
let _ = inner.input("a", 1);
let m = c.module("A");
let inner_inst = m.instance("inner_inst", "Inner");
inner_inst.drive_input("a", m.input("i1", 1));
inner_inst.drive_input("a", m.input("i2", 1));
}
#[test]
#[should_panic(
expected = "Attempted to drive an input called \"a\" on an instance of \"Inner\", but this input and the provided signal have different bit widths (1 and 32, respectively)."
)]
fn drive_input_incompatible_bit_widths_error() {
let c = Context::new();
let inner = c.module("Inner");
let _ = inner.input("a", 1);
let m = c.module("A");
let inner_inst = m.instance("inner_inst", "Inner");
inner_inst.drive_input("a", m.input("i1", 32));
}
#[test]
#[should_panic(
expected = "Attempted to create a signal for an output called \"nope\" on an instance of \"Inner\", but no such output with this name exists on this module."
)]
fn output_nonexistent_output_error() {
let c = Context::new();
let _ = c.module("Inner");
let m = c.module("A");
let inner_inst = m.instance("inner_inst", "Inner");
let _ = inner_inst.output("nope");
}
}