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