ppscpi
ppscpi is a standalone network server for remote control of a PulsePins device.
The implementation is in c++/ppscpi.cc and the SCPI session/server helpers are in c++/scpi_server.hh.
Transport and startup
ppscpi listens on TCP port 5025.
On startup it:
- configures realtime scheduling and locks memory pages
- creates the shared
HostRuntimeand top-levelFPGAwrapper - applies the shared clock/PLL startup policy; the FPGA reset-manager pulse runs only if
-reset_FPGAorPP_RESET_FPGAis requested - reports the measured clocks using the frequency-meter block
- accepts SCPI-style commands over the network
Session model
Each client connection gets its own SCPI session object.
Session state includes:
- one
streamer,readback, andcounterwrapper set bound to the sharedFPGA - the currently loaded
Sequence - whether readback checking is enabled
- whether streaming should use forced triggering
The session does not persist across reconnects.
Supported commands
Standard commands:
*IDN?- identify the instrument*RST- clear loaded sequence and session state*CLS- clear status and error queue*OPC/*OPC?- operation complete flag/query*WAI- compatibility no-op;STREAMitself is synchronous*ESR?- standard event status register*STB?- status byteSYST:ERR?- query and drain the error queue
PulsePins-specific commands:
TEST1- run a built-in short self-test sequenceSEQ <data>- parse and load a sequence from textual representation, includingf,final, and control-flow records supported byparse_sequence_from_stream(...)CHECK <bool>- enable or disable readback checking duringSTREAMCHECK?- query the current check settingCLOCK:STREAMER?- query the measured streamer clock frequency in HzSTREAM- send the currently loaded sequence and trigger executionDISCONNECT- close the current client session; theppscpiserver keeps runningTERMINATE- stop theppscpiserver process
Typical flow
- Connect to TCP port
5025 - Send
*RST - Send
SEQ ...with the sequence payload - Optionally send
CHECK ON - Send
STREAM - Query
SYST:ERR?if needed
Host-side Python and Jupyter
Jupyter should normally run on the host computer, not on the DE10-Nano. The board only needs to run ppscpi; the notebook talks to it over Ethernet.
The lightweight host-side Python client lives in python/pulsepins/ and uses only the Python standard library. From a checkout, make that directory importable first:
export PYTHONPATH=/path/to/PulsePins/python
For notebooks, an editable host-side install is often more convenient:
python3 -m pip install -e /path/to/PulsePins/python
That install also provides example commands such as pulsepins-ppscpi-check, pulsepins-ppscpi-hello, pulsepins-notebook-workflow, pulsepins-timeline-preview, pulsepins-timeline-stream, and pulsepins-timeline-sweep. Add --self-test to pulsepins-ppscpi-check to run the built-in TEST1 hardware smoke path after connecting.
Then a notebook or script can drive the board with:
from pulsepins import PulsePins
with PulsePins("de10nano") as pp:
print(pp.idn())
print(pp.streamer_clock_hz())
pp.reset()
pp.load_sequence("""
d 10 0xff
d 5 0x00
d 2 0b0101
f
""")
pp.check(False)
pp.stream()
load_sequence(...) accepts normal multiline PulsePins text sequence input and flattens it into the single-line SEQ ... command that ppscpi expects. Because the current SCPI transport is line-oriented, one uploaded sequence command must fit within the server's 64 KiB line limit. Larger notebook-generated sequences should be played through file-based tools for now, or wait for a future chunked/binary upload command.
If SEQ or STREAM returns an error response, the Python client drains SYST:ERR? and raises PulsePinsCommandError with the queued server-side diagnostic text.
For notebook-oriented pulse construction, the same package provides Timeline:
from pulsepins import PulsePins
with PulsePins("de10nano") as pp:
timeline = pp.timeline(unit="us")
timeline.channel("laser", bit=0)
timeline.channel("camera", bit=1)
timeline.pulse("laser", start=10, duration=5)
timeline.pulse("camera", start=20, duration=10)
pp.reset()
pp.run(timeline, force_trigger=True)
Timeline.to_sequence(...) returns the generated text sequence, Timeline.to_csv() writes browser-compatible Timeline CSV, Timeline.to_draft_json() writes browser-compatible draft JSON, Timeline.to_vcd(...) writes a scalar waveform trace, and notebooks render a lightweight SVG preview when the timeline object is evaluated.
The same workflow is available as runnable examples:
PYTHONPATH=python python3 python/examples/timeline_preview.py --svg timeline.svg --csv timeline.csv --draft timeline.json --vcd timeline.vcd
PYTHONPATH=python python3 python/examples/timeline_stream.py de10nano --print-sequence
PYTHONPATH=python python3 python/examples/timeline_sweep.py de10nano --delays-us 0 5 10
With the editable install, use the installed command names instead:
pulsepins-ppscpi-check de10nano --self-test
pulsepins-notebook-workflow de10nano --output-dir previews --run
pulsepins-timeline-preview --svg timeline.svg --csv timeline.csv --draft timeline.json --vcd timeline.vcd
pulsepins-timeline-stream de10nano --print-sequence
pulsepins-timeline-sweep de10nano --delays-us 0 5 10
Notes
STREAMuses the same send/trigger path as the local tools, including optional readback verification.SEQstores the parsed sequence in memory; nothing is transmitted to the streamer untilSTREAMis issued.- If the loaded sequence does not end with
final Vand the server was not started with-t,-random_final, orPP_RANDOM_FINAL,STREAMappends a no-modify final terminator so outputs remain at the last sequence value. Thefrecord only requests forced triggering. - Repeated
STREAMcommands reuse the stored sequence exactly as parsed; the cached session sequence is not rewritten by readback checking or final-output preparation. - Before each hardware-touching run (
TEST1andSTREAM),ppscpiresets the streamer core, readback encoder, and counters so repeated commands in the same process/session start from a clean hardware state. - If
TEST1orSTREAMreturnsFAILURE,ppscpialso pushes an execution-error record intoSYST:ERR?with the aggregated PulsePins return-code bits so remote clients can distinguish timeout, readback, CRC, buffer, and overflow failures. - When
CHECK ONis active and no explicit startup-timeoutwas supplied, the shared workflow uses a conservative default readback timeout: 2s for the first readback element and 2s for later idle gaps. Startppscpiwith-timeout 0to disable that protection or-timeout <value>to override it. - Finite
STREAMruns also inherit the internal 10 s streamer-completion timeout from the shared playback path. When that timeout fires, the user-facing message istimed out waiting for streamer completion (10 s internal limit). - Hardware-touching commands are serialized across sessions through the shared FPGA lock, so multiple clients can stay connected without racing each other on streamer/reset state.
- The server is intended for remote orchestration, not for high-throughput binary bulk transfer.
- Command-handler exceptions are converted into SCPI error/status state instead of tearing down the whole server process.
- After
DISCONNECT, clients can reconnect and start a fresh independent session. TERMINATEis the explicit server-shutdown command; it closes the current session and stops the process.
Related pages
pptool.mdcpp.mdreadback.mdbuild.md