Skip to main content

prjunnamed_netlist/
target.rs

1#![allow(clippy::len_without_is_empty)]
2
3use std::borrow::Cow;
4use std::fmt::{Debug, Display};
5use std::error::Error;
6use std::collections::BTreeMap;
7use std::ops::Range;
8use std::sync::{Arc, Mutex};
9
10use crate::{CellRef, Const, ConstBit, Design, Instance, IoValue, ParamValue, TargetCell, Value, ValueSlice};
11
12pub trait Target: Debug {
13    /// Get target name. The name of the target can be used to construct a new instance of it.
14    fn name(&self) -> &str;
15
16    /// Get target options. Target options define the exact variant of the target, and include
17    /// the device family, device name, speed grade, temperature variant, and any other parameters
18    /// required to fully define a specific device.
19    fn options(&self) -> BTreeMap<String, String>;
20
21    /// Get prototypes of target cells. Prototypes in conjunction with the target cell itself define
22    /// the connectivity and properties of the primitive instance.
23    fn prototype(&self, name: &str) -> Option<&TargetPrototype>;
24
25    /// Validate target-specific constraints. Conformance with the prototype is always validated
26    /// by `Design::validate`.
27    fn validate(&self, design: &Design, cell: &TargetCell);
28
29    /// Convert generic instances into target cells.
30    fn import(&self, design: &mut Design) -> Result<(), TargetImportError>;
31
32    /// Convert target cells into generic instances.
33    fn export(&self, design: &mut Design);
34
35    // TODO: get a better error type
36    #[allow(clippy::result_unit_err)]
37    /// Run the complete synthesis flow.
38    fn synthesize(&self, design: &mut Design) -> Result<(), ()>;
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum TargetParamKind {
43    Bits(usize),
44    Bool,
45    IntEnum(Vec<i64>),
46    StringEnum(Vec<String>),
47}
48
49impl TargetParamKind {
50    pub fn is_valid(&self, value: &ParamValue) -> bool {
51        match (self, value) {
52            (TargetParamKind::Bits(width), ParamValue::Const(value)) => value.len() == *width,
53            (TargetParamKind::Bool, ParamValue::Const(value)) => value.len() == 1 && !value.has_undef(),
54            (TargetParamKind::IntEnum(items), ParamValue::Int(value)) => items.contains(value),
55            (TargetParamKind::StringEnum(items), ParamValue::String(value)) => items.contains(value),
56            _ => false,
57        }
58    }
59
60    pub fn cast(&self, value: &ParamValue) -> Option<ParamValue> {
61        match (self, value) {
62            (TargetParamKind::Bits(width), ParamValue::Const(value)) if value.len() == *width => {
63                Some(ParamValue::Const(value.clone()))
64            }
65            (TargetParamKind::Bits(width), ParamValue::Int(value)) if *value >= 0 && *value < 1 << *width => {
66                Some(ParamValue::Const(Const::from_uint(u128::try_from(*value).unwrap(), *width)))
67            }
68            (TargetParamKind::Bool, ParamValue::Int(value)) => match *value {
69                1 => Some(ParamValue::Const(Const::ones(1))),
70                0 => Some(ParamValue::Const(Const::zero(1))),
71                _ => None,
72            },
73            (TargetParamKind::Bool, ParamValue::Const(value)) => match value.try_into() {
74                Ok(0u64) => Some(ParamValue::Const(Const::zero(1))),
75                Ok(1u64) => Some(ParamValue::Const(Const::ones(1))),
76                _ => None,
77            },
78            (TargetParamKind::IntEnum(items), ParamValue::Const(value)) => {
79                let value = value.try_into().ok()?;
80                if items.contains(&value) { Some(ParamValue::Int(value)) } else { None }
81            }
82            (TargetParamKind::IntEnum(items), ParamValue::Int(value)) if items.contains(value) => {
83                Some(ParamValue::Int(*value))
84            }
85            (TargetParamKind::StringEnum(items), ParamValue::String(value)) if items.contains(value) => {
86                Some(ParamValue::String(value.clone()))
87            }
88            _ => None,
89        }
90    }
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct TargetParam {
95    pub name: String,
96    pub kind: TargetParamKind,
97    pub default: ParamValue,
98    pub index: usize,
99}
100
101#[derive(Debug, Clone, PartialEq, Eq)]
102pub struct TargetInput {
103    pub name: String,
104    pub default: Const,
105    pub invert_param: Option<usize>,
106    pub range: Range<usize>,
107}
108
109impl TargetInput {
110    pub fn len(&self) -> usize {
111        self.default.len()
112    }
113}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
116pub struct TargetOutput {
117    pub name: String,
118    pub range: Range<usize>,
119}
120
121impl TargetOutput {
122    pub fn len(&self) -> usize {
123        self.range.len()
124    }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq)]
128pub struct TargetIo {
129    pub name: String,
130    pub range: Range<usize>,
131}
132
133impl TargetIo {
134    pub fn len(&self) -> usize {
135        self.range.len()
136    }
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub enum TargetCellPurity {
141    Pure,
142    HasState,
143    HasEffects,
144}
145
146#[derive(Debug, Clone, PartialEq, Eq)]
147pub struct TargetPrototype {
148    pub purity: TargetCellPurity,
149    pub params: Vec<TargetParam>,
150    pub params_by_name: BTreeMap<String, usize>,
151    pub inputs: Vec<TargetInput>,
152    pub inputs_by_name: BTreeMap<String, usize>,
153    pub input_len: usize,
154    pub outputs: Vec<TargetOutput>,
155    pub outputs_by_name: BTreeMap<String, usize>,
156    pub output_len: usize,
157    pub ios: Vec<TargetIo>,
158    pub ios_by_name: BTreeMap<String, usize>,
159    pub io_len: usize,
160}
161
162impl TargetPrototype {
163    fn new(purity: TargetCellPurity) -> TargetPrototype {
164        TargetPrototype {
165            purity,
166            params: vec![],
167            params_by_name: Default::default(),
168            inputs: vec![],
169            inputs_by_name: Default::default(),
170            input_len: 0,
171            outputs: vec![],
172            outputs_by_name: Default::default(),
173            output_len: 0,
174            ios: vec![],
175            ios_by_name: Default::default(),
176            io_len: 0,
177        }
178    }
179
180    pub fn new_pure() -> TargetPrototype {
181        Self::new(TargetCellPurity::Pure)
182    }
183
184    pub fn new_has_state() -> TargetPrototype {
185        Self::new(TargetCellPurity::HasState)
186    }
187
188    pub fn new_has_effects() -> TargetPrototype {
189        Self::new(TargetCellPurity::HasEffects)
190    }
191
192    fn add_param_raw(mut self, name: impl Into<String>, kind: TargetParamKind, default: ParamValue) -> Self {
193        let name = name.into();
194        let index = self.params.len();
195        self.params_by_name.insert(name.clone(), index);
196        self.params.push(TargetParam { name, kind, default, index });
197        self
198    }
199
200    pub fn add_param_bits(self, name: impl Into<String>, default: impl Into<Const>) -> Self {
201        let default = default.into();
202        self.add_param_raw(name, TargetParamKind::Bits(default.len()), ParamValue::Const(default))
203    }
204
205    pub fn add_param_bool(self, name: impl Into<String>, default: bool) -> Self {
206        self.add_param_raw(name, TargetParamKind::Bool, ParamValue::Const(ConstBit::from(default).into()))
207    }
208
209    pub fn add_param_int_enum(self, name: impl Into<String>, variants: &[i64]) -> Self {
210        let variants = variants.to_vec();
211        let default = variants[0];
212        self.add_param_raw(name, TargetParamKind::IntEnum(variants), ParamValue::Int(default))
213    }
214
215    pub fn add_param_string_enum(self, name: impl Into<String>, variants: &[impl AsRef<str>]) -> Self {
216        let variants = Vec::from_iter(variants.iter().map(|s| s.as_ref().to_owned()));
217        let default = variants[0].clone();
218        self.add_param_raw(name, TargetParamKind::StringEnum(variants), ParamValue::String(default))
219    }
220
221    fn add_input_raw(
222        mut self,
223        name: impl Into<String>,
224        default: impl Into<Const>,
225        invert_param: Option<usize>,
226    ) -> Self {
227        let default = default.into();
228        let range = self.input_len..self.input_len + default.len();
229        self.input_len += default.len();
230        let name = name.into();
231        self.inputs_by_name.insert(name.clone(), self.inputs.len());
232        self.inputs.push(TargetInput { name, default, invert_param, range });
233        self
234    }
235
236    pub fn add_input(self, name: impl Into<String>, default: impl Into<Const>) -> Self {
237        self.add_input_raw(name, default, None)
238    }
239
240    pub fn add_input_invertible(
241        self,
242        name: impl Into<String>,
243        default: impl Into<Const>,
244        invert_param: impl AsRef<str>,
245    ) -> Self {
246        let invert_param = self.params_by_name[invert_param.as_ref()];
247        let default = default.into();
248        match &self.params[invert_param].kind {
249            TargetParamKind::Bits(width) if *width == default.len() => (),
250            TargetParamKind::Bool if default.len() == 1 => (),
251            _ => panic!("invalid kind for inversion parameter {invert_param:?}"),
252        }
253        self.add_input_raw(name, default, Some(invert_param))
254    }
255
256    pub fn add_output(mut self, name: impl Into<String>, width: usize) -> Self {
257        let range = self.output_len..self.output_len + width;
258        self.output_len += width;
259        let name = name.into();
260        self.outputs_by_name.insert(name.clone(), self.outputs.len());
261        self.outputs.push(TargetOutput { name, range });
262        self
263    }
264
265    pub fn add_io(mut self, name: impl Into<String>, width: usize) -> Self {
266        let range = self.io_len..self.io_len + width;
267        self.io_len += width;
268        let name = name.into();
269        self.ios_by_name.insert(name.clone(), self.ios.len());
270        self.ios.push(TargetIo { name, range });
271        self
272    }
273
274    pub fn get_param(&self, name: &str) -> Option<&TargetParam> {
275        self.params_by_name.get(name).map(|&index| &self.params[index])
276    }
277
278    pub fn get_input(&self, name: &str) -> Option<&TargetInput> {
279        self.inputs_by_name.get(name).map(|&index| &self.inputs[index])
280    }
281
282    pub fn get_output(&self, name: &str) -> Option<&TargetOutput> {
283        self.outputs_by_name.get(name).map(|&index| &self.outputs[index])
284    }
285
286    pub fn get_io(&self, name: &str) -> Option<&TargetIo> {
287        self.ios_by_name.get(name).map(|&index| &self.ios[index])
288    }
289
290    pub fn apply_param(&self, target_cell: &mut TargetCell, name: impl AsRef<str>, value: impl Into<ParamValue>) {
291        let name = name.as_ref();
292        if let Some(TargetParam { index, .. }) = self.get_param(name) {
293            target_cell.params[*index] = value.into();
294        } else {
295            panic!("parameter {name:?} does not exist for target cell");
296        }
297    }
298
299    pub fn apply_input(&self, target_cell: &mut TargetCell, name: impl AsRef<str>, value: impl AsRef<ValueSlice>) {
300        let (name, value) = (name.as_ref(), value.as_ref());
301        if let Some(TargetInput { range, .. }) = self.get_input(name) {
302            target_cell.inputs[range.clone()].copy_from_slice(value);
303        } else {
304            panic!("input {name:?} does not exist for target cell");
305        }
306    }
307
308    pub fn apply_io<'a>(
309        &self,
310        target_cell: &mut TargetCell,
311        name: impl AsRef<str>,
312        value: impl Into<Cow<'a, IoValue>>,
313    ) {
314        let (name, value) = (name.as_ref(), value.into());
315        if let Some(TargetIo { range, .. }) = self.get_io(name) {
316            target_cell.ios[range.clone()].copy_from_slice(&value[..]);
317        } else {
318            panic!("input {name:?} does not exist for target cell");
319        }
320    }
321
322    pub fn extract_param<'a>(&self, target_cell: &'a TargetCell, name: impl AsRef<str>) -> &'a ParamValue {
323        let name = name.as_ref();
324        if let Some(TargetParam { index, .. }) = self.get_param(name) {
325            &target_cell.params[*index]
326        } else {
327            panic!("param {name:?} does not exist for target cell");
328        }
329    }
330
331    pub fn extract_param_bool(&self, target_cell: &TargetCell, name: impl AsRef<str>) -> bool {
332        let name = name.as_ref();
333        if let Some(TargetParam { index, kind, .. }) = self.get_param(name) {
334            assert_eq!(*kind, TargetParamKind::Bool);
335            let ParamValue::Const(ref value) = target_cell.params[*index] else { unreachable!() };
336            value[0] == ConstBit::One
337        } else {
338            panic!("param {name:?} does not exist for target cell");
339        }
340    }
341
342    pub fn extract_input(&self, target_cell: &TargetCell, name: impl AsRef<str>) -> Value {
343        let name = name.as_ref();
344        if let Some(TargetInput { range, .. }) = self.get_input(name) {
345            target_cell.inputs.slice(range.clone())
346        } else {
347            panic!("input {name:?} does not exist for target cell");
348        }
349    }
350
351    pub fn extract_output(&self, target_cell_output: &Value, name: impl AsRef<str>) -> Value {
352        let name = name.as_ref();
353        if let Some(TargetOutput { range, .. }) = self.get_output(name) {
354            target_cell_output.slice(range.clone())
355        } else {
356            panic!("output {name:?} does not exist for target cell");
357        }
358    }
359
360    pub fn target_cell_to_instance(&self, cell: &TargetCell) -> Instance {
361        let mut result = Instance::new(cell.kind.clone());
362        for (index, param) in self.params.iter().enumerate() {
363            result.params.insert(param.name.clone(), cell.params[index].clone());
364        }
365        for input in &self.inputs {
366            result.inputs.insert(input.name.clone(), cell.inputs.slice(input.range.clone()));
367        }
368        for output in &self.outputs {
369            result.outputs.insert(output.name.clone(), output.range.clone());
370        }
371        for io in &self.ios {
372            result.ios.insert(io.name.clone(), cell.ios.slice(io.range.clone()));
373        }
374        result
375    }
376
377    pub fn instance_to_target_cell(
378        &self,
379        design: &Design,
380        instance: &Instance,
381        instance_output: Value,
382    ) -> Result<(TargetCell, Value), TargetCellImportError> {
383        let mut target_cell = TargetCell::new(instance.kind.clone(), self);
384        for (name, value) in &instance.params {
385            let param = self.get_param(name).ok_or_else(|| TargetCellImportError::UnknownParameter(name.clone()))?;
386            let Some(value) = param.kind.cast(value) else {
387                return Err(TargetCellImportError::ParameterValueInvalid(name.clone(), value.clone()));
388            };
389            target_cell.params[param.index] = value;
390        }
391        for (name, value) in &instance.inputs {
392            let input = self.get_input(name).ok_or_else(|| TargetCellImportError::UnknownInput(name.clone()))?;
393            if value.len() != input.len() {
394                return Err(TargetCellImportError::InputSizeMismatch(name.clone()));
395            }
396            target_cell.inputs[input.range.clone()].copy_from_slice(&value[..]);
397        }
398        for (name, value) in &instance.ios {
399            let io = self.get_io(name).ok_or_else(|| TargetCellImportError::UnknownIo(name.clone()))?;
400            if value.len() != io.len() {
401                return Err(TargetCellImportError::IoSizeMismatch(name.clone()));
402            }
403            target_cell.ios[io.range.clone()].copy_from_slice(&value[..]);
404        }
405        let mut target_output = design.add_void(self.output_len);
406        for (name, range) in &instance.outputs {
407            let output = self.get_output(name).ok_or_else(|| TargetCellImportError::UnknownOutput(name.clone()))?;
408            if range.len() != output.len() {
409                return Err(TargetCellImportError::OutputSizeMismatch(name.clone()));
410            }
411            target_output[output.range.clone()].copy_from_slice(&instance_output[range.clone()]);
412        }
413        Ok((target_cell, target_output))
414    }
415}
416
417#[derive(Debug, Clone)]
418pub enum TargetCellImportError {
419    UnknownParameter(String),
420    ParameterTypeMismatch(String),
421    ParameterValueInvalid(String, ParamValue),
422    UnknownInput(String),
423    InputSizeMismatch(String),
424    UnknownOutput(String),
425    OutputSizeMismatch(String),
426    UnknownIo(String),
427    IoSizeMismatch(String),
428}
429
430impl Display for TargetCellImportError {
431    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
432        match self {
433            TargetCellImportError::UnknownParameter(name) => write!(f, "unknown parameter {name:?}"),
434            TargetCellImportError::ParameterTypeMismatch(name) => write!(f, "type mismatch for parameter {name:?}"),
435            TargetCellImportError::ParameterValueInvalid(name, value) => {
436                let value = match value {
437                    ParamValue::Const(value) => format!("{value}"),
438                    ParamValue::Int(value) => format!("{value}"),
439                    ParamValue::Float(value) => format!("{value}"),
440                    ParamValue::String(value) => format!("{value:?}"),
441                };
442                write!(f, "invalid value {value} for parameter {name:?}")
443            }
444            TargetCellImportError::UnknownInput(name) => write!(f, "unknown input {name:?}"),
445            TargetCellImportError::InputSizeMismatch(name) => write!(f, "size mismatch for input {name:?}"),
446            TargetCellImportError::UnknownOutput(name) => write!(f, "unknown output {name:?}"),
447            TargetCellImportError::OutputSizeMismatch(name) => write!(f, "size mismatch for output {name:?}"),
448            TargetCellImportError::UnknownIo(name) => write!(f, "unknown io {name:?}"),
449            TargetCellImportError::IoSizeMismatch(name) => write!(f, "size mismatch for io {name:?}"),
450        }
451    }
452}
453
454impl Error for TargetCellImportError {}
455
456#[derive(Debug, Clone)]
457pub struct TargetImportError {
458    cell_index: usize,
459    pub cause: TargetCellImportError,
460}
461
462impl TargetImportError {
463    pub fn new(cell_ref: CellRef, cause: TargetCellImportError) -> TargetImportError {
464        TargetImportError { cell_index: cell_ref.debug_index(), cause }
465    }
466
467    pub fn unknown_parameter(cell_ref: CellRef, name: impl Into<String>) -> TargetImportError {
468        Self::new(cell_ref, TargetCellImportError::UnknownParameter(name.into()))
469    }
470
471    pub fn parameter_type_mismatch(cell_ref: CellRef, name: impl Into<String>) -> TargetImportError {
472        Self::new(cell_ref, TargetCellImportError::ParameterTypeMismatch(name.into()))
473    }
474
475    pub fn parameter_value_invalid(cell_ref: CellRef, name: impl Into<String>, value: ParamValue) -> TargetImportError {
476        Self::new(cell_ref, TargetCellImportError::ParameterValueInvalid(name.into(), value))
477    }
478
479    pub fn unknown_input(cell_ref: CellRef, name: impl Into<String>) -> TargetImportError {
480        Self::new(cell_ref, TargetCellImportError::UnknownInput(name.into()))
481    }
482
483    pub fn input_size_mismatch(cell_ref: CellRef, name: impl Into<String>) -> TargetImportError {
484        Self::new(cell_ref, TargetCellImportError::InputSizeMismatch(name.into()))
485    }
486
487    pub fn unknown_output(cell_ref: CellRef, name: impl Into<String>) -> TargetImportError {
488        Self::new(cell_ref, TargetCellImportError::UnknownOutput(name.into()))
489    }
490
491    pub fn output_size_mismatch(cell_ref: CellRef, name: impl Into<String>) -> TargetImportError {
492        Self::new(cell_ref, TargetCellImportError::OutputSizeMismatch(name.into()))
493    }
494
495    pub fn unknown_io(cell_ref: CellRef, name: impl Into<String>) -> TargetImportError {
496        Self::new(cell_ref, TargetCellImportError::UnknownIo(name.into()))
497    }
498
499    pub fn io_size_mismatch(cell_ref: CellRef, name: impl Into<String>) -> TargetImportError {
500        Self::new(cell_ref, TargetCellImportError::IoSizeMismatch(name.into()))
501    }
502}
503
504impl Display for TargetImportError {
505    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
506        write!(f, "error importing cell %{}: {}", self.cell_index, self.cause)
507    }
508}
509
510impl Error for TargetImportError {}
511
512#[derive(Debug, Clone)]
513pub struct UnknownTargetError(String);
514
515impl Display for UnknownTargetError {
516    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
517        write!(f, "unknown target {:?} (known targets:", self.0)?;
518        for target in REGISTRY.lock().unwrap().keys() {
519            write!(f, " {target}")?;
520        }
521        write!(f, ")")
522    }
523}
524
525impl Error for UnknownTargetError {}
526
527// I'm sorry! I'm sorry!! I'm trying to remove this but trait aliases aren't stable yet...
528#[allow(clippy::type_complexity)]
529static REGISTRY: Mutex<
530    BTreeMap<String, Box<dyn Fn(BTreeMap<String, String>) -> Result<Arc<dyn Target>, Box<dyn Error>> + Send>>,
531> = Mutex::new(BTreeMap::new());
532
533pub fn register_target(
534    name: impl Into<String>,
535    builder: impl Fn(BTreeMap<String, String>) -> Result<Arc<dyn Target>, Box<dyn Error>> + Send + 'static,
536) {
537    let mut registry = REGISTRY.lock().unwrap();
538    assert!(registry.insert(name.into(), Box::new(builder)).is_none());
539}
540
541pub fn create_target(name: &str, options: BTreeMap<String, String>) -> Result<Arc<dyn Target>, Box<dyn Error>> {
542    let registry = REGISTRY.lock().unwrap();
543    match registry.get(name).map(|builder| builder(options)) {
544        Some(target) => target,
545        None => Err(UnknownTargetError(name.into()))?,
546    }
547}