1use jzon::JsonValue;
2use std::{cell::RefCell, collections::BTreeMap, io::Write};
3
4use crate::yosys::{self, CellDetails, MetadataValue, NetDetails, PortDetails};
5use prjunnamed_netlist::{
6 Cell, Const, ControlNet, Design, IoNet, IoValue, MemoryPortRelation, MetaItem, MetaItemRef, Net, Trit, Value,
7};
8
9struct Counter(usize);
10
11impl Counter {
12 fn advance(&mut self) -> usize {
13 let index = self.0;
14 self.0 += 1;
15 index
16 }
17}
18
19struct NetlistIndexerState {
20 map: BTreeMap<Net, usize>,
21 io_map: BTreeMap<IoNet, usize>,
22 next: Counter,
23}
24
25struct NetlistIndexer(RefCell<NetlistIndexerState>);
26
27impl NetlistIndexer {
28 fn new() -> NetlistIndexer {
29 NetlistIndexer(RefCell::new(NetlistIndexerState {
30 map: BTreeMap::new(),
31 io_map: BTreeMap::new(),
32 next: Counter(2), }))
34 }
35
36 fn net(&self, net: Net) -> yosys::Bit {
37 match net.as_const() {
38 Some(Trit::Undef) => yosys::Bit::Undef,
39 Some(Trit::Zero) => yosys::Bit::Zero,
40 Some(Trit::One) => yosys::Bit::One,
41 None => {
42 let state = &mut *self.0.borrow_mut();
43 yosys::Bit::Net(*state.map.entry(net).or_insert_with(|| state.next.advance()))
44 }
45 }
46 }
47
48 fn synthetic_net(&self) -> yosys::Bit {
49 yosys::Bit::Net(self.0.borrow_mut().next.advance())
50 }
51
52 fn value(&self, value: &Value) -> yosys::BitVector {
53 yosys::BitVector(value.iter().map(|n| self.net(n)).collect::<Vec<_>>())
54 }
55
56 fn synthetic_value(&self, size: usize) -> yosys::BitVector {
57 yosys::BitVector(std::iter::repeat_with(|| self.synthetic_net()).take(size).collect::<Vec<_>>())
58 }
59
60 fn io_net(&self, net: IoNet) -> yosys::Bit {
61 let state = &mut *self.0.borrow_mut();
62 yosys::Bit::Net(*state.io_map.entry(net).or_insert_with(|| state.next.advance()))
63 }
64
65 fn io_value(&self, value: &IoValue) -> yosys::BitVector {
66 yosys::BitVector(value.iter().map(|n| self.io_net(n)).collect::<Vec<_>>())
67 }
68}
69
70fn map_metadata(metadata: MetaItemRef) -> Vec<(String, yosys::MetadataValue)> {
71 let mut ys_attrs: Vec<(String, MetadataValue)> = Vec::new();
72 let mut ys_srcs = Vec::new();
73 for item in metadata.iter() {
74 match item.get() {
75 MetaItem::Source { file, start, end } => {
76 ys_srcs.push(format!(
77 "{}:{}.{}-{}.{}",
78 file.get(),
79 start.line + 1,
80 start.column + 1,
81 end.line + 1,
82 end.column + 1
83 ));
84 }
85 MetaItem::Attr { name, value } => {
86 ys_attrs.push((name.get().to_owned(), value.into()));
87 }
88 _ => (),
89 }
90 }
91 if !ys_srcs.is_empty() {
92 ys_attrs.push(("src".to_owned(), ys_srcs.join("|").into()));
93 }
94 ys_attrs
95}
96
97fn export_module(mut design: Design) -> yosys::Module {
98 let indexer = NetlistIndexer::new();
99 let mut ys_module = yosys::Module::new();
100
101 for cell_ref in design.iter_cells() {
103 if let Cell::Dff(flip_flop) = &*cell_ref.get() {
104 if flip_flop.has_clear() && flip_flop.has_reset() {
105 let mut flip_flop = flip_flop.clone();
106 flip_flop.unmap_reset(&design);
107 cell_ref.replace(Cell::Dff(flip_flop));
108 }
109 }
110 }
111 design.apply();
112
113 for (name, io_value) in design.iter_ios() {
114 ys_module.ports.add(name, PortDetails::new(yosys::PortDirection::Inout, indexer.io_value(&io_value)))
115 }
116
117 for cell_ref in design.iter_cells() {
118 let cell_index = cell_ref.debug_index();
119 let output = cell_ref.output();
120
121 let ys_cell_name = format!("${}", cell_index);
122
123 let ys_cell_unary = |module: &mut yosys::Module, ty: &str, a: &Value| {
124 CellDetails::new(ty)
125 .param("A_SIGNED", 0)
126 .param("A_WIDTH", a.len())
127 .param("Y_WIDTH", output.len())
128 .input("A", indexer.value(a))
129 .output("Y", indexer.value(&output))
130 .attrs(map_metadata(cell_ref.metadata()))
131 .add_to(&format!("${}", cell_index), module)
132 };
133
134 let ys_cell_binary = |module: &mut yosys::Module, ty: &str, a: &Value, b: &Value, signed: bool| {
135 CellDetails::new(ty)
136 .param("A_SIGNED", if signed { 1 } else { 0 })
137 .param("A_WIDTH", a.len())
138 .param("B_SIGNED", if signed { 1 } else { 0 })
139 .param("B_WIDTH", b.len())
140 .param("Y_WIDTH", output.len())
141 .input("A", indexer.value(a))
142 .input("B", indexer.value(b))
143 .output("Y", indexer.value(&output))
144 .attrs(map_metadata(cell_ref.metadata()))
145 .add_to(&format!("${}", cell_index), module)
146 };
147
148 let ys_shift_count = |module: &mut yosys::Module, a: &Value, stride: u32| -> yosys::BitVector {
149 if stride == 1 {
150 indexer.value(a)
151 } else if stride == 0 {
152 indexer.value(&Value::zero(1))
153 } else {
154 let stride_bits = stride.ilog2() + 1;
155 let stride = Const::from_uint(stride.into(), stride_bits as usize);
156 let result = indexer.synthetic_value(a.len() + stride.len());
157 CellDetails::new("$mul")
158 .param("A_SIGNED", 0)
159 .param("A_WIDTH", a.len())
160 .param("B_SIGNED", 0)
161 .param("B_WIDTH", stride.len())
162 .param("Y_WIDTH", result.len())
163 .input("A", indexer.value(a))
164 .input("B", indexer.value(&Value::from(stride)))
165 .output("Y", result.clone())
166 .attrs(map_metadata(cell_ref.metadata()))
167 .add_to(&format!("${}$stride", cell_index), module);
168 result
169 }
170 };
171
172 let ys_cell_shift = |module: &mut yosys::Module, ty: &str, a: &Value, b: &Value, stride: u32, signed: bool| {
173 let b = ys_shift_count(module, b, stride);
174 CellDetails::new(ty)
175 .param("A_SIGNED", if signed { 1 } else { 0 })
176 .param("A_WIDTH", a.len())
177 .param("B_SIGNED", 0)
178 .param("B_WIDTH", b.len())
179 .param("Y_WIDTH", output.len())
180 .input("A", indexer.value(a))
181 .input("B", b)
182 .output("Y", indexer.value(&output))
183 .attrs(map_metadata(cell_ref.metadata()))
184 .add_to(&format!("${}", cell_index), module)
185 };
186
187 let ys_control_net_pos = |module: &mut yosys::Module, not_name: &str, cnet: ControlNet| -> yosys::Bit {
188 match cnet {
189 ControlNet::Pos(net) => indexer.net(net),
190 ControlNet::Neg(net) => {
191 let result = indexer.synthetic_net();
192 CellDetails::new("$not")
193 .param("A_SIGNED", 0)
194 .param("A_WIDTH", 1)
195 .param("Y_WIDTH", output.len())
196 .input("A", indexer.value(&net.into()))
197 .output("Y", result)
198 .attrs(map_metadata(cell_ref.metadata()))
199 .add_to(not_name, module);
200 result
201 }
202 }
203 };
204
205 match &*cell_ref.get() {
206 Cell::Buf(arg) => ys_cell_unary(&mut ys_module, "$pos", arg),
207 Cell::Not(arg) => ys_cell_unary(&mut ys_module, "$not", arg),
208 Cell::And(arg1, arg2) => ys_cell_binary(&mut ys_module, "$and", arg1, arg2, false),
209 Cell::Or(arg1, arg2) => ys_cell_binary(&mut ys_module, "$or", arg1, arg2, false),
210 Cell::Xor(arg1, arg2) => ys_cell_binary(&mut ys_module, "$xor", arg1, arg2, false),
211 Cell::Mux(arg1, arg2, arg3) => CellDetails::new("$mux")
212 .param("WIDTH", output.len())
213 .input("A", indexer.value(arg3))
214 .input("B", indexer.value(arg2))
215 .input("S", indexer.net(*arg1))
216 .output("Y", indexer.value(&output))
217 .attrs(map_metadata(cell_ref.metadata()))
218 .add_to(&format!("${}", cell_index), &mut ys_module),
219 Cell::Adc(arg1, arg2, arg3) => {
220 match arg3.as_const() {
222 Some(Trit::Zero) => {
223 CellDetails::new("$add")
225 .param("A_SIGNED", 0)
226 .param("A_WIDTH", arg1.len())
227 .param("B_SIGNED", 0)
228 .param("B_WIDTH", arg2.len())
229 .param("Y_WIDTH", output.len())
230 .input("A", indexer.value(arg1))
231 .input("B", indexer.value(arg2))
232 .output("Y", indexer.value(&output))
233 .attrs(map_metadata(cell_ref.metadata()))
234 .add_to(&format!("${}", cell_index), &mut ys_module);
235 }
236 _ => {
237 let ys_a = Value::from(arg3).concat(arg1);
239 let ys_b = Value::from(Net::ONE).concat(arg2);
240 let ys_y = indexer.synthetic_value(1).concat(&indexer.value(&output));
241 CellDetails::new("$add")
242 .param("A_SIGNED", 0)
243 .param("A_WIDTH", 1 + arg1.len())
244 .param("B_SIGNED", 0)
245 .param("B_WIDTH", 1 + arg2.len())
246 .param("Y_WIDTH", 1 + output.len())
247 .input("A", indexer.value(&ys_a))
248 .input("B", indexer.value(&ys_b))
249 .output("Y", ys_y)
250 .attrs(map_metadata(cell_ref.metadata()))
251 .add_to(&format!("${}", cell_index), &mut ys_module);
252 }
253 }
254 }
255 Cell::Aig(arg1, arg2) => {
256 let arg1 = ys_control_net_pos(&mut ys_module, &format!("${}$not1", cell_index), *arg1);
257 let arg2 = ys_control_net_pos(&mut ys_module, &format!("${}$not2", cell_index), *arg2);
258 CellDetails::new("$and")
259 .param("A_SIGNED", 0)
260 .param("A_WIDTH", 1)
261 .param("B_SIGNED", 0)
262 .param("B_WIDTH", 1)
263 .param("Y_WIDTH", 1)
264 .input("A", arg1)
265 .input("B", arg2)
266 .output("Y", indexer.value(&output))
267 .attrs(map_metadata(cell_ref.metadata()))
268 .add_to(&format!("${}", cell_index), &mut ys_module)
269 }
270
271 Cell::Eq(arg1, arg2) => ys_cell_binary(&mut ys_module, "$eq", arg1, arg2, false),
272 Cell::ULt(arg1, arg2) => ys_cell_binary(&mut ys_module, "$lt", arg1, arg2, false),
273 Cell::SLt(arg1, arg2) => ys_cell_binary(&mut ys_module, "$lt", arg1, arg2, true),
274
275 Cell::Shl(arg1, arg2, stride) => ys_cell_shift(&mut ys_module, "$shl", arg1, arg2, *stride, false),
276 Cell::UShr(arg1, arg2, stride) => ys_cell_shift(&mut ys_module, "$shr", arg1, arg2, *stride, false),
277 Cell::SShr(arg1, arg2, stride) => ys_cell_shift(&mut ys_module, "$sshr", arg1, arg2, *stride, true),
278 Cell::XShr(arg1, arg2, stride) => ys_cell_shift(&mut ys_module, "$shiftx", arg1, arg2, *stride, false),
279
280 Cell::Mul(arg1, arg2) => ys_cell_binary(&mut ys_module, "$mul", arg1, arg2, false),
281 Cell::UDiv(arg1, arg2) => ys_cell_binary(&mut ys_module, "$div", arg1, arg2, false),
282 Cell::UMod(arg1, arg2) => ys_cell_binary(&mut ys_module, "$mod", arg1, arg2, false),
283 Cell::SDivTrunc(arg1, arg2) => ys_cell_binary(&mut ys_module, "$div", arg1, arg2, true),
284 Cell::SDivFloor(arg1, arg2) => ys_cell_binary(&mut ys_module, "$divfloor", arg1, arg2, true),
285 Cell::SModTrunc(arg1, arg2) => ys_cell_binary(&mut ys_module, "$mod", arg1, arg2, true),
286 Cell::SModFloor(arg1, arg2) => ys_cell_binary(&mut ys_module, "$modfloor", arg1, arg2, true),
287
288 Cell::Match { .. } => unimplemented!("match cells must be lowered first for Yosys JSON export"),
289 Cell::Assign { .. } => unimplemented!("assign cells must be lowered first for Yosys JSON export"),
290
291 Cell::Dff(flip_flop) => {
292 let ys_cell_type = match (
293 flip_flop.has_clear(),
294 flip_flop.has_reset(),
295 flip_flop.has_enable(),
296 flip_flop.reset_over_enable,
297 ) {
298 (true, true, _, _) => unreachable!(),
299 (true, false, false, _) => "$adff",
300 (true, false, true, _) => "$adffe",
301 (false, true, false, _) => "$sdff",
302 (false, true, true, false) => "$sdffce",
303 (false, true, true, true) => "$sdffe",
304 (false, false, false, _) => "$dff",
305 (false, false, true, _) => "$dffe",
306 };
307 let mut ys_cell = CellDetails::new(ys_cell_type);
308 if flip_flop.has_clear() {
309 ys_cell = ys_cell
310 .param("ARST_POLARITY", flip_flop.clear.is_positive())
311 .param("ARST_VALUE", flip_flop.clear_value.clone())
312 .input("ARST", indexer.net(flip_flop.clear.net()));
313 }
314 if flip_flop.has_reset() {
315 ys_cell = ys_cell
316 .param("SRST_POLARITY", flip_flop.reset.is_positive())
317 .param("SRST_VALUE", flip_flop.reset_value.clone())
318 .input("SRST", indexer.net(flip_flop.reset.net()));
319 }
320 if flip_flop.has_enable() {
321 ys_cell = ys_cell
322 .param("EN_POLARITY", flip_flop.enable.is_positive())
323 .input("EN", indexer.net(flip_flop.enable.net()));
324 }
325 ys_cell
326 .param("CLK_POLARITY", flip_flop.clock.is_positive())
327 .input("CLK", indexer.net(flip_flop.clock.net()))
328 .param("WIDTH", output.len())
329 .input("D", indexer.value(&flip_flop.data))
330 .output("Q", indexer.value(&output))
331 .attrs(map_metadata(cell_ref.metadata()))
332 .add_to(&ys_cell_name, &mut ys_module);
333 NetDetails::new(indexer.value(&output))
334 .attr("init", flip_flop.init_value.clone())
335 .add_to(&format!("{}$ff", ys_cell_name), &mut ys_module);
336 continue; }
338
339 Cell::Memory(memory) => {
340 let abits = memory
341 .write_ports
342 .iter()
343 .map(|port| port.addr.len() + port.wide_log2(memory))
344 .max()
345 .unwrap_or(0)
346 .max(
347 memory
348 .read_ports
349 .iter()
350 .map(|port| port.addr.len() + port.wide_log2(memory))
351 .max()
352 .unwrap_or(0),
353 );
354
355 let mut wr_ports = 0;
356 let mut wr_clk_polarity = Const::new();
357 let mut wr_wide_continuation = Const::new();
358 let mut wr_addr = Value::new();
359 let mut wr_data = Value::new();
360 let mut wr_clk = Value::new();
361 let mut wr_en = Value::new();
362 let mut write_port_indices = vec![];
363 for (port_index, port) in memory.write_ports.iter().enumerate() {
364 let wide_log2 = port.wide_log2(memory);
365 for index in 0..(1 << wide_log2) {
366 let addr = Value::from(Const::from_uint(index, wide_log2)).concat(&port.addr).zext(abits);
367 wr_clk.extend([port.clock.net()]);
368 wr_addr.extend(&addr);
369 wr_clk_polarity.push(port.clock.is_positive());
370 wr_wide_continuation.push(index != 0);
371 write_port_indices.push(port_index);
372 }
373 wr_data.extend(&port.data);
374 wr_en.extend(&port.mask);
375 wr_ports += 1 << wide_log2;
376 }
377 if wr_ports == 0 {
378 wr_clk_polarity.push(false);
379 wr_wide_continuation.push(false);
380 }
381
382 let mut rd_ports = 0;
383 let mut rd_clk_enable = Const::new();
384 let mut rd_clk_polarity = Const::new();
385 let mut rd_transparency_mask = Const::new();
386 let mut rd_collision_x_mask = Const::new();
387 let mut rd_wide_continuation = Const::new();
388 let mut rd_ce_over_srst = Const::new();
389 let mut rd_arst_value = Const::new();
390 let mut rd_srst_value = Const::new();
391 let mut rd_init_value = Const::new();
392 let mut rd_clk = Value::new();
393 let mut rd_en = yosys::BitVector(vec![]);
394 let mut rd_arst = yosys::BitVector(vec![]);
395 let mut rd_srst = yosys::BitVector(vec![]);
396 let mut rd_addr = Value::new();
397 for (port_index, port) in memory.read_ports.iter().enumerate() {
398 let wide_log2 = port.wide_log2(memory);
399 for index in 0..(1 << wide_log2) {
400 let addr = Value::from(Const::from_uint(index, wide_log2)).concat(&port.addr).zext(abits);
401 rd_addr.extend(&addr);
402 if let Some(ref flip_flop) = port.flip_flop {
403 rd_clk.extend([flip_flop.clock.net()]);
404 rd_clk_enable.push(true);
405 rd_clk_polarity.push(flip_flop.clock.is_positive());
406 rd_ce_over_srst.push(!flip_flop.reset_over_enable);
407 for &write_port_index in &write_port_indices {
408 let (trans, col_x) = if flip_flop.clock == memory.write_ports[write_port_index].clock {
409 match flip_flop.relations[write_port_index] {
410 MemoryPortRelation::Undefined => (false, true),
411 MemoryPortRelation::ReadBeforeWrite => (false, false),
412 MemoryPortRelation::Transparent => (true, false),
413 }
414 } else {
415 (false, false)
416 };
417 rd_transparency_mask.push(trans);
418 rd_collision_x_mask.push(col_x);
419 }
420 } else {
421 rd_clk.extend([Net::UNDEF]);
422 rd_clk_enable.push(false);
423 rd_clk_polarity.push(Trit::Undef);
424 rd_ce_over_srst.push(Trit::Undef);
425 rd_transparency_mask.extend(Const::undef(wr_ports));
426 rd_collision_x_mask.extend(Const::undef(wr_ports));
427 }
428 rd_wide_continuation.push(index != 0);
429 }
430 if let Some(ref flip_flop) = port.flip_flop {
431 rd_arst_value.extend(&flip_flop.clear_value);
432 rd_srst_value.extend(&flip_flop.reset_value);
433 rd_init_value.extend(&flip_flop.init_value);
434 let enable = ys_control_net_pos(
435 &mut ys_module,
436 &format!("${cell_index}$rd{port_index}$en$not"),
437 flip_flop.enable,
438 );
439 let clear = ys_control_net_pos(
440 &mut ys_module,
441 &format!("${cell_index}$rd{port_index}$arst$not"),
442 flip_flop.clear,
443 );
444 let reset = ys_control_net_pos(
445 &mut ys_module,
446 &format!("${cell_index}$rd{port_index}$srst$not"),
447 flip_flop.reset,
448 );
449 rd_en = rd_en.concat(&yosys::BitVector(vec![enable; 1 << wide_log2]));
450 rd_srst = rd_srst.concat(&yosys::BitVector(vec![reset; 1 << wide_log2]));
451 rd_arst = rd_arst.concat(&yosys::BitVector(vec![clear; 1 << wide_log2]));
452 } else {
453 rd_arst_value.extend(Const::undef(port.data_len));
454 rd_srst_value.extend(Const::undef(port.data_len));
455 rd_init_value.extend(Const::undef(port.data_len));
456 rd_en = rd_en.concat(&yosys::BitVector(vec![yosys::Bit::One; 1 << wide_log2]));
457 rd_srst = rd_srst.concat(&yosys::BitVector(vec![yosys::Bit::Zero; 1 << wide_log2]));
458 rd_arst = rd_arst.concat(&yosys::BitVector(vec![yosys::Bit::Zero; 1 << wide_log2]));
459 }
460 rd_ports += 1 << wide_log2;
461 }
462 if rd_ports == 0 {
463 rd_clk_enable.push(false);
464 rd_clk_polarity.push(false);
465 rd_wide_continuation.push(false);
466 rd_ce_over_srst.push(false);
467 rd_arst_value.push(false);
468 rd_srst_value.push(false);
469 rd_init_value.push(false);
470 }
471 if rd_ports == 0 || wr_ports == 0 {
472 rd_transparency_mask.push(false);
473 rd_collision_x_mask.push(false);
474 }
475
476 CellDetails::new("$mem_v2")
477 .param("MEMID", format!("${}$mem", cell_index))
478 .param("OFFSET", 0)
479 .param("SIZE", memory.depth)
480 .param("WIDTH", memory.width)
481 .param("ABITS", abits)
482 .param("INIT", memory.init_value.clone())
483 .param("WR_PORTS", wr_ports)
484 .param("WR_CLK_ENABLE", Const::ones(wr_ports.max(1)))
485 .param("WR_CLK_POLARITY", wr_clk_polarity)
486 .param("WR_PRIORITY_MASK", Const::zero(wr_ports.max(1)))
487 .param("WR_WIDE_CONTINUATION", wr_wide_continuation)
488 .input("WR_ADDR", indexer.value(&wr_addr))
489 .input("WR_DATA", indexer.value(&wr_data))
490 .input("WR_CLK", indexer.value(&wr_clk))
491 .input("WR_EN", indexer.value(&wr_en))
492 .param("RD_PORTS", rd_ports)
493 .param("RD_CLK_ENABLE", rd_clk_enable)
494 .param("RD_CLK_POLARITY", rd_clk_polarity)
495 .param("RD_TRANSPARENCY_MASK", rd_transparency_mask)
496 .param("RD_COLLISION_X_MASK", rd_collision_x_mask)
497 .param("RD_WIDE_CONTINUATION", rd_wide_continuation)
498 .param("RD_CE_OVER_SRST", rd_ce_over_srst)
499 .param("RD_ARST_VALUE", rd_arst_value)
500 .param("RD_SRST_VALUE", rd_srst_value)
501 .param("RD_INIT_VALUE", rd_init_value)
502 .input("RD_CLK", indexer.value(&rd_clk))
503 .input("RD_EN", rd_en)
504 .input("RD_ARST", rd_arst)
505 .input("RD_SRST", rd_srst)
506 .input("RD_ADDR", indexer.value(&rd_addr))
507 .output("RD_DATA", indexer.value(&output))
508 .add_to(&format!("${}", cell_index), &mut ys_module);
509 }
510
511 Cell::IoBuf(io_buffer) => {
512 let ys_enable =
513 ys_control_net_pos(&mut ys_module, &format!("${}$en$not", cell_index), io_buffer.enable);
514 let ys_attrs = map_metadata(cell_ref.metadata());
515 CellDetails::new("$tribuf")
516 .param("WIDTH", output.len())
517 .input("A", indexer.value(&io_buffer.output))
518 .input("EN", ys_enable)
519 .output("Y", indexer.io_value(&io_buffer.io))
520 .attrs(ys_attrs.clone())
521 .add_to(&format!("${}", cell_index), &mut ys_module);
522 CellDetails::new("$pos")
523 .param("A_SIGNED", 0)
524 .param("A_WIDTH", io_buffer.io.len())
525 .param("Y_WIDTH", output.len())
526 .input("A", indexer.io_value(&io_buffer.io))
527 .output("Y", indexer.value(&output))
528 .attrs(ys_attrs)
529 .add_to(&format!("${}$pos", cell_index), &mut ys_module);
530 }
531
532 Cell::Other(instance) => {
533 let mut ys_cell = CellDetails::new(&instance.kind);
534 for (name, value) in instance.params.iter() {
535 ys_cell = ys_cell.param(name, value);
536 }
537 for (name, value) in instance.inputs.iter() {
538 ys_cell = ys_cell.input(name, indexer.value(value));
539 }
540 for (name, value_range) in instance.outputs.iter() {
541 ys_cell = ys_cell.output(name, indexer.value(&Value::from(&output[value_range.clone()])));
542 }
543 for (name, io_value) in instance.ios.iter() {
544 ys_cell = ys_cell.inout(name, indexer.io_value(io_value));
545 }
546 ys_cell.attrs(map_metadata(cell_ref.metadata())).add_to(&ys_cell_name, &mut ys_module);
547 }
548 Cell::Target(_target_cell) => {
549 unimplemented!("target cells must be converted to instances first for Yosys JSON export")
550 }
551
552 Cell::Input(port_name, _size) => {
553 ys_module.ports.add(port_name, PortDetails::new(yosys::PortDirection::Input, indexer.value(&output)))
554 }
555 Cell::Output(port_name, value) => {
556 ys_module.ports.add(port_name, PortDetails::new(yosys::PortDirection::Output, indexer.value(value)))
557 }
558 Cell::Name(name, value) | Cell::Debug(name, value) => ys_module.netnames.add(
559 &name.replace(" ", "."),
560 NetDetails::new(indexer.value(value)).attrs(map_metadata(cell_ref.metadata())).attr("hdlname", name),
561 ),
562 };
563
564 if output.len() > 0 {
565 NetDetails::new(indexer.value(&output))
568 .attrs(map_metadata(cell_ref.metadata()))
569 .add_to(&format!("{}$out", ys_cell_name), &mut ys_module);
570 }
571 }
572
573 ys_module
574}
575
576pub fn export(writer: &mut impl Write, designs: BTreeMap<String, Design>) -> std::io::Result<()> {
578 let mut ys_modules = BTreeMap::new();
579 for (name, design) in designs {
580 ys_modules.insert(name, export_module(design));
581 }
582 let ys_design = yosys::Design { creator: "prjunnamed".into(), modules: ys_modules.into() };
583
584 let json = JsonValue::from(ys_design);
585 json.write_pretty(writer, 4)
586}