use crate::layout::prelude::{Geometry, SimpleTransform};
use crate::netlist::direction::Direction;
use crate::prelude::PropertyValue;
use crate::traits::*;
use std::ops::Deref;
pub enum NetlistUndoOp<T: NetlistBase> {
HierarchyOp(HierarchyUndoOp<T>),
CreatePin(T::PinId),
RenamePin(T::PinId, T::NameType),
CreateNet(T::NetId),
ConnectPin(T::PinId, Option<T::NetId>),
ConnectPinInstance(T::PinInstId, Option<T::NetId>),
RenameNet(T::NetId, Option<T::NameType>),
}
impl<T: NetlistBase> From<HierarchyUndoOp<T>> for NetlistUndoOp<T> {
fn from(op: HierarchyUndoOp<T>) -> Self {
Self::HierarchyOp(op)
}
}
pub enum LayoutUndoOp<T: LayoutBase> {
HierarchyOp(HierarchyUndoOp<T>),
SetDbu(T::Coord),
CreateLayer(T::LayerId),
SetLayerName(T::LayerId, Option<T::NameType>),
InsertShape(T::ShapeId),
RemoveShape {
parent_cell: T::CellId,
layer: T::LayerId,
geometry: Geometry<T::Coord>,
},
ReplaceShape(T::ShapeId, Geometry<T::Coord>),
SetTransform(T::CellInstId, SimpleTransform<T::Coord>),
}
impl<T: LayoutBase> From<HierarchyUndoOp<T>> for LayoutUndoOp<T> {
fn from(op: HierarchyUndoOp<T>) -> Self {
Self::HierarchyOp(op)
}
}
#[allow(missing_docs)]
pub enum L2NUndoOp<T: L2NBase> {
HierarchyOp(HierarchyUndoOp<T>),
NetlistOp(NetlistUndoOp<T>),
LayoutOp(LayoutUndoOp<T>),
SetNetOfShape {
shape_id: T::ShapeId,
previous_net: Option<T::NetId>,
},
SetPinOfShape {
shape_id: T::ShapeId,
previous_pin: Option<T::PinId>,
},
}
impl<T: L2NBase> From<HierarchyUndoOp<T>> for L2NUndoOp<T> {
fn from(op: HierarchyUndoOp<T>) -> Self {
Self::HierarchyOp(op)
}
}
impl<T: L2NBase> From<NetlistUndoOp<T>> for L2NUndoOp<T> {
fn from(op: NetlistUndoOp<T>) -> Self {
Self::NetlistOp(op)
}
}
impl<T: L2NBase> From<LayoutUndoOp<T>> for L2NUndoOp<T> {
fn from(op: LayoutUndoOp<T>) -> Self {
Self::LayoutOp(op)
}
}
pub enum HierarchyUndoOp<T: HierarchyBase> {
CreateCell(T::CellId),
CreateCellInstance(T::CellInstId),
RenameCell {
cell: T::CellId,
previous_name: T::NameType,
},
RenameCellInst {
inst: T::CellInstId,
previous_name: Option<T::NameType>,
},
}
pub struct Undo<'a, T, U> {
chip: &'a mut T,
transactions: Vec<U>,
}
impl<'a, T, U> Undo<'a, T, U> {
pub fn num_transactions(&self) -> usize {
self.transactions.len()
}
pub fn flush(&mut self) {
self.transactions.clear()
}
}
impl<'a, T, U> Deref for Undo<'a, T, U> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.chip
}
}
impl<'a, T: HierarchyEdit, U> Undo<'a, T, U> {
fn undo_hierarchy_op(&mut self, op: HierarchyUndoOp<T>) {
match op {
HierarchyUndoOp::CreateCell(c) => self.chip.remove_cell(&c),
HierarchyUndoOp::CreateCellInstance(c) => self.chip.remove_cell_instance(&c),
HierarchyUndoOp::RenameCell {
cell,
previous_name,
} => self.chip.rename_cell(&cell, previous_name),
HierarchyUndoOp::RenameCellInst {
inst,
previous_name,
} => self.chip.rename_cell_instance(&inst, previous_name),
}
}
}
impl<'a, T: L2NEdit, U> Undo<'a, T, U> {
fn undo_l2n_op(&mut self, op: L2NUndoOp<T>) {
match op {
L2NUndoOp::HierarchyOp(op) => self.undo_hierarchy_op(op),
L2NUndoOp::NetlistOp(op) => self.undo_netlist_op(op),
L2NUndoOp::LayoutOp(op) => self.undo_layout_op(op),
L2NUndoOp::SetNetOfShape {
shape_id,
previous_net,
} => {
self.chip.set_net_of_shape(&shape_id, previous_net);
}
L2NUndoOp::SetPinOfShape {
shape_id,
previous_pin,
} => {
self.chip.set_pin_of_shape(&shape_id, previous_pin);
}
}
}
}
impl<'a, T: L2NEdit> Undo<'a, T, L2NUndoOp<T>> {
pub fn new_l2n_undo(chip: &'a mut T) -> Self {
Self {
chip,
transactions: vec![],
}
}
pub fn undo(&mut self) {
if let Some(op) = self.transactions.pop() {
self.undo_l2n_op(op)
}
}
}
impl<'a, T: LayoutEdit, U> Undo<'a, T, U> {
fn undo_layout_op(&mut self, op: LayoutUndoOp<T>) {
match op {
LayoutUndoOp::HierarchyOp(op) => self.undo_hierarchy_op(op),
LayoutUndoOp::SetDbu(dbu) => self.chip.set_dbu(dbu),
LayoutUndoOp::CreateLayer(_id) => {
log::error!("Creating a layer cannot be undone.");
}
LayoutUndoOp::SetLayerName(id, old_name) => {
self.chip.set_layer_name(&id, old_name);
}
LayoutUndoOp::InsertShape(id) => {
self.chip.remove_shape(&id);
}
LayoutUndoOp::RemoveShape {
parent_cell,
layer,
geometry,
} => {
self.chip.insert_shape(&parent_cell, &layer, geometry);
}
LayoutUndoOp::ReplaceShape(id, geometry) => {
self.chip.replace_shape(&id, geometry);
}
LayoutUndoOp::SetTransform(inst, old_tf) => self.chip.set_transform(&inst, old_tf),
}
}
}
impl<'a, T: LayoutEdit> Undo<'a, T, LayoutUndoOp<T>> {
pub fn new_layout_undo(chip: &'a mut T) -> Self {
Self {
chip,
transactions: vec![],
}
}
pub fn undo(&mut self) {
if let Some(op) = self.transactions.pop() {
self.undo_layout_op(op)
}
}
}
impl<'a, T: NetlistEdit, U> Undo<'a, T, U> {
fn undo_netlist_op(&mut self, op: NetlistUndoOp<T>) {
match op {
NetlistUndoOp::HierarchyOp(op) => self.undo_hierarchy_op(op),
NetlistUndoOp::CreatePin(p) => self.chip.remove_pin(&p),
NetlistUndoOp::RenamePin(p, n) => {
self.chip.rename_pin(&p, n);
}
NetlistUndoOp::CreateNet(n) => self.chip.remove_net(&n),
NetlistUndoOp::ConnectPin(p, n) => {
self.chip.connect_pin(&p, n);
}
NetlistUndoOp::ConnectPinInstance(p, n) => {
self.chip.connect_pin_instance(&p, n);
}
NetlistUndoOp::RenameNet(net, name) => {
self.chip.rename_net(&net, name);
}
}
}
}
impl<'a, T: NetlistEdit> Undo<'a, T, NetlistUndoOp<T>> {
pub fn new_netlist_undo(chip: &'a mut T) -> Self {
Self {
chip,
transactions: vec![],
}
}
pub fn undo(&mut self) {
if let Some(op) = self.transactions.pop() {
self.undo_netlist_op(op)
}
}
}
impl<'a, T: HierarchyEdit> Undo<'a, T, HierarchyUndoOp<T>> {
pub fn new_hierarchy_undo(chip: &'a mut T) -> Self {
Self {
chip,
transactions: vec![],
}
}
pub fn undo(&mut self) {
if let Some(op) = self.transactions.pop() {
self.undo_hierarchy_op(op)
}
}
pub fn undo_all(&mut self) {
while !self.transactions.is_empty() {
self.undo();
}
}
}
#[portrait::fill(portrait::delegate(N))]
impl<'a, N, U> HierarchyIds for Undo<'a, N, U> where N: HierarchyIds {}
#[portrait::fill(portrait::delegate(N; self.chip))]
impl<'b, N, U> HierarchyBase for Undo<'b, N, U> where N: HierarchyBase {}
#[portrait::fill(portrait::delegate(N))]
impl<'b, N, U> NetlistIds for Undo<'b, N, U> where N: NetlistIds {}
#[portrait::fill(portrait::delegate(N; self.chip))]
impl<'b, N, U> NetlistBase for Undo<'b, N, U> where N: NetlistBase {}
#[portrait::fill(portrait::delegate(N))]
impl<'b, N, U> LayoutIds for Undo<'b, N, U> where N: LayoutIds {}
#[portrait::fill(portrait::delegate(N; self.chip))]
impl<'b, N, U> LayoutBase for Undo<'b, N, U> where N: LayoutBase {}
#[portrait::fill(portrait::delegate(N; self.chip))]
impl<'b, N, U> L2NBase for Undo<'b, N, U> where N: L2NBase {}
impl<'a, T: HierarchyEdit + 'static, U: From<HierarchyUndoOp<T>>> HierarchyEdit for Undo<'a, T, U> {
fn create_cell(&mut self, name: Self::NameType) -> Self::CellId {
let id = self.chip.create_cell(name);
self.transactions
.push(HierarchyUndoOp::CreateCell(id.clone()).into());
id
}
fn remove_cell(&mut self, _cell_id: &Self::CellId) {
unimplemented!()
}
fn create_cell_instance(
&mut self,
parent_cell: &Self::CellId,
template_cell: &Self::CellId,
name: Option<Self::NameType>,
) -> Self::CellInstId {
let id = self
.chip
.create_cell_instance(parent_cell, template_cell, name);
self.transactions
.push(HierarchyUndoOp::CreateCellInstance(id.clone()).into());
id
}
fn remove_cell_instance(&mut self, _inst: &Self::CellInstId) {
unimplemented!()
}
fn rename_cell_instance(&mut self, inst: &Self::CellInstId, new_name: Option<Self::NameType>) {
let previous_name = self.cell_instance_name(inst);
self.chip.rename_cell_instance(inst, new_name);
self.transactions.push(
HierarchyUndoOp::RenameCellInst {
inst: inst.clone(),
previous_name,
}
.into(),
);
}
fn rename_cell(&mut self, cell: &Self::CellId, new_name: Self::NameType) {
let previous_name = self.cell_name(cell);
self.chip.rename_cell(cell, new_name);
self.transactions.push(
HierarchyUndoOp::RenameCell {
cell: cell.clone(),
previous_name,
}
.into(),
);
}
}
impl<'a, T, U> NetlistEdit for Undo<'a, T, U>
where
T: NetlistEdit + 'static,
U: From<NetlistUndoOp<T>> + From<HierarchyUndoOp<T>>,
{
fn create_pin(
&mut self,
circuit: &Self::CellId,
name: Self::NameType,
direction: Direction,
) -> Self::PinId {
let id = self.chip.create_pin(circuit, name, direction);
self.transactions
.push(NetlistUndoOp::CreatePin(id.clone()).into());
id
}
fn remove_pin(&mut self, _id: &Self::PinId) {
unimplemented!("Removing a pin is not implemented to be undoable.")
}
fn rename_pin(&mut self, pin: &Self::PinId, new_name: Self::NameType) -> Self::NameType {
let prev_name = self.chip.pin_name(pin);
self.transactions
.push(NetlistUndoOp::RenamePin(pin.clone(), prev_name).into());
self.chip.rename_pin(pin, new_name)
}
fn create_net(&mut self, parent: &Self::CellId, name: Option<Self::NameType>) -> Self::NetId {
let id = self.chip.create_net(parent, name);
self.transactions
.push(NetlistUndoOp::CreateNet(id.clone()).into());
id
}
fn rename_net(
&mut self,
net_id: &Self::NetId,
new_name: Option<Self::NameType>,
) -> Option<Self::NameType> {
let old_name = self.chip.rename_net(net_id, new_name);
self.transactions
.push(NetlistUndoOp::RenameNet(net_id.clone(), old_name.clone()).into());
old_name
}
fn remove_net(&mut self, _net: &Self::NetId) {
unimplemented!("Removing a net is not implemented to be undoable.")
}
fn connect_pin(&mut self, pin: &Self::PinId, net: Option<Self::NetId>) -> Option<Self::NetId> {
let prev_net = self.chip.connect_pin(pin, net);
self.transactions
.push(NetlistUndoOp::ConnectPin(pin.clone(), prev_net.clone()).into());
prev_net
}
fn connect_pin_instance(
&mut self,
pin: &Self::PinInstId,
net: Option<Self::NetId>,
) -> Option<Self::NetId> {
let prev_net = self.chip.connect_pin_instance(pin, net);
self.transactions
.push(NetlistUndoOp::ConnectPinInstance(pin.clone(), prev_net.clone()).into());
prev_net
}
}
impl<'a, T, U> LayoutEdit for Undo<'a, T, U>
where
T: LayoutEdit + 'static,
U: From<LayoutUndoOp<T>> + From<HierarchyUndoOp<T>>,
{
fn set_dbu(&mut self, dbu: Self::Coord) {
self.transactions
.push(LayoutUndoOp::SetDbu(self.dbu()).into());
self.chip.set_dbu(dbu)
}
fn create_layer(&mut self, index: u32, datatype: u32) -> Self::LayerId {
let id = self.chip.create_layer(index, datatype);
self.transactions
.push(LayoutUndoOp::CreateLayer(id.clone()).into());
id
}
fn create_layer_with_id(
&mut self,
layer_id: Self::LayerId,
index: u32,
datatype: u32,
) -> Result<(), ()> {
self.chip
.create_layer_with_id(layer_id.clone(), index, datatype)?;
self.transactions
.push(LayoutUndoOp::CreateLayer(layer_id).into());
Ok(())
}
fn set_layer_name(
&mut self,
layer: &Self::LayerId,
name: Option<Self::NameType>,
) -> Option<Self::NameType> {
let old_name = self.layer_info(layer).name;
self.transactions
.push(LayoutUndoOp::SetLayerName(layer.clone(), old_name).into());
self.chip.set_layer_name(layer, name)
}
fn insert_shape(
&mut self,
parent_cell: &T::CellId,
layer: &T::LayerId,
geometry: Geometry<Self::Coord>,
) -> Self::ShapeId {
let id = self.chip.insert_shape(parent_cell, layer, geometry);
self.transactions
.push(LayoutUndoOp::InsertShape(id.clone()).into());
id
}
fn remove_shape(&mut self, shape_id: &Self::ShapeId) -> Option<Geometry<Self::Coord>> {
let geometry = self.chip.remove_shape(shape_id);
let (parent_cell, layer) = self.parent_of_shape(shape_id);
if let Some(geometry) = &geometry {
self.transactions.push(
LayoutUndoOp::RemoveShape {
parent_cell,
layer,
geometry: geometry.clone(),
}
.into(),
);
}
geometry
}
fn replace_shape(
&mut self,
shape_id: &Self::ShapeId,
geometry: Geometry<Self::Coord>,
) -> Geometry<Self::Coord> {
let old_geometry = self.chip.replace_shape(shape_id, geometry);
self.transactions
.push(LayoutUndoOp::ReplaceShape(shape_id.clone(), old_geometry.clone()).into());
old_geometry
}
fn set_transform(&mut self, cell_inst: &Self::CellInstId, tf: SimpleTransform<Self::Coord>) {
let old_transform = self.get_transform(cell_inst);
self.transactions
.push(LayoutUndoOp::SetTransform(cell_inst.clone(), old_transform).into());
self.chip.set_transform(cell_inst, tf)
}
fn set_shape_property(
&mut self,
shape: &Self::ShapeId,
key: Self::NameType,
_value: PropertyValue,
) {
let _old_property = self.get_shape_property(shape, &key);
unimplemented!("set_shape_property() is currently not undoable.")
}
}
impl<'a, T, U> L2NEdit for Undo<'a, T, U>
where
T: L2NEdit + 'static,
U: From<L2NUndoOp<T>>
+ From<LayoutUndoOp<T>>
+ From<NetlistUndoOp<T>>
+ From<HierarchyUndoOp<T>>,
{
fn set_pin_of_shape(
&mut self,
shape_id: &Self::ShapeId,
pin: Option<Self::PinId>,
) -> Option<Self::PinId> {
let previous_pin = self.get_pin_of_shape(shape_id);
self.transactions.push(
L2NUndoOp::SetPinOfShape {
shape_id: shape_id.clone(),
previous_pin,
}
.into(),
);
self.chip.set_pin_of_shape(shape_id, pin)
}
fn set_net_of_shape(
&mut self,
shape_id: &Self::ShapeId,
net: Option<Self::NetId>,
) -> Option<Self::NetId> {
let previous_net = self.get_net_of_shape(shape_id);
self.transactions.push(
L2NUndoOp::SetNetOfShape {
shape_id: shape_id.clone(),
previous_net,
}
.into(),
);
self.chip.set_net_of_shape(shape_id, net)
}
}
#[test]
fn test_hierarchy_undoing() {
use crate::chip::Chip;
let mut chip = Chip::new();
let mut undo = Undo::new_netlist_undo(&mut chip);
let top = undo.create_cell("TOP".into());
let _top_a = undo.create_pin(&top, "A".into(), Direction::Input);
let sub = undo.create_cell("SUB".into());
let _sub_b = undo.create_pin(&sub, "B".into(), Direction::Input);
let inst = undo.create_cell_instance(&top, &sub, Some("inst1".into()));
undo.rename_cell(&top, "NewName".into());
undo.rename_cell_instance(&inst, None);
undo.undo();
undo.undo();
assert!(undo.cell_by_name("TOP").is_some());
assert!(undo.cell_instance_by_name(&top, "inst1").is_some());
assert_eq!(undo.num_child_instances(&top), 1);
undo.undo();
assert_eq!(undo.num_child_instances(&top), 0);
assert_eq!(undo.num_cells(), 2);
undo.undo();
undo.undo();
undo.undo();
undo.undo();
assert_eq!(undo.num_cells(), 0);
}