1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
// Copyright (c) 2020-2021 Thomas Kramer.
// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
//! Very simple clock/reset-tree generator.
//!
//! # Core ideas
//! Similar to the spirit of placement and routing engines, also a clock-tree generator
//! should have a standardized interface. This makes it possible to create larger systems
//! which depend on clock-tree generators.
//!
//! This module contains a proposal of how a clock tree generator interface could look like: [`SimpleClockTreeGenerator`]
use crate::clock_tree_specification::SimpleClockTreeSpecification;
use db::L2NEdit;
use libreda_pnr::db;
use libreda_pnr::legalize::stdcell_legalizer::{legalize, SimpleStdCellLegalizer};
use libreda_pnr::rebuffer::buffer_insertion::SimpleBufferInsertion;
use libreda_pnr::route::simple_router::SimpleRouter;
use num_traits::PrimInt;
use std::collections::{HashMap, HashSet};
use std::marker::PhantomData;
/// Simple clock-tree synthesis engine.
pub trait SimpleClockTreeGenerator<LN: L2NEdit> {
/// Error happening during clock-tree synthesis.
type Error;
/// Create unrouted clock trees based on given clock source nets and target skews.
/// Typically an implementation inserts buffer trees and assigns locations to the buffer gates.
///
/// # Parameters
/// * `chip`: A mutable reference to a fused netlist-layout struct.
/// * `clock_tree_specifications`: Definitions of the clock-trees to be generated.
///
/// # Return
/// Returns for each clock net a list of cell instances (e.g. buffers) and nets of the clock tree.
fn create_unrouted_clock_trees(
&self,
chip: &mut LN,
clock_tree_specifications: Vec<&dyn SimpleClockTreeSpecification<LN>>,
) -> Result<HashMap<LN::NetId, (Vec<LN::CellInstId>, Vec<LN::NetId>)>, Self::Error>;
}
/// Simple clock-tree synthesis engine.
/// Assembles a buffer insertion engine, a legalizer and a router into a clock-tree generator.
///
pub struct SimpleCTS<'a, LN, BufferEngine, Legalizer, Router>
where
LN: L2NEdit,
LN::Coord: PrimInt,
BufferEngine: SimpleBufferInsertion<LN>,
Legalizer: SimpleStdCellLegalizer<LN>,
Router: SimpleRouter,
{
buffer_tree_engine: &'a BufferEngine,
_legalizer: &'a Legalizer,
_router: &'a Router,
netlist_layout_type: PhantomData<LN>,
}
impl<'a, LN, BufferEngine, Legalizer, Router> SimpleClockTreeGenerator<LN>
for SimpleCTS<'a, LN, BufferEngine, Legalizer, Router>
where
LN: L2NEdit<Coord = db::Coord>,
BufferEngine: SimpleBufferInsertion<LN>,
LN::Coord: PrimInt,
Legalizer: SimpleStdCellLegalizer<LN>,
Router: SimpleRouter,
{
type Error = ();
fn create_unrouted_clock_trees(
&self,
chip: &mut LN,
clock_tree_specifications: Vec<&dyn SimpleClockTreeSpecification<LN>>,
) -> Result<HashMap<LN::NetId, (Vec<LN::CellInstId>, Vec<LN::NetId>)>, Self::Error> {
let mut resulting_trees = HashMap::new();
if clock_tree_specifications.is_empty() {
// Nothing to be done.
return Ok(resulting_trees);
}
// Get all clock nets.
let clock_nets: Vec<_> = clock_tree_specifications
.iter()
.map(|spec| spec.clock_net())
.collect();
let top_cell = chip.parent_cell_of_net(&clock_nets[0]);
// Check that all nets are in the same cell.
assert!(
clock_nets
.iter()
.all(|n| chip.parent_cell_of_net(&n) == top_cell),
"All clock nets must live in the same cell."
);
let mut all_new_nets = vec![];
let mut all_new_buffers = vec![];
// Create unrouted clock trees sequentially for each specified net.
for clock_net in clock_nets {
log::info!(
"Create clock tree for signal '{:?}'",
chip.net_name(&clock_net)
);
// Insert the buffer tree.
let (buffers, nets) = self
.buffer_tree_engine
.add_buffer_tree_on_net(chip, &clock_net)
.map_err(|_err| ())?; // TODO: Convert error.
all_new_nets.extend(nets.iter().cloned());
all_new_buffers.extend(buffers.iter().cloned());
resulting_trees.insert(clock_net.clone(), (buffers, nets));
}
// TODO:
// // Legalize the inserted buffers.
// let fixed_instances = HashSet::new();
// legalize(
// self.legalizer,
// chip,
// &top_cell,
// core_area,
// cell_outlines,
// &fixed_instances,
// );
// TODO:
// // Route the created nets.
// let routing_result = router.route_nets(
// chip,
// top_cell.clone(),
// routing_layers,
// via_layers,
// &all_new_nets,
// );
//
// if let Err(failed_nets) = routing_result {
// log::error!("Failed to route {} nets.", failed_nets.len());
// }
Ok(resulting_trees)
}
}
/// Create clock trees for a set of clock nets by inserting placement-aware buffer trees.
/// Routes the clock nets.
///
/// Returns on success a hash map which maps the original clock nets
/// to the new buffers and new nets that were created for this clock tree.
pub fn create_clock_trees<LN, B, L, R>(
chip: &mut LN,
buffer_engine: &B,
legalizer: &L,
router: &R,
clock_nets: &Vec<LN::NetId>,
core_area: &db::SimplePolygon<LN::Coord>,
cell_outlines: &HashMap<LN::CellId, db::Rect<LN::Coord>>, // TODO: Pass this to legalizer or store in chip data structure.
routing_layers: &Vec<LN::LayerId>, // TODO: Store technology information in the router implementation.
via_layers: &Vec<LN::LayerId>, // TODO: Store technology information in the router implementation.
) -> Result<HashMap<LN::NetId, (Vec<LN::CellInstId>, Vec<LN::NetId>)>, ()>
where
LN: L2NEdit<Coord = db::Coord>,
LN::Coord: PrimInt,
B: SimpleBufferInsertion<LN>,
L: SimpleStdCellLegalizer<LN>,
R: SimpleRouter,
{
let mut resulting_trees = HashMap::new();
if clock_nets.is_empty() {
return Ok(resulting_trees);
}
let top_cell = chip.parent_cell_of_net(&clock_nets[0]);
// Check that all nets are in the same cell.
assert!(
clock_nets
.iter()
.all(|n| chip.parent_cell_of_net(&n) == top_cell),
"All clock nets must live in the same cell."
);
let mut all_new_nets = vec![];
let mut all_new_buffers = vec![];
// Create unrouted clock trees sequentially for each specified net.
for clock_net in clock_nets {
log::info!(
"Create clock tree for signal '{:?}'",
chip.net_name(clock_net)
);
// Insert the buffer tree.
let (buffers, nets) = buffer_engine
.add_buffer_tree_on_net(chip, &clock_net)
.map_err(|_err| ())?; // TODO: Convert error.
all_new_nets.extend(nets.iter().cloned());
all_new_buffers.extend(buffers.iter().cloned());
resulting_trees.insert(clock_net.clone(), (buffers, nets));
}
// Legalize the inserted buffers.
let fixed_instances = HashSet::new();
legalize(
legalizer,
chip,
&top_cell,
core_area,
cell_outlines,
&fixed_instances,
);
// Route the created nets.
let routing_result = router.route_nets(
chip,
top_cell.clone(),
routing_layers,
via_layers,
&all_new_nets,
);
if let Err(failed_nets) = routing_result {
log::error!("Failed to route {} nets.", failed_nets.len());
}
Ok(resulting_trees)
}