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 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 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 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 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 }
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 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}