yosys_netlist_json/
lib.rs

1use serde_derive::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::io::{Read, Write};
4
5/// Legal values for the direction of a port on a module
6#[derive(Copy, Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Hash)]
7pub enum PortDirection {
8    #[serde(rename = "input")]
9    Input,
10    #[serde(rename = "output")]
11    Output,
12    #[serde(rename = "inout")]
13    InOut,
14}
15
16/// Special constant bit values
17#[derive(Copy, Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Hash)]
18pub enum SpecialBit {
19    /// Constant 0
20    #[serde(rename = "0")]
21    _0,
22    /// Constant 1
23    #[serde(rename = "1")]
24    _1,
25    /// Constant X (invalid)
26    #[serde(rename = "x")]
27    X,
28    /// Constant Z (tri-state)
29    #[serde(rename = "z")]
30    Z,
31}
32
33#[cfg(feature = "slog")]
34impl slog::Value for SpecialBit {
35    fn serialize(
36        &self,
37        _record: &slog::Record,
38        key: slog::Key,
39        serializer: &mut dyn slog::Serializer,
40    ) -> slog::Result {
41        match self {
42            &SpecialBit::_0 => serializer.emit_str(key, "0"),
43            &SpecialBit::_1 => serializer.emit_str(key, "1"),
44            &SpecialBit::X => serializer.emit_str(key, "x"),
45            &SpecialBit::Z => serializer.emit_str(key, "z"),
46        }
47    }
48}
49
50/// A number representing a single bit of a wire
51#[derive(Copy, Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Hash)]
52#[serde(untagged)]
53pub enum BitVal {
54    /// An actual signal number
55    N(usize),
56    /// A special constant value
57    S(SpecialBit),
58}
59
60#[cfg(feature = "slog")]
61impl slog::Value for BitVal {
62    fn serialize(
63        &self,
64        record: &slog::Record,
65        key: slog::Key,
66        serializer: &mut dyn slog::Serializer,
67    ) -> slog::Result {
68        match self {
69            &BitVal::N(n) => serializer.emit_usize(key, n),
70            &BitVal::S(s) => s.serialize(record, key, serializer),
71        }
72    }
73}
74
75/// The value of an attribute/parameter
76#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Hash)]
77#[serde(untagged)]
78pub enum AttributeVal {
79    /// Numeric attribute value
80    N(usize),
81    /// String attribute value
82    S(String),
83}
84
85impl AttributeVal {
86    pub fn to_number(&self) -> Option<usize> {
87        match self {
88            &AttributeVal::N(n) => Some(n),
89            &AttributeVal::S(ref s) => {
90                // If it's an empty string, the value was zero
91                if s.len() == 0 {
92                    Some(0)
93                } else {
94                    usize::from_str_radix(s, 2).ok()
95                }
96            }
97        }
98    }
99
100    pub fn to_string_if_string(&self) -> Option<&str> {
101        match self {
102            &AttributeVal::N(_) => None,
103            &AttributeVal::S(ref s) => {
104                if s.len() == 0 {
105                    // If it's an empty string then it wasn't originally a string
106                    None
107                } else if s
108                    .find(|c| !(c == '0' || c == '1' || c == 'x' || c == 'z'))
109                    .is_none()
110                {
111                    // If it only contains 01xz, then it wasn't originally a string
112                    None
113                } else {
114                    if *s.as_bytes().last().unwrap() == b' ' {
115                        // If the last character is a space, drop it
116                        Some(s.split_at(s.len() - 1).0)
117                    } else {
118                        Some(s)
119                    }
120                }
121            }
122        }
123    }
124}
125
126#[cfg(feature = "slog")]
127impl slog::Value for AttributeVal {
128    fn serialize(
129        &self,
130        _record: &slog::Record,
131        key: slog::Key,
132        serializer: &mut dyn slog::Serializer,
133    ) -> slog::Result {
134        match self {
135            &AttributeVal::N(n) => serializer.emit_usize(key, n),
136            &AttributeVal::S(ref s) => serializer.emit_str(key, s),
137        }
138    }
139}
140
141/// Represents an entire .json file used by Yosys
142#[derive(Clone, Serialize, Deserialize, Debug, Default, Eq, PartialEq)]
143pub struct Netlist {
144    /// The program that created this file.
145    #[serde(default)]
146    pub creator: String,
147    /// A map from module names to module objects contained in this .json file
148    #[serde(default)]
149    pub modules: HashMap<String, Module>,
150}
151
152/// Represents one module in the Yosys hierarchy
153#[derive(Clone, Serialize, Deserialize, Debug, Default, Eq, PartialEq)]
154pub struct Module {
155    /// Module attributes (Verilog `(* attr *)`)
156    #[serde(default)]
157    pub attributes: HashMap<String, AttributeVal>,
158    /// Module parameter (Verilog `parameter`) default values
159    #[serde(default)]
160    pub parameter_default_values: HashMap<String, AttributeVal>,
161    /// Module ports (interfaces to other modules)
162    #[serde(default)]
163    pub ports: HashMap<String, Port>,
164    /// Module cells (objects inside this module)
165    #[serde(default)]
166    pub cells: HashMap<String, Cell>,
167    /// Module memories
168    #[serde(default)]
169    pub memories: HashMap<String, Memory>,
170    /// Module netnames (names of wires in this module)
171    #[serde(default)]
172    pub netnames: HashMap<String, Netname>,
173}
174
175/// Represents a port on a module
176#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
177pub struct Port {
178    /// Port direction
179    pub direction: PortDirection,
180    /// Bit value(s) representing the wire(s) connected to this port
181    pub bits: Vec<BitVal>,
182    /// Bit offset for mapping to HDL bit numbering
183    #[serde(default)]
184    pub offset: usize,
185    /// Whether or not HDL bit numbering is MSB-first
186    #[serde(default)]
187    pub upto: usize,
188    /// Whether or not HDL considers value signed
189    #[serde(default)]
190    pub signed: usize,
191}
192
193/// Represents a cell in a module
194#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
195pub struct Cell {
196    /// Indicates an internal/auto-generated name that starts with `$`
197    #[serde(default)]
198    pub hide_name: usize,
199    /// Name of the type of this cell
200    #[serde(rename = "type")]
201    pub cell_type: String,
202    /// Parameters specified on this cell
203    #[serde(default)]
204    pub parameters: HashMap<String, AttributeVal>,
205    /// Attributes specified on this cell
206    #[serde(default)]
207    pub attributes: HashMap<String, AttributeVal>,
208    /// The direction of the ports on this cell
209    #[serde(default)]
210    pub port_directions: HashMap<String, PortDirection>,
211    /// Bit value(s) representing the wire(s) connected to the inputs/outputs of this cell
212    pub connections: HashMap<String, Vec<BitVal>>,
213}
214
215/// Represents a memory in a module
216#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
217pub struct Memory {
218    /// Indicates an internal/auto-generated name that starts with `$`
219    #[serde(default)]
220    pub hide_name: usize,
221    /// Attributes for this memory
222    #[serde(default)]
223    pub attributes: HashMap<String, AttributeVal>,
224    /// Memory width
225    pub width: usize,
226    /// Memory size
227    pub size: usize,
228    /// Lowest valid memory address
229    #[serde(default)]
230    pub start_offset: usize,
231}
232
233/// Represents the name of a net in a module
234#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
235pub struct Netname {
236    /// Indicates an internal/auto-generated name that starts with `$`
237    #[serde(default)]
238    pub hide_name: usize,
239    /// Bit value(s) that should be given this name
240    pub bits: Vec<BitVal>,
241    /// Bit offset for mapping to HDL bit numbering
242    #[serde(default)]
243    pub offset: usize,
244    /// Whether or not HDL bit numbering is MSB-first
245    #[serde(default)]
246    pub upto: usize,
247    /// Whether or not HDL considers value signed
248    #[serde(default)]
249    pub signed: usize,
250    /// Attributes for this netname
251    #[serde(default)]
252    pub attributes: HashMap<String, AttributeVal>,
253}
254
255impl Netlist {
256    /// Create a new netlist
257    pub fn new(creator: &str) -> Self {
258        Self {
259            creator: creator.to_owned(),
260            modules: HashMap::new(),
261        }
262    }
263
264    /// Read netlist data from a reader
265    pub fn from_reader<R: Read>(reader: R) -> Result<Netlist, serde_json::Error> {
266        serde_json::from_reader(reader)
267    }
268
269    /// Read netlist data from a slice containing the bytes from a Yosys .json file
270    pub fn from_slice(input: &[u8]) -> Result<Netlist, serde_json::Error> {
271        serde_json::from_slice(input)
272    }
273
274    /// Serialize to a String
275    pub fn to_string(&self) -> Result<String, serde_json::Error> {
276        serde_json::to_string(self)
277    }
278
279    /// Serialize to a writer
280    pub fn to_writer<W: Write>(&self, writer: W) -> Result<(), serde_json::Error> {
281        serde_json::to_writer(writer, self)
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288
289    #[test]
290    fn super_empty_json() {
291        let result = Netlist::from_slice(
292            br#"
293            {}"#,
294        )
295        .unwrap();
296        assert_eq!(result.creator, "");
297        assert_eq!(result.modules.len(), 0);
298    }
299
300    #[test]
301    fn empty_json() {
302        let result = Netlist::from_slice(
303            br#"
304            {
305              "creator": "this is a test",
306              "modules": {
307              }
308            }"#,
309        )
310        .unwrap();
311        assert_eq!(result.creator, "this is a test");
312        assert_eq!(result.modules.len(), 0);
313    }
314
315    #[test]
316    fn empty_json_2() {
317        let result = Netlist::from_slice(
318            br#"
319            {
320              "modules": {
321              }
322            }"#,
323        )
324        .unwrap();
325        assert_eq!(result.creator, "");
326        assert_eq!(result.modules.len(), 0);
327    }
328
329    #[test]
330    fn bit_values_test() {
331        let result = Netlist::from_slice(
332            br#"
333            {
334              "modules": {
335                "mymodule": {
336                  "cells": {
337                    "mycell": {
338                      "type": "celltype",
339                      "connections": {
340                        "IN": [ "x", 0, "z", 234, "1", "0" ]
341                      }
342                    }
343                  }
344                }
345              }
346            }"#,
347        )
348        .unwrap();
349        assert_eq!(
350            result
351                .modules
352                .get("mymodule")
353                .unwrap()
354                .cells
355                .get("mycell")
356                .unwrap()
357                .connections
358                .get("IN")
359                .unwrap(),
360            &vec![
361                BitVal::S(SpecialBit::X),
362                BitVal::N(0),
363                BitVal::S(SpecialBit::Z),
364                BitVal::N(234),
365                BitVal::S(SpecialBit::_1),
366                BitVal::S(SpecialBit::_0)
367            ]
368        );
369    }
370
371    #[test]
372    #[should_panic]
373    fn invalid_bit_value_test() {
374        Netlist::from_slice(
375            br#"
376            {
377              "modules": {
378                "mymodule": {
379                  "cells": {
380                    "mycell": {
381                      "type": "celltype",
382                      "connections": {
383                        "IN": [ "w" ]
384                      }
385                    }
386                  }
387                }
388              }
389            }"#,
390        )
391        .unwrap();
392    }
393
394    #[test]
395    fn attribute_value_test() {
396        let result = Netlist::from_slice(
397            br#"
398            {
399              "modules": {
400                "mymodule": {
401                  "cells": {
402                    "mycell": {
403                      "type": "celltype",
404                      "parameters": {
405                        "testparam": 123
406                      },
407                      "connections": {}
408                    }
409                  }
410                }
411              }
412            }"#,
413        )
414        .unwrap();
415        assert_eq!(
416            result
417                .modules
418                .get("mymodule")
419                .unwrap()
420                .cells
421                .get("mycell")
422                .unwrap()
423                .parameters
424                .get("testparam")
425                .unwrap(),
426            &AttributeVal::N(123)
427        );
428    }
429
430    #[test]
431    #[should_panic]
432    fn invalid_attribute_value_test() {
433        Netlist::from_slice(
434            br#"
435            {
436              "modules": {
437                "mymodule": {
438                  "cells": {
439                    "mycell": {
440                      "type": "celltype",
441                      "parameters": {
442                        "testparam": [123]
443                      },
444                      "connections": {}
445                    }
446                  }
447                }
448              }
449            }"#,
450        )
451        .unwrap();
452    }
453
454    #[test]
455    fn integration_test() {
456        let result = Netlist::from_slice(
457            br#"{
458  "creator": "Yosys 0.14+51 (git sha1 286caa09b, gcc 9.3.0-13 -fPIC -Os)",
459  "modules": {
460    "test": {
461      "attributes": {
462        "cells_not_processed": "00000000000000000000000000000001",
463        "src": "test-for-json.v:1.1-12.10"
464      },
465      "parameter_default_values": {
466        "TESTPARAM": "00000000000000001010010001010101"
467      },
468      "ports": {
469        "a": {
470          "direction": "input",
471          "offset": 1,
472          "bits": [ 2, 3, 4, 5, 6, 7, 8, 9 ]
473        },
474        "b": {
475          "direction": "input",
476          "upto": 1,
477          "bits": [ 10, 11, 12, 13, 14, 15, 16, 17 ]
478        },
479        "o": {
480          "direction": "output",
481          "bits": [ 18, 19, 20, 21, 22, 23, 24, 25 ]
482        }
483      },
484      "cells": {
485        "$xor$test-for-json.v:10$1": {
486          "hide_name": 1,
487          "type": "$xor",
488          "parameters": {
489            "A_SIGNED": "00000000000000000000000000000000",
490            "A_WIDTH": "00000000000000000000000000001000",
491            "B_SIGNED": "00000000000000000000000000000000",
492            "B_WIDTH": "00000000000000000000000000001000",
493            "Y_WIDTH": "00000000000000000000000000001000"
494          },
495          "attributes": {
496            "src": "test-for-json.v:10.12-10.17"
497          },
498          "port_directions": {
499            "A": "input",
500            "B": "input",
501            "Y": "output"
502          },
503          "connections": {
504            "A": [ 2, 3, 4, 5, 6, 7, 8, 9 ],
505            "B": [ 10, 11, 12, 13, 14, 15, 16, 17 ],
506            "Y": [ 18, 19, 20, 21, 22, 23, 24, 25 ]
507          }
508        }
509      },
510      "memories": {
511        "testmemory": {
512          "hide_name": 0,
513          "attributes": {
514            "src": "test-for-json.v:8.12-8.22"
515          },
516          "width": 8,
517          "start_offset": 1,
518          "size": 1111
519        }
520      },
521      "netnames": {
522        "$xor$test-for-json.v:10$1_Y": {
523          "hide_name": 1,
524          "bits": [ 18, 19, 20, 21, 22, 23, 24, 25 ],
525          "attributes": {
526            "src": "test-for-json.v:10.12-10.17"
527          }
528        },
529        "a": {
530          "hide_name": 0,
531          "bits": [ 2, 3, 4, 5, 6, 7, 8, 9 ],
532          "offset": 1,
533          "attributes": {
534            "src": "test-for-json.v:2.17-2.18"
535          }
536        },
537        "b": {
538          "hide_name": 0,
539          "bits": [ 10, 11, 12, 13, 14, 15, 16, 17 ],
540          "upto": 1,
541          "attributes": {
542            "src": "test-for-json.v:3.17-3.18"
543          }
544        },
545        "o": {
546          "hide_name": 0,
547          "bits": [ 18, 19, 20, 21, 22, 23, 24, 25 ],
548          "attributes": {
549            "src": "test-for-json.v:4.18-4.19"
550          }
551        }
552      }
553    }
554  }
555}
556"#,
557        )
558        .unwrap();
559
560        assert_eq!(
561            result.creator,
562            "Yosys 0.14+51 (git sha1 286caa09b, gcc 9.3.0-13 -fPIC -Os)"
563        );
564        let mod_test = result.modules.get("test").unwrap();
565
566        assert_eq!(
567            mod_test
568                .parameter_default_values
569                .get("TESTPARAM")
570                .unwrap()
571                .to_number()
572                .unwrap(),
573            42069
574        );
575
576        assert_eq!(mod_test.ports.get("a").unwrap().offset, 1);
577        assert_eq!(mod_test.ports.get("b").unwrap().upto, 1);
578        assert_eq!(mod_test.ports.get("o").unwrap().offset, 0);
579        assert_eq!(mod_test.ports.get("o").unwrap().upto, 0);
580
581        assert_eq!(mod_test.memories.get("testmemory").unwrap().size, 1111);
582    }
583
584    #[test]
585    fn write_test() {
586        let netlist = Netlist::new("integration test");
587        let json = netlist.to_string().unwrap();
588
589        assert_eq!(json, r#"{"creator":"integration test","modules":{}}"#);
590    }
591}