1use serde_derive::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::io::{Read, Write};
4
5#[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#[derive(Copy, Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Hash)]
18pub enum SpecialBit {
19 #[serde(rename = "0")]
21 _0,
22 #[serde(rename = "1")]
24 _1,
25 #[serde(rename = "x")]
27 X,
28 #[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#[derive(Copy, Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Hash)]
52#[serde(untagged)]
53pub enum BitVal {
54 N(usize),
56 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#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Hash)]
77#[serde(untagged)]
78pub enum AttributeVal {
79 N(usize),
81 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 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 None
107 } else if s
108 .find(|c| !(c == '0' || c == '1' || c == 'x' || c == 'z'))
109 .is_none()
110 {
111 None
113 } else {
114 if *s.as_bytes().last().unwrap() == b' ' {
115 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#[derive(Clone, Serialize, Deserialize, Debug, Default, Eq, PartialEq)]
143pub struct Netlist {
144 #[serde(default)]
146 pub creator: String,
147 #[serde(default)]
149 pub modules: HashMap<String, Module>,
150}
151
152#[derive(Clone, Serialize, Deserialize, Debug, Default, Eq, PartialEq)]
154pub struct Module {
155 #[serde(default)]
157 pub attributes: HashMap<String, AttributeVal>,
158 #[serde(default)]
160 pub parameter_default_values: HashMap<String, AttributeVal>,
161 #[serde(default)]
163 pub ports: HashMap<String, Port>,
164 #[serde(default)]
166 pub cells: HashMap<String, Cell>,
167 #[serde(default)]
169 pub memories: HashMap<String, Memory>,
170 #[serde(default)]
172 pub netnames: HashMap<String, Netname>,
173}
174
175#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
177pub struct Port {
178 pub direction: PortDirection,
180 pub bits: Vec<BitVal>,
182 #[serde(default)]
184 pub offset: usize,
185 #[serde(default)]
187 pub upto: usize,
188 #[serde(default)]
190 pub signed: usize,
191}
192
193#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
195pub struct Cell {
196 #[serde(default)]
198 pub hide_name: usize,
199 #[serde(rename = "type")]
201 pub cell_type: String,
202 #[serde(default)]
204 pub parameters: HashMap<String, AttributeVal>,
205 #[serde(default)]
207 pub attributes: HashMap<String, AttributeVal>,
208 #[serde(default)]
210 pub port_directions: HashMap<String, PortDirection>,
211 pub connections: HashMap<String, Vec<BitVal>>,
213}
214
215#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
217pub struct Memory {
218 #[serde(default)]
220 pub hide_name: usize,
221 #[serde(default)]
223 pub attributes: HashMap<String, AttributeVal>,
224 pub width: usize,
226 pub size: usize,
228 #[serde(default)]
230 pub start_offset: usize,
231}
232
233#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
235pub struct Netname {
236 #[serde(default)]
238 pub hide_name: usize,
239 pub bits: Vec<BitVal>,
241 #[serde(default)]
243 pub offset: usize,
244 #[serde(default)]
246 pub upto: usize,
247 #[serde(default)]
249 pub signed: usize,
250 #[serde(default)]
252 pub attributes: HashMap<String, AttributeVal>,
253}
254
255impl Netlist {
256 pub fn new(creator: &str) -> Self {
258 Self {
259 creator: creator.to_owned(),
260 modules: HashMap::new(),
261 }
262 }
263
264 pub fn from_reader<R: Read>(reader: R) -> Result<Netlist, serde_json::Error> {
266 serde_json::from_reader(reader)
267 }
268
269 pub fn from_slice(input: &[u8]) -> Result<Netlist, serde_json::Error> {
271 serde_json::from_slice(input)
272 }
273
274 pub fn to_string(&self) -> Result<String, serde_json::Error> {
276 serde_json::to_string(self)
277 }
278
279 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}