prjunnamed_yosys_json/
import.rs

1use std::{
2    collections::{btree_map, BTreeMap, BTreeSet},
3    sync::Arc,
4};
5
6use prjunnamed_netlist::{
7    Const, ControlNet, Design, FlipFlop, Instance, IoBuffer, IoNet, IoValue, Net, ParamValue, Target, Trit, Value,
8    Memory, MemoryWritePort, MemoryReadPort, MemoryReadFlipFlop, MemoryPortRelation, WithMetadataGuard, SourcePosition,
9    MetaItem, MetaItemRef,
10};
11
12use crate::yosys;
13
14#[derive(Debug)]
15pub enum Error {
16    Io(std::io::Error),
17    Json(jzon::Error),
18    Syntax(yosys::SyntaxError),
19    MetaDataType(yosys::MetadataTypeError),
20    Semantic,
21    Unsupported(String),
22}
23
24impl From<std::io::Error> for Error {
25    fn from(error: std::io::Error) -> Self {
26        Self::Io(error)
27    }
28}
29
30impl From<jzon::Error> for Error {
31    fn from(error: jzon::Error) -> Self {
32        Self::Json(error)
33    }
34}
35
36impl From<yosys::SyntaxError> for Error {
37    fn from(error: yosys::SyntaxError) -> Self {
38        Self::Syntax(error)
39    }
40}
41
42impl From<yosys::MetadataTypeError> for Error {
43    fn from(error: yosys::MetadataTypeError) -> Self {
44        Self::MetaDataType(error)
45    }
46}
47
48impl std::fmt::Display for Error {
49    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
50        match self {
51            Error::Io(error) => write!(f, "I/O error: {}", error),
52            Error::Json(error) => write!(f, "JSON parse error: {}", error),
53            Error::Syntax(error) => write!(f, "{}", error),
54            Error::MetaDataType(error) => write!(f, "{}", error),
55            Error::Semantic => write!(f, "semantic error"),
56            Error::Unsupported(feature) => write!(f, "unsupported feature: {}", feature),
57        }
58    }
59}
60
61impl std::error::Error for Error {}
62
63struct ModuleImporter<'a> {
64    module: &'a yosys::Module,
65    design_io_ports: &'a BTreeSet<(&'a str, &'a str)>,
66    io_nets: BTreeMap<usize, IoNet>,
67    driven_nets: BTreeSet<usize>,
68    nets: BTreeMap<usize, Net>,
69    init: BTreeMap<usize, Trit>,
70    design: &'a Design,
71    scope_top: MetaItemRef<'a>,
72}
73
74impl ModuleImporter<'_> {
75    fn drive(&mut self, bits: &yosys::BitVector, value: impl Into<Value>) {
76        let value = value.into();
77        assert_eq!(bits.len(), value.len());
78        for (&bit, net) in bits.iter().zip(value.iter()) {
79            let yosys::Bit::Net(ynet) = bit else { unreachable!() };
80            assert!(!self.driven_nets.contains(&ynet));
81            match self.nets.entry(ynet) {
82                btree_map::Entry::Occupied(e) => {
83                    self.design.replace_net(*e.get(), net);
84                }
85                btree_map::Entry::Vacant(e) => {
86                    e.insert(net);
87                }
88            }
89            if let Some(&io_net) = self.io_nets.get(&ynet) {
90                self.design.add_iobuf(IoBuffer {
91                    enable: ControlNet::Pos(Net::ONE),
92                    output: Value::from(net),
93                    io: IoValue::from(io_net),
94                });
95            }
96            self.driven_nets.insert(ynet);
97        }
98    }
99
100    fn port_drive(&mut self, cell: &yosys::CellDetails, port: &str, value: impl Into<Value>) {
101        self.drive(cell.connections.get(port).unwrap(), value);
102    }
103
104    fn value(&mut self, bits: &yosys::BitVector) -> Value {
105        let mut nets = vec![];
106        for &bit in bits.iter() {
107            let net = match bit {
108                yosys::Bit::HiZ | yosys::Bit::Undef => Net::UNDEF,
109                yosys::Bit::Zero => Net::ZERO,
110                yosys::Bit::One => Net::ONE,
111                yosys::Bit::Net(ynet) => *self.nets.entry(ynet).or_insert_with(|| self.design.add_void(1).unwrap_net()),
112            };
113            nets.push(net);
114        }
115        Value::from_iter(nets)
116    }
117
118    fn io_value(&self, bits: &yosys::BitVector) -> IoValue {
119        let mut io = vec![];
120        for &bit in bits.iter() {
121            let yosys::Bit::Net(ynet) = bit else { unreachable!() };
122            let io_net = *self.io_nets.get(&ynet).unwrap();
123            io.push(io_net);
124        }
125        IoValue::from_iter(io)
126    }
127
128    fn port_value(&mut self, cell: &yosys::CellDetails, port: &str) -> Value {
129        self.value(cell.connections.get(port).unwrap())
130    }
131
132    fn port_control_net(&mut self, cell: &yosys::CellDetails, port: &str) -> ControlNet {
133        let net = self.port_value(cell, port).unwrap_net();
134        let polarity = cell.parameters.get(&format!("{port}_POLARITY")).unwrap().as_bool().unwrap();
135        if polarity { ControlNet::Pos(net) } else { ControlNet::Neg(net) }
136    }
137
138    fn init_value(&self, cell: &yosys::CellDetails, port: &str) -> Const {
139        let bits = cell.connections.get(port).unwrap();
140        let mut res = vec![];
141        for &bit in bits.iter() {
142            let yosys::Bit::Net(ynet) = bit else { unreachable!() };
143            res.push(match self.init.get(&ynet) {
144                None => Trit::Undef,
145                Some(&val) => val,
146            });
147        }
148        Const::from_iter(res)
149    }
150
151    fn value_ext(&mut self, cell: &yosys::CellDetails, port: &str, width: usize, signed: bool) -> Value {
152        if signed { self.port_value(cell, port).sext(width) } else { self.port_value(cell, port).zext(width) }
153    }
154
155    fn handle_init(&mut self) -> Result<(), Error> {
156        for net_details in self.module.netnames.0.values() {
157            let Some(init) = net_details.attributes.get("init") else { continue };
158            let init = init.as_const()?;
159            if init.len() != net_details.bits.len() {
160                Err(yosys::MetadataTypeError)?;
161            }
162            for (&bit, binit) in net_details.bits.iter().zip(init.iter()) {
163                if binit == Trit::Undef {
164                    continue;
165                }
166                let yosys::Bit::Net(ynet) = bit else { Err(yosys::MetadataTypeError)? };
167                match self.init.entry(ynet) {
168                    btree_map::Entry::Occupied(e) => {
169                        assert_eq!(*e.get(), binit);
170                    }
171                    btree_map::Entry::Vacant(e) => {
172                        e.insert(binit);
173                    }
174                }
175            }
176        }
177        Ok(())
178    }
179
180    fn handle_ports(&mut self) -> Result<(), Error> {
181        // determine set of nets that need to be IOs from cell port connections.
182        let mut io_net_conns = BTreeSet::new();
183        for cell in self.module.cells.0.values() {
184            for (port_name, bits) in cell.connections.iter() {
185                if self.design_io_ports.contains(&(&cell.type_, port_name)) {
186                    for &bit in bits.iter() {
187                        let yosys::Bit::Net(net) = bit else { return Err(Error::Semantic) };
188                        io_net_conns.insert(net);
189                    }
190                }
191            }
192        }
193
194        // now create all IoValues we're going to need.
195        let mut io_ports = BTreeSet::new();
196        for (port_name, port) in self.module.ports.iter() {
197            let mut is_inout = port.direction == yosys::PortDirection::Inout;
198            for &bit in port.bits.iter() {
199                if let yosys::Bit::Net(net) = bit {
200                    if io_net_conns.contains(&net) {
201                        is_inout = true;
202                    }
203                }
204            }
205            if is_inout {
206                let ioval = self.design.add_io(port_name.clone(), port.bits.0.len());
207                for (index, &bit) in port.bits.iter().enumerate() {
208                    let yosys::Bit::Net(net) = bit else { return Err(Error::Semantic) };
209                    match self.io_nets.entry(net) {
210                        btree_map::Entry::Occupied(_) => {
211                            return Err(Error::Semantic);
212                        }
213                        btree_map::Entry::Vacant(e) => {
214                            e.insert(ioval[index]);
215                        }
216                    }
217                }
218                io_ports.insert(port_name);
219            }
220        }
221        // handle all other ports
222        for (port_name, port) in self.module.ports.iter() {
223            if io_ports.contains(port_name) {
224                continue;
225            }
226            match port.direction {
227                yosys::PortDirection::Input => {
228                    let value = self.design.add_input(port_name, port.bits.len());
229                    self.drive(&port.bits, value);
230                }
231                yosys::PortDirection::Output => {
232                    let value = self.value(&port.bits);
233                    self.design.add_output(port_name, value);
234                }
235                yosys::PortDirection::Inout => unreachable!(),
236            }
237        }
238        Ok(())
239    }
240
241    fn handle_names(&mut self) -> Result<(), Error> {
242        for (name, details) in self.module.netnames.iter() {
243            if details.hide_name {
244                continue;
245            }
246            if details
247                .bits
248                .iter()
249                .any(|bit| if let yosys::Bit::Net(net) = bit { self.io_nets.contains_key(net) } else { false })
250            {
251                continue;
252            }
253            let value = self.value(&details.bits);
254            self.design.add_name(name, value);
255        }
256        Ok(())
257    }
258
259    fn use_attribute_metadata<'a>(
260        &self,
261        design: &'a Design,
262        name: &str,
263        attributes: &yosys::Metadata,
264    ) -> WithMetadataGuard<'a> {
265        fn parse_position(text: &str) -> Option<SourcePosition> {
266            if let Some((line, column)) = text.split_once(".") {
267                Some(SourcePosition { line: line.parse::<u32>().ok()?, column: column.parse::<u32>().ok()? })
268            } else {
269                Some(SourcePosition { line: text.parse::<u32>().ok()?, column: 0 })
270            }
271        }
272
273        let mut items: Vec<MetaItemRef> = Vec::new();
274
275        // TODO: add a counter for failed src parsing, and emit warning if nonzero at the end of import
276        if let Some(yosys::MetadataValue::String(string)) = attributes.get("src") {
277            for piece in string.split("|") {
278                let Some((filename, loc_text)) = piece.rsplit_once(":") else {
279                    continue;
280                };
281                let (start_text, end_text) = loc_text.split_once("-").unwrap_or((loc_text, loc_text));
282                let Some(start) = parse_position(start_text) else {
283                    continue;
284                };
285                let Some(end) = parse_position(end_text) else {
286                    continue;
287                };
288
289                let meta_filename = design.add_metadata_string(filename);
290                items.push(design.add_metadata_item(&MetaItem::Source { file: meta_filename, start, end }));
291            }
292        }
293
294        let meta_none = MetaItemRef::from_iter(design, []);
295
296        if let Some(yosys::MetadataValue::String(string)) = attributes.get("hdlname") {
297            let mut scope = self.scope_top;
298            let mut parts = string.split(' ');
299            if let Some(last) = parts.next_back() {
300                for name in parts {
301                    let name = design.add_metadata_string(name);
302                    scope = design.add_metadata_item(&MetaItem::NamedScope { name, source: meta_none, parent: scope });
303                }
304                let name = design.add_metadata_string(last);
305                let ident = design.add_metadata_item(&MetaItem::Ident { name, scope });
306                items.push(ident);
307            }
308        } else if !name.starts_with('$') {
309            let name = design.add_metadata_string(name);
310            let ident = design.add_metadata_item(&MetaItem::Ident { name, scope: self.scope_top });
311            items.push(ident);
312        }
313
314        design.use_metadata(MetaItemRef::from_iter(design, items))
315    }
316
317    fn handle_cell(&mut self, name: &str, cell: &yosys::CellDetails) -> Result<(), Error> {
318        let _guard = self.use_attribute_metadata(&self.design, name, &cell.attributes);
319
320        match &cell.type_[..] {
321            "$not" | "$pos" | "$neg" => {
322                let a_width = cell.parameters.get("A_WIDTH").unwrap().as_i32()? as usize;
323                let y_width = cell.parameters.get("Y_WIDTH").unwrap().as_i32()? as usize;
324                let width = std::cmp::max(a_width, y_width);
325                let a_signed = cell.parameters.get("A_SIGNED").unwrap().as_bool()?;
326                let a = self.value_ext(cell, "A", width, a_signed);
327                let value = match &cell.type_[..] {
328                    "$not" => self.design.add_not(a),
329                    "$pos" => a,
330                    "$neg" => {
331                        let value = self.design.add_not(a);
332                        self.design.add_adc(value, Value::zero(width), Net::ONE)[..width].into()
333                    }
334                    _ => unreachable!(),
335                };
336                self.port_drive(cell, "Y", &value[..y_width]);
337            }
338            "$reduce_and" | "$reduce_or" | "$reduce_xor" | "$reduce_xnor" | "$reduce_bool" | "$logic_not" => {
339                let width = cell.parameters.get("Y_WIDTH").unwrap().as_i32()? as usize;
340                let a = self.port_value(cell, "A");
341                let net = match &cell.type_[..] {
342                    "$reduce_and" => self.design.add_eq(Value::ones(a.len()), a),
343                    "$reduce_or" | "$reduce_bool" => self.design.add_ne(Value::zero(a.len()), a),
344                    "$reduce_xor" => {
345                        let mut net = Net::ZERO;
346                        for bit in &a {
347                            net = self.design.add_xor1(net, bit);
348                        }
349                        net
350                    }
351                    "$reduce_xnor" => {
352                        let mut net = Net::ONE;
353                        for bit in &a {
354                            net = self.design.add_xor1(net, bit);
355                        }
356                        net
357                    }
358                    "$logic_not" => self.design.add_eq(Value::zero(a.len()), a),
359                    _ => unreachable!(),
360                };
361                let mut value = Value::from(net);
362                if width == 0 {
363                    value = Value::new();
364                } else if width > 1 {
365                    value = value.zext(width);
366                }
367                self.port_drive(cell, "Y", value);
368            }
369            "$and" | "$or" | "$xor" | "$xnor" | "$add" | "$sub" | "$mul" | "$div" | "$mod" | "$divfloor"
370            | "$modfloor" => {
371                let a_width = cell.parameters.get("A_WIDTH").unwrap().as_i32()? as usize;
372                let b_width = cell.parameters.get("B_WIDTH").unwrap().as_i32()? as usize;
373                let y_width = cell.parameters.get("Y_WIDTH").unwrap().as_i32()? as usize;
374                let width = std::cmp::max(std::cmp::max(a_width, b_width), y_width);
375                let a_signed = cell.parameters.get("A_SIGNED").unwrap().as_bool()?;
376                let b_signed = cell.parameters.get("B_SIGNED").unwrap().as_bool()?;
377                assert_eq!(a_signed, b_signed);
378                let a = self.value_ext(cell, "A", width, a_signed);
379                let b = self.value_ext(cell, "B", width, b_signed);
380                let value = match &cell.type_[..] {
381                    "$and" => self.design.add_and(a, b),
382                    "$or" => self.design.add_or(a, b),
383                    "$xor" => self.design.add_xor(a, b),
384                    "$xnor" => {
385                        let val = self.design.add_xor(a, b);
386                        self.design.add_not(val)
387                    }
388                    "$add" => self.design.add_adc(a, b, Net::ZERO)[..width].into(),
389                    "$sub" => {
390                        let inv_b = self.design.add_not(b);
391                        self.design.add_adc(a, inv_b, Net::ONE)[..width].into()
392                    }
393                    "$mul" => self.design.add_mul(a, b),
394                    "$div" | "$divfloor" if !a_signed => self.design.add_udiv(a, b),
395                    "$mod" | "$modfloor" if !a_signed => self.design.add_umod(a, b),
396                    "$div" => self.design.add_sdiv_trunc(a, b),
397                    "$mod" => self.design.add_smod_trunc(a, b),
398                    "$divfloor" => self.design.add_sdiv_floor(a, b),
399                    "$modfloor" => self.design.add_smod_floor(a, b),
400                    _ => unreachable!(),
401                };
402                self.port_drive(cell, "Y", &value[..y_width]);
403            }
404            "$alu" => {
405                let a_width = cell.parameters.get("A_WIDTH").unwrap().as_i32()? as usize;
406                let b_width = cell.parameters.get("B_WIDTH").unwrap().as_i32()? as usize;
407                let y_width = cell.parameters.get("Y_WIDTH").unwrap().as_i32()? as usize;
408                let width = std::cmp::max(std::cmp::max(a_width, b_width), y_width);
409                let bi = self.port_value(cell, "BI").unwrap_net();
410                let ci = self.port_value(cell, "CI").unwrap_net();
411                let a_signed = cell.parameters.get("A_SIGNED").unwrap().as_bool()?;
412                let b_signed = cell.parameters.get("B_SIGNED").unwrap().as_bool()?;
413                let a = self.value_ext(cell, "A", width, a_signed);
414                let b = self.value_ext(cell, "B", width, b_signed);
415                let b_inv = self.design.add_not(&b);
416                let b = self.design.add_mux(bi, b_inv, &b);
417                let x = self.design.add_xor(&a, &b);
418                self.port_drive(cell, "X", &x[..y_width]);
419                let y = self.design.add_adc(&a, &b, ci);
420                self.port_drive(cell, "Y", &y[..y_width]);
421                let xor = self.design.add_xor(&a[1..], &b[1..]);
422                let co = self.design.add_xor(xor, &y[1..width]).concat(y[width]);
423                self.port_drive(cell, "CO", &co[..y_width]);
424            }
425            "$shl" | "$sshl" | "$shr" | "$sshr" | "$shift" | "$shiftx" => {
426                let a_width = cell.parameters.get("A_WIDTH").unwrap().as_i32()? as usize;
427                let y_width = cell.parameters.get("Y_WIDTH").unwrap().as_i32()? as usize;
428                let width = std::cmp::max(a_width, y_width);
429                let a_signed = cell.parameters.get("A_SIGNED").unwrap().as_bool()?;
430                let b_signed = cell.parameters.get("B_SIGNED").unwrap().as_bool()?;
431                let mut a = self.port_value(cell, "A");
432                if a.len() < width {
433                    if cell.type_ == "$shiftx" {
434                        let a_width = a.len();
435                        a = a.concat(Value::undef(width - a_width))
436                    } else if a_signed {
437                        a = a.sext(width);
438                    } else {
439                        a = a.zext(width);
440                    }
441                }
442                let b = self.port_value(cell, "B");
443                let value = match &cell.type_[..] {
444                    "$shl" | "$sshl" => self.design.add_shl(a, b, 1),
445                    "$shr" => self.design.add_ushr(a, b, 1),
446                    "$sshr" => {
447                        if a_signed {
448                            self.design.add_sshr(a, b, 1)
449                        } else {
450                            self.design.add_ushr(a, b, 1)
451                        }
452                    }
453                    "$shift" | "$shiftx" => {
454                        let val_shr = if cell.type_ == "$shiftx" {
455                            self.design.add_xshr(&a, &b, 1)
456                        } else if a_signed {
457                            self.design.add_sshr(&a, &b, 1)
458                        } else {
459                            self.design.add_ushr(&a, &b, 1)
460                        };
461                        if b_signed {
462                            let b_inv = self.design.add_not(&b);
463                            let b_neg =
464                                Value::from(&self.design.add_adc(Value::zero(b.len()), &b_inv, Net::ONE)[..b.len()]);
465                            let val_shl = self.design.add_shl(&a, b_neg, 1);
466                            self.design.add_mux(b[b.len() - 1], val_shl, val_shr)
467                        } else {
468                            val_shr
469                        }
470                    }
471                    _ => unreachable!(),
472                };
473                self.port_drive(cell, "Y", &value[..y_width]);
474            }
475            "$lt" | "$le" | "$gt" | "$ge" | "$eq" | "$ne" => {
476                let y_width = cell.parameters.get("Y_WIDTH").unwrap().as_i32()? as usize;
477                let a_width = cell.parameters.get("A_WIDTH").unwrap().as_i32()? as usize;
478                let b_width = cell.parameters.get("B_WIDTH").unwrap().as_i32()? as usize;
479                let a_signed = cell.parameters.get("A_SIGNED").unwrap().as_bool()?;
480                let b_signed = cell.parameters.get("B_SIGNED").unwrap().as_bool()?;
481                let width = std::cmp::max(a_width, b_width);
482                assert_eq!(a_signed, b_signed);
483                let a = self.value_ext(cell, "A", width, a_signed);
484                let b = self.value_ext(cell, "B", width, b_signed);
485                let (mut net, inv) = match &cell.type_[..] {
486                    "$lt" if !a_signed => (self.design.add_ult(a, b), false),
487                    "$gt" if !a_signed => (self.design.add_ult(b, a), false),
488                    "$le" if !a_signed => (self.design.add_ult(b, a), true),
489                    "$ge" if !a_signed => (self.design.add_ult(a, b), true),
490                    "$lt" if a_signed => (self.design.add_slt(a, b), false),
491                    "$gt" if a_signed => (self.design.add_slt(b, a), false),
492                    "$le" if a_signed => (self.design.add_slt(b, a), true),
493                    "$ge" if a_signed => (self.design.add_slt(a, b), true),
494                    "$eq" => (self.design.add_eq(a, b), false),
495                    "$ne" => (self.design.add_eq(a, b), true),
496                    _ => unreachable!(),
497                };
498                if inv {
499                    net = self.design.add_not1(net);
500                }
501                let mut value = Value::from(net);
502                if y_width == 0 {
503                    value = Value::new();
504                } else if y_width > 1 {
505                    value = value.zext(width);
506                }
507                self.port_drive(cell, "Y", value);
508            }
509            "$logic_and" | "$logic_or" => {
510                let y_width = cell.parameters.get("Y_WIDTH").unwrap().as_i32()? as usize;
511                let a = self.port_value(cell, "A");
512                let a = self.design.add_eq(Value::zero(a.len()), a);
513                let a = self.design.add_not(a);
514                let b = self.port_value(cell, "B");
515                let b = self.design.add_eq(Value::zero(b.len()), b);
516                let b = self.design.add_not(b);
517                let mut value = match &cell.type_[..] {
518                    "$logic_and" => self.design.add_and(a, b),
519                    "$logic_or" => self.design.add_or(a, b),
520                    _ => unreachable!(),
521                };
522                if y_width == 0 {
523                    value = Value::new();
524                } else if y_width > 1 {
525                    value = value.zext(y_width);
526                }
527                self.port_drive(cell, "Y", value);
528            }
529            "$mux" => {
530                let a = self.port_value(cell, "A");
531                let b = self.port_value(cell, "B");
532                let s = self.port_value(cell, "S");
533                assert_eq!(a.len(), b.len());
534                let y = self.design.add_mux(s.unwrap_net(), b, a);
535                self.port_drive(cell, "Y", y);
536            }
537            "$bmux" => {
538                let mut value = self.port_value(cell, "A");
539                let sel = self.port_value(cell, "S");
540                for s in sel.iter().rev() {
541                    assert_eq!(value.len() % 2, 0);
542                    let lo = Value::from(&value[..(value.len() / 2)]);
543                    let hi = Value::from(&value[(value.len() / 2)..]);
544                    value = self.design.add_mux(s, hi, lo);
545                }
546                self.port_drive(cell, "Y", value);
547            }
548            "$pmux" => {
549                let mut value = self.port_value(cell, "A");
550                let b = self.port_value(cell, "B");
551                let sel = self.port_value(cell, "S");
552                assert_eq!(b.len(), value.len() * sel.len());
553                for (i, s) in sel.iter().enumerate() {
554                    value = self.design.add_mux(s, &b[(i * value.len())..((i + 1) * value.len())], value);
555                }
556                self.port_drive(cell, "Y", value);
557            }
558            "$tribuf" => {
559                let output = self.port_value(cell, "A");
560                let enable = self.port_value(cell, "EN");
561                let enable = ControlNet::Pos(enable.unwrap_net());
562                let bits = cell.connections.get("Y").unwrap();
563                let io = self.io_value(bits);
564                let value = self.design.add_iobuf(IoBuffer { output, enable, io });
565                for (&bit, net) in bits.iter().zip(value.iter()) {
566                    let yosys::Bit::Net(ynet) = bit else { unreachable!() };
567                    assert!(!self.driven_nets.contains(&ynet));
568                    match self.nets.entry(ynet) {
569                        btree_map::Entry::Occupied(e) => {
570                            self.design.replace_net(*e.get(), net);
571                        }
572                        btree_map::Entry::Vacant(e) => {
573                            e.insert(net);
574                        }
575                    }
576                    self.driven_nets.insert(ynet);
577                }
578            }
579            "$dff" | "$dffe" | "$adff" | "$adffe" | "$sdff" | "$sdffe" | "$sdffce" => {
580                let data = self.port_value(cell, "D");
581                let enable = if cell.connections.contains_key("EN") {
582                    self.port_control_net(cell, "EN")
583                } else {
584                    ControlNet::Pos(Net::ONE)
585                };
586                let (clear, clear_value) = if cell.connections.contains_key("ARST") {
587                    (self.port_control_net(cell, "ARST"), cell.parameters.get("ARST_VALUE").unwrap().as_const()?)
588                } else {
589                    (ControlNet::Pos(Net::ZERO), Const::undef(data.len()))
590                };
591                let (reset, reset_value) = if cell.connections.contains_key("SRST") {
592                    (self.port_control_net(cell, "SRST"), cell.parameters.get("SRST_VALUE").unwrap().as_const()?)
593                } else {
594                    (ControlNet::Pos(Net::ZERO), Const::undef(data.len()))
595                };
596                let clock = self.port_control_net(cell, "CLK");
597                let init_value = self.init_value(cell, "Q");
598                let q = self.design.add_dff(FlipFlop {
599                    data,
600                    clock,
601                    enable,
602                    reset,
603                    reset_over_enable: cell.type_ != "$sdffce",
604                    clear,
605                    init_value,
606                    reset_value,
607                    clear_value,
608                });
609                self.port_drive(cell, "Q", q);
610            }
611            "$mem_v2" => {
612                let offset = usize::try_from(cell.parameters.get("OFFSET").unwrap().as_i32()?).unwrap();
613                let size = usize::try_from(cell.parameters.get("SIZE").unwrap().as_i32()?).unwrap();
614                let depth = offset + size;
615                let abits = usize::try_from(cell.parameters.get("ABITS").unwrap().as_i32()?).unwrap();
616                let width = usize::try_from(cell.parameters.get("WIDTH").unwrap().as_i32()?).unwrap();
617                let mut init_value = cell.parameters.get("INIT").unwrap().as_const().unwrap();
618                assert_eq!(init_value.len(), size * width);
619                init_value = Const::undef(offset * width).concat(init_value);
620
621                let mut memory = Memory { depth, width, init_value, write_ports: vec![], read_ports: vec![] };
622
623                let wr_ports = usize::try_from(cell.parameters.get("WR_PORTS").unwrap().as_i32()?).unwrap();
624                let wr_clk_enable = cell.parameters.get("WR_CLK_ENABLE").unwrap().as_const().unwrap();
625                let wr_clk_polarity = cell.parameters.get("WR_CLK_POLARITY").unwrap().as_const().unwrap();
626                let wr_priority_mask = cell.parameters.get("WR_PRIORITY_MASK").unwrap().as_const().unwrap();
627                let wr_wide_continuation = cell.parameters.get("WR_WIDE_CONTINUATION").unwrap().as_const().unwrap();
628                let wr_clk = self.port_value(cell, "WR_CLK");
629                let wr_en = self.port_value(cell, "WR_EN");
630                let wr_addr = self.port_value(cell, "WR_ADDR");
631                let wr_data = self.port_value(cell, "WR_DATA");
632                let mut wr_port_indices = vec![];
633                if wr_ports != 0 {
634                    assert!(wr_priority_mask.iter().all(|x| x == Trit::Zero));
635                    assert!(wr_clk_enable.iter().all(|x| x == Trit::One));
636                }
637                let mut index = 0;
638                while index < wr_ports {
639                    let mut end_index = index + 1;
640                    while end_index < wr_ports && wr_wide_continuation[end_index] == Trit::One {
641                        end_index += 1;
642                    }
643                    let wide = end_index - index;
644                    let wide_log2 = wide.ilog2() as usize;
645                    assert!(wide.is_power_of_two());
646                    wr_port_indices.push(index);
647                    let clock = if wr_clk_polarity[index] == Trit::One {
648                        ControlNet::Pos(wr_clk[index])
649                    } else {
650                        ControlNet::Neg(wr_clk[index])
651                    };
652                    let addr = wr_addr.slice(index * abits..(index + 1) * abits).slice(wide_log2..);
653                    let data = wr_data.slice(index * width..end_index * width);
654                    let mask = wr_en.slice(index * width..end_index * width);
655                    memory.write_ports.push(MemoryWritePort { clock, addr, data, mask });
656                    index = end_index;
657                }
658
659                let rd_ports = usize::try_from(cell.parameters.get("RD_PORTS").unwrap().as_i32()?).unwrap();
660                let rd_clk_enable = cell.parameters.get("RD_CLK_ENABLE").unwrap().as_const().unwrap();
661                let rd_clk_polarity = cell.parameters.get("RD_CLK_POLARITY").unwrap().as_const().unwrap();
662                let rd_transparency_mask = cell.parameters.get("RD_TRANSPARENCY_MASK").unwrap().as_const().unwrap();
663                let rd_collision_x_mask = cell.parameters.get("RD_COLLISION_X_MASK").unwrap().as_const().unwrap();
664                let rd_wide_continuation = cell.parameters.get("RD_WIDE_CONTINUATION").unwrap().as_const().unwrap();
665                let rd_ce_over_srst = cell.parameters.get("RD_CE_OVER_SRST").unwrap().as_const().unwrap();
666                let rd_arst_value = cell.parameters.get("RD_ARST_VALUE").unwrap().as_const().unwrap();
667                let rd_srst_value = cell.parameters.get("RD_SRST_VALUE").unwrap().as_const().unwrap();
668                let rd_init_value = cell.parameters.get("RD_INIT_VALUE").unwrap().as_const().unwrap();
669                let rd_clk = self.port_value(cell, "RD_CLK");
670                let rd_en = self.port_value(cell, "RD_EN");
671                let rd_srst = self.port_value(cell, "RD_SRST");
672                let rd_arst = self.port_value(cell, "RD_ARST");
673                let rd_addr = self.port_value(cell, "RD_ADDR");
674                let mut index = 0;
675                while index < rd_ports {
676                    let mut end_index = index + 1;
677                    while end_index < wr_ports && rd_wide_continuation[end_index] == Trit::One {
678                        end_index += 1;
679                    }
680                    let wide = end_index - index;
681                    let wide_log2 = wide.ilog2() as usize;
682                    assert!(wide.is_power_of_two());
683                    let flip_flop = if rd_clk_enable[index] == Trit::One {
684                        let clock = if rd_clk_polarity[index] == Trit::One {
685                            ControlNet::Pos(rd_clk[index])
686                        } else {
687                            ControlNet::Neg(rd_clk[index])
688                        };
689                        let mut relations = vec![];
690                        for (new_index, &orig_index) in wr_port_indices.iter().enumerate() {
691                            let mask_index = index * wr_ports + orig_index;
692                            let relation = if memory.write_ports[new_index].clock != clock
693                                || rd_collision_x_mask[mask_index] == Trit::One
694                            {
695                                MemoryPortRelation::Undefined
696                            } else if rd_transparency_mask[mask_index] == Trit::One {
697                                MemoryPortRelation::Transparent
698                            } else {
699                                MemoryPortRelation::ReadBeforeWrite
700                            };
701                            relations.push(relation);
702                        }
703                        Some(MemoryReadFlipFlop {
704                            clock,
705                            clear: rd_arst[index].into(),
706                            reset: rd_srst[index].into(),
707                            enable: rd_en[index].into(),
708                            reset_over_enable: rd_ce_over_srst[index] != Trit::One,
709                            clear_value: rd_arst_value.slice(index * width..end_index * width),
710                            reset_value: rd_srst_value.slice(index * width..end_index * width),
711                            init_value: rd_init_value.slice(index * width..end_index * width),
712                            relations,
713                        })
714                    } else {
715                        None
716                    };
717                    let addr = rd_addr.slice(index * abits..(index + 1) * abits).slice(wide_log2..);
718                    let data_len = wide * width;
719                    memory.read_ports.push(MemoryReadPort { addr, data_len, flip_flop });
720                    index = end_index;
721                }
722
723                let mem_out = self.design.add_memory(memory);
724                self.port_drive(cell, "RD_DATA", mem_out);
725            }
726            "$scopeinfo" => {
727                // not quite yet
728            }
729            "$memrd" | "$memwr" | "$meminit" | "$memrd_v2" | "$memwr_v2" | "$meminit_v2" => {
730                return Err(Error::Unsupported(format!(
731                    "{} cell; run the Yosys `memory_collect` pass before writing JSON",
732                    cell.type_
733                )));
734            }
735            _ => {
736                if cell.type_.starts_with('$') {
737                    return Err(Error::Unsupported(format!("{} cell", cell.type_)));
738                }
739                // instance
740                let mut out_bits = vec![];
741                let mut next_out = 0;
742                let mut inputs = BTreeMap::new();
743                let mut outputs = BTreeMap::new();
744                let mut ios = BTreeMap::new();
745                for (name, bits) in cell.connections.iter() {
746                    let direction = *cell.port_directions.get(name).unwrap();
747                    if self.design_io_ports.contains(&(&cell.type_, name)) || direction == yosys::PortDirection::Inout {
748                        ios.insert(name.clone(), self.io_value(bits));
749                    } else if direction == yosys::PortDirection::Output {
750                        let range = next_out..(next_out + bits.len());
751                        next_out += bits.len();
752                        out_bits.push((bits, range.clone()));
753                        outputs.insert(name.clone(), range);
754                    } else {
755                        inputs.insert(name.clone(), self.value(bits));
756                    }
757                }
758                let mut parameters = BTreeMap::new();
759                for (name, val) in cell.parameters.iter() {
760                    let val = match val {
761                        yosys::MetadataValue::Const(val) => ParamValue::Const(val.clone()),
762                        yosys::MetadataValue::String(val) => ParamValue::String(val.clone()),
763                    };
764                    parameters.insert(name.clone(), val);
765                }
766                let output = self.design.add_other(Instance {
767                    kind: cell.type_.strip_prefix('\\').unwrap_or(cell.type_.as_str()).to_string(),
768                    params: parameters,
769                    inputs,
770                    outputs,
771                    ios,
772                });
773                for (bits, range) in out_bits {
774                    self.drive(bits, &output[range]);
775                }
776            }
777        }
778        Ok(())
779    }
780
781    fn handle_undriven_nets(&mut self) {
782        for (&ynet, &io_net) in &self.io_nets {
783            if self.driven_nets.contains(&ynet) {
784                continue;
785            }
786            let Some(&net) = self.nets.get(&ynet) else { continue };
787            self.design.replace_net(
788                net,
789                self.design
790                    .add_iobuf(IoBuffer {
791                        output: Value::undef(1),
792                        enable: ControlNet::Pos(Net::ZERO),
793                        io: io_net.into(),
794                    })
795                    .unwrap_net(),
796            );
797            self.driven_nets.insert(ynet);
798        }
799        for (&ynet, &net) in &self.nets {
800            if self.driven_nets.contains(&ynet) {
801                continue;
802            }
803            self.design.replace_net(net, Net::UNDEF);
804        }
805    }
806}
807
808fn import_module(
809    target: Option<Arc<dyn Target>>,
810    module: &yosys::Module,
811    design_io_ports: &BTreeSet<(&str, &str)>,
812) -> Result<Option<Design>, Error> {
813    if let Some(val) = module.attributes.get("blackbox") {
814        if val.as_bool()? {
815            return Ok(None);
816        }
817    }
818
819    let mut design = Design::with_target(target);
820    let meta_none = MetaItemRef::from_iter(&design, []);
821    let scope_top = design.add_metadata_item(&MetaItem::NamedScope {
822        name: design.add_metadata_string("top"),
823        source: meta_none,
824        parent: meta_none,
825    });
826
827    let mut importer = ModuleImporter {
828        module,
829        design_io_ports,
830        io_nets: BTreeMap::new(),
831        driven_nets: BTreeSet::new(),
832        nets: BTreeMap::new(),
833        init: BTreeMap::new(),
834        design: &design,
835        scope_top,
836    };
837
838    importer.handle_init()?;
839    importer.handle_ports()?;
840    importer.handle_names()?;
841    for (name, cell) in &module.cells.0 {
842        importer.handle_cell(name, cell)?;
843    }
844    importer.handle_undriven_nets();
845    design.compact();
846
847    Ok(Some(design))
848}
849
850fn index_io_ports(design: &yosys::Design) -> Result<BTreeSet<(&str, &str)>, Error> {
851    let mut io_ports: BTreeSet<(&str, &str)> = BTreeSet::new();
852    io_ports.insert(("$tribuf", "Y"));
853    for (mod_name, module) in design.modules.iter() {
854        for (port_name, port) in module.ports.iter() {
855            if port.direction == yosys::PortDirection::Inout {
856                io_ports.insert((mod_name, port_name));
857                continue;
858            }
859            let Some(net_details) = module.netnames.get(port_name) else { continue };
860            if let Some(val) = net_details.attributes.get("iopad_external_pin") {
861                if val.as_bool()? == true {
862                    io_ports.insert((mod_name, port_name));
863                }
864            }
865        }
866    }
867    Ok(io_ports)
868}
869
870pub fn import(
871    target: Option<Arc<dyn Target>>,
872    reader: &mut impl std::io::Read,
873) -> Result<BTreeMap<String, Design>, Error> {
874    let mut text = String::new();
875    reader.read_to_string(&mut text)?;
876    let json = jzon::parse(text.as_str())?;
877    let yosys_design = yosys::Design::try_from(json)?;
878
879    let io_ports = index_io_ports(&yosys_design)?;
880    let mut designs = BTreeMap::new();
881    for (name, module) in yosys_design.modules.iter() {
882        if let Some(design) = import_module(target.clone(), module, &io_ports)? {
883            designs.insert(name.clone(), design);
884        }
885    }
886    Ok(designs)
887}