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