pyadi-jif 0.1.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. adijif/__init__.py +32 -0
  2. adijif/adijif.py +1 -0
  3. adijif/cli.py +21 -0
  4. adijif/clocks/__init__.py +10 -0
  5. adijif/clocks/ad9523.py +321 -0
  6. adijif/clocks/ad9523_1_bf.py +91 -0
  7. adijif/clocks/ad9528.py +444 -0
  8. adijif/clocks/ad9528_bf.py +70 -0
  9. adijif/clocks/ad9545.py +553 -0
  10. adijif/clocks/clock.py +153 -0
  11. adijif/clocks/hmc7044.py +558 -0
  12. adijif/clocks/hmc7044_bf.py +68 -0
  13. adijif/clocks/ltc6952.py +624 -0
  14. adijif/clocks/ltc6952_bf.py +67 -0
  15. adijif/clocks/ltc6953.py +509 -0
  16. adijif/common.py +70 -0
  17. adijif/converters/__init__.py +3 -0
  18. adijif/converters/ad9081.py +679 -0
  19. adijif/converters/ad9081_dp.py +206 -0
  20. adijif/converters/ad9081_util.py +124 -0
  21. adijif/converters/ad9084.py +588 -0
  22. adijif/converters/ad9084_dp.py +111 -0
  23. adijif/converters/ad9084_draw.py +203 -0
  24. adijif/converters/ad9084_util.py +365 -0
  25. adijif/converters/ad9144.py +316 -0
  26. adijif/converters/ad9144_bf.py +44 -0
  27. adijif/converters/ad9680.py +201 -0
  28. adijif/converters/ad9680_bf.py +43 -0
  29. adijif/converters/ad9680_draw.py +184 -0
  30. adijif/converters/adc.py +83 -0
  31. adijif/converters/adrv9009.py +426 -0
  32. adijif/converters/adrv9009_bf.py +43 -0
  33. adijif/converters/adrv9009_util.py +89 -0
  34. adijif/converters/converter.py +399 -0
  35. adijif/converters/dac.py +85 -0
  36. adijif/converters/resources/AD9084_JTX_JRX.xlsx +0 -0
  37. adijif/converters/resources/ad9081_JRx_204B.csv +180 -0
  38. adijif/converters/resources/ad9081_JRx_204C.csv +411 -0
  39. adijif/converters/resources/ad9081_JTx_204B.csv +1488 -0
  40. adijif/converters/resources/ad9081_JTx_204C.csv +1064 -0
  41. adijif/converters/resources/full_rx_mode_table_ad9081.csv +1904 -0
  42. adijif/converters/resources/full_tx_mode_table_ad9081.csv +994 -0
  43. adijif/d2/__init__.py +26 -0
  44. adijif/d2/d2lib.h +81 -0
  45. adijif/draw.py +498 -0
  46. adijif/fpgas/__init__.py +1 -0
  47. adijif/fpgas/fpga.py +64 -0
  48. adijif/fpgas/xilinx/__init__.py +1143 -0
  49. adijif/fpgas/xilinx/bf.py +101 -0
  50. adijif/fpgas/xilinx/pll.py +232 -0
  51. adijif/fpgas/xilinx/sevenseries.py +531 -0
  52. adijif/fpgas/xilinx/ultrascaleplus.py +485 -0
  53. adijif/fpgas/xilinx/xilinx_draw.py +516 -0
  54. adijif/gekko_trans.py +295 -0
  55. adijif/jesd.py +760 -0
  56. adijif/plls/__init__.py +3 -0
  57. adijif/plls/adf4030.py +259 -0
  58. adijif/plls/adf4371.py +419 -0
  59. adijif/plls/adf4382.py +581 -0
  60. adijif/plls/pll.py +103 -0
  61. adijif/solvers.py +54 -0
  62. adijif/sys/__init__.py +1 -0
  63. adijif/sys/s_plls.py +185 -0
  64. adijif/system.py +567 -0
  65. adijif/system_draw.py +65 -0
  66. adijif/types.py +151 -0
  67. adijif/utils.py +191 -0
  68. pyadi_jif-0.1.0.dist-info/METADATA +62 -0
  69. pyadi_jif-0.1.0.dist-info/RECORD +73 -0
  70. pyadi_jif-0.1.0.dist-info/WHEEL +6 -0
  71. pyadi_jif-0.1.0.dist-info/licenses/AUTHORS.rst +13 -0
  72. pyadi_jif-0.1.0.dist-info/licenses/LICENSE +277 -0
  73. pyadi_jif-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,516 @@
1
+ """Drawing features for Xilinx FPGA designs."""
2
+
3
+ from typing import Dict, List
4
+
5
+ from adijif.converters.converter import converter as Converter
6
+ from adijif.draw import Layout, Node
7
+
8
+
9
+ class xilinx_draw:
10
+ """Xilinx drawing features."""
11
+
12
+ def _init_diagram(self) -> None:
13
+ """Initialize the diagram for a Xilinx FPGA alone."""
14
+ self.ic_diagram_node = None
15
+
16
+ self.ic_diagram_node = Node(self.name)
17
+
18
+ # Add generic transceiver since we don't know what is used until later
19
+ transceiver = Node("Transceiver", ntype="transceiver")
20
+ self.ic_diagram_node.add_child(transceiver)
21
+
22
+ # Add Link layer JESD204 core
23
+ jesd204_link = Node("JESD204-Link-IP", ntype="ip")
24
+ self.ic_diagram_node.add_child(jesd204_link)
25
+
26
+ # Add Transport Layer JESD204 core
27
+ jesd204_transport = Node("JESD204-Transport-IP", ntype="ip")
28
+ self.ic_diagram_node.add_child(jesd204_transport)
29
+
30
+ # Add Application layer JESD204 core
31
+ jesd204_application = Node("JESD204-Application-IP", ntype="ip")
32
+ self.ic_diagram_node.add_child(jesd204_application)
33
+
34
+ # Add connections
35
+ self.ic_diagram_node.add_connection({"from": transceiver, "to": jesd204_link})
36
+ self.ic_diagram_node.add_connection(
37
+ {"from": jesd204_link, "to": jesd204_transport}
38
+ )
39
+ self.ic_diagram_node.add_connection(
40
+ {"from": jesd204_transport, "to": jesd204_application, "type": "data"}
41
+ )
42
+
43
+ def _draw_phy(self, config: Dict, converter: Converter = None) -> tuple:
44
+ """Draw the PHY layer for the Xilinx FPGA.
45
+
46
+ Args:
47
+ config (Dict): Configuration dictionary
48
+ converter (Converter): Converter object (optional)
49
+
50
+ Returns:
51
+ tuple: Tuple of in_c, out_c, connect_to_input
52
+
53
+ Raises:
54
+ Exception: If unknown out_clk_select
55
+ """
56
+ # cfg = {
57
+ # "clocks": {"FPGA_REF": 500000000.0, "LINK_OUT_REF": 125000000.0},
58
+ # "fpga": {
59
+ # "type": "cpll",
60
+ # "m": 2,
61
+ # "d": 1,
62
+ # "n1": 5,
63
+ # "n2": 4,
64
+ # "vco": 5000000000.0,
65
+ # "sys_clk_select": "XCVR_CPLL",
66
+ # "progdiv": 40.0,
67
+ # "out_clk_select": "XCVR_PROGDIV_CLK",
68
+ # "separate_device_clock_required": 1,
69
+ # "transport_samples_per_clock": 8,
70
+ # },
71
+ # }
72
+
73
+ if converter:
74
+ name = f"{self.name}_{converter.name.upper()}_ref_clk"
75
+ ref_in_rate = config["clocks"][name]
76
+ else:
77
+ ref_in_rate = config["clocks"]["FPGA_REF"]
78
+
79
+ phy = Node("JESD204-PHY-IP", ntype="phy")
80
+ self.ic_diagram_node.add_child(phy)
81
+
82
+ # PLL
83
+ if config["fpga"]["type"] == "cpll":
84
+ cpll = Node("CPLL", ntype="cpll")
85
+ phy.add_child(cpll)
86
+
87
+ # Put stuff in CPLL
88
+ m = Node("M", ntype="divider")
89
+ cpll.add_child(m)
90
+ m.value = config["fpga"]["m"]
91
+
92
+ pfd = Node("PFD", ntype="phase-frequency-detector")
93
+ cpll.add_child(pfd)
94
+ cpll.add_connection({"from": m, "to": pfd, "rate": ref_in_rate})
95
+
96
+ cp = Node("CP", ntype="charge-pump")
97
+ cpll.add_child(cp)
98
+ cpll.add_connection({"from": pfd, "to": cp})
99
+
100
+ lpf = Node("LPF", ntype="loop-filter")
101
+ cpll.add_child(lpf)
102
+ cpll.add_connection({"from": cp, "to": lpf})
103
+
104
+ vco = Node("VCO", ntype="vco")
105
+ cpll.add_child(vco)
106
+ cpll.add_connection({"from": lpf, "to": vco})
107
+
108
+ d = Node("D", ntype="divider")
109
+ cpll.add_child(d)
110
+ d.value = config["fpga"]["d"]
111
+ cpll.add_connection({"from": vco, "to": d, "rate": config["fpga"]["vco"]})
112
+
113
+ n1 = Node("N1", ntype="divider")
114
+ cpll.add_child(n1)
115
+ n1.value = config["fpga"]["n1"]
116
+ cpll.add_connection({"from": vco, "to": n1})
117
+
118
+ n2 = Node("N2", ntype="divider")
119
+ cpll.add_child(n2)
120
+ n2.value = config["fpga"]["n2"]
121
+ cpll.add_connection({"from": n1, "to": n2})
122
+ cpll.add_connection({"from": n2, "to": pfd})
123
+
124
+ transceiver = Node("Transceiver", ntype="transceiver")
125
+ phy.add_child(transceiver)
126
+ phy.add_connection({"from": d, "to": transceiver})
127
+
128
+ # Define common in/out
129
+ in_c = m
130
+ xcvr_out = d
131
+ xcvr_out_rate = config["fpga"]["vco"] / config["fpga"]["d"]
132
+
133
+ else:
134
+
135
+ qpll = Node("QPLL", ntype="qpll")
136
+ phy.add_child(qpll)
137
+
138
+ # Put stuff in QPLL
139
+ m = Node("M", ntype="divider")
140
+ m.value = config["fpga"]["m"]
141
+ qpll.add_child(m)
142
+
143
+ pfd = Node("PFD", ntype="phase-frequency-detector")
144
+ qpll.add_child(pfd)
145
+
146
+ cp = Node("CP", ntype="charge-pump")
147
+ qpll.add_child(cp)
148
+
149
+ lpf = Node("LPF", ntype="loop-filter")
150
+ qpll.add_child(lpf)
151
+
152
+ vco = Node("VCO", ntype="vco")
153
+ qpll.add_child(vco)
154
+
155
+ n = Node("N", ntype="divider")
156
+ n.value = config["fpga"]["n"]
157
+ qpll.add_child(n)
158
+
159
+ # d2 = Node("/2", ntype="divider")
160
+ # qpll.add_child(d2)
161
+
162
+ d = Node("D", ntype="divider")
163
+ d.value = config["fpga"]["d"]
164
+ qpll.add_child(d)
165
+
166
+ # Connect
167
+ phy.add_connection({"from": m, "to": pfd})
168
+ phy.add_connection({"from": pfd, "to": cp})
169
+ phy.add_connection({"from": cp, "to": lpf})
170
+ phy.add_connection({"from": lpf, "to": vco})
171
+ phy.add_connection({"from": vco, "to": n})
172
+ # TRX is DDR devices so it uses both clock edges
173
+ # (skipping /2 and *2 dividers)
174
+ # phy.add_connection({"from": vco, "to": d2,
175
+ # "rate": config['fpga']['vco']})
176
+ # phy.add_connection({"from": d2, "to": d,
177
+ # "rate": config['fpga']['vco'] / 2})
178
+ phy.add_connection({"from": vco, "to": d, "rate": config["fpga"]["vco"]})
179
+ phy.add_connection({"from": n, "to": pfd})
180
+
181
+ xcvr_out = d
182
+ xcvr_out_rate = config["fpga"]["vco"] / config["fpga"]["d"]
183
+
184
+ in_c = m
185
+
186
+ # Divider complex
187
+ trx_dividers = Node("Transceiver Dividers", ntype="trx-dividers")
188
+ phy.add_child(trx_dividers)
189
+
190
+ if config["fpga"]["out_clk_select"] == "XCVR_OUTCLK_PCS":
191
+ raise Exception("Only XCVR_PROGDIV_CLK supported for now")
192
+ elif config["fpga"]["out_clk_select"] == "XCVR_OUTCLK_PMA":
193
+ raise Exception("Only XCVR_PROGDIV_CLK supported for now")
194
+ elif config["fpga"]["out_clk_select"] == "XCVR_REFCLK":
195
+
196
+ rmux = Node("REFCLKSEL-Mux", ntype="mux")
197
+ trx_dividers.add_child(rmux)
198
+ connect_to_input = [rmux]
199
+
200
+ smux = Node("SYSCLKSEL-Mux", ntype="mux")
201
+ trx_dividers.add_child(smux)
202
+ trx_dividers.add_connection({"from": rmux, "to": smux, "rate": ref_in_rate})
203
+
204
+ out_rate = ref_in_rate
205
+ out = smux
206
+
207
+ elif config["fpga"]["out_clk_select"] == "XCVR_REFCLK_DIV2":
208
+
209
+ rmux = Node("REFCLKSEL-Mux", ntype="mux")
210
+ trx_dividers.add_child(rmux)
211
+ # trx_dividers.add_connection(
212
+ # {"from": in_c, "to": rmux, "rate": ref_in_rate}
213
+ # )
214
+ connect_to_input = [rmux]
215
+
216
+ smux = Node("SYSCLKSEL-Mux", ntype="mux")
217
+ trx_dividers.add_child(smux)
218
+ trx_dividers.add_connection({"from": rmux, "to": smux, "rate": ref_in_rate})
219
+
220
+ div2 = Node("DIV2", ntype="divider")
221
+ trx_dividers.add_child(div2)
222
+ div2.value = 2
223
+ trx_dividers.add_connection({"from": smux, "to": div2, "rate": ref_in_rate})
224
+
225
+ out_rate = ref_in_rate / 2
226
+ out = div2
227
+
228
+ elif config["fpga"]["out_clk_select"] == "XCVR_PROGDIV_CLK":
229
+
230
+ mux = Node("PLLCLKSEL-Mux", ntype="mux")
231
+ mux.value = config["fpga"]["type"]
232
+ trx_dividers.add_child(mux)
233
+ phy.add_connection({"from": xcvr_out, "to": mux, "rate": xcvr_out_rate})
234
+
235
+ cdr = Node("CDR", ntype="cdr")
236
+ trx_dividers.add_child(cdr)
237
+ trx_dividers.add_connection({"from": mux, "to": cdr})
238
+
239
+ progdiv = Node("ProgDiv", ntype="divider")
240
+ trx_dividers.add_child(progdiv)
241
+ progdiv.value = int(config["fpga"]["progdiv"])
242
+ trx_dividers.add_connection({"from": cdr, "to": progdiv})
243
+
244
+ out_rate = xcvr_out_rate / config["fpga"]["progdiv"]
245
+ out = progdiv
246
+ connect_to_input = []
247
+ else:
248
+ raise Exception(
249
+ f"Unknown out_clk_select: {config['fpga']['out_clk_select']}"
250
+ )
251
+
252
+ out_mux = Node("OUTCLKSEL-Mux", ntype="mux")
253
+ trx_dividers.add_child(out_mux)
254
+ trx_dividers.add_connection({"from": out, "to": out_mux, "rate": out_rate})
255
+
256
+ return in_c, out_mux, connect_to_input, xcvr_out
257
+
258
+ def draw(
259
+ self, config: Dict, lo: Layout = None, converters: List[Converter] = None
260
+ ) -> str:
261
+ """Draw diagram in d2 language for IC alone with reference clock.
262
+
263
+ Args:
264
+ config (Dict): Clock settings
265
+ lo (Layout): Layout object to draw on (optional)
266
+ converters (List): List of converters to draw (optional)
267
+
268
+ Returns:
269
+ str: SVG data
270
+
271
+ Raises:
272
+ Exception: If no solution is saved
273
+ Exception: If no converter is found
274
+ """
275
+ if not self._saved_solution:
276
+ raise Exception("No solution to draw. Must call solve first.")
277
+
278
+ system_draw = lo is not None
279
+
280
+ if not system_draw:
281
+ lo = Layout(f"{self.name} Example")
282
+ converters = []
283
+ else:
284
+ # Verify lo is a Layout object
285
+ assert isinstance(lo, Layout), "lo must be a Layout object"
286
+ assert len(converters) == 1, "Only one converter supported"
287
+
288
+ lo.add_node(self.ic_diagram_node)
289
+
290
+ clocks = config["clocks"]
291
+ device_clock_source = config["fpga"]["device_clock_source"]
292
+
293
+ if not system_draw:
294
+ ref_in = Node("REF_IN", ntype="input")
295
+ lo.add_node(ref_in)
296
+ else:
297
+ for converter in converters:
298
+ to_node = lo.get_node(f"{self.name}_{converter.name.upper()}_ref_clk")
299
+ # Locate node connected to this one
300
+ from_node = lo.get_connection(to=to_node.name)
301
+ assert from_node, "No connection found"
302
+ assert isinstance(from_node, list), "Connection must be a list"
303
+ ref_in = from_node[0]["from"]
304
+ lo.remove_node(to_node.name)
305
+
306
+ # TO DO, ADD PHY PER CONVERTER
307
+ if not converters:
308
+ converters_first = None
309
+ else:
310
+ converters_first = converters[0]
311
+ in_c, out_c, connect_to_input, xcvr_out = self._draw_phy(
312
+ config, converters_first
313
+ )
314
+
315
+ if not system_draw:
316
+ self.ic_diagram_node.add_connection(
317
+ {"from": ref_in, "to": in_c, "rate": clocks["FPGA_REF"]}
318
+ )
319
+
320
+ else:
321
+ for converter in converters:
322
+ rcn = f"{self.name}_{converter.name.upper()}_ref_clk"
323
+ assert rcn in clocks, f"Missing clock {rcn}"
324
+ self.ic_diagram_node.add_connection(
325
+ {"from": ref_in, "to": in_c, "rate": clocks[rcn]}
326
+ )
327
+ if connect_to_input:
328
+ for c in connect_to_input:
329
+ self.ic_diagram_node.add_connection(
330
+ {"from": ref_in, "to": c, "rate": clocks[rcn]}
331
+ )
332
+
333
+ # Delete Transceiver node
334
+ self.ic_diagram_node.remove_child("Transceiver")
335
+
336
+ # Connect out_c to JESD204-Link-IP
337
+ if not system_draw:
338
+ self.ic_diagram_node.add_connection(
339
+ {
340
+ "from": out_c,
341
+ "to": self.ic_diagram_node.get_child("JESD204-Link-IP"),
342
+ "rate": clocks["LINK_OUT_REF"],
343
+ }
344
+ )
345
+ else:
346
+ for converter in converters:
347
+ self.ic_diagram_node.add_connection(
348
+ {
349
+ "from": out_c,
350
+ "to": self.ic_diagram_node.get_child("JESD204-Link-IP"),
351
+ "rate": clocks[
352
+ f"{self.name}_{converter.name.upper()}_device_clk"
353
+ ],
354
+ }
355
+ )
356
+
357
+ # Connect device clock to JESD204-Link-IP and JESD204-Transport-IP
358
+ if not system_draw:
359
+ if device_clock_source == "external":
360
+ device_clock = Node("Device Clock", ntype="input")
361
+ lo.add_node(device_clock)
362
+ elif device_clock_source == "link_clock":
363
+ device_clock = out_c
364
+ elif device_clock_source == "ref_clock":
365
+ device_clock = ref_in
366
+ else:
367
+ raise Exception(f"Unknown device clock source: {device_clock_source}")
368
+ self.ic_diagram_node.add_connection(
369
+ {
370
+ "from": device_clock,
371
+ "to": self.ic_diagram_node.get_child("JESD204-Link-IP"),
372
+ "rate": clocks["LINK_OUT_REF"],
373
+ }
374
+ )
375
+ self.ic_diagram_node.add_connection(
376
+ {
377
+ "from": device_clock,
378
+ "to": self.ic_diagram_node.get_child("JESD204-Transport-IP"),
379
+ "rate": clocks["LINK_OUT_REF"],
380
+ }
381
+ )
382
+
383
+ else:
384
+ for converter in converters:
385
+
386
+ c_name = f"{self.name}_{converter.name.upper()}_device_clk"
387
+ to_node = lo.get_node(c_name)
388
+ # Locate node connected to this one
389
+ from_node = lo.get_connection(to=to_node.name)
390
+ assert from_node, "No connection found"
391
+ assert isinstance(from_node, list), "Connection must be a list"
392
+ lo.remove_node(to_node.name)
393
+
394
+ if device_clock_source == "external":
395
+ device_clock = from_node[0]["from"]
396
+ elif device_clock_source == "link_clock":
397
+ device_clock = out_c
398
+ elif device_clock_source == "ref_clock":
399
+ device_clock = ref_in
400
+ else:
401
+ raise Exception(
402
+ f"Unknown device clock source: {device_clock_source}"
403
+ )
404
+
405
+ self.ic_diagram_node.add_connection(
406
+ {
407
+ "from": device_clock,
408
+ "to": self.ic_diagram_node.get_child("JESD204-Link-IP"),
409
+ "rate": clocks[c_name],
410
+ }
411
+ )
412
+ self.ic_diagram_node.add_connection(
413
+ {
414
+ "from": device_clock,
415
+ "to": self.ic_diagram_node.get_child("JESD204-Transport-IP"),
416
+ "rate": clocks[c_name],
417
+ }
418
+ )
419
+
420
+ # Connect SYSREF to JESD204-Link-IP
421
+ if not system_draw:
422
+ sysref = Node("SYSREF", ntype="input")
423
+ lo.add_node(sysref)
424
+ else:
425
+ for converter in converters:
426
+ parent = lo.get_node(converter.name.upper())
427
+ to_node = parent.get_child("JESD204 Framer")
428
+ # Locate node connected to this one
429
+ from_node = lo.get_connection(to=to_node.name)
430
+ assert from_node, "No connection found"
431
+ assert isinstance(from_node, list), "Connection must be a list"
432
+ sysref = from_node[0]["from"]
433
+ # lo.remove_node(to_node.name)
434
+
435
+ self.ic_diagram_node.add_connection(
436
+ {
437
+ "from": sysref,
438
+ "to": self.ic_diagram_node.get_child("JESD204-Link-IP"),
439
+ }
440
+ )
441
+
442
+ # Update with config settings
443
+
444
+ # Datapath
445
+
446
+ # Get deframer
447
+ if system_draw:
448
+ deframer = lo.get_node("JESD204 Deframer")
449
+ assert deframer, "No JESD204 Deframer found in layout"
450
+ parent = lo.get_node(converter.name.upper())
451
+ framer = parent.get_child("JESD204 Framer")
452
+ assert framer, "No JESD204 Framer found in layout"
453
+
454
+ # Replace deframer with a new one inside the FPGA IC diagram
455
+ lo.remove_node("JESD204 Deframer")
456
+ phy_parent = self.ic_diagram_node.get_child("JESD204-PHY-IP")
457
+ new_deframer = Node("DESERIALIZER", ntype="serdes")
458
+ phy_parent.add_child(new_deframer)
459
+ lo.add_connection(
460
+ {
461
+ "from": xcvr_out,
462
+ "to": new_deframer,
463
+ "rate": converter.bit_clock,
464
+ }
465
+ )
466
+ # Add connect for each lane
467
+ for _ in range(converter.L):
468
+ lane_rate = converter.bit_clock
469
+ lo.add_connection(
470
+ {
471
+ "from": framer,
472
+ "to": new_deframer,
473
+ "rate": lane_rate,
474
+ "type": "data",
475
+ }
476
+ )
477
+ if system_draw:
478
+ # Connect DESERIALIZER to link layer decoder
479
+ link_layer = self.ic_diagram_node.get_child("JESD204-Link-IP")
480
+ assert link_layer, "No JESD204-Link-IP found in layout"
481
+ decoder = Node("Link Layer Decoder", ntype="decoder")
482
+ link_layer.add_child(decoder)
483
+ div = 40 if converter.encoding == "8b10b" else 66
484
+ for _ in range(converter.L):
485
+ # Add connect for each lane
486
+ lo.add_connection(
487
+ {
488
+ "from": new_deframer,
489
+ "to": decoder,
490
+ "rate": converter.bit_clock / div,
491
+ "type": "data",
492
+ }
493
+ )
494
+
495
+ if system_draw:
496
+ # Add deframer in transport layer
497
+ transport_layer = self.ic_diagram_node.get_child("JESD204-Transport-IP")
498
+ assert transport_layer, "No JESD204-Transport-IP found in layout"
499
+ deframer = Node("JESD204 Deframer", ntype="deframer")
500
+ transport_layer.add_child(deframer)
501
+ # Connect to link layer decoder
502
+ lo.add_connection(
503
+ {
504
+ "from": decoder,
505
+ "to": deframer,
506
+ "rate": converter.bit_clock
507
+ / (40 if converter.encoding == "8b10b" else 66),
508
+ "type": "data",
509
+ }
510
+ )
511
+ # Remove connection between link and transport layers
512
+ self.ic_diagram_node.remove_connection(
513
+ from_s=link_layer.name, to=transport_layer.name
514
+ )
515
+
516
+ return lo.draw()