#
# ------------------------------------------------------------
# Copyright (c) All rights reserved
# SiLab, Institute of Physics, University of Bonn
# ------------------------------------------------------------
#
"""Sequence generator driver for the seq_gen FPGA module."""
from basil.HL.RegisterHardwareLayer import RegisterHardwareLayer
[docs]
class seq_gen(RegisterHardwareLayer):
"""Sequence generator controller interface for seq_gen FPGA module."""
_registers = {
"RESET": {"descr": {"addr": 0, "size": 8, "properties": ["writeonly"]}},
"VERSION": {"descr": {"addr": 0, "size": 8, "properties": ["ro"]}},
"READY": {"descr": {"addr": 1, "size": 1, "properties": ["ro"]}},
"START": {"descr": {"addr": 1, "size": 8, "properties": ["writeonly"]}},
"EN_EXT_START": {"descr": {"addr": 2, "size": 1}},
"CLK_DIV": {"descr": {"addr": 3, "size": 8}},
"SIZE": {"descr": {"addr": 4, "size": 32}},
"WAIT": {"descr": {"addr": 8, "size": 32}},
"REPEAT": {"descr": {"addr": 12, "size": 32}},
"REPEAT_START": {"descr": {"addr": 16, "size": 32}},
"NESTED_START": {"descr": {"addr": 20, "size": 32}},
"NESTED_STOP": {"descr": {"addr": 24, "size": 32}},
"NESTED_REPEAT": {"descr": {"addr": 28, "size": 32}},
"MEM_BYTES": {"descr": {"addr": 32, "size": 32, "properties": ["ro"]}},
}
_require_version = "==3"
def __init__(self, intf, conf):
"""Initialize the sequencer driver."""
super(seq_gen, self).__init__(intf, conf)
self._seq_mem_offset = 64 # in bytes
[docs]
def init(self):
"""Initialize the sequencer and read the memory size from hardware."""
super(seq_gen, self).init()
self._seq_mem_size = self.get_mem_size()
[docs]
def reset(self):
"""Soft reset the sequencer.
Clears internal counters and output state on the next clock edge.
Must have a rising edge on the sequencer clock before new data is
written to memory.
"""
self.RESET = 0
[docs]
def start(self):
"""Start the sequencer.
Writes to the START register (addr 1). The sequence begins on the
next SEQ_CLK edge after the write. Only effective when DONE/READY
is high (sequence not already running).
"""
self.START = 0
[docs]
def set_size(self, value):
"""Set the number of output words in the sequence.
Each word contains OUT_BITS (one sample per track). Addresses 4-7.
"""
self.SIZE = value
[docs]
def get_size(self):
"""Return the configured sequence size in output words."""
return self.SIZE
[docs]
def set_wait(self, value):
"""Set wait cycles inserted between repetitions.
Only applies when REPEAT > 0. Addresses 8-11.
"""
self.WAIT = value
[docs]
def get_wait(self):
"""Return the configured wait cycles between repetitions."""
return self.WAIT
[docs]
def set_clk_divide(self, value):
"""Set the clock division factor for SEQ_CLK.
The sequencer advances one step every CLK_DIV + 1 clock cycles.
Default: 1 (divide by 1, i.e. full rate). Address 3.
"""
self.CLK_DIV = value
[docs]
def get_clk_divide(self):
"""Return the clock division factor."""
return self.CLK_DIV
[docs]
def set_repeat_start(self, value):
"""Set the repeat start position.
When repeating, the sequence jumps to this position instead of
starting from 0. Addresses 16-19.
"""
self.REPEAT_START = value
[docs]
def get_repeat_start(self):
"""Return the repeat start position."""
return self.REPEAT_START
[docs]
def set_repeat(self, value):
"""Set the repeat count.
0 = repeat forever. The sequence repeats from REP_START (or 0)
each time. Addresses 12-15.
"""
self.REPEAT = value
[docs]
def get_repeat(self):
"""Return the repeat count."""
return self.REPEAT
[docs]
def is_done(self):
"""Return True if the sequencer has finished its sequence.
Includes all repeats. Returns False while running. Aliases is_ready.
"""
return self.is_ready
@property
def is_ready(self):
"""Read the DONE/READY register (addr 1, bit 0).
Returns True when the sequencer is idle and ready to accept a new
start trigger. While the sequence is running (including all
configured repetitions) this reads False.
The ``@property`` decorator makes this an attribute-like access —
call it without parentheses as ``daq["seq0"].is_ready``, not
``.is_ready()``.
``.is_done()`` and ``.get_done()`` are aliases that return the
same value.
"""
return self.READY
[docs]
def get_done(self):
"""Alias for is_ready. Returns True if sequencer is finished."""
return self.is_ready
[docs]
def set_en_ext_start(self, value):
"""Enable or disable external start via the SEQ_EXT_START pin.
When enabled (1), the SEQ_EXT_START pin rising edge triggers the
sequence. When disabled (0), only software .start() works.
Address 2.
"""
self.EN_EXT_START = value
[docs]
def get_en_ext_start(self):
"""Return whether external start is enabled."""
return self.EN_EXT_START
[docs]
def set_nested_start(self, value):
"""Set the nested loop start position. Addresses 20-23."""
self.NESTED_START = value
[docs]
def get_nested_start(self):
"""Return the nested loop start position."""
return self.NESTED_START
[docs]
def set_nested_stop(self, value):
"""Set the nested loop stop position. Addresses 24-27."""
self.NESTED_STOP = value
[docs]
def get_nested_stop(self):
"""Return the nested loop stop position."""
return self.NESTED_STOP
[docs]
def set_nested_repeat(self, value):
"""Set the nested loop repeat count. 0 = disabled. Addresses 28-31."""
self.NESTED_REPEAT = value
[docs]
def get_nested_repeat(self):
"""Return the nested loop repeat count."""
return self.NESTED_REPEAT
[docs]
def get_mem_size(self):
"""Return the memory size in bytes."""
return self.MEM_BYTES
[docs]
def set_data(self, data, addr=0):
"""Write sequencer memory (the pattern data) via the bus interface.
Data is interleaved per track by the TrackRegister RL.
Args:
data: Bytes to write to sequencer memory.
addr: Optional byte offset into memory.
"""
if self._seq_mem_size < len(data):
raise ValueError(
"Size of data (%d bytes) is too big for memory (%d bytes)" % (len(data), self._seq_mem_size)
)
self._intf.write(self._conf["base_addr"] + self._seq_mem_offset + addr, data)
[docs]
def get_data(self, size=None, addr=0):
"""Read sequencer memory (the pattern data) via the bus interface.
Args:
size: Number of bytes to read (default: all).
addr: Optional byte offset into memory.
Returns:
bytes: The pattern data.
"""
if size and self._seq_mem_size < size:
raise ValueError("Size is too big")
if not size:
return self._intf.read(self._conf["base_addr"] + self._seq_mem_offset + addr, self._seq_mem_size)
else:
return self._intf.read(self._conf["base_addr"] + self._seq_mem_offset + addr, size)