oscura 0.3.0__py3-none-any.whl → 0.5.0__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.
- oscura/__init__.py +1 -7
- oscura/acquisition/__init__.py +147 -0
- oscura/acquisition/file.py +255 -0
- oscura/acquisition/hardware.py +186 -0
- oscura/acquisition/saleae.py +340 -0
- oscura/acquisition/socketcan.py +315 -0
- oscura/acquisition/streaming.py +38 -0
- oscura/acquisition/synthetic.py +229 -0
- oscura/acquisition/visa.py +376 -0
- oscura/analyzers/__init__.py +3 -0
- oscura/analyzers/digital/__init__.py +48 -0
- oscura/analyzers/digital/clock.py +9 -1
- oscura/analyzers/digital/edges.py +1 -1
- oscura/analyzers/digital/extraction.py +195 -0
- oscura/analyzers/digital/ic_database.py +498 -0
- oscura/analyzers/digital/timing.py +41 -11
- oscura/analyzers/digital/timing_paths.py +339 -0
- oscura/analyzers/digital/vintage.py +377 -0
- oscura/analyzers/digital/vintage_result.py +148 -0
- oscura/analyzers/protocols/__init__.py +22 -1
- oscura/analyzers/protocols/parallel_bus.py +449 -0
- oscura/analyzers/side_channel/__init__.py +52 -0
- oscura/analyzers/side_channel/power.py +690 -0
- oscura/analyzers/side_channel/timing.py +369 -0
- oscura/analyzers/signal_integrity/sparams.py +1 -1
- oscura/automotive/__init__.py +4 -2
- oscura/automotive/can/patterns.py +3 -1
- oscura/automotive/can/session.py +277 -78
- oscura/automotive/can/state_machine.py +5 -2
- oscura/builders/__init__.py +9 -11
- oscura/builders/signal_builder.py +99 -191
- oscura/core/exceptions.py +5 -1
- oscura/export/__init__.py +12 -0
- oscura/export/wavedrom.py +430 -0
- oscura/exporters/json_export.py +47 -0
- oscura/exporters/vintage_logic_csv.py +247 -0
- oscura/loaders/__init__.py +1 -0
- oscura/loaders/chipwhisperer.py +393 -0
- oscura/loaders/touchstone.py +1 -1
- oscura/reporting/__init__.py +7 -0
- oscura/reporting/vintage_logic_report.py +523 -0
- oscura/session/session.py +54 -46
- oscura/sessions/__init__.py +70 -0
- oscura/sessions/base.py +323 -0
- oscura/sessions/blackbox.py +640 -0
- oscura/sessions/generic.py +189 -0
- oscura/utils/autodetect.py +5 -1
- oscura/visualization/digital_advanced.py +718 -0
- oscura/visualization/figure_manager.py +156 -0
- {oscura-0.3.0.dist-info → oscura-0.5.0.dist-info}/METADATA +86 -5
- {oscura-0.3.0.dist-info → oscura-0.5.0.dist-info}/RECORD +54 -33
- oscura/automotive/dtc/data.json +0 -2763
- oscura/schemas/bus_configuration.json +0 -322
- oscura/schemas/device_mapping.json +0 -182
- oscura/schemas/packet_format.json +0 -418
- oscura/schemas/protocol_definition.json +0 -363
- {oscura-0.3.0.dist-info → oscura-0.5.0.dist-info}/WHEEL +0 -0
- {oscura-0.3.0.dist-info → oscura-0.5.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.3.0.dist-info → oscura-0.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"""IC timing database for vintage and modern logic ICs.
|
|
2
|
+
|
|
3
|
+
This module provides timing specifications for common ICs to enable automatic
|
|
4
|
+
validation and identification.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from oscura.analyzers.digital.ic_database import IC_DATABASE, identify_ic
|
|
8
|
+
>>> spec = IC_DATABASE["74LS74"]
|
|
9
|
+
>>> print(f"Setup time: {spec.timing['t_su']*1e9:.1f}ns")
|
|
10
|
+
>>>
|
|
11
|
+
>>> # Auto-identify IC from measurements
|
|
12
|
+
>>> ic_name, conf = identify_ic(measured_timings)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class ICTiming:
|
|
22
|
+
"""Timing specification for an IC.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
part_number: IC part number (e.g., "74LS74").
|
|
26
|
+
description: Brief description of function.
|
|
27
|
+
family: Logic family (e.g., "TTL", "LS-TTL", "HC-CMOS").
|
|
28
|
+
vcc_nom: Nominal supply voltage.
|
|
29
|
+
vcc_range: Supply voltage range (min, max).
|
|
30
|
+
timing: Dictionary of timing parameters in seconds.
|
|
31
|
+
voltage_levels: Dictionary of voltage thresholds.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
part_number: str
|
|
35
|
+
description: str
|
|
36
|
+
family: str
|
|
37
|
+
vcc_nom: float
|
|
38
|
+
vcc_range: tuple[float, float]
|
|
39
|
+
timing: dict[str, float] = field(default_factory=dict)
|
|
40
|
+
voltage_levels: dict[str, float] = field(default_factory=dict)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Timing parameter definitions:
|
|
44
|
+
# t_pd: Propagation delay
|
|
45
|
+
# t_su: Setup time
|
|
46
|
+
# t_h: Hold time
|
|
47
|
+
# t_w: Pulse width (minimum)
|
|
48
|
+
# t_r: Rise time
|
|
49
|
+
# t_f: Fall time
|
|
50
|
+
# t_co: Clock-to-output delay
|
|
51
|
+
|
|
52
|
+
# 74xx TTL Series (Standard TTL - 1970s era)
|
|
53
|
+
IC_7400_STD = ICTiming(
|
|
54
|
+
part_number="7400",
|
|
55
|
+
description="Quad 2-input NAND gate",
|
|
56
|
+
family="TTL",
|
|
57
|
+
vcc_nom=5.0,
|
|
58
|
+
vcc_range=(4.75, 5.25),
|
|
59
|
+
timing={
|
|
60
|
+
"t_pd": 22e-9, # Typ 22ns, max 33ns
|
|
61
|
+
"t_r": 12e-9,
|
|
62
|
+
"t_f": 8e-9,
|
|
63
|
+
},
|
|
64
|
+
voltage_levels={
|
|
65
|
+
"VIL_max": 0.8,
|
|
66
|
+
"VIH_min": 2.0,
|
|
67
|
+
"VOL_max": 0.4,
|
|
68
|
+
"VOH_min": 2.4,
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
IC_7474_STD = ICTiming(
|
|
73
|
+
part_number="7474",
|
|
74
|
+
description="Dual D-type flip-flop",
|
|
75
|
+
family="TTL",
|
|
76
|
+
vcc_nom=5.0,
|
|
77
|
+
vcc_range=(4.75, 5.25),
|
|
78
|
+
timing={
|
|
79
|
+
"t_pd": 25e-9, # Clock-to-output
|
|
80
|
+
"t_su": 20e-9, # Setup time
|
|
81
|
+
"t_h": 5e-9, # Hold time
|
|
82
|
+
"t_w": 25e-9, # Minimum clock pulse width
|
|
83
|
+
"t_r": 12e-9,
|
|
84
|
+
"t_f": 8e-9,
|
|
85
|
+
},
|
|
86
|
+
voltage_levels={
|
|
87
|
+
"VIL_max": 0.8,
|
|
88
|
+
"VIH_min": 2.0,
|
|
89
|
+
"VOL_max": 0.4,
|
|
90
|
+
"VOH_min": 2.4,
|
|
91
|
+
},
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# 74LSxx Low-Power Schottky TTL Series (1970s-1980s)
|
|
95
|
+
IC_74LS00 = ICTiming(
|
|
96
|
+
part_number="74LS00",
|
|
97
|
+
description="Quad 2-input NAND gate",
|
|
98
|
+
family="LS-TTL",
|
|
99
|
+
vcc_nom=5.0,
|
|
100
|
+
vcc_range=(4.75, 5.25),
|
|
101
|
+
timing={
|
|
102
|
+
"t_pd": 10e-9, # Typ 10ns, max 15ns
|
|
103
|
+
"t_r": 10e-9,
|
|
104
|
+
"t_f": 7e-9,
|
|
105
|
+
},
|
|
106
|
+
voltage_levels={
|
|
107
|
+
"VIL_max": 0.8,
|
|
108
|
+
"VIH_min": 2.0,
|
|
109
|
+
"VOL_max": 0.5,
|
|
110
|
+
"VOH_min": 2.7,
|
|
111
|
+
},
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
IC_74LS74 = ICTiming(
|
|
115
|
+
part_number="74LS74",
|
|
116
|
+
description="Dual D-type flip-flop",
|
|
117
|
+
family="LS-TTL",
|
|
118
|
+
vcc_nom=5.0,
|
|
119
|
+
vcc_range=(4.75, 5.25),
|
|
120
|
+
timing={
|
|
121
|
+
"t_pd": 25e-9, # Clock-to-output (typ), max 40ns
|
|
122
|
+
"t_su": 20e-9, # Setup time
|
|
123
|
+
"t_h": 5e-9, # Hold time
|
|
124
|
+
"t_w": 25e-9, # Min clock pulse width
|
|
125
|
+
"t_r": 10e-9,
|
|
126
|
+
"t_f": 7e-9,
|
|
127
|
+
},
|
|
128
|
+
voltage_levels={
|
|
129
|
+
"VIL_max": 0.8,
|
|
130
|
+
"VIH_min": 2.0,
|
|
131
|
+
"VOL_max": 0.5,
|
|
132
|
+
"VOH_min": 2.7,
|
|
133
|
+
},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
IC_74LS244 = ICTiming(
|
|
137
|
+
part_number="74LS244",
|
|
138
|
+
description="Octal buffer/line driver",
|
|
139
|
+
family="LS-TTL",
|
|
140
|
+
vcc_nom=5.0,
|
|
141
|
+
vcc_range=(4.75, 5.25),
|
|
142
|
+
timing={
|
|
143
|
+
"t_pd": 12e-9, # Typ 12ns, max 18ns
|
|
144
|
+
"t_r": 7e-9,
|
|
145
|
+
"t_f": 5e-9,
|
|
146
|
+
},
|
|
147
|
+
voltage_levels={
|
|
148
|
+
"VIL_max": 0.8,
|
|
149
|
+
"VIH_min": 2.0,
|
|
150
|
+
"VOL_max": 0.5,
|
|
151
|
+
"VOH_min": 2.7,
|
|
152
|
+
},
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
IC_74LS245 = ICTiming(
|
|
156
|
+
part_number="74LS245",
|
|
157
|
+
description="Octal bus transceiver",
|
|
158
|
+
family="LS-TTL",
|
|
159
|
+
vcc_nom=5.0,
|
|
160
|
+
vcc_range=(4.75, 5.25),
|
|
161
|
+
timing={
|
|
162
|
+
"t_pd": 12e-9, # Typ 12ns, max 18ns
|
|
163
|
+
"t_r": 7e-9,
|
|
164
|
+
"t_f": 5e-9,
|
|
165
|
+
},
|
|
166
|
+
voltage_levels={
|
|
167
|
+
"VIL_max": 0.8,
|
|
168
|
+
"VIH_min": 2.0,
|
|
169
|
+
"VOL_max": 0.5,
|
|
170
|
+
"VOH_min": 2.7,
|
|
171
|
+
},
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
IC_74LS138 = ICTiming(
|
|
175
|
+
part_number="74LS138",
|
|
176
|
+
description="3-to-8 line decoder/demultiplexer",
|
|
177
|
+
family="LS-TTL",
|
|
178
|
+
vcc_nom=5.0,
|
|
179
|
+
vcc_range=(4.75, 5.25),
|
|
180
|
+
timing={
|
|
181
|
+
"t_pd": 21e-9, # Typ 21ns, max 41ns
|
|
182
|
+
"t_r": 10e-9,
|
|
183
|
+
"t_f": 7e-9,
|
|
184
|
+
},
|
|
185
|
+
voltage_levels={
|
|
186
|
+
"VIL_max": 0.8,
|
|
187
|
+
"VIH_min": 2.0,
|
|
188
|
+
"VOL_max": 0.5,
|
|
189
|
+
"VOH_min": 2.7,
|
|
190
|
+
},
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
IC_74LS273 = ICTiming(
|
|
194
|
+
part_number="74LS273",
|
|
195
|
+
description="Octal D-type flip-flop with clear",
|
|
196
|
+
family="LS-TTL",
|
|
197
|
+
vcc_nom=5.0,
|
|
198
|
+
vcc_range=(4.75, 5.25),
|
|
199
|
+
timing={
|
|
200
|
+
"t_pd": 20e-9, # Clock-to-output
|
|
201
|
+
"t_su": 20e-9, # Setup time
|
|
202
|
+
"t_h": 5e-9, # Hold time
|
|
203
|
+
"t_w": 20e-9, # Min clock pulse width
|
|
204
|
+
"t_r": 10e-9,
|
|
205
|
+
"t_f": 7e-9,
|
|
206
|
+
},
|
|
207
|
+
voltage_levels={
|
|
208
|
+
"VIL_max": 0.8,
|
|
209
|
+
"VIH_min": 2.0,
|
|
210
|
+
"VOL_max": 0.5,
|
|
211
|
+
"VOH_min": 2.7,
|
|
212
|
+
},
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
IC_74LS374 = ICTiming(
|
|
216
|
+
part_number="74LS374",
|
|
217
|
+
description="Octal D-type flip-flop with 3-state outputs",
|
|
218
|
+
family="LS-TTL",
|
|
219
|
+
vcc_nom=5.0,
|
|
220
|
+
vcc_range=(4.75, 5.25),
|
|
221
|
+
timing={
|
|
222
|
+
"t_pd": 20e-9, # Clock-to-output
|
|
223
|
+
"t_su": 20e-9, # Setup time
|
|
224
|
+
"t_h": 5e-9, # Hold time
|
|
225
|
+
"t_w": 20e-9, # Min clock pulse width
|
|
226
|
+
"t_pz": 18e-9, # Output enable to output
|
|
227
|
+
"t_pzh": 30e-9, # Output disable to high-Z
|
|
228
|
+
"t_r": 10e-9,
|
|
229
|
+
"t_f": 7e-9,
|
|
230
|
+
},
|
|
231
|
+
voltage_levels={
|
|
232
|
+
"VIL_max": 0.8,
|
|
233
|
+
"VIH_min": 2.0,
|
|
234
|
+
"VOL_max": 0.5,
|
|
235
|
+
"VOH_min": 2.7,
|
|
236
|
+
},
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# 74HCxx High-Speed CMOS Series (1980s-present)
|
|
240
|
+
IC_74HC00 = ICTiming(
|
|
241
|
+
part_number="74HC00",
|
|
242
|
+
description="Quad 2-input NAND gate",
|
|
243
|
+
family="HC-CMOS",
|
|
244
|
+
vcc_nom=5.0,
|
|
245
|
+
vcc_range=(2.0, 6.0),
|
|
246
|
+
timing={
|
|
247
|
+
"t_pd": 8e-9, # At 5V, typ 8ns
|
|
248
|
+
"t_r": 6e-9,
|
|
249
|
+
"t_f": 6e-9,
|
|
250
|
+
},
|
|
251
|
+
voltage_levels={
|
|
252
|
+
"VIL_max": 1.35,
|
|
253
|
+
"VIH_min": 3.15,
|
|
254
|
+
"VOL_max": 0.33,
|
|
255
|
+
"VOH_min": 3.84,
|
|
256
|
+
},
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
IC_74HC74 = ICTiming(
|
|
260
|
+
part_number="74HC74",
|
|
261
|
+
description="Dual D-type flip-flop",
|
|
262
|
+
family="HC-CMOS",
|
|
263
|
+
vcc_nom=5.0,
|
|
264
|
+
vcc_range=(2.0, 6.0),
|
|
265
|
+
timing={
|
|
266
|
+
"t_pd": 16e-9, # Clock-to-output at 5V
|
|
267
|
+
"t_su": 14e-9, # Setup time
|
|
268
|
+
"t_h": 3e-9, # Hold time
|
|
269
|
+
"t_w": 14e-9, # Min clock pulse width
|
|
270
|
+
"t_r": 6e-9,
|
|
271
|
+
"t_f": 6e-9,
|
|
272
|
+
},
|
|
273
|
+
voltage_levels={
|
|
274
|
+
"VIL_max": 1.35,
|
|
275
|
+
"VIH_min": 3.15,
|
|
276
|
+
"VOL_max": 0.33,
|
|
277
|
+
"VOH_min": 3.84,
|
|
278
|
+
},
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
IC_74HC595 = ICTiming(
|
|
282
|
+
part_number="74HC595",
|
|
283
|
+
description="8-bit shift register with output latches",
|
|
284
|
+
family="HC-CMOS",
|
|
285
|
+
vcc_nom=5.0,
|
|
286
|
+
vcc_range=(2.0, 6.0),
|
|
287
|
+
timing={
|
|
288
|
+
"t_pd": 16e-9, # Clock-to-output
|
|
289
|
+
"t_su": 14e-9, # Setup time
|
|
290
|
+
"t_h": 3e-9, # Hold time
|
|
291
|
+
"t_w": 14e-9, # Min clock pulse width
|
|
292
|
+
"t_r": 6e-9,
|
|
293
|
+
"t_f": 6e-9,
|
|
294
|
+
},
|
|
295
|
+
voltage_levels={
|
|
296
|
+
"VIL_max": 1.35,
|
|
297
|
+
"VIH_min": 3.15,
|
|
298
|
+
"VOL_max": 0.33,
|
|
299
|
+
"VOH_min": 3.84,
|
|
300
|
+
},
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# 4000 Series CMOS (1970s-1980s)
|
|
304
|
+
IC_4011 = ICTiming(
|
|
305
|
+
part_number="4011",
|
|
306
|
+
description="Quad 2-input NAND gate",
|
|
307
|
+
family="CMOS_5V",
|
|
308
|
+
vcc_nom=5.0,
|
|
309
|
+
vcc_range=(3.0, 18.0),
|
|
310
|
+
timing={
|
|
311
|
+
"t_pd": 90e-9, # At 5V, typ 90ns
|
|
312
|
+
"t_r": 60e-9,
|
|
313
|
+
"t_f": 60e-9,
|
|
314
|
+
},
|
|
315
|
+
voltage_levels={
|
|
316
|
+
"VIL_max": 1.5,
|
|
317
|
+
"VIH_min": 3.5,
|
|
318
|
+
"VOL_max": 0.05,
|
|
319
|
+
"VOH_min": 4.95,
|
|
320
|
+
},
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
IC_4013 = ICTiming(
|
|
324
|
+
part_number="4013",
|
|
325
|
+
description="Dual D-type flip-flop",
|
|
326
|
+
family="CMOS_5V",
|
|
327
|
+
vcc_nom=5.0,
|
|
328
|
+
vcc_range=(3.0, 18.0),
|
|
329
|
+
timing={
|
|
330
|
+
"t_pd": 140e-9, # Clock-to-output at 5V
|
|
331
|
+
"t_su": 60e-9, # Setup time
|
|
332
|
+
"t_h": 40e-9, # Hold time
|
|
333
|
+
"t_w": 100e-9, # Min clock pulse width
|
|
334
|
+
"t_r": 60e-9,
|
|
335
|
+
"t_f": 60e-9,
|
|
336
|
+
},
|
|
337
|
+
voltage_levels={
|
|
338
|
+
"VIL_max": 1.5,
|
|
339
|
+
"VIH_min": 3.5,
|
|
340
|
+
"VOL_max": 0.05,
|
|
341
|
+
"VOH_min": 4.95,
|
|
342
|
+
},
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
# Comprehensive IC database
|
|
346
|
+
IC_DATABASE: dict[str, ICTiming] = {
|
|
347
|
+
# Standard TTL
|
|
348
|
+
"7400": IC_7400_STD,
|
|
349
|
+
# LS-TTL (74LS series preferred over 74 for specificity)
|
|
350
|
+
"74LS00": IC_74LS00,
|
|
351
|
+
"74LS74": IC_74LS74,
|
|
352
|
+
"74LS138": IC_74LS138,
|
|
353
|
+
"74LS244": IC_74LS244,
|
|
354
|
+
"74LS245": IC_74LS245,
|
|
355
|
+
"74LS273": IC_74LS273,
|
|
356
|
+
"74LS374": IC_74LS374,
|
|
357
|
+
# HC-CMOS
|
|
358
|
+
"74HC00": IC_74HC00,
|
|
359
|
+
"74HC74": IC_74HC74,
|
|
360
|
+
"74HC595": IC_74HC595,
|
|
361
|
+
# 4000 series CMOS
|
|
362
|
+
"4011": IC_4011,
|
|
363
|
+
"4013": IC_4013,
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def identify_ic(
|
|
368
|
+
measured_timings: dict[str, float],
|
|
369
|
+
*,
|
|
370
|
+
tolerance: float = 0.5,
|
|
371
|
+
min_confidence: float = 0.6,
|
|
372
|
+
) -> tuple[str, float]:
|
|
373
|
+
"""Identify IC from measured timing parameters.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
measured_timings: Dictionary of measured timing values (e.g., {'t_pd': 25e-9}).
|
|
377
|
+
tolerance: Allowable deviation (0.0-1.0, 0.5 = 50% tolerance).
|
|
378
|
+
min_confidence: Minimum confidence score (0.0-1.0).
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
Tuple of (ic_name, confidence_score).
|
|
382
|
+
Returns ("unknown", 0.0) if no match above min_confidence.
|
|
383
|
+
|
|
384
|
+
Example:
|
|
385
|
+
>>> timings = {'t_pd': 25e-9, 't_su': 20e-9, 't_h': 5e-9}
|
|
386
|
+
>>> ic, conf = identify_ic(timings)
|
|
387
|
+
>>> print(f"Identified: {ic} ({conf*100:.1f}% confidence)")
|
|
388
|
+
"""
|
|
389
|
+
scores: dict[str, float] = {}
|
|
390
|
+
|
|
391
|
+
for ic_name, ic_spec in IC_DATABASE.items():
|
|
392
|
+
# Calculate match score for this IC
|
|
393
|
+
param_scores = []
|
|
394
|
+
|
|
395
|
+
for param, measured_value in measured_timings.items():
|
|
396
|
+
if param not in ic_spec.timing:
|
|
397
|
+
continue
|
|
398
|
+
|
|
399
|
+
spec_value = ic_spec.timing[param]
|
|
400
|
+
|
|
401
|
+
# Calculate relative error
|
|
402
|
+
if spec_value == 0:
|
|
403
|
+
continue
|
|
404
|
+
|
|
405
|
+
error = abs(measured_value - spec_value) / spec_value
|
|
406
|
+
|
|
407
|
+
# Score based on error (within tolerance gets high score)
|
|
408
|
+
if error <= tolerance:
|
|
409
|
+
param_score = 1.0 - (error / tolerance)
|
|
410
|
+
else:
|
|
411
|
+
param_score = 0.0
|
|
412
|
+
|
|
413
|
+
param_scores.append(param_score)
|
|
414
|
+
|
|
415
|
+
# Overall score is average of parameter scores
|
|
416
|
+
if param_scores:
|
|
417
|
+
scores[ic_name] = sum(param_scores) / len(param_scores)
|
|
418
|
+
|
|
419
|
+
# Find best match
|
|
420
|
+
if not scores:
|
|
421
|
+
return ("unknown", 0.0)
|
|
422
|
+
|
|
423
|
+
best_ic = max(scores.items(), key=lambda x: x[1])
|
|
424
|
+
|
|
425
|
+
if best_ic[1] < min_confidence:
|
|
426
|
+
return ("unknown", best_ic[1])
|
|
427
|
+
|
|
428
|
+
return best_ic
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def validate_ic_timing(
|
|
432
|
+
ic_name: str,
|
|
433
|
+
measured_timings: dict[str, float],
|
|
434
|
+
*,
|
|
435
|
+
tolerance: float = 0.3,
|
|
436
|
+
) -> dict[str, dict[str, float | bool | None]]:
|
|
437
|
+
"""Validate measured timings against IC specification.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
ic_name: IC part number (e.g., "74LS74").
|
|
441
|
+
measured_timings: Dictionary of measured timing values.
|
|
442
|
+
tolerance: Allowable deviation (0.0-1.0).
|
|
443
|
+
|
|
444
|
+
Returns:
|
|
445
|
+
Dictionary mapping parameter names to validation results:
|
|
446
|
+
{'t_pd': {'measured': 25e-9, 'spec': 25e-9, 'passes': True, 'error': 0.0}}
|
|
447
|
+
|
|
448
|
+
Raises:
|
|
449
|
+
KeyError: If IC not found in database.
|
|
450
|
+
|
|
451
|
+
Example:
|
|
452
|
+
>>> results = validate_ic_timing("74LS74", {'t_pd': 30e-9})
|
|
453
|
+
>>> if not results['t_pd']['passes']:
|
|
454
|
+
... print(f"Propagation delay out of spec!")
|
|
455
|
+
"""
|
|
456
|
+
if ic_name not in IC_DATABASE:
|
|
457
|
+
raise KeyError(f"IC '{ic_name}' not found in database")
|
|
458
|
+
|
|
459
|
+
ic_spec = IC_DATABASE[ic_name]
|
|
460
|
+
results: dict[str, dict[str, float | bool | None]] = {}
|
|
461
|
+
|
|
462
|
+
for param, measured_value in measured_timings.items():
|
|
463
|
+
if param not in ic_spec.timing:
|
|
464
|
+
results[param] = {
|
|
465
|
+
"measured": measured_value,
|
|
466
|
+
"spec": None,
|
|
467
|
+
"passes": None,
|
|
468
|
+
"error": None,
|
|
469
|
+
}
|
|
470
|
+
continue
|
|
471
|
+
|
|
472
|
+
spec_value = ic_spec.timing[param]
|
|
473
|
+
|
|
474
|
+
# Calculate relative error
|
|
475
|
+
if spec_value == 0:
|
|
476
|
+
error = 0.0
|
|
477
|
+
else:
|
|
478
|
+
error = abs(measured_value - spec_value) / spec_value
|
|
479
|
+
|
|
480
|
+
# Check if within tolerance
|
|
481
|
+
passes = error <= tolerance
|
|
482
|
+
|
|
483
|
+
results[param] = {
|
|
484
|
+
"measured": measured_value,
|
|
485
|
+
"spec": spec_value,
|
|
486
|
+
"passes": passes,
|
|
487
|
+
"error": error,
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return results
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
__all__ = [
|
|
494
|
+
"IC_DATABASE",
|
|
495
|
+
"ICTiming",
|
|
496
|
+
"identify_ic",
|
|
497
|
+
"validate_ic_timing",
|
|
498
|
+
]
|
|
@@ -642,20 +642,33 @@ def recover_clock_fft(
|
|
|
642
642
|
Detects the dominant frequency component in the signal using
|
|
643
643
|
FFT analysis, suitable for periodic digital signals.
|
|
644
644
|
|
|
645
|
+
**Best for**: Long signals (>64 samples) with clear periodicity.
|
|
646
|
+
**Not recommended for**: Short random data, aperiodic signals.
|
|
647
|
+
For short signals, use recover_clock_edge() instead.
|
|
648
|
+
|
|
645
649
|
Args:
|
|
646
|
-
trace: Input trace (analog or digital).
|
|
650
|
+
trace: Input trace (analog or digital). Should have at least
|
|
651
|
+
4-5 cycles of the clock signal for reliable detection.
|
|
647
652
|
min_freq: Minimum frequency to consider (Hz). Default: sample_rate/1000.
|
|
648
653
|
max_freq: Maximum frequency to consider (Hz). Default: sample_rate/2.
|
|
649
654
|
|
|
650
655
|
Returns:
|
|
651
656
|
ClockRecoveryResult with recovered frequency and confidence.
|
|
657
|
+
Confidence < 0.5 indicates unreliable detection (warning issued).
|
|
652
658
|
|
|
653
659
|
Raises:
|
|
654
|
-
InsufficientDataError: If trace has fewer than
|
|
660
|
+
InsufficientDataError: If trace has fewer than 64 samples.
|
|
661
|
+
ValueError: If no frequency components found in specified range.
|
|
662
|
+
|
|
663
|
+
Warnings:
|
|
664
|
+
UserWarning: Issued when confidence < 0.5 (unreliable result).
|
|
655
665
|
|
|
656
666
|
Example:
|
|
657
667
|
>>> result = recover_clock_fft(trace)
|
|
658
|
-
>>>
|
|
668
|
+
>>> if result.confidence > 0.7:
|
|
669
|
+
... print(f"Clock: {result.frequency / 1e6:.3f} MHz")
|
|
670
|
+
>>> else:
|
|
671
|
+
... print("Low confidence - try edge-based recovery")
|
|
659
672
|
|
|
660
673
|
References:
|
|
661
674
|
IEEE 1241-2010 Section 4.1
|
|
@@ -665,12 +678,17 @@ def recover_clock_fft(
|
|
|
665
678
|
n = len(data)
|
|
666
679
|
sample_rate = trace.metadata.sample_rate
|
|
667
680
|
|
|
668
|
-
|
|
681
|
+
# FFT requires sufficient samples for reliable frequency resolution
|
|
682
|
+
# Rule of thumb: At least 4-5 cycles of the signal for accurate peak detection
|
|
683
|
+
# With typical bit rates, this means ~100-200 samples minimum
|
|
684
|
+
min_samples = 64 # Increased from 16 for better frequency resolution
|
|
685
|
+
if n < min_samples:
|
|
669
686
|
raise InsufficientDataError(
|
|
670
|
-
"FFT clock recovery requires at least
|
|
671
|
-
required=
|
|
687
|
+
f"FFT clock recovery requires at least {min_samples} samples for reliable frequency detection",
|
|
688
|
+
required=min_samples,
|
|
672
689
|
available=n,
|
|
673
690
|
analysis_type="clock_recovery_fft",
|
|
691
|
+
fix_hint="Use edge-based clock recovery for short signals or acquire more data",
|
|
674
692
|
)
|
|
675
693
|
|
|
676
694
|
# Set frequency range defaults
|
|
@@ -691,11 +709,11 @@ def recover_clock_fft(
|
|
|
691
709
|
valid_indices = np.where(mask)[0]
|
|
692
710
|
|
|
693
711
|
if len(valid_indices) == 0:
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
712
|
+
# No valid frequencies in range - signal may be DC or out of range
|
|
713
|
+
raise ValueError(
|
|
714
|
+
f"No frequency components found in range [{min_freq:.0f} Hz, {max_freq:.0f} Hz]. "
|
|
715
|
+
f"Signal may be constant (DC) or frequency is outside specified range. "
|
|
716
|
+
f"Adjust min_freq/max_freq or check signal integrity."
|
|
699
717
|
)
|
|
700
718
|
|
|
701
719
|
# Find peak in valid range
|
|
@@ -721,6 +739,18 @@ def recover_clock_fft(
|
|
|
721
739
|
|
|
722
740
|
period = 1.0 / peak_freq if peak_freq > 0 else np.nan
|
|
723
741
|
|
|
742
|
+
# Warn on low confidence results (may be unreliable)
|
|
743
|
+
if confidence < 0.5:
|
|
744
|
+
import warnings
|
|
745
|
+
|
|
746
|
+
warnings.warn(
|
|
747
|
+
f"FFT clock recovery has low confidence ({confidence:.2f}). "
|
|
748
|
+
f"Detected frequency: {peak_freq / 1e6:.3f} MHz. "
|
|
749
|
+
f"Consider using longer signal, edge-based recovery, or verifying signal periodicity.",
|
|
750
|
+
UserWarning,
|
|
751
|
+
stacklevel=2,
|
|
752
|
+
)
|
|
753
|
+
|
|
724
754
|
return ClockRecoveryResult(
|
|
725
755
|
frequency=float(peak_freq),
|
|
726
756
|
period=float(period),
|