ebyst 0.1.0__tar.gz
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.
- ebyst-0.1.0/.gitignore +3 -0
- ebyst-0.1.0/LICENSE +19 -0
- ebyst-0.1.0/Makefile +15 -0
- ebyst-0.1.0/PKG-INFO +87 -0
- ebyst-0.1.0/README.md +72 -0
- ebyst-0.1.0/pyproject.toml +26 -0
- ebyst-0.1.0/src/ebyst/__init__.py +4 -0
- ebyst-0.1.0/src/ebyst/bsdl.py +125 -0
- ebyst-0.1.0/src/ebyst/device.py +178 -0
- ebyst-0.1.0/src/ebyst/drivers/__init__.py +2 -0
- ebyst-0.1.0/src/ebyst/drivers/driver.py +53 -0
- ebyst-0.1.0/src/ebyst/drivers/ft2232h.py +185 -0
- ebyst-0.1.0/src/ebyst/drivers/sim.py +164 -0
- ebyst-0.1.0/src/ebyst/interfaces/__init__.py +1 -0
- ebyst-0.1.0/src/ebyst/interfaces/i2c.py +116 -0
- ebyst-0.1.0/src/ebyst/tap_controller.py +336 -0
- ebyst-0.1.0/tests/bsdl/BSDLLCMXO2-256HCQFN32.BSM +353 -0
- ebyst-0.1.0/tests/bsdl/MPF100Tfcg484.bsdl +2388 -0
- ebyst-0.1.0/tests/bsdl/XA3S400_FG456.bsdl +1570 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-1200hccsbga132.BSM +683 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-1200hctqfp100.BSM +612 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-1200hctqfp144.BSM +699 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-1200uhcftbga256.BSM +1176 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-1200zehecsbga132.BSM +683 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-1200zehetqfp100.BSM +612 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-1200zehetqfp144.BSM +699 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-1200zewlcsp25.BSM +441 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000hccabga256.BSM +1177 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000hccsbga132.BSM +901 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000hcftbga256.BSM +1177 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000hctqfp100.BSM +830 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000hctqfp144.BSM +923 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000uhcfpbga484.BSM +1643 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000zehecabga256.BSM +1177 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000zehecsbga132.BSM +901 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000zeheftbga256.BSM +1177 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000zehetqfp100.BSM +830 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-2000zehetqfp144.BSM +923 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-256hccsbga132.BSM +505 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-256hcqfn32.BSM +353 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-256hctqfp100.BSM +473 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-256hcucbga64.BSM +421 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-256zehecsbga132.BSM +505 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-256zehetqfp100.BSM +473 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-256zeheucbga64.BSM +421 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-256zeqfn32.BSM +353 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000hccabga256.BSM +1305 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000hccabga332.BSM +1484 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000hccsbga132.BSM +1029 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000hcfpbga484.BSM +1642 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000hcftbga256.BSM +1305 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000hctqfp144.BSM +1056 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000zehecabga256.BSM +1305 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000zehecabga332.BSM +1484 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000zehecsbga132.BSM +1029 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000zehecsbga184.BSM +1151 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000zehefpbga484.BSM +1642 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000zeheftbga256.BSM +1305 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-4000zehetqfp144.BSM +1056 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-640hccsbga132.BSM +589 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-640hctqfp100.BSM +555 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-640uhctqfp144.BSM +699 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-640zehecsbga132.BSM +589 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-640zehetqfp100.BSM +555 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-7000hccabga256.BSM +1417 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-7000hccabga332.BSM +1602 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-7000hcfpbga484.BSM +1838 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-7000hcftbga256.BSM +1417 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-7000zehecabga256.BSM +1417 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-7000zehecabga332.BSM +1602 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-7000zehefpbga484.BSM +1838 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-7000zeheftbga256.BSM +1417 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2-7000zehetqfp144.BSM +1168 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2_1200hc_qfn32.bsm +457 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2_1200ze_qfn32.bsm +457 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2_4000hc_qfn84.bsm +923 -0
- ebyst-0.1.0/tests/bsdl/lattice/bsdllcmxo2_4000ze_qfn84.bsm +923 -0
- ebyst-0.1.0/tests/bsdl/lattice/fpga-md-02013-1-04-lcmxo2-1200ze-bsdl-wlcsp36.bsm +470 -0
- ebyst-0.1.0/tests/bsdl/lattice/fpga-md-02014-1-04-lcmxo2-4000ze-bsdl-wlcsp81.bsm +912 -0
- ebyst-0.1.0/tests/bsdl/lattice/fpga-md-02033-1-04-lcmxo2-7000hc-tqfp144.bsm +1171 -0
- ebyst-0.1.0/tests/bsdl/lattice/lcmxo2_2000ze_wlcsp49.bsm +714 -0
- ebyst-0.1.0/tests/bsdl/lattice/lcmxo2_256hc_qfn48.bsm +395 -0
- ebyst-0.1.0/tests/bsdl/lattice/lcmxo2_256ze_qfn48.bsm +395 -0
- ebyst-0.1.0/tests/bsdl/lattice/lcmxo2_640hc_qfn48.bsm +443 -0
- ebyst-0.1.0/tests/bsdl/lattice/lcmxo2_640ze_qfn48.bsm +443 -0
- ebyst-0.1.0/tests/bsdl/nxp/mpc5/mpc567xr_416_r1.bsdl +839 -0
- ebyst-0.1.0/tests/bsdl/nxp/mpc5/mpc567xr_516_r1.bsdl +872 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/CortexMx.bsd +103 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F301_F302_LQFP48.bsd +368 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F301_F302_LQFP64.bsd +403 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F301_F302_UFQFPN32.bsd +340 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F301_F302_WLCSP49.bsd +374 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F302_F303_B_C_LQFP100.bsd +572 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F302_F303_B_C_LQFP48.bsd +470 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F302_F303_B_C_LQFP64.bsd +500 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F302_F303_D_E_LQFP100.bsd +656 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F302_F303_D_E_LQFP144.bsd +718 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F302_F303_D_E_LQFP64.bsd +583 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F302_F303_D_E_UFBGA100.bsd +659 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F303_F334_LQFP32.bsd +333 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F303_F334_LQFP48.bsd +361 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F303_F334_LQFP64.bsd +389 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F318_UFQFPN32.bsd +331 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F318_WLCSP49.bsd +358 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F328_LQFP48 .bsd +361 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F358_LQFP100.bsd +572 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F358_LQFP48.bsd +470 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F358_LQFP64.bsd +500 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F373_LQFP100.bsd +565 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F373_LQFP48.bsd +466 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F373_LQFP64.bsd +497 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F373_UFBGA100.bsd +565 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F378_LQFP100.bsd +565 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F378_LQFP48.bsd +466 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F378_LQFP64.bsd +497 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F378_UFBGA100.bsd +565 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F378_WLCSP66.bsd +497 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/STM32F398_LQFP100.bsd +658 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f3/readme.txt +71 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f4/CortexMx.bsd +103 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f4/STM32F405_415_407_417_LQFP100.bsd +717 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f4/STM32F405_415_407_417_LQFP144.bsd +783 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f4/STM32F405_415_407_417_LQFP176.bsd +836 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f4/STM32F405_415_407_417_LQFP64.bsd +653 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f4/STM32F405_415_407_417_UFBGA176.bsd +839 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f4/STM32F405_415_407_417_WLCSP90.bsd +701 -0
- ebyst-0.1.0/tests/bsdl/st/stm32f4/readme.txt +50 -0
- ebyst-0.1.0/tests/te0790_blink.py +41 -0
- ebyst-0.1.0/tests/test_async.py +34 -0
- ebyst-0.1.0/tests/test_bsdl.py +25 -0
- ebyst-0.1.0/tests/test_chain.py +33 -0
- ebyst-0.1.0/tests/test_fsm.py +36 -0
- ebyst-0.1.0/tests/test_ftdi.py +16 -0
- ebyst-0.1.0/tests/test_i2c.py +38 -0
ebyst-0.1.0/.gitignore
ADDED
ebyst-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2024 Sijmen Woutersen
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
ebyst-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/bash
|
|
2
|
+
|
|
3
|
+
default:
|
|
4
|
+
@echo Usage: make [dist|venv]
|
|
5
|
+
|
|
6
|
+
.PHONY: dist
|
|
7
|
+
dist:
|
|
8
|
+
python3 -m build .
|
|
9
|
+
|
|
10
|
+
.PHONY: venv
|
|
11
|
+
venv:
|
|
12
|
+
python3 -m venv venv
|
|
13
|
+
venv/bin/pip install --upgrade pip
|
|
14
|
+
venv/bin/pip install build
|
|
15
|
+
venv/bin/pip install -e .
|
ebyst-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: ebyst
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Boundary scan test library
|
|
5
|
+
Project-URL: Homepage, https://github.com/swolix/ebyst
|
|
6
|
+
Project-URL: Issues, https://github.com/swolix/ebyst/issues
|
|
7
|
+
Author-email: Sijmen Woutersen <sijmen.woutersen@gmail.com>
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Requires-Dist: bitarray
|
|
13
|
+
Requires-Dist: pyftdi
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# EByST
|
|
17
|
+
Boundary scan test framework for board validation
|
|
18
|
+
|
|
19
|
+
# Basic example
|
|
20
|
+
```python
|
|
21
|
+
# initialize JTAG interface driver (currently only FT2232H is supported)
|
|
22
|
+
drv = ebyst.drivers.FT2232H(ebyst.drivers.FT2232H.list_devices()[0])
|
|
23
|
+
ctl = ebyst.TapController(drv)
|
|
24
|
+
ctl.detect_chain()
|
|
25
|
+
|
|
26
|
+
# Add device(s) to chain
|
|
27
|
+
dev = ebyst.Device.from_bsdl("bsdl/BSDLLCMXO2-256HCQFN32.BSM")
|
|
28
|
+
ctl.add_device(dev)
|
|
29
|
+
ctl.validate_chain()
|
|
30
|
+
|
|
31
|
+
# Start test
|
|
32
|
+
ctl.extest()
|
|
33
|
+
|
|
34
|
+
# Loopback test (assuming loopback on pins
|
|
35
|
+
dev.pinmap['O'].output_enable(True)
|
|
36
|
+
dev.pinmap['I'].output_enable(False)
|
|
37
|
+
dev.pinmap['O'].set_value(1)
|
|
38
|
+
await ctl.cycle() # drive output
|
|
39
|
+
await ctl.cycle() # sample input
|
|
40
|
+
print(dev.pinmap['I'].get_value())
|
|
41
|
+
dev.pinmap['O'].set_value(0)
|
|
42
|
+
await ctl.cycle() # drive output
|
|
43
|
+
await ctl.cycle() # sample input
|
|
44
|
+
print(dev.pinmap['I'].get_value())
|
|
45
|
+
|
|
46
|
+
# I2C test
|
|
47
|
+
i2c = ebyst.interfaces.I2C(ctl, dev.pinmap['PB9A'], dev.pinmap['PB4B'])
|
|
48
|
+
await i2c.init()
|
|
49
|
+
dev_address = 0xa0
|
|
50
|
+
reg_address = 0x10
|
|
51
|
+
data = 0xa5
|
|
52
|
+
print(f"Writing {dev_address:02x}:{reg_address:02x} <= {data:02x}")
|
|
53
|
+
await i2c.write(0xa0, 0x10, 0xa5)
|
|
54
|
+
print(f"Reading {dev_address:02x}:{reg_address:02x} => ", end='')
|
|
55
|
+
await x = i2c.read(0xa0, 0x10)
|
|
56
|
+
print(f"{x:02x}")
|
|
57
|
+
|
|
58
|
+
ctl.reset()
|
|
59
|
+
```
|
|
60
|
+
# Async
|
|
61
|
+
The library uses `asyncio` to allow running multiple tests in parallel.
|
|
62
|
+
When the loopback test and I2C test above are put in different tasks, they share the same boundary scan cycles,
|
|
63
|
+
meaning they run completely parallel
|
|
64
|
+
(Note that this only works when they are not using different pins, if pins are shared between tests, make sure to
|
|
65
|
+
schedule them appropriately)
|
|
66
|
+
|
|
67
|
+
Example;
|
|
68
|
+
```python
|
|
69
|
+
async def main():
|
|
70
|
+
drv = ebyst.drivers.FT2232H(ebyst.drivers.FT2232H.list_devices()[0])
|
|
71
|
+
dev = ebyst.Device.from_bsdl("bsdl/BSDLLCMXO2-256HCQFN32.BSM")
|
|
72
|
+
ctl = ebyst.TapController(drv)
|
|
73
|
+
ctl.detect_chain()
|
|
74
|
+
ctl.add_device(dev)
|
|
75
|
+
ctl.validate_chain()
|
|
76
|
+
ctl.extest()
|
|
77
|
+
async with asyncio.TaskGroup() as tg:
|
|
78
|
+
tg.create_task(loopback_test(ctl, dev.pinmap['PB2C'], dev.pinmap['PB2A']))
|
|
79
|
+
tg.create_task(loopback_test(ctl, dev.pinmap['PB4C'], dev.pinmap['PB4D']))
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
logging.basicConfig()
|
|
83
|
+
logging.getLogger().setLevel(logging.INFO)
|
|
84
|
+
asyncio.run(main())
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
see also `tests/test_async.py`
|
ebyst-0.1.0/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# EByST
|
|
2
|
+
Boundary scan test framework for board validation
|
|
3
|
+
|
|
4
|
+
# Basic example
|
|
5
|
+
```python
|
|
6
|
+
# initialize JTAG interface driver (currently only FT2232H is supported)
|
|
7
|
+
drv = ebyst.drivers.FT2232H(ebyst.drivers.FT2232H.list_devices()[0])
|
|
8
|
+
ctl = ebyst.TapController(drv)
|
|
9
|
+
ctl.detect_chain()
|
|
10
|
+
|
|
11
|
+
# Add device(s) to chain
|
|
12
|
+
dev = ebyst.Device.from_bsdl("bsdl/BSDLLCMXO2-256HCQFN32.BSM")
|
|
13
|
+
ctl.add_device(dev)
|
|
14
|
+
ctl.validate_chain()
|
|
15
|
+
|
|
16
|
+
# Start test
|
|
17
|
+
ctl.extest()
|
|
18
|
+
|
|
19
|
+
# Loopback test (assuming loopback on pins
|
|
20
|
+
dev.pinmap['O'].output_enable(True)
|
|
21
|
+
dev.pinmap['I'].output_enable(False)
|
|
22
|
+
dev.pinmap['O'].set_value(1)
|
|
23
|
+
await ctl.cycle() # drive output
|
|
24
|
+
await ctl.cycle() # sample input
|
|
25
|
+
print(dev.pinmap['I'].get_value())
|
|
26
|
+
dev.pinmap['O'].set_value(0)
|
|
27
|
+
await ctl.cycle() # drive output
|
|
28
|
+
await ctl.cycle() # sample input
|
|
29
|
+
print(dev.pinmap['I'].get_value())
|
|
30
|
+
|
|
31
|
+
# I2C test
|
|
32
|
+
i2c = ebyst.interfaces.I2C(ctl, dev.pinmap['PB9A'], dev.pinmap['PB4B'])
|
|
33
|
+
await i2c.init()
|
|
34
|
+
dev_address = 0xa0
|
|
35
|
+
reg_address = 0x10
|
|
36
|
+
data = 0xa5
|
|
37
|
+
print(f"Writing {dev_address:02x}:{reg_address:02x} <= {data:02x}")
|
|
38
|
+
await i2c.write(0xa0, 0x10, 0xa5)
|
|
39
|
+
print(f"Reading {dev_address:02x}:{reg_address:02x} => ", end='')
|
|
40
|
+
await x = i2c.read(0xa0, 0x10)
|
|
41
|
+
print(f"{x:02x}")
|
|
42
|
+
|
|
43
|
+
ctl.reset()
|
|
44
|
+
```
|
|
45
|
+
# Async
|
|
46
|
+
The library uses `asyncio` to allow running multiple tests in parallel.
|
|
47
|
+
When the loopback test and I2C test above are put in different tasks, they share the same boundary scan cycles,
|
|
48
|
+
meaning they run completely parallel
|
|
49
|
+
(Note that this only works when they are not using different pins, if pins are shared between tests, make sure to
|
|
50
|
+
schedule them appropriately)
|
|
51
|
+
|
|
52
|
+
Example;
|
|
53
|
+
```python
|
|
54
|
+
async def main():
|
|
55
|
+
drv = ebyst.drivers.FT2232H(ebyst.drivers.FT2232H.list_devices()[0])
|
|
56
|
+
dev = ebyst.Device.from_bsdl("bsdl/BSDLLCMXO2-256HCQFN32.BSM")
|
|
57
|
+
ctl = ebyst.TapController(drv)
|
|
58
|
+
ctl.detect_chain()
|
|
59
|
+
ctl.add_device(dev)
|
|
60
|
+
ctl.validate_chain()
|
|
61
|
+
ctl.extest()
|
|
62
|
+
async with asyncio.TaskGroup() as tg:
|
|
63
|
+
tg.create_task(loopback_test(ctl, dev.pinmap['PB2C'], dev.pinmap['PB2A']))
|
|
64
|
+
tg.create_task(loopback_test(ctl, dev.pinmap['PB4C'], dev.pinmap['PB4D']))
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
logging.basicConfig()
|
|
68
|
+
logging.getLogger().setLevel(logging.INFO)
|
|
69
|
+
asyncio.run(main())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
see also `tests/test_async.py`
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ebyst"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
dependencies = [
|
|
5
|
+
"pyftdi",
|
|
6
|
+
"bitarray",
|
|
7
|
+
]
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Sijmen Woutersen", email="sijmen.woutersen@gmail.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "Boundary scan test library"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/swolix/ebyst"
|
|
22
|
+
Issues = "https://github.com/swolix/ebyst/issues"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["hatchling"]
|
|
26
|
+
build-backend = "hatchling.build"
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Copyright (c) 2024 Sijmen Woutersen
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
# copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
# SOFTWARE.
|
|
20
|
+
import logging
|
|
21
|
+
import pyparsing as pp
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
class BSDLFile:
|
|
26
|
+
"""BSDL parser"""
|
|
27
|
+
|
|
28
|
+
class Declaration:
|
|
29
|
+
def __init__(self, name, type_, value=None, direction=None, range_start=None, range_end=None, owner=None):
|
|
30
|
+
self.name = name
|
|
31
|
+
self.type_ = type_
|
|
32
|
+
self.value = value
|
|
33
|
+
self.direction = direction
|
|
34
|
+
self.range_start = range_start
|
|
35
|
+
self.range_end = range_end
|
|
36
|
+
self.owner = owner
|
|
37
|
+
|
|
38
|
+
def __init__(self, name):
|
|
39
|
+
self.name = name
|
|
40
|
+
self.generics = {}
|
|
41
|
+
self.ports = {}
|
|
42
|
+
self.constants = {}
|
|
43
|
+
self.attributes = {}
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def parse(cls, f):
|
|
47
|
+
comments = "--" + pp.SkipTo(pp.LineEnd())
|
|
48
|
+
identifier = pp.Word(init_chars=pp.srange("[a-zA-Z]"), body_chars=pp.srange("[a-zA-Z0-9_]"))
|
|
49
|
+
string_literal = pp.Combine(pp.QuotedString("\"") +
|
|
50
|
+
pp.ZeroOrMore(pp.Suppress(pp.Literal("&")) + pp.QuotedString("\"")), adjacent=False)
|
|
51
|
+
numeric_literal = pp.Or((pp.pyparsing_common.sci_real, pp.pyparsing_common.integer))
|
|
52
|
+
primary = pp.Forward()
|
|
53
|
+
enumeration_literal = (pp.Suppress(pp.Literal("(")) + primary +
|
|
54
|
+
pp.ZeroOrMore(pp.Suppress(pp.Literal(",")) + primary) + pp.Suppress(pp.Literal(")")))
|
|
55
|
+
literal = pp.Or((numeric_literal, enumeration_literal, string_literal))
|
|
56
|
+
primary <<= pp.Or((identifier, literal))
|
|
57
|
+
expression = primary
|
|
58
|
+
mode = pp.Or((pp.CaselessKeyword("in"), pp.CaselessKeyword("out"), pp.CaselessKeyword("inout"),
|
|
59
|
+
pp.CaselessKeyword("buffer"), pp.CaselessKeyword("linkage")))
|
|
60
|
+
range = (pp.Suppress(pp.Literal("(")) + pp.pyparsing_common.integer +
|
|
61
|
+
pp.Or((pp.CaselessKeyword("to"), pp.CaselessKeyword("downto"))) +
|
|
62
|
+
pp.pyparsing_common.integer + pp.Suppress(pp.Literal(")")))
|
|
63
|
+
subtype_indication = identifier + pp.Optional(range)
|
|
64
|
+
declaration = pp.Group(identifier + pp.Suppress(pp.Literal(":")) +
|
|
65
|
+
pp.Optional(mode) + subtype_indication +
|
|
66
|
+
pp.Optional(pp.Suppress(pp.Literal(":=")) + expression))
|
|
67
|
+
interface_list = declaration + pp.ZeroOrMore(pp.Suppress(pp.Literal(";")) + declaration)
|
|
68
|
+
generic_clause = pp.Group(pp.CaselessKeyword("generic") + pp.Suppress(pp.Literal("(")) + interface_list +
|
|
69
|
+
pp.Suppress(pp.Literal(")")) + pp.Suppress(pp.Literal(";")))
|
|
70
|
+
port_clause = pp.Group(pp.CaselessKeyword("port") + pp.Suppress(pp.Literal("(")) + interface_list +
|
|
71
|
+
pp.Suppress(pp.Literal(")")) + pp.Suppress(pp.Literal(";")))
|
|
72
|
+
use_clause = pp.Group(pp.CaselessKeyword("use") + identifier + pp.Literal(".") + identifier +
|
|
73
|
+
pp.ZeroOrMore(pp.Literal(",") + identifier) +
|
|
74
|
+
pp.Suppress(pp.Literal(";")))
|
|
75
|
+
class_ = pp.Or((pp.CaselessKeyword("entity"), pp.CaselessKeyword("signal")))
|
|
76
|
+
attribute_specification = pp.Group(pp.CaselessKeyword("attribute") + identifier +
|
|
77
|
+
pp.Suppress(pp.CaselessKeyword("of")) + identifier +
|
|
78
|
+
pp.Suppress(pp.Literal(":")) + class_ +
|
|
79
|
+
pp.Suppress(pp.CaselessKeyword("is")) + expression +
|
|
80
|
+
pp.Suppress(pp.Literal(";")))
|
|
81
|
+
constant_declaration = pp.Group(pp.CaselessKeyword("constant") + declaration + pp.Suppress(pp.Literal(";")))
|
|
82
|
+
entity_header = pp.Optional(generic_clause) + pp.Optional(port_clause)
|
|
83
|
+
entity_declarative_item = pp.Or((use_clause, attribute_specification, constant_declaration))
|
|
84
|
+
entity_declaration = (pp.Suppress(pp.CaselessKeyword("entity")) + identifier + pp.Suppress(pp.CaselessKeyword("is")) +
|
|
85
|
+
entity_header + pp.ZeroOrMore(entity_declarative_item) +
|
|
86
|
+
pp.Suppress(pp.CaselessKeyword("end") + pp.Optional(pp.CaselessKeyword("entity")) +
|
|
87
|
+
pp.Optional(identifier)) +
|
|
88
|
+
pp.Suppress(pp.Literal(";")))
|
|
89
|
+
bsdl_file = entity_declaration + pp.StringEnd()
|
|
90
|
+
|
|
91
|
+
bsdl_file.ignore(comments)
|
|
92
|
+
|
|
93
|
+
parsed = bsdl_file.parse_string(f.read())
|
|
94
|
+
# parsed.pprint()
|
|
95
|
+
r = cls(parsed[0])
|
|
96
|
+
for item in parsed[1:]:
|
|
97
|
+
if item[0] == "generic":
|
|
98
|
+
for generic in item[1:]:
|
|
99
|
+
assert len(generic) == 3
|
|
100
|
+
generic = BSDLFile.Declaration(generic[0], generic[1], value=generic[2])
|
|
101
|
+
r.generics[generic.name] = generic
|
|
102
|
+
elif item[0] == "port":
|
|
103
|
+
for port in item[1:]:
|
|
104
|
+
if len(port) == 3:
|
|
105
|
+
port = BSDLFile.Declaration(port[0], port[2], direction=port[1])
|
|
106
|
+
elif len(port) == 6:
|
|
107
|
+
port = BSDLFile.Declaration(port[0], port[2], direction=port[1],
|
|
108
|
+
range_start=port[3], range_end=port[5])
|
|
109
|
+
r.ports[port.name] = port
|
|
110
|
+
elif item[0] == "use":
|
|
111
|
+
pass
|
|
112
|
+
elif item[0] == "constant":
|
|
113
|
+
assert len(item) == 2
|
|
114
|
+
assert len(item[1]) == 3
|
|
115
|
+
constant = BSDLFile.Declaration(name=item[1][0], type_=item[1][1], value=item[1][2])
|
|
116
|
+
r.constants[constant.name] = constant
|
|
117
|
+
elif item[0] == "attribute":
|
|
118
|
+
assert len(item) >= 5
|
|
119
|
+
# TODO fix tuples
|
|
120
|
+
attribute = BSDLFile.Declaration(name=item[1], type_ = item[3], owner=item[2], value=item[4])
|
|
121
|
+
r.attributes[attribute.name] = attribute
|
|
122
|
+
else:
|
|
123
|
+
assert False
|
|
124
|
+
|
|
125
|
+
return r
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Copyright (c) 2024 Sijmen Woutersen
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
# copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
# SOFTWARE.
|
|
20
|
+
import logging
|
|
21
|
+
import re
|
|
22
|
+
from pprint import pprint
|
|
23
|
+
|
|
24
|
+
from bitarray import bitarray
|
|
25
|
+
|
|
26
|
+
from .bsdl import BSDLFile
|
|
27
|
+
|
|
28
|
+
SPACE = "[ \r\n\t]*"
|
|
29
|
+
|
|
30
|
+
RE_OPCODE = re.compile(f"{SPACE}(?P<instruction>[A-Za-z][A-Za-z_0-9]*){SPACE}\\((?P<opcode>[01]+(,{SPACE}[01]+)*)\\){SPACE}(,)?")
|
|
31
|
+
RE_CELL = re.compile(f"{SPACE}(?P<index>[0-9]+){SPACE}\\((?P<format>([^\\)\\(]*(\\([^\\)]*\\))*)*)\\)({SPACE},)?")
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
class StdLogicPattern:
|
|
36
|
+
"""Bit pattern supporting std_logic values"""
|
|
37
|
+
def __init__(self, pattern):
|
|
38
|
+
for c in pattern.upper():
|
|
39
|
+
if not c in "01X": raise Exception(f"{c} not supported in bit pattern")
|
|
40
|
+
self.pattern = pattern.upper()
|
|
41
|
+
|
|
42
|
+
def __eq__(self, other):
|
|
43
|
+
if len(self.pattern) != len(other): return False
|
|
44
|
+
for c1, c2 in zip(self.pattern, other):
|
|
45
|
+
if c1 != "X" and int(c1) != int(c2): return False
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
def __str__(self):
|
|
49
|
+
return f"StdLogicPattern('{self.pattern}')"
|
|
50
|
+
|
|
51
|
+
def to_bitarray(self):
|
|
52
|
+
return bitarray(self.pattern.replace("X", "0"))
|
|
53
|
+
|
|
54
|
+
class Cell:
|
|
55
|
+
"""Represents a boundary scan cell"""
|
|
56
|
+
def __init__(self, num, cell, port, function, safe, ctl_cell=None, out_dis_ctl=None, out_dis_val=None):
|
|
57
|
+
self.num = num
|
|
58
|
+
self.cell = cell
|
|
59
|
+
self.port = port
|
|
60
|
+
self.function = function
|
|
61
|
+
self.safe = safe
|
|
62
|
+
self.ctl_cell = int(ctl_cell) if not ctl_cell is None else None
|
|
63
|
+
self.out_dis_ctl = int(out_dis_ctl) if not out_dis_ctl is None else None
|
|
64
|
+
self.out_dis_val = out_dis_val
|
|
65
|
+
self.in_value = None
|
|
66
|
+
self.out_value = 0
|
|
67
|
+
|
|
68
|
+
self.set_safe()
|
|
69
|
+
|
|
70
|
+
def set_safe(self):
|
|
71
|
+
if self.safe.upper() != 'X':
|
|
72
|
+
self.out_value = int(self.safe)
|
|
73
|
+
|
|
74
|
+
def __repr__(self):
|
|
75
|
+
return f"{self.cell} @ {self.num}"
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def parse(cls, num, parameters):
|
|
79
|
+
return cls(num, *[p.strip() for p in parameters.split(",")])
|
|
80
|
+
|
|
81
|
+
class Pin:
|
|
82
|
+
"""Represents a pin with control & data cell"""
|
|
83
|
+
def __init__(self, name, data_cell, control_cell):
|
|
84
|
+
self.name = name
|
|
85
|
+
self.data_cell = data_cell
|
|
86
|
+
self.control_cell = control_cell
|
|
87
|
+
|
|
88
|
+
def output_enabled(self):
|
|
89
|
+
if self.data_cell.cell != "BC_7" or self.control_cell.cell != "BC_2": raise Exception("Not supported")
|
|
90
|
+
return self.control_cell.out_value != self.data_cell.out_dis_ctl
|
|
91
|
+
|
|
92
|
+
def output_enable(self, enable=True):
|
|
93
|
+
if self.data_cell.cell != "BC_7" or self.control_cell.cell != "BC_2": raise Exception("Not supported")
|
|
94
|
+
if enable:
|
|
95
|
+
self.control_cell.out_value = [1, 0][self.data_cell.out_dis_ctl]
|
|
96
|
+
else:
|
|
97
|
+
self.control_cell.out_value = self.data_cell.out_dis_ctl
|
|
98
|
+
|
|
99
|
+
def set_value(self, value):
|
|
100
|
+
if self.data_cell.cell != "BC_7" or self.control_cell.cell != "BC_2": raise Exception("Not supported")
|
|
101
|
+
self.data_cell.out_value = 1 if value else 0
|
|
102
|
+
|
|
103
|
+
def get_value(self):
|
|
104
|
+
if self.data_cell.cell != "BC_7" or self.control_cell.cell != "BC_2": raise Exception("Not supported")
|
|
105
|
+
return self.data_cell.out_value if self.output_enabled() else self.data_cell.in_value
|
|
106
|
+
|
|
107
|
+
def __repr__(self):
|
|
108
|
+
if self.output_enabled():
|
|
109
|
+
return f"<PIN {self.name}: output: {self.data_cell.out_value}>"
|
|
110
|
+
else:
|
|
111
|
+
return f"<PIN {self.name}: input>: {self.data_cell.in_value}>"
|
|
112
|
+
|
|
113
|
+
class Device:
|
|
114
|
+
def __init__(self, irlen, idcode=None, opcodes=None, cells=[]):
|
|
115
|
+
self.irlen = irlen
|
|
116
|
+
self.idcode = idcode
|
|
117
|
+
if opcodes is None: opcodes = {'BYPASS': bitarray('1' * irlen)}
|
|
118
|
+
if not 'BYPASS' in opcodes: raise ValueError("BYPASS command is required")
|
|
119
|
+
self.opcodes = opcodes
|
|
120
|
+
self.cells = cells
|
|
121
|
+
self.pinmap = {}
|
|
122
|
+
for cell in self.cells:
|
|
123
|
+
if cell.port != "*":
|
|
124
|
+
pin = Pin(cell.port, cell, self.cells[cell.ctl_cell] if not cell.ctl_cell is None else None)
|
|
125
|
+
self.pinmap[pin.name] = pin
|
|
126
|
+
|
|
127
|
+
def update_br(self, br):
|
|
128
|
+
if len(br) != len(self.cells): raise ValueError("Invalid br length")
|
|
129
|
+
for i, v in enumerate(br):
|
|
130
|
+
self.cells[i].in_value = v
|
|
131
|
+
|
|
132
|
+
def generate_br(self):
|
|
133
|
+
r = bitarray()
|
|
134
|
+
for cell in self.cells:
|
|
135
|
+
r.append(cell.out_value)
|
|
136
|
+
return r
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def from_bsdl(fn):
|
|
140
|
+
with open(fn, "rt") as f:
|
|
141
|
+
bsdi_file = BSDLFile.parse(f)
|
|
142
|
+
|
|
143
|
+
irlen = int(bsdi_file.attributes['INSTRUCTION_LENGTH'].value)
|
|
144
|
+
idcode = StdLogicPattern(bsdi_file.attributes['IDCODE_REGISTER'].value[::-1])
|
|
145
|
+
|
|
146
|
+
opcodes = {}
|
|
147
|
+
opcode_str = bsdi_file.attributes['INSTRUCTION_OPCODE'].value
|
|
148
|
+
while True:
|
|
149
|
+
m = RE_OPCODE.match(opcode_str)
|
|
150
|
+
if m:
|
|
151
|
+
opcode = m['opcode'].split(",")
|
|
152
|
+
if len(opcode) == 1:
|
|
153
|
+
ba = bitarray(opcode[0].strip())
|
|
154
|
+
ba.reverse()
|
|
155
|
+
opcodes[m['instruction'].upper()] = ba
|
|
156
|
+
else:
|
|
157
|
+
# not supported
|
|
158
|
+
pass
|
|
159
|
+
opcode_str = opcode_str[m.end():]
|
|
160
|
+
else:
|
|
161
|
+
break
|
|
162
|
+
if len(opcode_str) != 0: raise Exception("Invalid INSTRUCTION_OPCODE format")
|
|
163
|
+
|
|
164
|
+
brlen = int(bsdi_file.attributes['BOUNDARY_LENGTH'].value)
|
|
165
|
+
|
|
166
|
+
cells = [None] * brlen
|
|
167
|
+
cell_str = bsdi_file.attributes['BOUNDARY_REGISTER'].value.strip()
|
|
168
|
+
while True:
|
|
169
|
+
m = RE_CELL.match(cell_str)
|
|
170
|
+
if m:
|
|
171
|
+
cell = Cell.parse(int(m['index']), m['format'])
|
|
172
|
+
cells[cell.num] = cell
|
|
173
|
+
cell_str = cell_str[m.end():]
|
|
174
|
+
else:
|
|
175
|
+
break
|
|
176
|
+
if len(cell_str) != 0: raise Exception("Invalid BOUNDARY_REGISTER format")
|
|
177
|
+
|
|
178
|
+
return Device(irlen=irlen, idcode=idcode, opcodes=opcodes, cells=cells)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Copyright (c) 2024 Sijmen Woutersen
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
# copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
# SOFTWARE.
|
|
20
|
+
from bitarray import bitarray
|
|
21
|
+
|
|
22
|
+
class Driver:
|
|
23
|
+
def reset(self):
|
|
24
|
+
self.transmit_tms_str(bitarray('11111'))
|
|
25
|
+
|
|
26
|
+
def transfer(self, tms: int, tdi: int) -> int:
|
|
27
|
+
raise NotImplementedError()
|
|
28
|
+
|
|
29
|
+
def transmit_tms_str(self, tms_str: bitarray, tdi=0):
|
|
30
|
+
for tms in tms_str:
|
|
31
|
+
self.transfer(tms, tdi)
|
|
32
|
+
|
|
33
|
+
def transfer_tdi_tdo_str(self, tdi_str: bitarray, first_tms=0, last_tms=0) -> bitarray:
|
|
34
|
+
r = bitarray()
|
|
35
|
+
for tdi in tdi_str[:-1]:
|
|
36
|
+
r.append(self.transfer(first_tms, tdi))
|
|
37
|
+
r.append(self.transfer(last_tms, tdi_str[-1]))
|
|
38
|
+
return r
|
|
39
|
+
|
|
40
|
+
def transmit_tdi_str(self, tdi_str: bitarray, first_tms=0, last_tms=0):
|
|
41
|
+
self.transfer_tdi_tdo_str(tdi_str, first_tms, last_tms)
|
|
42
|
+
|
|
43
|
+
def receive_tdo_str(self, n, first_tms=0, first_tdi=0, last_tms=None, last_tdi=None) -> bitarray:
|
|
44
|
+
if last_tms is None: last_tms = first_tms
|
|
45
|
+
if last_tdi is None: last_tdi = first_tdi
|
|
46
|
+
if n < 1: raise ValueError("n must be > 0")
|
|
47
|
+
if n == 1 and first_tms != last_tms: raise ValueError("last_tms must be first_tms when n == 1")
|
|
48
|
+
if n == 1 and first_tdi != last_tdi: raise ValueError("last_tdi must be first_tdi when n == 1")
|
|
49
|
+
r = bitarray()
|
|
50
|
+
for i in range(n-1):
|
|
51
|
+
r.append(self.transfer(first_tms, first_tdi))
|
|
52
|
+
r.append(self.transfer(last_tms, last_tdi))
|
|
53
|
+
return r
|