use std::ptr;
use super::module_context::*;
use crate::graph;
use typed_arena::Arena;
struct ModuleStackFrame<'graph, 'frame> {
parent: Option<(
&'graph graph::Instance<'graph>,
&'frame ModuleStackFrame<'graph, 'frame>,
)>,
module: &'graph graph::Module<'graph>,
}
pub fn validate_module_hierarchy<'graph>(m: &'graph graph::Module<'graph>) {
detect_recursive_definitions(
m,
&ModuleStackFrame {
parent: None,
module: m,
},
m,
);
detect_undriven_registers(
m,
&ModuleStackFrame {
parent: None,
module: m,
},
m,
);
detect_mem_errors(
m,
&ModuleStackFrame {
parent: None,
module: m,
},
m,
);
let context_arena = Arena::new();
let root_context = context_arena.alloc(ModuleContext::new());
detect_combinational_loops(m, root_context, &context_arena, m);
}
fn detect_recursive_definitions<'graph, 'frame>(
m: &graph::Module<'graph>,
module_stack_frame: &ModuleStackFrame<'graph, 'frame>,
root: &graph::Module<'graph>,
) {
for instance in m.instances.borrow().iter() {
let instantiated_module = instance.instantiated_module;
if ptr::eq(instantiated_module, m) {
panic!("Cannot generate code for module \"{}\" because it has a recursive definition formed by an instance of itself called \"{}\".", m.name, instance.name);
}
let mut frame = module_stack_frame;
loop {
if ptr::eq(instantiated_module, frame.module) {
panic!("Cannot generate code for module \"{}\" because it has a recursive definition formed by an instance of itself called \"{}\" in module \"{}\".", root.name, instance.name, m.name);
}
if let Some((_, parent)) = frame.parent {
frame = parent;
} else {
break;
}
}
for input_name in instantiated_module.inputs.borrow().keys() {
if !instance.driven_inputs.borrow().contains_key(input_name) {
panic!("Cannot generate code for module \"{}\" because module \"{}\" contains an instance of module \"{}\" called \"{}\" whose input \"{}\" is not driven.", root.name, m.name, instantiated_module.name, instance.name, input_name);
}
}
detect_recursive_definitions(
instantiated_module,
&ModuleStackFrame {
parent: Some((instance, module_stack_frame)),
module: instantiated_module,
},
root,
);
}
}
fn detect_undriven_registers<'graph, 'frame>(
m: &graph::Module<'graph>,
module_stack_frame: &ModuleStackFrame<'graph, 'frame>,
root: &graph::Module<'graph>,
) {
for register in m.registers.borrow().iter() {
match register.data {
graph::SignalData::Reg { ref data } => {
if data.next.borrow().is_none() {
panic!("Cannot generate code for module \"{}\" because module \"{}\" contains a register called \"{}\" which is not driven.", root.name, m.name, data.name);
}
}
_ => unreachable!(),
}
}
for instance in m.instances.borrow().iter() {
let instantiated_module = instance.instantiated_module;
detect_undriven_registers(
instantiated_module,
&ModuleStackFrame {
parent: Some((instance, module_stack_frame)),
module: instantiated_module,
},
root,
);
}
}
fn detect_mem_errors<'graph, 'frame>(
m: &graph::Module<'graph>,
module_stack_frame: &ModuleStackFrame<'graph, 'frame>,
root: &graph::Module<'graph>,
) {
for mem in m.mems.borrow().iter() {
if mem.read_ports.borrow().is_empty() {
panic!("Cannot generate code for module \"{}\" because module \"{}\" contains a memory called \"{}\" which doesn't have any read ports.", root.name, m.name, mem.name);
}
if mem.initial_contents.borrow().is_none() && mem.write_port.borrow().is_none() {
panic!("Cannot generate code for module \"{}\" because module \"{}\" contains a memory called \"{}\" which doesn't have initial contents or a write port specified. At least one of the two is required.", root.name, m.name, mem.name);
}
}
for instance in m.instances.borrow().iter() {
let instantiated_module = instance.instantiated_module;
detect_mem_errors(
instantiated_module,
&ModuleStackFrame {
parent: Some((instance, module_stack_frame)),
module: instantiated_module,
},
root,
);
}
}
fn detect_combinational_loops<'graph, 'arena>(
m: &graph::Module<'graph>,
context: &'arena ModuleContext<'graph, 'arena>,
context_arena: &'arena Arena<ModuleContext<'graph, 'arena>>,
root: &graph::Module<'graph>,
) {
for instance in m.instances.borrow().iter() {
let instantiated_module = instance.instantiated_module;
let context = context.get_child(instance, context_arena);
for (_, output) in instantiated_module.outputs.borrow().iter() {
trace_signal(output, context, context_arena, (context, output), root);
}
detect_combinational_loops(instantiated_module, context, context_arena, root);
}
}
fn trace_signal<'graph, 'arena>(
signal: &'graph graph::Signal<'graph>,
context: &'arena ModuleContext<'graph, 'arena>,
context_arena: &'arena Arena<ModuleContext<'graph, 'arena>>,
source_output: (
&'arena ModuleContext<'graph, 'arena>,
&'graph graph::Signal<'graph>,
),
root: &graph::Module<'graph>,
) {
struct Frame<'graph, 'arena> {
signal: &'graph graph::Signal<'graph>,
context: &'arena ModuleContext<'graph, 'arena>,
}
let mut frames = Vec::new();
frames.push(Frame { signal, context });
while let Some(frame) = frames.pop() {
let signal = frame.signal;
let context = frame.context;
match signal.data {
graph::SignalData::Lit { .. } => (),
graph::SignalData::Input { ref name, .. } => {
if let Some((instance, parent)) = context.instance_and_parent {
frames.push(Frame {
signal: instance.driven_inputs.borrow()[name],
context: parent,
});
}
}
graph::SignalData::Reg { .. } => (),
graph::SignalData::UnOp { ref source, .. } => {
frames.push(Frame {
signal: source,
context,
});
}
graph::SignalData::SimpleBinOp {
ref lhs, ref rhs, ..
} => {
frames.push(Frame {
signal: lhs,
context,
});
frames.push(Frame {
signal: rhs,
context,
});
}
graph::SignalData::AdditiveBinOp {
ref lhs, ref rhs, ..
} => {
frames.push(Frame {
signal: lhs,
context,
});
frames.push(Frame {
signal: rhs,
context,
});
}
graph::SignalData::ComparisonBinOp {
ref lhs, ref rhs, ..
} => {
frames.push(Frame {
signal: lhs,
context,
});
frames.push(Frame {
signal: rhs,
context,
});
}
graph::SignalData::ShiftBinOp {
ref lhs, ref rhs, ..
} => {
frames.push(Frame {
signal: lhs,
context,
});
frames.push(Frame {
signal: rhs,
context,
});
}
graph::SignalData::Mul {
ref lhs, ref rhs, ..
} => {
frames.push(Frame {
signal: lhs,
context,
});
frames.push(Frame {
signal: rhs,
context,
});
}
graph::SignalData::MulSigned {
ref lhs, ref rhs, ..
} => {
frames.push(Frame {
signal: lhs,
context,
});
frames.push(Frame {
signal: rhs,
context,
});
}
graph::SignalData::Bits { ref source, .. } => {
frames.push(Frame {
signal: source,
context,
});
}
graph::SignalData::Repeat { ref source, .. } => {
frames.push(Frame {
signal: source,
context,
});
}
graph::SignalData::Concat {
ref lhs, ref rhs, ..
} => {
frames.push(Frame {
signal: lhs,
context,
});
frames.push(Frame {
signal: rhs,
context,
});
}
graph::SignalData::Mux {
ref cond,
ref when_true,
ref when_false,
..
} => {
frames.push(Frame {
signal: cond,
context,
});
frames.push(Frame {
signal: when_true,
context,
});
frames.push(Frame {
signal: when_false,
context,
});
}
graph::SignalData::InstanceOutput {
instance, ref name, ..
} => {
let instantiated_module = instance.instantiated_module;
let output = instantiated_module.outputs.borrow()[name];
let context = context.get_child(instance, context_arena);
if context == source_output.0 && output == source_output.1 {
panic!("Cannot generate code for module \"{}\" because module \"{}\" contains an output called \"{}\" which forms a combinational loop with itself.", root.name, instantiated_module.name, name);
}
frames.push(Frame {
signal: output,
context,
});
}
graph::SignalData::MemReadPortOutput { .. } => (),
}
}
}