# EByST
Boundary scan test framework for board validation

# Basic example
```python
# initialize JTAG interface driver (currently only FTDI chips with MPSSE are supported)
drv = ebyst.drivers.MPSSE(ebyst.drivers.MPSSE.list_devices()[0])
ctl = ebyst.TapController(drv)
ctl.detect_chain()

# Add device(s) to chain
dev = ebyst.Device.from_bsdl("bsdl/BSDLLCMXO2-256HCQFN32.BSM")
ctl.add_device(dev)
ctl.validate_chain()

# Start test
ctl.extest()

# Loopback test (assuming loopback on pins
dev.pinmap['O'].output_enable(True)
dev.pinmap['I'].output_enable(False)
dev.pinmap['O'].set_value(1)
await ctl.cycle() # drive output
await ctl.cycle() # sample input
print(dev.pinmap['I'].get_value())
dev.pinmap['O'].set_value(0)
await ctl.cycle() # drive output
await ctl.cycle() # sample input
print(dev.pinmap['I'].get_value())
```
# Interfaces
Ebyst supports various interfaces to test (connections with) other ICs connected to the UUT

```python
# I2C test
i2c = ebyst.interfaces.I2C(dev.pinmap['PB9A'], dev.pinmap['PB4B'])
await i2c.init()
dev_address = 0xa0
reg_address = 0x10
data = 0xa5
print(f"Writing {dev_address:02x}:{reg_address:02x} <= {data:02x}")
await i2c.write(0xa0, 0x10, 0xa5)
print(f"Reading {dev_address:02x}:{reg_address:02x} => ", end='')
await x = i2c.read(0xa0, 0x10)
print(f"{x:02x}")

ctl.reset()
```

Other interfaces (a.o.):
* Memory (DDR3, DDR4, HyperRAM)
* I2C
* SPI (and various SPI Flashes)
* MDI/O

See `src/interfaces` for the complete list

# Async
The library uses `asyncio` to allow running multiple tests in parallel.
When the loopback test and I2C test above are put in different tasks, they share the same boundary scan cycles,
meaning they run completely parallel
(Note that this only works when they are not using different pins, if pins are shared between tests, make sure to
schedule them appropriately)

Example;
```python
async def main():
    drv = ebyst.drivers.MPSSE(ebyst.drivers.MPSSE.list_devices()[0])
    dev = ebyst.Device.from_bsdl("bsdl/BSDLLCMXO2-256HCQFN32.BSM")
    ctl = ebyst.TapController(drv)
    ctl.detect_chain()
    ctl.add_device(dev)
    ctl.validate_chain()
    ctl.extest()
    async with asyncio.TaskGroup() as tg:
        tg.create_task(loopback_test(dev.pinmap['PB2C'], dev.pinmap['PB2A']))
        tg.create_task(loopback_test(dev.pinmap['PB4C'], dev.pinmap['PB4D']))

if __name__ == "__main__":
    logging.basicConfig()
    logging.getLogger().setLevel(logging.INFO)
    asyncio.run(main())
```

see also `tests/test_async.py`

# Tracing
Generate .vcd traces for selected pins;
```python
    pins = {
        'MDC':      dev.pinmap["IO_Y12"],
        'MDIO':     dev.pinmap["IO_Y13"],
    }
    ctl.trace("mdio.vcd", **pins)
    mdio = MDIO(**pins)
```

# AC coupled nets
1149.6 is supported to test AC coupled nets;

```python
# Start test
ctl.extest_pulse()

# Loopback test (assuming loopback on pins)
dev.pinmap['O'].output_enable(True)
dev.pinmap['I'].output_enable(False)
dev.pinmap['O'].set_value(1) # generate a pulse
await ctl.cycle() # drive output
print(dev.pinmap['I'].get_value())
await ctl.cycle() # sample input
print(dev.pinmap['I'].get_value()) # should be 1
await ctl.cycle()
print(dev.pinmap['I'].get_value()) # should be 0
```

# STAPL player
ebyst comes with a basic stapl player called stapyl;
```
staplay ftdi://0x1514:0x2008:001UD001/1 polarfire.stapl READ_IDCODE
```

# STAPL recorder
ebyst comes with a basic stapl recorder, allowing the generation of (limited) stapl files of performed boundary scan options.

```
    ctl.detect_chain()
    ctl.add_device(dev)
    ctl.validate_chain()

    ctl.start_stapl_recording(sys.stdout)

    try:
        ctl.extest()
        await configure_dev(dev)
    except KeyboardInterrupt:
        pass
    finally:
        ctl.stop_stapl_recording(reset=False)
        ctl.reset()
```

this allows for easily generating stapl files for (e.g.) board intializations to be replayed on other stapl players.

# Installation
Releases are pushed to pypi, install via; `pip install ebyst`.
