prjunnamed_yosys_json/
export.rs

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), // "to avoid confusion", as write_json claims
33        }))
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    // Yosys IR cannot express DFFs with both asynchronous and synchronous reset.
120    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                // The $alu cell isn't supported by `write_verilog`, so we have to pattern-match here.
239                match arg3.as_const() {
240                    Some(Trit::Zero) => {
241                        // no carry-in
242                        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                        // generic
256                        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; // skip default $out wire (init-less) creation
355            }
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            // Duplicating the cell metadata on the cell output net is the only way to get source locations
584            // to show up in nextpnr's timing reports.
585            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
594// Exporting a design to Yosys JSON can require modifying it if it has target cells that must be mapped to instances.
595pub 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, /*spaces=*/ 4)
604}