use std::collections::HashMap;
use super::*;
#[derive(Debug, Clone, SExpr)]
pub enum RuleSignature {
#[pp(surrounded)]
Always {
inputs: Vec<ValueId>,
outputs: Vec<ValueId>,
},
Method {
inputs: Vec<ValueId>,
outputs: Vec<ValueId>,
side_effect: Option<bool>,
},
}
impl RuleSignature {
pub fn inputs(&self) -> impl Iterator<Item = ValueId> {
match self {
RuleSignature::Always { inputs, .. } => inputs.clone().into_iter(),
RuleSignature::Method { inputs, .. } => inputs.clone().into_iter(),
}
}
pub fn inputs_mut(&mut self) -> &mut Vec<ValueId> {
match self {
RuleSignature::Always { inputs, .. } => inputs,
RuleSignature::Method { inputs, .. } => inputs,
}
}
pub fn outputs(&self) -> impl Iterator<Item = ValueId> {
match self {
RuleSignature::Always { outputs, .. } => outputs.clone().into_iter(),
RuleSignature::Method { outputs, .. } => outputs.clone().into_iter(),
}
}
pub fn outputs_mut(&mut self) -> &mut Vec<ValueId> {
match self {
RuleSignature::Always { outputs, .. } => outputs,
RuleSignature::Method { outputs, .. } => outputs,
}
}
pub fn is_method(&self) -> bool {
matches!(self, RuleSignature::Method { .. })
}
}
#[derive(Debug, Clone, SExpr)]
pub enum RuleTiming {
#[pp(surrounded)]
SingleCycle,
MultiCycle {
num_cycles: Option<u32>,
intv: TimingIntv,
},
FSM,
Pipeline,
}
impl RuleTiming {
pub fn interval(&self) -> TimingIntv {
match self {
RuleTiming::SingleCycle => TimingIntv::none(),
RuleTiming::MultiCycle {
num_cycles: _,
intv,
} => intv.clone(),
_ => unimplemented!(),
}
}
}
#[derive(Debug, Clone, SExpr)]
pub struct Rule {
#[pp(open)]
#[pp(surrounded = "ext?")]
pub is_ext: bool,
#[pp(surrounded = "private?")]
pub is_private: bool,
#[pp(kw = "rule")]
pub name: String,
pub signature: RuleSignature,
pub timing: RuleTiming,
pub enable: Option<String>,
pub ready: Option<String>,
#[pp(list_ml)]
pub guard_ops: Vec<Op>,
#[pp(list_ml)]
#[pp(close)]
pub ops: Vec<Op>,
pub annotations: json::object::Object,
}
impl Rule {
pub fn is_ext(&self) -> bool {
self.is_ext
}
pub fn is_private(&self) -> bool {
self.is_private
}
pub fn set_private(&mut self, is_private: bool) {
self.is_private = is_private;
}
pub fn is_always(&self) -> bool {
matches!(self.signature, RuleSignature::Always { .. })
}
pub fn is_method(&self) -> bool {
matches!(self.signature, RuleSignature::Method { .. })
}
pub fn has_side_effect(&self) -> bool {
matches!(self.signature, RuleSignature::Method { side_effect, .. } if side_effect.unwrap_or(false))
}
pub fn set_side_effect(&mut self, se: bool) {
if let RuleSignature::Method { side_effect, .. } = &mut self.signature {
*side_effect = Some(se);
}
}
pub fn is_single_cycle(&self) -> bool {
matches!(self.timing, RuleTiming::SingleCycle)
}
pub fn guard(&self) -> impl Iterator<Item = &Op> {
self.guard_ops.iter()
}
pub fn guard_mut(&mut self) -> impl Iterator<Item = &mut Op> {
self.guard_ops.iter_mut()
}
pub fn ops(&self) -> impl Iterator<Item = &Op> {
self.ops.iter()
}
pub fn ops_mut(&mut self) -> impl Iterator<Item = &mut Op> {
self.ops.iter_mut()
}
pub fn name(&self) -> &str {
&self.name
}
pub fn new(name: String) -> Rule {
Rule {
is_ext: false,
is_private: false,
name,
signature: RuleSignature::Always {
inputs: vec![],
outputs: vec![],
},
timing: RuleTiming::SingleCycle,
guard_ops: vec![],
ops: vec![],
enable: None,
ready: None,
annotations: json::object::Object::new(),
}
}
pub fn always(
name: String,
inputs: Vec<ValueId>,
outputs: Vec<ValueId>,
guard_ops: Vec<Op>,
ops: Vec<Op>,
timing: RuleTiming,
) -> Rule {
Rule {
is_ext: false,
is_private: false,
name,
signature: RuleSignature::Always { inputs, outputs },
guard_ops,
timing,
ops,
enable: None,
ready: None,
annotations: json::object::Object::new(),
}
}
pub fn method(
name: String,
inputs: Vec<ValueId>,
outputs: Vec<ValueId>,
side_effect: Option<bool>,
guard_ops: Vec<Op>,
ops: Vec<Op>,
timing: RuleTiming,
) -> Rule {
Rule {
is_ext: false,
is_private: false,
name,
signature: RuleSignature::Method {
inputs,
outputs,
side_effect,
},
timing,
guard_ops,
ops,
enable: None,
ready: None,
annotations: json::object::Object::new(),
}
}
pub fn ext(
name: String,
inputs: Vec<ValueId>,
outputs: Vec<ValueId>,
enable: Option<String>,
ready: Option<String>,
side_effect: Option<bool>,
) -> Rule {
Rule {
is_ext: true,
is_private: false,
name,
signature: RuleSignature::Method {
inputs,
outputs,
side_effect,
},
guard_ops: vec![],
timing: RuleTiming::SingleCycle,
ops: vec![],
enable,
ready,
annotations: json::object::Object::new(),
}
}
pub fn to_be_private(self) -> Rule {
Rule {
is_private: true,
..self
}
}
pub fn span(&self) -> impl Iterator<Item = MySpan> + '_ {
self.annotations.iter().filter_map(|(k, v)| {
if k.ends_with("span") {
MySpan::from_json(v)
} else {
None
}
})
}
pub fn inputs(&self) -> Vec<ValueId> {
self.signature.inputs().collect()
}
pub fn inputs_mut(&mut self) -> &mut Vec<ValueId> {
self.signature.inputs_mut()
}
pub fn outputs(&self) -> Vec<ValueId> {
self.signature.outputs().collect()
}
pub fn outputs_mut(&mut self) -> &mut Vec<ValueId> {
self.signature.outputs_mut()
}
pub fn return_values(&self) -> Vec<ValueId> {
self
.ops
.last()
.map(|op| op.outputs().collect())
.unwrap_or_default()
}
pub fn replace_guard_op(&mut self, index: usize, new_ops: Vec<Op>) {
if index < self.guard_ops.len() {
self.guard_ops.splice(index..=index, new_ops);
} else {
panic!("Index out of bounds for guard_ops");
}
}
pub fn replace_op(&mut self, index: usize, new_ops: Vec<Op>) {
if index < self.ops.len() {
self.ops.splice(index + 1..=index + 1, new_ops);
} else {
panic!("Index out of bounds for ops");
}
}
pub fn replace_all_op_with_map(
&mut self,
replacements: &HashMap<ir::ValueId, ir::ValueId>,
) {
for op in self.guard_mut() {
op.replace_value_with_map(replacements);
}
for op in self.ops_mut() {
op.replace_value_with_map(replacements);
}
}
pub fn remove_unused_op(&mut self) {
let mut indices_to_remove = vec![];
for (index, op) in self.guard_mut().enumerate() {
if let OpEnum::Assign(AssignOp { res, value }) = op.inner_mut() {
if res == value {
indices_to_remove.push(index);
}
} else {
op.remove_unused_op();
}
}
indices_to_remove.sort_unstable_by(|a, b| b.cmp(a));
for &index in &indices_to_remove {
self.guard_ops.remove(index);
}
indices_to_remove.clear();
for (index, op) in self.ops_mut().enumerate() {
if let OpEnum::Assign(AssignOp { res, value }) = op.inner_mut() {
if res == value {
indices_to_remove.push(index);
}
} else {
op.remove_unused_op();
}
}
indices_to_remove.sort_unstable_by(|a, b| b.cmp(a));
for &index in &indices_to_remove {
self.ops.remove(index);
}
}
}
#[derive(Debug, Clone, SExpr)]
pub enum RuleRel {
#[pp(surrounded)]
Method {
rel: MethodRel,
lhs: Vec<InstRule>,
rhs: Vec<InstRule>,
},
Schedule(Vec<InstRule>),
}
impl RuleRel {
pub fn method(
rel: MethodRel,
lhs: Vec<InstRule>,
rhs: Vec<InstRule>,
) -> RuleRel {
RuleRel::Method { rel, lhs, rhs }
}
pub fn schedule(inst_rules: Vec<InstRule>) -> RuleRel {
RuleRel::Schedule(inst_rules)
}
}