1use crate::{
6 analysis::{TemporalRegion, TemporalRegionGraph},
7 ir::{prelude::*, InstData},
8 opt::prelude::*,
9 value::IntValue,
10};
11use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
12
13pub struct Desequentialization;
18
19impl Pass for Desequentialization {
20 fn run_on_cfg(ctx: &PassContext, unit: &mut UnitBuilder) -> bool {
21 if unit.kind() == UnitKind::Process {
22 match deseq_process(ctx, unit) {
23 Some(entity) => {
24 *unit.data() = entity;
25 true
26 }
27 _ => false,
28 }
29 } else {
30 false
31 }
32 }
33}
34
35fn deseq_process(ctx: &PassContext, unit: &mut UnitBuilder) -> Option<UnitData> {
36 info!("Deseq [{}]", unit.name());
37 let trg = unit.trg();
38
39 if trg.regions().count() != 2 {
41 trace!("Skipping (incorrect number of TRs)");
42 return None;
43 }
44 let (tr0, tr1) = {
45 let mut it = trg.regions();
46 (it.next().unwrap().id, it.next().unwrap().id)
47 };
48 if !trg[tr0].entry {
49 trace!("Skipping (TR0 is not entry)");
50 return None;
51 }
52 if trg[tr1].entry {
53 trace!("Skipping (TR1 is entry)");
54 return None;
55 }
56 trace!("Head region {}, trigger region {}", tr0, tr1);
57
58 let (wait_inst, sensitivity) = {
61 let mut it = trg[tr0].tail_insts();
62 let inst = match it.next() {
63 Some(i) => i,
64 None => {
65 trace!("Skipping ({} has no tail inst)", tr0);
66 return None;
67 }
68 };
69 let data = &unit[inst];
70 let sensitivity: BTreeSet<_> = match data.opcode() {
71 Opcode::Wait => data.args().iter().cloned().collect(),
72 Opcode::WaitTime => data.args().iter().skip(1).cloned().collect(),
73 _ => {
74 trace!("Skipping ({} tail inst is not a wait)", tr0);
75 return None;
76 }
77 };
78 (inst, sensitivity)
79 };
80 trace!("Wait Inst: {}", wait_inst.dump(&unit));
81 trace!("Sensitivity: {:?}", sensitivity);
82
83 let tr0_num_bb = trg[tr0].blocks().count();
87 let tr1_num_bb = trg[tr1].blocks().count();
88 if tr0_num_bb != 1 || tr1_num_bb != 1 {
89 trace!(
90 "Skipping ({} TR0 blocks, {} TR1 blocks, instead of 1 each)",
91 tr0_num_bb,
92 tr1_num_bb
93 );
94 return None;
95 }
96
97 let mut all_drives = HashSet::new();
99 let mut conds = vec![];
100 for bb in unit.blocks() {
101 for inst in unit.insts(bb) {
102 let data = &unit[inst];
103 if data.opcode() == Opcode::DrvCond {
104 trace!("Canonicalizing condition of {}", inst.dump(&unit));
105 conds.push((
106 inst,
107 bb,
108 canonicalize(ctx, unit, &trg, data.args()[3], false),
109 ));
110 all_drives.insert(inst);
111 } else if data.opcode() == Opcode::Drv {
112 all_drives.insert(inst);
113 }
114 }
115 }
116
117 let triggers: Vec<(Inst, Block, Vec<Trigger>)> = conds
119 .iter()
120 .flat_map(|(inst, bb, dnf)| {
121 detect_triggers(ctx, unit, tr0, tr1, dnf).map(|trig| (*inst, *bb, trig))
122 })
123 .collect();
124
125 let mut entity = UnitData::new(UnitKind::Entity, unit.name().clone(), unit.sig().clone());
127 let mut builder = UnitBuilder::new_anonymous(&mut entity);
128 for arg in unit.unit().sig().args() {
129 if let Some(name) = unit.get_name(unit.arg_value(arg)) {
130 let v = builder.arg_value(arg);
131 builder.set_name(v, name.to_string());
132 }
133 }
134 let mut mig = Migrator::new(unit, &mut builder, &trg, tr0, tr1);
135
136 let mut migrated = true;
140 for (inst, bb, trigs) in triggers {
141 migrated &= mig.migrate_drive(inst, bb, &trigs);
142 }
143 all_drives
150 .difference(&mig.migrated_drives)
151 .for_each(|inst| {
152 migrated = false;
153 trace!("Skipping ({} not migrated)", inst.dump(&unit));
154 });
155
156 if migrated {
157 Some(entity)
158 } else {
159 trace!("Process {} not migrated", unit.name());
160 None
161 }
162}
163
164fn canonicalize(
170 ctx: &PassContext,
171 unit: &UnitBuilder,
172 trg: &TemporalRegionGraph,
173 cond: Value,
174 inv: bool,
175) -> Dnf {
176 let dnf = canonicalize_inner(ctx, unit, trg, cond, inv);
177 let desc = if let Some(inst) = unit.get_value_inst(cond) {
178 inst.dump(&unit).to_string()
179 } else {
180 cond.dump(&unit).to_string()
181 };
182 trace!(
183 " {} {{ {} }} => {}",
184 if inv { "neg" } else { "pos" },
185 desc,
186 dnf.dump(&unit),
187 );
188 dnf
189}
190
191fn canonicalize_inner(
192 ctx: &PassContext,
193 unit: &UnitBuilder,
194 trg: &TemporalRegionGraph,
195 cond: Value,
196 inv: bool,
197) -> Dnf {
198 let dfg = unit;
199
200 let ty = unit.value_type(cond);
202 if ty != crate::ty::int_ty(1) {
203 return Dnf::single(Term::Invalid(cond), inv);
204 }
205
206 if let Some(inst) = unit.get_value_inst(cond) {
208 let data = &dfg[inst];
209 match data.opcode() {
210 Opcode::ConstInt => {
211 return Dnf::single(Term::Zero, data.get_const_int().unwrap().is_one() ^ inv);
212 }
213 Opcode::Not => return canonicalize(ctx, unit, trg, data.args()[0], !inv),
214 Opcode::And | Opcode::Or => {
215 let lhs = canonicalize(ctx, unit, trg, data.args()[0], inv);
216 let rhs = canonicalize(ctx, unit, trg, data.args()[1], inv);
217 let out = match (data.opcode(), inv) {
218 (Opcode::And, false) | (Opcode::Or, true) => Dnf::and(&lhs, &rhs),
219 (Opcode::And, true) | (Opcode::Or, false) => Dnf::or(&lhs, &rhs),
220 _ => unreachable!(),
221 };
222 return out;
223 }
224 Opcode::Xor | Opcode::Eq | Opcode::Neq => {
225 let lhs_pos = canonicalize(ctx, unit, trg, data.args()[0], false);
226 let rhs_pos = canonicalize(ctx, unit, trg, data.args()[1], false);
227 let lhs_neg = canonicalize(ctx, unit, trg, data.args()[0], true);
228 let rhs_neg = canonicalize(ctx, unit, trg, data.args()[1], true);
229 let polarity = match data.opcode() {
230 Opcode::Eq => !inv,
231 _ => inv,
232 };
233 let out = if polarity {
234 Dnf::or(&Dnf::and(&lhs_pos, &rhs_pos), &Dnf::and(&lhs_neg, &rhs_neg))
235 } else {
236 Dnf::or(&Dnf::and(&lhs_pos, &rhs_neg), &Dnf::and(&lhs_neg, &rhs_pos))
237 };
238 return out;
239 }
240 Opcode::Prb => {
241 let bb = unit.inst_block(inst).unwrap();
242 return Dnf::single(Term::Signal(data.args()[0], trg[bb]), inv);
243 }
244 _ => (),
245 }
246 }
247 Dnf::single(Term::Invalid(cond), inv)
248}
249
250struct Dnf(BTreeSet<BTreeMap<Term, bool>>);
255
256impl Dnf {
257 pub fn zero() -> Dnf {
259 Dnf(BTreeSet::new())
260 }
261
262 pub fn one() -> Dnf {
264 let mut set = BTreeSet::new();
265 set.insert(Default::default());
266 Dnf(set)
267 }
268
269 pub fn single(term: Term, inv: bool) -> Dnf {
271 match term {
272 Term::Zero if inv => Self::one(),
273 Term::Zero => Self::zero(),
274 _ => {
275 let mut set = BTreeSet::new();
276 set.insert(Some((term, inv)).into_iter().collect());
277 Dnf(set)
278 }
279 }
280 }
281
282 pub fn dump<'a>(&'a self, unit: &Unit<'a>) -> DnfDumper<'a> {
283 DnfDumper(self, *unit)
284 }
285
286 pub fn dump_term<'a>(term: &'a BTreeMap<Term, bool>, unit: &Unit<'a>) -> DnfTermDumper<'a> {
287 DnfTermDumper(term, *unit)
288 }
289
290 pub fn or(lhs: &Dnf, rhs: &Dnf) -> Dnf {
292 let lhs = lhs.0.iter().cloned();
293 let rhs = rhs.0.iter().cloned();
294 Dnf(lhs.chain(rhs).collect())
295 }
296
297 pub fn and(lhs: &Dnf, rhs: &Dnf) -> Dnf {
299 let mut set = BTreeSet::new();
300 for lhs_term in &lhs.0 {
301 for rhs_term in &rhs.0 {
302 if let Some(term) = Self::and_term(lhs_term, rhs_term) {
303 set.insert(term);
304 }
305 }
306 }
307 Dnf(set)
308 }
309
310 fn and_term(
312 lhs: &BTreeMap<Term, bool>,
313 rhs: &BTreeMap<Term, bool>,
314 ) -> Option<BTreeMap<Term, bool>> {
315 let mut out = BTreeMap::new();
316 for (term, &inv) in lhs.iter().chain(rhs.iter()) {
317 if out.insert(term.clone(), inv) == Some(!inv) {
320 return None;
321 }
322 }
323 Some(out)
324 }
325}
326
327struct DnfDumper<'a>(&'a Dnf, Unit<'a>);
328
329impl std::fmt::Display for DnfDumper<'_> {
330 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
331 use std::iter::{once, repeat};
332 if (self.0).0.is_empty() {
333 return write!(f, "0");
334 }
335 if (self.0).0.len() == 1 {
336 if (self.0).0.iter().next().unwrap().is_empty() {
337 return write!(f, "1");
338 }
339 }
340 for (vs, sep) in (self.0).0.iter().zip(once("").chain(repeat(" | "))) {
341 write!(f, "{}({})", sep, Dnf::dump_term(vs, &self.1))?;
342 }
343 Ok(())
344 }
345}
346
347struct DnfTermDumper<'a>(&'a BTreeMap<Term, bool>, Unit<'a>);
348
349impl std::fmt::Display for DnfTermDumper<'_> {
350 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
351 use std::iter::{once, repeat};
352 if (self.0).is_empty() {
353 return write!(f, "1");
354 }
355 for ((term, inv), sep) in self.0.iter().zip(once("").chain(repeat(" & "))) {
356 write!(f, "{}", sep)?;
357 if *inv {
358 write!(f, "!")?;
359 }
360 match term {
361 Term::Zero => write!(f, "0")?,
362 Term::Signal(sig, tr) => write!(f, "{}@{}", sig.dump(&self.1), tr)?,
363 Term::Invalid(v) => write!(f, "{}?", v.dump(&self.1))?,
364 }
365 }
366 Ok(())
367 }
368}
369
370#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
371enum Term {
372 Zero,
373 Signal(Value, TemporalRegion),
374 Invalid(Value),
375}
376
377fn detect_triggers(
379 ctx: &PassContext,
380 unit: &UnitBuilder,
381 tr0: TemporalRegion,
382 tr1: TemporalRegion,
383 dnf: &Dnf,
384) -> Option<Vec<Trigger>> {
385 trace!("Detecting triggers in {}", dnf.dump(&unit));
386 let mut trigs = vec![];
387 for conds in &dnf.0 {
388 let trig = match detect_term_triggers(ctx, unit, tr0, tr1, conds) {
389 Some(trig) => trig,
390 None => return None,
391 };
392 trigs.push(trig);
393 }
394 Some(trigs)
395}
396
397fn detect_term_triggers(
398 _ctx: &PassContext,
399 unit: &UnitBuilder,
400 tr0: TemporalRegion,
401 tr1: TemporalRegion,
402 conds: &BTreeMap<Term, bool>,
403) -> Option<Trigger> {
404 trace!(" Analyzing {}", Dnf::dump_term(conds, &unit));
405
406 let mut edges = BTreeMap::new();
408 let mut levels = BTreeMap::new();
409 for (term, &inv) in conds {
410 match *term {
411 Term::Signal(sig, tr) if tr == tr0 => {
414 if conds.get(&Term::Signal(sig, tr1)).cloned() == Some(!inv) {
415 trace!(
416 " {} {}",
417 if inv { "rising" } else { "falling" },
418 sig.dump(&unit)
419 );
420 edges.insert(
421 sig,
422 match inv {
423 true => TriggerEdge::Rise,
424 false => TriggerEdge::Fall,
425 },
426 );
427 } else {
428 trace!(
429 " Skipping ({}@{} without corresponding {}@{})",
430 sig.dump(&unit),
431 tr0,
432 sig.dump(&unit),
433 tr1
434 );
435 return None;
436 }
437 }
438
439 Term::Signal(sig, tr) if tr == tr1 => {
442 if conds.get(&Term::Signal(sig, tr0)).cloned() != Some(!inv) {
443 trace!(
444 " {} {}",
445 if inv { "low" } else { "high" },
446 sig.dump(&unit)
447 );
448 levels.insert(
449 sig,
450 match inv {
451 false => TriggerLevel::High,
452 true => TriggerLevel::Low,
453 },
454 );
455 }
456 }
457
458 _ => {
459 trace!(" Skipping (invalid term)");
460 return None;
461 }
462 }
463 }
464
465 if edges.len() > 1 {
467 trace!(" Skipping (multiple edge triggers)");
468 return None;
469 }
470
471 if let Some((sig, edge)) = edges.into_iter().next() {
474 Some(Trigger::Edge(sig, edge, levels))
475 } else {
476 Some(Trigger::Level(levels))
477 }
478}
479
480#[derive(Debug)]
481enum Trigger {
482 Edge(Value, TriggerEdge, BTreeMap<Value, TriggerLevel>),
483 Level(BTreeMap<Value, TriggerLevel>),
484}
485
486#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
487enum TriggerEdge {
488 Rise,
489 Fall,
490}
491
492#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
493enum TriggerLevel {
494 Low,
495 High,
496}
497
498struct Migrator<'a, 'b> {
500 src: &'a UnitBuilder<'b>,
501 dst: &'a mut UnitBuilder<'b>,
502 trg: &'a TemporalRegionGraph,
503 tr0: TemporalRegion,
504 tr1: TemporalRegion,
505 cache: HashMap<InstData, Value>,
507 migrated_drives: HashSet<Inst>,
509}
510
511impl<'a, 'b> Migrator<'a, 'b> {
512 pub fn new(
513 src: &'a UnitBuilder<'b>,
514 dst: &'a mut UnitBuilder<'b>,
515 trg: &'a TemporalRegionGraph,
516 tr0: TemporalRegion,
517 tr1: TemporalRegion,
518 ) -> Self {
519 Self {
520 src,
521 dst,
522 trg,
523 tr0,
524 tr1,
525 cache: Default::default(),
526 migrated_drives: Default::default(),
527 }
528 }
529
530 pub fn migrate_drive(&mut self, drive: Inst, _bb: Block, trigs: &Vec<Trigger>) -> bool {
531 trace!("Migrating {}", drive.dump(&self.src));
532 let drive_target = self.src[drive].args()[0];
533 let drive_value = self.src[drive].args()[1];
534
535 let mig_target = match self.migrate_value(drive_target, &Default::default()) {
536 Some(v) => v,
537 None => return false,
538 };
539
540 let mut reg_triggers = vec![];
541 for trig in trigs {
542 trace!(" Migrating {:?}", trig);
543 match trig {
544 Trigger::Edge(sig, edge, conds) => {
545 let mut ties = BTreeMap::new();
548 let (l0, l1) = match edge {
549 TriggerEdge::Rise => (TriggerLevel::Low, TriggerLevel::High),
550 TriggerEdge::Fall => (TriggerLevel::High, TriggerLevel::Low),
551 };
552 ties.insert((*sig, self.tr0), l0);
553 ties.insert((*sig, self.tr1), l1);
554
555 let mut gate = None;
557 for (&sig, &level) in conds {
558 ties.insert((sig, self.tr1), level);
559 let value = self.dst.ins().prb(sig);
560 let value = match level {
561 TriggerLevel::Low => self.dst.ins().not(value),
562 TriggerLevel::High => value,
563 };
564 gate = Some(match gate {
565 Some(gate) => self.dst.ins().and(gate, value),
566 None => value,
567 });
568 }
569
570 let data = match self.migrate_value(drive_value, &ties) {
572 Some(v) => v,
573 None => return false,
574 };
575
576 let trigger = self.dst.ins().prb(*sig);
578 let mode = match edge {
579 TriggerEdge::Rise => RegMode::Rise,
580 TriggerEdge::Fall => RegMode::Fall,
581 };
582 reg_triggers.push(RegTrigger {
583 data,
584 mode,
585 trigger,
586 gate,
587 });
588 }
589 Trigger::Level(conds) => {
590 let mut ties = BTreeMap::new();
592 let mut trigger = None;
593 for (&sig, &level) in conds {
594 ties.insert((sig, self.tr1), level);
595 let value = self.dst.ins().prb(sig);
596 let value = match level {
597 TriggerLevel::Low => self.dst.ins().not(value),
598 TriggerLevel::High => value,
599 };
600 trigger = Some(match trigger {
601 Some(trigger) => self.dst.ins().and(trigger, value),
602 None => value,
603 });
604 }
605 let trigger = match trigger {
606 Some(c) => c,
607 None => {
608 trace!(
609 " Skipping {} (level-sensitive with no trigger)",
610 drive.dump(&self.src)
611 );
612 return false;
613 }
614 };
615
616 let data = match self.migrate_value(drive_value, &ties) {
618 Some(v) => v,
619 None => return false,
620 };
621
622 reg_triggers.push(RegTrigger {
624 data,
625 mode: RegMode::High,
626 trigger,
627 gate: None,
628 });
629 }
630 }
631 }
632
633 self.dst.ins().reg(mig_target, reg_triggers);
635
636 self.migrated_drives.insert(drive);
638 true
639 }
640
641 pub fn migrate_value(
642 &mut self,
643 value: Value,
644 ties: &BTreeMap<(Value, TemporalRegion), TriggerLevel>,
645 ) -> Option<Value> {
646 if let Some(arg) = self.src.get_value_arg(value) {
648 return Some(self.dst.arg_value(arg));
649 }
650
651 if let Some(inst) = self.src.get_value_inst(value) {
653 let bb = self.src.inst_block(inst)?;
654 let tr = self.trg[bb];
655
656 if self.src[inst].opcode() == Opcode::Prb {
658 let sig = self.src[inst].args()[0];
660 if let Some(&level) = ties.get(&(sig, tr)) {
661 let data = InstData::ConstInt {
662 opcode: Opcode::ConstInt,
663 imm: IntValue::from_usize(
664 1,
665 match level {
666 TriggerLevel::High => 1,
667 TriggerLevel::Low => 0,
668 },
669 ),
670 };
671 return Some(self.migrate_inst_data(data, value));
672 }
673
674 if tr != self.tr1 {
677 trace!(" Skipping {} (probe in wrong TR)", inst.dump(&self.src));
678 return None;
679 }
680 }
681
682 let mut data = self.src[inst].clone();
684 #[allow(deprecated)]
685 for arg in data.args_mut() {
686 *arg = self.migrate_value(*arg, ties)?;
687 }
688 return Some(self.migrate_inst_data(data, value));
689 }
690
691 trace!(
693 " Skipping {} (cannot be migrated)",
694 value.dump(&self.src)
695 );
696 None
697 }
698
699 fn migrate_inst_data(&mut self, data: InstData, src_value: Value) -> Value {
700 if let Some(&v) = self.cache.get(&data) {
701 v
702 } else {
703 trace!(" Migrated {}", src_value.dump(&self.src));
704 let ty = self.src.value_type(src_value);
705 let inst = self.dst.ins().build(data.clone(), ty);
706 let value = self.dst.inst_result(inst);
707 self.cache.insert(data, value);
708 if let Some(name) = self.src.get_name(src_value) {
709 self.dst.set_name(value, name.to_string());
710 }
711 value
712 }
713 }
714}