Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement decoder sizing logic #452

Merged
merged 12 commits into from
Sep 24, 2024
273 changes: 216 additions & 57 deletions src/blocks/decoder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
use serde::{Deserialize, Serialize};
use substrate::component::{Component, NoParams};
use substrate::index::IndexOwned;
use substrate::schematic::circuit::Direction;

use self::layout::{
decoder_stage_layout, LastBitDecoderPhysicalDesignScript, PredecoderPhysicalDesignScript,
RoutingStyle,
};
use crate::blocks::decoder::sizing::{path_map_tree, Tree, ValueTree};
use serde::{Deserialize, Serialize};
use subgeom::snap_to_grid;
use substrate::component::{Component, NoParams};
use substrate::index::IndexOwned;
use substrate::logic::delay::{GateModel, LogicPath, OptimizerOpts};
use substrate::schematic::circuit::Direction;

use super::gate::{AndParams, Gate, GateParams, GateType, PrimitiveGateParams};

pub mod layout;
pub mod schematic;
pub mod sim;

pub mod sizing;

pub struct Decoder {
params: DecoderParams,
}
Expand Down Expand Up @@ -49,6 +53,7 @@ pub struct TreeNode {
// Number of one-hot outputs.
pub num: usize,
pub children: Vec<TreeNode>,
pub child_nums: Vec<usize>,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
Expand Down Expand Up @@ -310,57 +315,211 @@ fn size_decoder(tree: &PlanTreeNode) -> TreeNode {
size_helper_tmp(tree, tree.skew_rising, tree.cols)
}

fn size_helper_tmp(x: &PlanTreeNode, skew_rising: bool, cols: bool) -> TreeNode {
let gate_params = if cols {
AndParams {
nand: PrimitiveGateParams {
nwidth: 10_000,
pwidth: 8_000,
length: 150,
},
inv: PrimitiveGateParams {
nwidth: 8_000,
pwidth: 10_000,
length: 150,
},
}
} else if skew_rising {
AndParams {
nand: PrimitiveGateParams {
nwidth: 4_000,
pwidth: 1_000,
length: 150,
},
inv: PrimitiveGateParams {
nwidth: 600,
pwidth: 6_800,
length: 150,
},
/// The on-resistance and capacitances of a 1x inverter ([`INV_PARAMS`]).
const INV_MODEL: GateModel = GateModel {
res: 1.0,
cin: 1.0,
cout: 1.0,
};

/// The on-resistance and capacitances of a 1x NAND2 gate ([`NAND2_PARAMS`]).
const NAND2_MODEL: GateModel = GateModel {
res: 1.0,
cin: 1.0,
cout: 1.0,
};

/// The on-resistance and capacitances of a 1x NAND3 gate ([`NAND3_PARAMS`]).
const NAND3_MODEL: GateModel = GateModel {
res: 1.0,
cin: 1.0,
cout: 1.0,
};

/// The on-resistance and capacitances of a 1x NOR2 gate ([`NOR2_PARAMS`]).
const NOR2_MODEL: GateModel = GateModel {
res: 1.0,
cin: 1.0,
cout: 1.0,
};

/// The sizing of a 1x inverter.
const INV_PARAMS: PrimitiveGateParams = PrimitiveGateParams {
nwidth: 1_000,
pwidth: 1_600,
length: 150,
};

/// The sizing of a 1x NAND2 gate.
const NAND2_PARAMS: PrimitiveGateParams = PrimitiveGateParams {
nwidth: 2_000,
pwidth: 1_600,
length: 150,
};

/// The sizing of a 1x NAND3 gate.
const NAND3_PARAMS: PrimitiveGateParams = PrimitiveGateParams {
nwidth: 3_000,
pwidth: 1_600,
length: 150,
};

/// The sizing of a 1x NOR2 gate.
const NOR2_PARAMS: PrimitiveGateParams = PrimitiveGateParams {
nwidth: 1_000,
pwidth: 3_200,
length: 150,
};

fn gate_params(gate: GateType) -> PrimitiveGateParams {
match gate {
GateType::Inv => INV_PARAMS,
GateType::Nand2 => NAND2_PARAMS,
GateType::Nand3 => NAND3_PARAMS,
GateType::Nor2 => NOR2_PARAMS,
gate => panic!("unsupported gate type: {gate:?}"),
}
}

fn gate_model(gate: GateType) -> GateModel {
match gate {
GateType::Inv => INV_MODEL,
GateType::Nand2 => NAND2_MODEL,
GateType::Nand3 => NAND3_MODEL,
GateType::Nor2 => NOR2_MODEL,
gate => panic!("unsupported gate type: {gate:?}"),
}
}

fn scale(gate: PrimitiveGateParams, scale: f64) -> PrimitiveGateParams {
let nwidth = snap_to_grid((gate.nwidth as f64 * scale).round() as i64, 50);
let pwidth = snap_to_grid((gate.pwidth as f64 * scale).round() as i64, 50);
PrimitiveGateParams {
nwidth,
pwidth,
length: gate.length,
}
}

fn size_path(path: &[&PlanTreeNode], end: &f64) -> TreeNode {
let mut lp = LogicPath::new();
let mut vars = Vec::new();
for (i, node) in path.iter().copied().rev().enumerate() {
for (j, gate) in node.gate.primitive_gates().iter().copied().enumerate() {
if i == 0 && j == 0 {
lp.append_sized_gate(gate_model(gate));
} else {
let var = lp.create_variable_with_initial(2.);
let model = gate_model(gate);
if i != 0 && j == 0 {
let mult = (node.num / node.children[0].num) as f64 * model.cin - 1.;
assert!(mult >= 0.0);
lp.append_variable_capacitor(mult, var);
}
lp.append_unsized_gate(model, var);
vars.push(var);
}
}
} else {
AndParams {
nand: PrimitiveGateParams {
nwidth: 2_400,
pwidth: 800,
length: 150,
},
inv: PrimitiveGateParams {
nwidth: 3_100,
pwidth: 4_300,
length: 150,
},
}
lp.append_capacitor(*end);

lp.size_with_opts(OptimizerOpts {
lr: 0.2,
lr_decay: 0.999995,
max_iter: 10_000_000,
});

let mut cnode: Option<&mut TreeNode> = None;
let mut tree = None;

let mut values = vars
.iter()
.rev()
.map(|v| {
let v = lp.value(*v);
assert!(v >= 0.5);
v
})
.collect::<Vec<_>>();
values.push(1.);
let mut values = values.into_iter();

for &node in path {
let gate = match node.gate {
GateType::And2 => GateParams::And2(AndParams {
inv: scale(INV_PARAMS, values.next().unwrap()),
nand: scale(NAND2_PARAMS, values.next().unwrap()),
}),
GateType::And3 => GateParams::And3(AndParams {
inv: scale(INV_PARAMS, values.next().unwrap()),
nand: scale(NAND3_PARAMS, values.next().unwrap()),
}),
GateType::Inv => GateParams::Inv(scale(INV_PARAMS, values.next().unwrap())),
GateType::Nand2 => GateParams::Nand2(scale(NAND2_PARAMS, values.next().unwrap())),
GateType::Nand3 => GateParams::Nand3(scale(NAND3_PARAMS, values.next().unwrap())),
GateType::Nor2 => GateParams::Nor2(scale(NOR2_PARAMS, values.next().unwrap())),
};

let n = TreeNode {
gate,
num: node.num,
children: vec![],
child_nums: node.children.iter().map(|n| n.num).collect(),
};

if let Some(parent) = cnode {
parent.children.push(n);
cnode = Some(&mut parent.children[0])
} else {
tree = Some(n);
cnode = Some(tree.as_mut().unwrap());
}
};
// TODO size decoder
TreeNode {
gate: GateParams::new_and(x.gate, gate_params),
num: x.num,
children: x
.children
.iter()
.map(|n| size_helper_tmp(n, skew_rising, cols))
.collect::<Vec<_>>(),
}

tree.unwrap()
}

impl Tree for PlanTreeNode {
fn children(&self) -> &[Self] {
&self.children
}

fn children_mut(&mut self) -> &mut [Self] {
&mut self.children
}

fn add_right_child(&mut self, child: Self) {
self.children.push(child);
}
}

impl Tree for TreeNode {
fn children(&self) -> &[Self] {
&self.children
}

fn children_mut(&mut self) -> &mut [Self] {
&mut self.children
}

fn add_right_child(&mut self, child: Self) {
self.children.push(child);
}
}

impl ValueTree<f64> for TreeNode {
fn value_for_child(&self, idx: usize) -> f64 {
let first_gate_type = self.gate.gate_type().primitive_gates()[0];
let first_gate = self.gate.first_gate_sizing();
let model = gate_model(first_gate_type);
(self.num / self.child_nums[idx]) as f64 * model.cin * first_gate.nwidth as f64
/ (gate_params(first_gate_type).nwidth as f64)
}
}

fn size_helper_tmp(x: &PlanTreeNode, skew_rising: bool, cols: bool) -> TreeNode {
// TODO: use real load capacitance
path_map_tree(x, &size_path, &128.)
}

fn plan_decoder(bits: usize, top: bool, skew_rising: bool, cols: bool) -> PlanTreeNode {
Expand Down Expand Up @@ -407,8 +566,8 @@ fn partition_bits(bits: usize, top: bool) -> Vec<usize> {
assert!(bits > 3);

if top {
let left = bits / 2;
return vec![left, bits - left];
let right = bits / 2;
return vec![bits - right, right];
}

if bits % 2 == 0 {
Expand All @@ -421,8 +580,8 @@ fn partition_bits(bits: usize, top: bool) -> Vec<usize> {
_ => panic!("unexpected remainder of `bits` divided by 3"),
}
} else {
let left = bits / 2;
vec![left, bits - left]
let right = bits / 2;
vec![bits - right, right]
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/blocks/decoder/schematic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl Decoder {
let decode = ctx.bus_port("decode", out_bits, Direction::Output);
let decode_b = ctx.bus_port("decode_b", out_bits, Direction::Output);

let port_names = vec!["a", "b", "c"];
let port_names = ["a", "b", "c"];

// Initialize all gates in the decoder tree using BFS.
let mut queue = VecDeque::<(Option<Slice>, &TreeNode)>::new();
Expand Down
Loading
Loading