nsqdriver 0.3.0__py3-none-any.whl → 0.3.1__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.
Potentially problematic release.
This version of nsqdriver might be problematic. Click here for more details.
- nsqdriver/__init__.py +1 -1
- nsqdriver/compiler/ns_wave.cpython-310-darwin.so +0 -0
- nsqdriver/compiler/py_wave_asm.cpython-310-darwin.so +0 -0
- nsqdriver/setup.py +2 -7
- nsqdriver-0.3.1.dist-info/METADATA +99 -0
- {nsqdriver-0.3.0.dist-info → nsqdriver-0.3.1.dist-info}/RECORD +8 -8
- {nsqdriver-0.3.0.dist-info → nsqdriver-0.3.1.dist-info}/WHEEL +1 -1
- nsqdriver/compiler/ns_wave.py +0 -483
- nsqdriver/compiler/py_wave_asm.py +0 -538
- nsqdriver-0.3.0.dist-info/METADATA +0 -14
- {nsqdriver-0.3.0.dist-info → nsqdriver-0.3.1.dist-info}/top_level.txt +0 -0
nsqdriver/__init__.py
CHANGED
|
@@ -3,7 +3,7 @@ from .NS_QSYNC import Driver as QSYNCDriver
|
|
|
3
3
|
from .NS_CST import Driver as CSTDriver
|
|
4
4
|
from .compiler.ns_wave import InsChannel
|
|
5
5
|
|
|
6
|
-
version_pack = (0, 3,
|
|
6
|
+
version_pack = (0, 3, 1)
|
|
7
7
|
|
|
8
8
|
__version__ = '.'.join(str(_) for _ in version_pack)
|
|
9
9
|
__all__ = ['MCIDriver', 'QSYNCDriver', 'CSTDriver', 'InsChannel', '__version__']
|
|
Binary file
|
|
Binary file
|
nsqdriver/setup.py
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
from setuptools import setup
|
|
2
|
-
from setuptools.extension import Extension
|
|
3
2
|
from Cython.Build import cythonize
|
|
4
3
|
import numpy as np
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
extension = [Extension("*", ["nsqdriver/wrapper/*.py"], language='c++'),
|
|
8
|
-
Extension("*", ["nsqdriver/*.py"], language='c++')]
|
|
9
|
-
|
|
10
5
|
setup(
|
|
11
|
-
name='
|
|
12
|
-
ext_modules=cythonize(
|
|
6
|
+
name='compiler',
|
|
7
|
+
ext_modules=cythonize([r'nsqdriver/compiler/ns_wave.py', r'nsqdriver/compiler/py_wave_asm.py'], language_level=3),
|
|
13
8
|
include_path=[np.get_include()]
|
|
14
9
|
)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: nsqdriver
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Naishu Q series quantum measurement and control equipment driver interface
|
|
5
|
+
Home-page: https://g2hoyqcmh4.feishu.cn/wiki/wikcnzvyMd82DLZUe2NsI6HxsFc
|
|
6
|
+
Author: Naishu Technology
|
|
7
|
+
Author-email: jilianyi@naishu.tech
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: numpy (>=1.18)
|
|
15
|
+
Requires-Dist: waveforms
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
_ _ ____ ___ ____ _
|
|
19
|
+
| \ | | / ___| / _ \ | _ \ _ __ (_) __ __ ___ _ __
|
|
20
|
+
| \| | \___ \ | | | | | | | | | '__| | | \ \ / / / _ \ | '__|
|
|
21
|
+
| |\ | ___) | | |_| | | |_| | | | | | \ V / | __/ | |
|
|
22
|
+
|_| \_| |____/ \__\_\ |____/ |_| |_| \_/ \___| |_|
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
[](https://github.com/mianyan-echo/UltraMCI/)
|
|
26
|
+
***
|
|
27
|
+
|
|
28
|
+
### 简介
|
|
29
|
+
NSQDriver为一套耐数®量子测控系列设备的Python通用驱动接口。可使用Python作为编程语言,基于此驱动接口可以实现对本公司测控设备的控制,进而完成各种超导量子芯片的表征实验和高保真门操作。
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
+------------------------+
|
|
33
|
+
| Python |
|
|
34
|
+
| +------------------+
|
|
35
|
+
| | NSQDriver |
|
|
36
|
+
+------------------------+
|
|
37
|
+
: : :
|
|
38
|
+
+---------- : -----+
|
|
39
|
+
: : :
|
|
40
|
+
V V V
|
|
41
|
+
+-----------+ +-----------+ +-----------+
|
|
42
|
+
| Device A | | Device B | | QSYNC |
|
|
43
|
+
+-----------+ +-----------+ +-----------+
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
NSQDriver接口可用于统一控制耐数®生产的Q100、MC、QSYNC等一系列量子测控仪器,可详细控制每台仪器每个OUT、IN通道的播放与采集行为,以及查看设备的运行状态等。在一个多台测控设备组成的多量子比特测控系统中,可以有效的在软件层面实现对系统中每台设备的详细控制。
|
|
47
|
+
|
|
48
|
+
耐数®量子测控系列设备详细信息参考: [耐数](http://naishu.tech)
|
|
49
|
+
|
|
50
|
+
***
|
|
51
|
+
### 快速开始
|
|
52
|
+
- 驱动接口的通用流程如下
|
|
53
|
+
```python
|
|
54
|
+
from nsqdriver import MCIDriver, QSYNCDriver
|
|
55
|
+
|
|
56
|
+
# 实例化设备驱动接口
|
|
57
|
+
driver = MCIDriver('127.0.0.1')
|
|
58
|
+
qsync = QSYNCDriver('127.0.0.1')
|
|
59
|
+
|
|
60
|
+
# 连接设备
|
|
61
|
+
qsync.open()
|
|
62
|
+
driver.open()
|
|
63
|
+
|
|
64
|
+
# 初始化系统
|
|
65
|
+
qsync.sync_system()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# 相关参数,参数细节相关参考 8.可用参数列表
|
|
69
|
+
driver.set('Shot', 1024)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- NSWave接口
|
|
73
|
+
- NSWave是一种对于耐数®参数化波形技术的控制接口,参数化波形将量子测控所需的各种门的编辑由主控计算机直接生成波形转化成了设备实时计算
|
|
74
|
+
- 提供了一套直观的时序编辑接口
|
|
75
|
+
```python
|
|
76
|
+
from nsqdriver import InsChannel
|
|
77
|
+
import numpy as np
|
|
78
|
+
|
|
79
|
+
ch = InsChannel() # 生成参数化波形通道实例
|
|
80
|
+
|
|
81
|
+
frame_65e9 = ch.ins_frame(6.5e9) # 生成一个frame
|
|
82
|
+
frame_67e9 = ch.ins_frame(6.7e9) # 生成一个frame
|
|
83
|
+
|
|
84
|
+
gaussian = ch.evlp_gaussian(4500e-9) # 生成一个高斯包络
|
|
85
|
+
square = ch.evlp_square(2500e-9) # 生成一个方波包络
|
|
86
|
+
|
|
87
|
+
wave1 = gaussian * frame_67e9 # 生成一段高斯包络的波形
|
|
88
|
+
wave2 = square * frame_65e9 # 生成一段方波包络的波形
|
|
89
|
+
|
|
90
|
+
ch.ins_reset_frame('phase', frame_65e9)
|
|
91
|
+
ch.ins_reset_frame('phase', frame_67e9)
|
|
92
|
+
ch.wait_for_trigger() # 等待触发到来
|
|
93
|
+
# 循环播放200次 3μs延迟+ 7μs波形
|
|
94
|
+
with ch.ins_loop(times=200) as _loop:
|
|
95
|
+
_loop.play_zero(3000e-9)
|
|
96
|
+
_loop.inc_phase(frame_65e9, np.pi / 2)
|
|
97
|
+
_loop.play_wave(wave2)
|
|
98
|
+
_loop.play_wave(wave1)
|
|
99
|
+
```
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
nsqdriver/NS_CST.py,sha256=_QYCjg1YglIJ4YVmxvISqvy5rExWGlMhyrK9zYIRbRY,7877
|
|
2
2
|
nsqdriver/NS_MCI.py,sha256=ZRf_waF7Q3VnClZnvjogfhVkqW7ltmwZaXXCRU8qA0o,19542
|
|
3
3
|
nsqdriver/NS_QSYNC.py,sha256=68qFZ04KVXb0MSaeKIZVZYqR0UV_tkJYWz7KSz4Dvg0,23281
|
|
4
|
-
nsqdriver/__init__.py,sha256=
|
|
4
|
+
nsqdriver/__init__.py,sha256=BXs30hyT5ovY79xD1SYbTce9c1CPM7q0siC1EjyzGwk,326
|
|
5
5
|
nsqdriver/common.py,sha256=IHfShqgNN3THbe7KD7ICdfFY7JKC_kQj0_KJk-449gQ,499
|
|
6
|
-
nsqdriver/setup.py,sha256=
|
|
6
|
+
nsqdriver/setup.py,sha256=qLy-jDmc3nxnXVoJo4g2cnTvFL3lOiYu_t-vaJ8Z_8o,269
|
|
7
7
|
nsqdriver/compiler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
nsqdriver/compiler/ns_wave.
|
|
9
|
-
nsqdriver/compiler/py_wave_asm.
|
|
8
|
+
nsqdriver/compiler/ns_wave.cpython-310-darwin.so,sha256=WBh_NOyU4e6G6xBitLFXLkkFvKqCn9ptf3cGTEkjEaE,410360
|
|
9
|
+
nsqdriver/compiler/py_wave_asm.cpython-310-darwin.so,sha256=noQ3HaHLl1NUsT29eU_XA3j5Nlp4_egB-nQqjYiy8p8,432200
|
|
10
10
|
nsqdriver/wrapper/AWG_ADC.py,sha256=8K5xLEa_3CKnStWcoJeTj8ufEQfrx6o4l4XGNuOP3Ug,19211
|
|
11
11
|
nsqdriver/wrapper/BD_NSMCI.py,sha256=SMnDbS6iXDiPBhoR9QVJB_lJXhIXRQIVLJRSBEihLzw,12245
|
|
12
12
|
nsqdriver/wrapper/ND_NSMCI.py,sha256=gTyrDObhbggl35V_q-kpDoPsfttIEnDFc6f0-pk_P0k,6288
|
|
13
13
|
nsqdriver/wrapper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
nsqdriver-0.3.
|
|
15
|
-
nsqdriver-0.3.
|
|
16
|
-
nsqdriver-0.3.
|
|
17
|
-
nsqdriver-0.3.
|
|
14
|
+
nsqdriver-0.3.1.dist-info/METADATA,sha256=qGCBFfF-7sVIK42k9E5AqUPxEWrawBmI1fSMhoNDOJU,4091
|
|
15
|
+
nsqdriver-0.3.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
16
|
+
nsqdriver-0.3.1.dist-info/top_level.txt,sha256=o7EbQoFO6BoaG3KGbS9Lg_aRheZSY5KYzoYuI9vx-AI,10
|
|
17
|
+
nsqdriver-0.3.1.dist-info/RECORD,,
|
nsqdriver/compiler/ns_wave.py
DELETED
|
@@ -1,483 +0,0 @@
|
|
|
1
|
-
import copy
|
|
2
|
-
from typing import Union, List, Dict
|
|
3
|
-
from itertools import chain, repeat
|
|
4
|
-
from enum import IntEnum
|
|
5
|
-
from collections import namedtuple
|
|
6
|
-
|
|
7
|
-
import numpy as np
|
|
8
|
-
import waveforms as wf
|
|
9
|
-
|
|
10
|
-
from .py_wave_asm import *
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class QInsPlaceholder(NSQCommand):
|
|
14
|
-
...
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class Wave(GenTagMixin):
|
|
18
|
-
Identity = namedtuple('Relation', ['frame', 'envelop'])
|
|
19
|
-
|
|
20
|
-
class Tag(IntEnum):
|
|
21
|
-
none = 0
|
|
22
|
-
frame = 1
|
|
23
|
-
envelope = 2
|
|
24
|
-
signal = 3
|
|
25
|
-
|
|
26
|
-
def __init__(self, ins_obj: "InstructionQ", ins_id: dict):
|
|
27
|
-
cls = self.__class__
|
|
28
|
-
self.instruction = ins_obj
|
|
29
|
-
self.id = cls.Identity(**ins_id)
|
|
30
|
-
if ins_id['frame'] != -1 and ins_id['envelop'] != -1:
|
|
31
|
-
self.wtag = cls.Tag.signal
|
|
32
|
-
elif ins_id['frame'] != -1:
|
|
33
|
-
self.wtag = cls.Tag.envelope
|
|
34
|
-
elif ins_id['envelop'] != -1:
|
|
35
|
-
self.wtag = cls.Tag.frame
|
|
36
|
-
else:
|
|
37
|
-
self.wtag = cls.Tag.none
|
|
38
|
-
|
|
39
|
-
def __repr__(self):
|
|
40
|
-
return f'<{self.__class__}::{self.id}::{self.wtag}>'
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class Frame(Wave):
|
|
44
|
-
def __init__(self, ins_obj: "InstructionQ", ins_id: int, freq: float):
|
|
45
|
-
super().__init__(ins_obj, {'frame': ins_id, 'envelop': -1})
|
|
46
|
-
self.tag = self.generate_tag
|
|
47
|
-
self.freq = freq
|
|
48
|
-
|
|
49
|
-
@property
|
|
50
|
-
def freq(self):
|
|
51
|
-
return self._freq
|
|
52
|
-
|
|
53
|
-
@freq.setter
|
|
54
|
-
def freq(self, value):
|
|
55
|
-
self._freq = value
|
|
56
|
-
self._ins = QInsFrame(freq=value, tag=self.tag)
|
|
57
|
-
|
|
58
|
-
def __mul__(self, other: Wave):
|
|
59
|
-
if isinstance(other, Envelope) or isinstance(other, Signal):
|
|
60
|
-
if other.instruction is not self.instruction:
|
|
61
|
-
raise ValueError(f'隶属于的instruction不同,不能直接相乘')
|
|
62
|
-
res = Signal(self.instruction, {'frame': self.id.frame, 'envelop': other.id.envelop})
|
|
63
|
-
res.frame = self
|
|
64
|
-
res.envelope = other
|
|
65
|
-
return res
|
|
66
|
-
else:
|
|
67
|
-
raise ValueError(f'{self.__class__}不能与{other.__class__}做乘法运算')
|
|
68
|
-
|
|
69
|
-
def format(self):
|
|
70
|
-
return self._ins
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
class Envelope(Wave):
|
|
74
|
-
def __init__(self, ins_obj: "InstructionQ", ins_id: int, content):
|
|
75
|
-
super().__init__(ins_obj, {'frame': -1, 'envelop': ins_id})
|
|
76
|
-
self.tag = self.generate_tag
|
|
77
|
-
self.content = content
|
|
78
|
-
|
|
79
|
-
@property
|
|
80
|
-
def content(self):
|
|
81
|
-
return self._content
|
|
82
|
-
|
|
83
|
-
@content.setter
|
|
84
|
-
def content(self, value):
|
|
85
|
-
self._content = value
|
|
86
|
-
self._ins = QInsEnvelop(envelop=self.content, tag=self.tag)
|
|
87
|
-
|
|
88
|
-
def __mul__(self, other: Wave):
|
|
89
|
-
if isinstance(other, Frame) or isinstance(other, Signal):
|
|
90
|
-
if other.instruction is not self.instruction:
|
|
91
|
-
raise ValueError(f'They belong to different instructions and cannot be directly multiplied')
|
|
92
|
-
res = Signal(self.instruction, {'frame': other.id.frame, 'envelop': self.id.envelop})
|
|
93
|
-
res.frame = other
|
|
94
|
-
res.envelope = self
|
|
95
|
-
return res
|
|
96
|
-
else:
|
|
97
|
-
raise ValueError(f'{self.__class__}不能与{other.__class__}做乘法运算')
|
|
98
|
-
|
|
99
|
-
def format(self):
|
|
100
|
-
return self._ins
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
class Signal(Wave):
|
|
104
|
-
def __init__(self, ins_obj: "InstructionQ", ins_id: dict):
|
|
105
|
-
super().__init__(ins_obj, ins_id)
|
|
106
|
-
self.frame = None
|
|
107
|
-
self.envelope = None
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
class InstructionQ(GenTagMixin):
|
|
111
|
-
"""!
|
|
112
|
-
包含各种具体的指令
|
|
113
|
-
"""
|
|
114
|
-
|
|
115
|
-
def __init__(self, freqs=None, envelopes=None):
|
|
116
|
-
"""!
|
|
117
|
-
|
|
118
|
-
@param freqs:
|
|
119
|
-
@param envelopes:
|
|
120
|
-
"""
|
|
121
|
-
if freqs is None:
|
|
122
|
-
freqs = []
|
|
123
|
-
if envelopes is None:
|
|
124
|
-
envelopes = []
|
|
125
|
-
self.i_set: "List[Union[NSQCommand, InstructionQ]]" = []
|
|
126
|
-
self.f_set: "Dict[int, Frame]" = {}
|
|
127
|
-
self.e_set: "Dict[int, Envelope]" = {}
|
|
128
|
-
self.symbol_set: Dict[str, int] = {}
|
|
129
|
-
self.symbol_idx = -1
|
|
130
|
-
self.is_first_trig = True
|
|
131
|
-
self.last_ins = None
|
|
132
|
-
|
|
133
|
-
for i, f in enumerate(freqs):
|
|
134
|
-
self.ins_frame(f)
|
|
135
|
-
for i, e in enumerate(envelopes):
|
|
136
|
-
self.ins_envelope(e)
|
|
137
|
-
|
|
138
|
-
def __enter__(self):
|
|
139
|
-
return self
|
|
140
|
-
|
|
141
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
142
|
-
...
|
|
143
|
-
|
|
144
|
-
def clear(self):
|
|
145
|
-
self.i_set.clear()
|
|
146
|
-
self.symbol_set: Dict[str, int] = {}
|
|
147
|
-
self.symbol_idx = -1
|
|
148
|
-
self.is_first_trig = True
|
|
149
|
-
|
|
150
|
-
@property
|
|
151
|
-
def length(self):
|
|
152
|
-
return len(self.i_set)
|
|
153
|
-
|
|
154
|
-
def ins_frame(self, freq, idx=None) -> Frame:
|
|
155
|
-
raise RuntimeError(f'Cannot create a new frame in the branch')
|
|
156
|
-
|
|
157
|
-
def ins_envelope(self, envelope: "Union[np.ndarray, wf.Waveform, str]", idx=None) -> Envelope:
|
|
158
|
-
raise RuntimeError(f'Cannot create a new envelope in the branch')
|
|
159
|
-
|
|
160
|
-
def evlp_gaussian(self, width) -> Envelope:
|
|
161
|
-
wave = wf.gaussian(width) >> (width/2)
|
|
162
|
-
wave.start = 0
|
|
163
|
-
wave.stop = width
|
|
164
|
-
return self.ins_envelope(wave)
|
|
165
|
-
|
|
166
|
-
def evlp_cospulse(self, width) -> Envelope:
|
|
167
|
-
wave = wf.cosPulse(width) >> (width / 2)
|
|
168
|
-
wave.start = 0
|
|
169
|
-
wave.stop = width
|
|
170
|
-
return self.ins_envelope(wave)
|
|
171
|
-
|
|
172
|
-
def evlp_square(self, width) -> Envelope:
|
|
173
|
-
wave = wf.square(width) >> (width / 2)
|
|
174
|
-
wave.start = 0
|
|
175
|
-
wave.stop = width
|
|
176
|
-
return self.ins_envelope(wave)
|
|
177
|
-
|
|
178
|
-
def _append_ins(self, cmd: "Union[NSQCommand, InstructionQ]"):
|
|
179
|
-
self.last_ins = cmd
|
|
180
|
-
self.i_set.append(cmd)
|
|
181
|
-
|
|
182
|
-
def _map_var(self, var):
|
|
183
|
-
if var in self.symbol_set:
|
|
184
|
-
return self.symbol_set[var]
|
|
185
|
-
self.symbol_idx += 1
|
|
186
|
-
if self.symbol_idx >= 16:
|
|
187
|
-
raise RuntimeError(f'Up to 16 variables can be configured')
|
|
188
|
-
self.symbol_set[var] = self.symbol_idx
|
|
189
|
-
return self.symbol_set[var]
|
|
190
|
-
|
|
191
|
-
def wait_for_trigger(self):
|
|
192
|
-
if not self.is_first_trig:
|
|
193
|
-
self.wait()
|
|
194
|
-
cmd = QInsWaitTrig(tag=self.generate_tag)
|
|
195
|
-
self._append_ins(cmd)
|
|
196
|
-
return cmd
|
|
197
|
-
|
|
198
|
-
def ins_variable(self, reg: str, value: int):
|
|
199
|
-
cmd = QInsMov(self._map_var(reg), value, type='=', tag=self.generate_tag)
|
|
200
|
-
self._append_ins(cmd)
|
|
201
|
-
return cmd
|
|
202
|
-
|
|
203
|
-
def ins_add(self, reg: str, value: int):
|
|
204
|
-
cmd = QInsMov(self._map_var(reg), value, type='+', tag=self.generate_tag)
|
|
205
|
-
self._append_ins(cmd)
|
|
206
|
-
return cmd
|
|
207
|
-
|
|
208
|
-
def ins_reset_frame(self, flag, frame=''):
|
|
209
|
-
cmd = QInsResetF(flag, frame, tag=self.generate_tag)
|
|
210
|
-
self._append_ins(cmd)
|
|
211
|
-
return cmd
|
|
212
|
-
|
|
213
|
-
def inc_phase(self, frame, phase):
|
|
214
|
-
cmd = QInsIncPhase(phase, frame.tag, tag=self.generate_tag)
|
|
215
|
-
self._append_ins(cmd)
|
|
216
|
-
return cmd
|
|
217
|
-
|
|
218
|
-
def play_wave(self, wave: Signal, amp=1, freq=0, phase=0):
|
|
219
|
-
if not isinstance(wave, Signal):
|
|
220
|
-
raise RuntimeError(f'The parameter wave should be Signal, not {type(wave)}')
|
|
221
|
-
cmd = QInsPlayWave(wave.frame.tag, wave.envelope.tag, amp, freq, phase, tag=self.generate_tag)
|
|
222
|
-
self._append_ins(cmd)
|
|
223
|
-
return cmd
|
|
224
|
-
|
|
225
|
-
def play_zero(self, width):
|
|
226
|
-
cmd = QInsPlayZero(width, tag=self.generate_tag)
|
|
227
|
-
self._append_ins(cmd)
|
|
228
|
-
return cmd
|
|
229
|
-
|
|
230
|
-
def wait(self):
|
|
231
|
-
cmd = QInsWait(tag=self.generate_tag)
|
|
232
|
-
self._append_ins(cmd)
|
|
233
|
-
return cmd
|
|
234
|
-
|
|
235
|
-
def end(self):
|
|
236
|
-
cmd = QInsEnd(tag=self.generate_tag)
|
|
237
|
-
self._append_ins(cmd)
|
|
238
|
-
return cmd
|
|
239
|
-
|
|
240
|
-
def capture(self, width, delay=0):
|
|
241
|
-
cmd = QInsCapture(width=width, probe_delay=delay, tag=self.generate_tag)
|
|
242
|
-
self._append_ins(cmd)
|
|
243
|
-
return cmd
|
|
244
|
-
|
|
245
|
-
def _compile(self) -> "List[NSQCommand]":
|
|
246
|
-
res = []
|
|
247
|
-
for ins in self.i_set:
|
|
248
|
-
if isinstance(ins, InstructionQ):
|
|
249
|
-
res.extend(ins._compile())
|
|
250
|
-
else:
|
|
251
|
-
res.append(copy.copy(ins))
|
|
252
|
-
return res
|
|
253
|
-
|
|
254
|
-
def compile(self) -> "List[NSQCommand]":
|
|
255
|
-
queue = []
|
|
256
|
-
for idx in sorted(self.f_set.keys()):
|
|
257
|
-
ins = self.f_set[idx]
|
|
258
|
-
queue.append(ins.format())
|
|
259
|
-
for idx in sorted(self.e_set.keys()):
|
|
260
|
-
ins = self.e_set[idx]
|
|
261
|
-
queue.append(ins.format())
|
|
262
|
-
|
|
263
|
-
queue.extend(self._compile())
|
|
264
|
-
# 向指令队列中添加End指令
|
|
265
|
-
if not isinstance(queue[-1], QInsEnd):
|
|
266
|
-
queue.append(self.end())
|
|
267
|
-
|
|
268
|
-
res = []
|
|
269
|
-
for idx, ins in enumerate(queue):
|
|
270
|
-
if isinstance(ins, QInsPlaceholder):
|
|
271
|
-
f_tag = ins.tag
|
|
272
|
-
t_tag = queue[idx+1].tag
|
|
273
|
-
for _ins in queue:
|
|
274
|
-
if getattr(_ins, 'target', '') == f_tag:
|
|
275
|
-
_ins.target = t_tag
|
|
276
|
-
continue
|
|
277
|
-
res.append(ins)
|
|
278
|
-
return res
|
|
279
|
-
|
|
280
|
-
def lookup(self):
|
|
281
|
-
import json
|
|
282
|
-
ins_list = [str(ins) for ins in self.compile()]
|
|
283
|
-
print(json.dumps(ins_list, indent=4))
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
class InsChannel(InstructionQ):
|
|
287
|
-
def __init__(self, freqs=None, envelopes=None):
|
|
288
|
-
super(InsChannel, self).__init__(freqs, envelopes)
|
|
289
|
-
self.if_stack: "List[InsIF]" = []
|
|
290
|
-
self.looping = False
|
|
291
|
-
|
|
292
|
-
def clear(self):
|
|
293
|
-
super().clear()
|
|
294
|
-
self.looping = False
|
|
295
|
-
|
|
296
|
-
def ins_frame(self, freq, idx=None) -> Frame:
|
|
297
|
-
if idx is None:
|
|
298
|
-
idx = sorted(self.f_set)[-1]+1 if len(self.f_set) != 0 else 0
|
|
299
|
-
if not isinstance(idx, int):
|
|
300
|
-
raise ValueError(f'frame的idx应为整数,而不是{type(idx)}')
|
|
301
|
-
frame = Frame(self, idx, freq)
|
|
302
|
-
self.f_set[idx] = frame
|
|
303
|
-
return frame
|
|
304
|
-
|
|
305
|
-
def ins_envelope(self, envelope: "Union[np.ndarray, wf.Waveform, str]", idx=None) -> Envelope:
|
|
306
|
-
if idx is None:
|
|
307
|
-
idx = sorted(self.e_set)[-1] + 1 if len(self.e_set) != 0 else 0
|
|
308
|
-
if not isinstance(idx, int):
|
|
309
|
-
raise ValueError(f'envelop的idx应为整数,而不是{type(idx)}')
|
|
310
|
-
obj = Envelope(self, idx, envelope)
|
|
311
|
-
self.e_set[idx] = obj
|
|
312
|
-
return obj
|
|
313
|
-
|
|
314
|
-
def ins_if(self, formula) -> "InsIF":
|
|
315
|
-
channel = self if self.__class__ is InsChannel else self.channel
|
|
316
|
-
if_ins = InsIF.from_channel(channel)
|
|
317
|
-
if_ins.formula = formula
|
|
318
|
-
self._append_ins(if_ins)
|
|
319
|
-
self.if_stack.append(if_ins)
|
|
320
|
-
return if_ins
|
|
321
|
-
|
|
322
|
-
def ins_else(self) -> "InsElse":
|
|
323
|
-
# if与else之间不能间隔其它指令
|
|
324
|
-
if not isinstance(self.last_ins, InsIF):
|
|
325
|
-
raise RuntimeError(f'The last call of ins_else must be ins_if, not {type(self.last_ins)}')
|
|
326
|
-
# 不能没有if直接调用else
|
|
327
|
-
if len(self.if_stack) == 0:
|
|
328
|
-
raise RuntimeError(f'Please call if before calling else')
|
|
329
|
-
channel = self if self.__class__ is InsChannel else self.channel
|
|
330
|
-
_if = self.if_stack.pop()
|
|
331
|
-
_else = InsElse.from_channel(channel)
|
|
332
|
-
_else._if = _if
|
|
333
|
-
_if.ins_else = _else
|
|
334
|
-
return _else
|
|
335
|
-
|
|
336
|
-
def ins_loop(self, times) -> "InsLoop":
|
|
337
|
-
if self.looping:
|
|
338
|
-
raise RuntimeError(f'The current object is already in the loop')
|
|
339
|
-
channel = self if self.__class__ is InsChannel else self.channel
|
|
340
|
-
loop_ins = InsLoop.from_channel(channel)
|
|
341
|
-
loop_ins.times = times
|
|
342
|
-
self._append_ins(loop_ins)
|
|
343
|
-
return loop_ins
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
class InsIF(InsChannel):
|
|
347
|
-
ch_judge_name = {f'FREQ_{i}': 1<<i for i in range(6)}
|
|
348
|
-
|
|
349
|
-
def __init__(self, freqs=None, envelopes=None):
|
|
350
|
-
"""!
|
|
351
|
-
|
|
352
|
-
@param freqs:
|
|
353
|
-
@param envelopes:
|
|
354
|
-
"""
|
|
355
|
-
super().__init__(freqs, envelopes)
|
|
356
|
-
self.channel: "InsChannel" = None
|
|
357
|
-
self.key_ins = None
|
|
358
|
-
self.ins_else = None
|
|
359
|
-
|
|
360
|
-
@classmethod
|
|
361
|
-
def from_channel(cls, channel):
|
|
362
|
-
self = cls()
|
|
363
|
-
self.channel = channel
|
|
364
|
-
return self
|
|
365
|
-
|
|
366
|
-
@property
|
|
367
|
-
def formula(self):
|
|
368
|
-
raise RuntimeError(f'{self.__class__.__name__}.formula is a static property')
|
|
369
|
-
|
|
370
|
-
@formula.setter
|
|
371
|
-
def formula(self, formula: str):
|
|
372
|
-
formula = formula.replace(' ', '')
|
|
373
|
-
ast = formula.split('==')
|
|
374
|
-
if len(ast) != 2:
|
|
375
|
-
raise RuntimeError(f'The format of formula should be [name] == [value]')
|
|
376
|
-
name, value = ast
|
|
377
|
-
if name in self.channel.symbol_set:
|
|
378
|
-
self.key_ins = QInsJumpWithReg(self.channel.symbol_set[name], int(value), None, tag=self.generate_tag)
|
|
379
|
-
elif name in self.ch_judge_name:
|
|
380
|
-
self.key_ins = QInsJumpWithJudge(int(value), self.ch_judge_name[name], None, tag=self.generate_tag)
|
|
381
|
-
else:
|
|
382
|
-
raise RuntimeError(f'The specified variable {name} does not exist')
|
|
383
|
-
|
|
384
|
-
def _compile(self) -> "List[NSQCommand]":
|
|
385
|
-
"""!
|
|
386
|
-
递归生成分支结构,支持结构嵌套
|
|
387
|
-
@return:
|
|
388
|
-
"""
|
|
389
|
-
res = []
|
|
390
|
-
if_group = super()._compile()
|
|
391
|
-
else_group = self.ins_else._compile() if isinstance(self.ins_else, InsElse) else []
|
|
392
|
-
while len(else_group) < 3:
|
|
393
|
-
else_group.append(QInsWait(tag=self.generate_tag))
|
|
394
|
-
if_group.append(QInsPlaceholder(tag=self.generate_tag))
|
|
395
|
-
jump = QInsJump(None, tag=self.generate_tag)
|
|
396
|
-
self.key_ins.target = if_group[0].tag
|
|
397
|
-
jump.target = if_group[-1].tag
|
|
398
|
-
|
|
399
|
-
res.append(self.key_ins)
|
|
400
|
-
res.extend(else_group)
|
|
401
|
-
res.append(jump)
|
|
402
|
-
res.extend(if_group)
|
|
403
|
-
return res
|
|
404
|
-
|
|
405
|
-
def capture(self, width, delay=0):
|
|
406
|
-
raise RuntimeError(f'Capture is not allowed to call in a branch structure')
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
class InsElse(InsChannel):
|
|
410
|
-
def __init__(self, freqs=None, envelopes=None):
|
|
411
|
-
"""!
|
|
412
|
-
|
|
413
|
-
@param freqs:
|
|
414
|
-
@param envelopes:
|
|
415
|
-
"""
|
|
416
|
-
super().__init__(freqs, envelopes)
|
|
417
|
-
self.channel = None
|
|
418
|
-
self._if = None
|
|
419
|
-
|
|
420
|
-
@classmethod
|
|
421
|
-
def from_channel(cls, channel):
|
|
422
|
-
self = cls()
|
|
423
|
-
self.channel = channel
|
|
424
|
-
return self
|
|
425
|
-
|
|
426
|
-
def capture(self, width, delay=0):
|
|
427
|
-
raise RuntimeError(f'Capture is not allowed to call in a branch structure')
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
class InsLoop(InsChannel):
|
|
431
|
-
def __init__(self, freqs=None, envelopes=None):
|
|
432
|
-
"""!
|
|
433
|
-
|
|
434
|
-
@param freqs:
|
|
435
|
-
@param envelopes:
|
|
436
|
-
"""
|
|
437
|
-
super().__init__(freqs, envelopes)
|
|
438
|
-
self.channel: "InsChannel" = None
|
|
439
|
-
self.times = None
|
|
440
|
-
self.var = self.generate_tag
|
|
441
|
-
|
|
442
|
-
def __enter__(self):
|
|
443
|
-
self.channel.looping = True
|
|
444
|
-
return self
|
|
445
|
-
|
|
446
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
447
|
-
self.looping = False
|
|
448
|
-
|
|
449
|
-
@classmethod
|
|
450
|
-
def from_channel(cls, channel):
|
|
451
|
-
self = cls()
|
|
452
|
-
self.channel = channel
|
|
453
|
-
return self
|
|
454
|
-
|
|
455
|
-
def capture(self, width, delay=0):
|
|
456
|
-
raise RuntimeError(f'Calling capture in a loop construct is not allowed')
|
|
457
|
-
|
|
458
|
-
def _compile(self) -> "List[NSQCommand]":
|
|
459
|
-
body = super()._compile()
|
|
460
|
-
res = []
|
|
461
|
-
set_cmd = QInsMov(self.channel._map_var(self.var), 0, type='=', tag=self.generate_tag)
|
|
462
|
-
body.append(QInsMov(self.channel._map_var(self.var), 1, type='+', tag=self.generate_tag))
|
|
463
|
-
placeholder = QInsPlaceholder(tag=self.generate_tag)
|
|
464
|
-
jump = [
|
|
465
|
-
QInsJumpWithReg(self.channel._map_var(self.var), self.times, placeholder.tag, tag=self.generate_tag),
|
|
466
|
-
QInsWait(tag=self.generate_tag),
|
|
467
|
-
QInsWait(tag=self.generate_tag),
|
|
468
|
-
QInsJump(body[0].tag, tag=self.generate_tag),
|
|
469
|
-
placeholder
|
|
470
|
-
]
|
|
471
|
-
res.append(set_cmd)
|
|
472
|
-
res.extend(body)
|
|
473
|
-
res.extend(jump)
|
|
474
|
-
return res
|
|
475
|
-
|
|
476
|
-
# def _compile(self) -> "List[NSQCommand]":
|
|
477
|
-
# ins_list = super()._compile()
|
|
478
|
-
# res = []
|
|
479
|
-
# for ins in chain.from_iterable(repeat(ins_list, self.times)):
|
|
480
|
-
# _ins: "NSQCommand" = copy.copy(ins)
|
|
481
|
-
# _ins.tag = self.generate_tag
|
|
482
|
-
# res.append(_ins)
|
|
483
|
-
# return res
|
|
@@ -1,538 +0,0 @@
|
|
|
1
|
-
import struct
|
|
2
|
-
import copy
|
|
3
|
-
import uuid
|
|
4
|
-
from typing import Union, List, Tuple, Dict
|
|
5
|
-
from enum import IntEnum
|
|
6
|
-
from dataclasses import dataclass, field
|
|
7
|
-
from itertools import chain
|
|
8
|
-
from collections import ChainMap
|
|
9
|
-
|
|
10
|
-
import numpy as np
|
|
11
|
-
import waveforms as wf
|
|
12
|
-
|
|
13
|
-
__all__ = [
|
|
14
|
-
'AssemblyError', 'GenTagMixin', 'Assembler', 'NSQCommand',
|
|
15
|
-
'QInsWait', 'QInsWaitTrig', 'QInsEnd', 'QInsFrame', 'QInsEnvelop',
|
|
16
|
-
'QInsJumpWithJudge', 'QInsJump', 'QInsJumpWithReg', 'QInsCapture',
|
|
17
|
-
'QInsMov', 'QInsIncPhase', 'QInsPlayZero', 'QInsPlayWave', 'QInsResetF',
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
global_config = {
|
|
21
|
-
'play_zero_step': 4e-9,
|
|
22
|
-
'OUTSrate': 8e9,
|
|
23
|
-
'envelope_dtype': np.int16, # 描述包络每个点的数据类型
|
|
24
|
-
'envelope_step': 64, # 包络步进粒度,单位为bytes
|
|
25
|
-
'envelope_quant': 16383, # 包络量化范围
|
|
26
|
-
'envelope_cache': 204800, # 包络缓存大小,单位bytes
|
|
27
|
-
'envelope_head': np.array([2, 0, 0, 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=np.int16), # 包络更新包头
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class AssemblyError(RuntimeError):
|
|
32
|
-
...
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class GenTagMixin:
|
|
36
|
-
@property
|
|
37
|
-
def generate_tag(self):
|
|
38
|
-
return uuid.uuid4().hex[:10]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@dataclass
|
|
42
|
-
class NSQCommand:
|
|
43
|
-
"""!
|
|
44
|
-
所有参数化波形指令队列的基类
|
|
45
|
-
"""
|
|
46
|
-
tag: str = field(kw_only=True)
|
|
47
|
-
|
|
48
|
-
def _check_attr(self):
|
|
49
|
-
...
|
|
50
|
-
|
|
51
|
-
def __bytes__(self):
|
|
52
|
-
self._check_attr()
|
|
53
|
-
cmd = self._pack_cmd()
|
|
54
|
-
return self.list2bytes(cmd)
|
|
55
|
-
|
|
56
|
-
def _pack_cmd(self) -> list:
|
|
57
|
-
return [0x00, 0, 0, 0x00000000, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00000000]
|
|
58
|
-
|
|
59
|
-
@property
|
|
60
|
-
def overhead(self):
|
|
61
|
-
return 16e-9
|
|
62
|
-
|
|
63
|
-
@staticmethod
|
|
64
|
-
def list2bytes(cmd: list):
|
|
65
|
-
cmd += [0x00] * 8
|
|
66
|
-
cmd_raw = struct.pack('=BBIIHHHII' + 'B' * 8, *cmd)
|
|
67
|
-
return cmd_raw
|
|
68
|
-
|
|
69
|
-
@staticmethod
|
|
70
|
-
def frequency_normalization32(freq) -> np.uint32:
|
|
71
|
-
return np.uint32(round(freq / global_config['OUTSrate'] * (1 << 32)))
|
|
72
|
-
|
|
73
|
-
@staticmethod
|
|
74
|
-
def phase_normalization32(phase) -> np.uint32:
|
|
75
|
-
return np.uint32(round(np.fmod(np.fmod(phase / np.pi / 2, 1) + 1, 1) * (1 << 32)))
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@dataclass
|
|
79
|
-
class _ProbeCommand:
|
|
80
|
-
tag: str = field(kw_only=True)
|
|
81
|
-
|
|
82
|
-
def _check_attr(self):
|
|
83
|
-
...
|
|
84
|
-
|
|
85
|
-
def __bytes__(self):
|
|
86
|
-
self._check_attr()
|
|
87
|
-
cmd = self._pack_cmd()
|
|
88
|
-
return self.list2bytes(cmd)
|
|
89
|
-
|
|
90
|
-
def _pack_cmd(self) -> list:
|
|
91
|
-
return [0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000]
|
|
92
|
-
|
|
93
|
-
@property
|
|
94
|
-
def overhead(self):
|
|
95
|
-
return 16e-9
|
|
96
|
-
|
|
97
|
-
@staticmethod
|
|
98
|
-
def list2bytes(cmd: list):
|
|
99
|
-
cmd_raw = struct.pack('=HHHHHHHH', *cmd[::-1])
|
|
100
|
-
return cmd_raw
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
@dataclass
|
|
104
|
-
class QInsEnvelop(NSQCommand):
|
|
105
|
-
envelop: Union[np.ndarray, wf.Waveform]
|
|
106
|
-
|
|
107
|
-
def __post_init__(self):
|
|
108
|
-
self.envelop_slice = slice(0, None, 1)
|
|
109
|
-
|
|
110
|
-
def __bytes__(self):
|
|
111
|
-
if isinstance(self.envelop, wf.Waveform):
|
|
112
|
-
if self.envelop.start is None or self.envelop.stop is None:
|
|
113
|
-
raise AssemblyError(f'When the type of {self.__class__.__name__}.envelop is wf.Waveform, '
|
|
114
|
-
f'it must have start and stop attributes')
|
|
115
|
-
wave = self.envelop.sample(global_config['OUTSrate'])
|
|
116
|
-
elif isinstance(self.envelop, np.ndarray):
|
|
117
|
-
wave = self.envelop
|
|
118
|
-
else:
|
|
119
|
-
raise AssemblyError(f'The type of {self.__class__.__name__}.envelop must be one of np.ndarray or wf.Waveform')
|
|
120
|
-
wave *= global_config['envelope_quant']
|
|
121
|
-
return wave.astype(global_config['envelope_dtype']).tobytes()
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
@dataclass
|
|
125
|
-
class QInsFrame(NSQCommand):
|
|
126
|
-
freq: float
|
|
127
|
-
|
|
128
|
-
def __post_init__(self):
|
|
129
|
-
self.frame_idx = 0
|
|
130
|
-
|
|
131
|
-
def _pack_cmd(self) -> list:
|
|
132
|
-
return [0x11, self.frame_idx * 4 + 0, self.frequency_normalization32(self.freq),
|
|
133
|
-
0x00000000, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00000000]
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
@dataclass
|
|
137
|
-
class QInsResetF(NSQCommand):
|
|
138
|
-
type: str
|
|
139
|
-
frame: str = ''
|
|
140
|
-
|
|
141
|
-
def __post_init__(self):
|
|
142
|
-
self.frame_idx = 0
|
|
143
|
-
self.type2head = {'all': 0x51, 'single': 0x41, 'phase': 0x31}
|
|
144
|
-
|
|
145
|
-
def _pack_cmd(self) -> list:
|
|
146
|
-
head = self.type2head.get(self.type.lower(), None)
|
|
147
|
-
if head is None:
|
|
148
|
-
raise AssemblyError(f'{self.__class__.__name__}.type can only be one of {self.type2head.keys()}')
|
|
149
|
-
return [head, self.frame_idx * 4, 0, 0x00000000, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00000000]
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
@dataclass
|
|
153
|
-
class QInsMov(NSQCommand):
|
|
154
|
-
reg: int
|
|
155
|
-
value: int
|
|
156
|
-
type: str = '='
|
|
157
|
-
|
|
158
|
-
def __post_init__(self):
|
|
159
|
-
self.type2head = {'=': 0x38, '+': 0x48}
|
|
160
|
-
|
|
161
|
-
def _pack_cmd(self) -> list:
|
|
162
|
-
head = self.type2head.get(self.type, None)
|
|
163
|
-
if head is None:
|
|
164
|
-
raise AssemblyError(f'{self.__class__.__name__}.type can only be one of {self.type2head.keys()}')
|
|
165
|
-
return [head, self.reg, self.value, 0x00000000, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00000000]
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
@dataclass
|
|
169
|
-
class QInsWaitTrig(NSQCommand):
|
|
170
|
-
def _pack_cmd(self) -> list:
|
|
171
|
-
return [0xF4, 0, 0, 0x00000000, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00000000]
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
@dataclass
|
|
175
|
-
class QInsIncPhase(NSQCommand):
|
|
176
|
-
phase: float
|
|
177
|
-
frame: str
|
|
178
|
-
|
|
179
|
-
def __post_init__(self):
|
|
180
|
-
self.frame_idx = 0
|
|
181
|
-
|
|
182
|
-
def _pack_cmd(self) -> list:
|
|
183
|
-
return [0x21, self.frame_idx * 4 + 1, self.phase_normalization32(self.phase),
|
|
184
|
-
0x00000000, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00000000]
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
@dataclass
|
|
188
|
-
class QInsPlayZero(NSQCommand):
|
|
189
|
-
width: float
|
|
190
|
-
|
|
191
|
-
@property
|
|
192
|
-
def overhead(self):
|
|
193
|
-
return self.width
|
|
194
|
-
|
|
195
|
-
def _pack_cmd(self) -> list:
|
|
196
|
-
frames = round(self.width / global_config['play_zero_step'])
|
|
197
|
-
return [0x44, 0, 0, frames, 0, 0x0000, 0x0000, 0, 0]
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
@dataclass
|
|
201
|
-
class QInsPlayWave(NSQCommand):
|
|
202
|
-
frame: str
|
|
203
|
-
envelop: str
|
|
204
|
-
amp: float
|
|
205
|
-
attach_freq: float
|
|
206
|
-
attach_phase: float
|
|
207
|
-
|
|
208
|
-
@property
|
|
209
|
-
def overhead(self):
|
|
210
|
-
return self.envelop_slice.step/global_config['OUTSrate']
|
|
211
|
-
|
|
212
|
-
def __post_init__(self):
|
|
213
|
-
self.frame_idx = 0
|
|
214
|
-
self.envelop_slice = slice(0, None, 1)
|
|
215
|
-
|
|
216
|
-
def _pack_cmd(self) -> list:
|
|
217
|
-
return [
|
|
218
|
-
0x04, self.frame_idx * 4,
|
|
219
|
-
np.uint32(self.envelop_slice.start / global_config['envelope_step']),
|
|
220
|
-
np.uint32(self.envelop_slice.step / global_config['envelope_step']),
|
|
221
|
-
np.int32(self.amp * (1 << 15)), 0x0000, 0x0000,
|
|
222
|
-
self.frequency_normalization32(self.attach_freq),
|
|
223
|
-
self.phase_normalization32(self.attach_phase)
|
|
224
|
-
]
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
@dataclass
|
|
228
|
-
class QInsWait(NSQCommand):
|
|
229
|
-
def _pack_cmd(self) -> list:
|
|
230
|
-
return [0x00, 0, 0, 0x00000000, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00000000]
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
@dataclass
|
|
234
|
-
class QInsJump(NSQCommand):
|
|
235
|
-
target: str
|
|
236
|
-
|
|
237
|
-
def __post_init__(self):
|
|
238
|
-
self.ins_idx = 0
|
|
239
|
-
|
|
240
|
-
def _pack_cmd(self) -> list:
|
|
241
|
-
return [0x18, 0, 0, self.ins_idx, 0, 0x0000, 0x0000, 0, 0]
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
@dataclass
|
|
245
|
-
class QInsJumpWithReg(NSQCommand):
|
|
246
|
-
reg: int
|
|
247
|
-
value: int
|
|
248
|
-
target: str
|
|
249
|
-
|
|
250
|
-
def __post_init__(self):
|
|
251
|
-
self.ins_idx = 0
|
|
252
|
-
|
|
253
|
-
def _pack_cmd(self) -> list:
|
|
254
|
-
return [0x28, self.reg, self.value, self.ins_idx, 0, 0x0000, 0x0000, 0, 0]
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
@dataclass
|
|
258
|
-
class QInsJumpWithJudge(NSQCommand):
|
|
259
|
-
value: int
|
|
260
|
-
mask: int
|
|
261
|
-
target: str
|
|
262
|
-
|
|
263
|
-
def __post_init__(self):
|
|
264
|
-
self.ins_idx = 0
|
|
265
|
-
|
|
266
|
-
def _pack_cmd(self) -> list:
|
|
267
|
-
return [0x28, 0x10, self.mask << 16 | self.value, self.ins_idx, 0, 0x0000, 0x0000, 0, 0]
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
@dataclass
|
|
271
|
-
class QInsEnd(NSQCommand):
|
|
272
|
-
def _pack_cmd(self) -> list:
|
|
273
|
-
return [0xF8, 0, 0, 0x00000000, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00000000]
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
@dataclass
|
|
277
|
-
class QInsCapture(NSQCommand):
|
|
278
|
-
width: float
|
|
279
|
-
probe_delay: float = 0
|
|
280
|
-
|
|
281
|
-
def __post_init__(self):
|
|
282
|
-
self.ch_flag = {'all': 3, 'ad': 2, 'da': 1}
|
|
283
|
-
|
|
284
|
-
def __bytes__(self):
|
|
285
|
-
delay_length = int(round(self.probe_delay/16e-9))
|
|
286
|
-
length = int(round(self.width/16e-9))
|
|
287
|
-
cmds = [
|
|
288
|
-
[0x0002, self.ch_flag['da'], 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, delay_length],
|
|
289
|
-
[0x0002, self.ch_flag['all'], 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, length],
|
|
290
|
-
]
|
|
291
|
-
return b''.join(self.list2bytes(cmd) for cmd in cmds)
|
|
292
|
-
|
|
293
|
-
@staticmethod
|
|
294
|
-
def list2bytes(cmd: list):
|
|
295
|
-
cmd_raw = struct.pack('=HHHHHHHH', *cmd[::-1])
|
|
296
|
-
return cmd_raw
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
@dataclass
|
|
300
|
-
class _QInsPTrigDelay(_ProbeCommand):
|
|
301
|
-
tag: str = field(kw_only=True)
|
|
302
|
-
delay: float
|
|
303
|
-
|
|
304
|
-
def __post_init__(self):
|
|
305
|
-
self.ch_flag = {'all': 3, 'ad': 2, 'da': 1}
|
|
306
|
-
|
|
307
|
-
def _pack_cmd(self) -> list:
|
|
308
|
-
length = int(round(self.delay / 16e-9))
|
|
309
|
-
return [0x0002, self.ch_flag['all'], 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, length]
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
@dataclass
|
|
313
|
-
class _QInsPWaitTrig(_ProbeCommand):
|
|
314
|
-
def _pack_cmd(self) -> list:
|
|
315
|
-
return [0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000]
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
@dataclass
|
|
319
|
-
class _QInsPJump(_ProbeCommand):
|
|
320
|
-
target: str
|
|
321
|
-
|
|
322
|
-
def __post_init__(self):
|
|
323
|
-
self.ins_idx = 0
|
|
324
|
-
|
|
325
|
-
def _pack_cmd(self) -> list:
|
|
326
|
-
return [0x0003, 0x0000, 0x0000, self.ins_idx, 0x0000, 0x0000, 0x0000, 0x0000]
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
class Assembler(GenTagMixin):
|
|
330
|
-
def __init__(self, ch_map=None):
|
|
331
|
-
if ch_map is None:
|
|
332
|
-
ch_map = {
|
|
333
|
-
1: [3, 4, 5, 6, 7, 8],
|
|
334
|
-
2: [11, 12, 13, 14, 15, 16]
|
|
335
|
-
}
|
|
336
|
-
self.ch_map = ch_map
|
|
337
|
-
self.drive2probe = {j: i for i in ch_map.keys() for j in ch_map[i]}
|
|
338
|
-
self.raw_cmdq = {j: [] for i in ch_map.values() for j in i}
|
|
339
|
-
self.probe_cmdq = {i: [] for i in ch_map.keys()}
|
|
340
|
-
self.drive_cmdq = {j: [] for i in ch_map.values() for j in i}
|
|
341
|
-
|
|
342
|
-
self.envelop_cache = {
|
|
343
|
-
j: np.zeros((global_config['envelope_cache'] // 2,), dtype=np.int16) for i in ch_map.values() for j in i
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
self.frame_symbol = {j: {} for i in ch_map.values() for j in i}
|
|
347
|
-
self.envelop_symbol = {j: {} for i in ch_map.values() for j in i}
|
|
348
|
-
self.drive_symbol = {j: {} for i in ch_map.values() for j in i}
|
|
349
|
-
self.probe_symbol = {i: {} for i in ch_map.keys()}
|
|
350
|
-
|
|
351
|
-
def set_chnl_cmdq(self, ch_id, cmdq):
|
|
352
|
-
if ch_id not in self.drive2probe:
|
|
353
|
-
raise AssemblyError(f'OUT channel {ch_id} does not exist')
|
|
354
|
-
self.raw_cmdq[ch_id] = cmdq
|
|
355
|
-
self.envelop_symbol[ch_id], cmdq = self.extract_envelop(ch_id, cmdq)
|
|
356
|
-
self.frame_symbol[ch_id] = self.extract_frame(cmdq)
|
|
357
|
-
(self.drive_cmdq[ch_id], self.probe_cmdq[self.drive2probe[ch_id]],
|
|
358
|
-
self.drive_symbol[ch_id], self.probe_symbol[self.drive2probe[ch_id]]) = self.divide_cmd(ch_id, cmdq)
|
|
359
|
-
|
|
360
|
-
def extract_envelop(self, ch_id, cmdq: List[NSQCommand]) -> "Tuple[dict, List[NSQCommand]]":
|
|
361
|
-
"""!
|
|
362
|
-
在指令队列中提取包络指令,并建立符号表
|
|
363
|
-
|
|
364
|
-
@param ch_id:
|
|
365
|
-
@param cmdq:
|
|
366
|
-
@return:
|
|
367
|
-
"""
|
|
368
|
-
res_cmdq, envelops = [], []
|
|
369
|
-
for cmd in cmdq:
|
|
370
|
-
if not isinstance(cmd, QInsEnvelop):
|
|
371
|
-
res_cmdq.append(copy.copy(cmd))
|
|
372
|
-
else:
|
|
373
|
-
envelops.append(cmd)
|
|
374
|
-
|
|
375
|
-
cache = self.envelop_cache[ch_id]
|
|
376
|
-
# 给cache头部打上包络更新指令包头
|
|
377
|
-
cache[:16] = global_config['envelope_head']
|
|
378
|
-
data_cache = cache[16:]
|
|
379
|
-
data_cache[:] = 0
|
|
380
|
-
|
|
381
|
-
start = 0
|
|
382
|
-
symbol_map = {}
|
|
383
|
-
for env in envelops:
|
|
384
|
-
wave = bytes(env)
|
|
385
|
-
length = len(wave)
|
|
386
|
-
end = start + length
|
|
387
|
-
data_cache[start//2:end//2] = np.frombuffer(wave, dtype=global_config['envelope_dtype'])
|
|
388
|
-
env.envelop_slice = slice(start, end, length)
|
|
389
|
-
start += length
|
|
390
|
-
symbol_map[env.tag] = env
|
|
391
|
-
return symbol_map, res_cmdq
|
|
392
|
-
|
|
393
|
-
@staticmethod
|
|
394
|
-
def extract_frame(cmdq: List[NSQCommand]) -> "dict":
|
|
395
|
-
"""!
|
|
396
|
-
在指令队列中提取包络配置指令,并且建立包络符号表
|
|
397
|
-
|
|
398
|
-
@param cmdq:
|
|
399
|
-
@return:
|
|
400
|
-
"""
|
|
401
|
-
frames = []
|
|
402
|
-
for cmd in cmdq:
|
|
403
|
-
if not isinstance(cmd, QInsFrame):
|
|
404
|
-
continue
|
|
405
|
-
frames.append(cmd)
|
|
406
|
-
|
|
407
|
-
symbol_map = {}
|
|
408
|
-
for idx, frame in enumerate(frames):
|
|
409
|
-
frame.frame_idx = idx
|
|
410
|
-
symbol_map[frame.tag] = frame
|
|
411
|
-
return symbol_map
|
|
412
|
-
|
|
413
|
-
def divide_cmd(self, ch_id, cmdq) -> "Tuple[list, list, dict, dict]":
|
|
414
|
-
"""!
|
|
415
|
-
|
|
416
|
-
@param ch_id:
|
|
417
|
-
@param cmdq:
|
|
418
|
-
@return:
|
|
419
|
-
"""
|
|
420
|
-
drive_queue, probe_queue, drive_symbol, probe_symbol = self._link_wave(ch_id, cmdq)
|
|
421
|
-
drive_queue, drive_symbol = self._optimize_drive_q(drive_queue, drive_symbol)
|
|
422
|
-
probe_queue, probe_symbol = self._optimize_probe_q(probe_queue)
|
|
423
|
-
drive_queue, probe_queue = self._link_jump(drive_queue, probe_queue, drive_symbol, probe_symbol)
|
|
424
|
-
return drive_queue, probe_queue, drive_symbol, probe_symbol
|
|
425
|
-
|
|
426
|
-
def _link_wave(self, ch_id, cmdq):
|
|
427
|
-
"""!
|
|
428
|
-
建立frame和envelop到指令的关联,并生成基础的指令符号表,初步分割drive与probe的指令
|
|
429
|
-
|
|
430
|
-
@param ch_id:
|
|
431
|
-
@param cmdq:
|
|
432
|
-
@return:
|
|
433
|
-
"""
|
|
434
|
-
symbols = ChainMap(self.frame_symbol[ch_id], self.envelop_symbol[ch_id])
|
|
435
|
-
drive_queue, probe_queue, drive_symbol, probe_symbol = [], [], {}, {}
|
|
436
|
-
probe_queue.append(_QInsPWaitTrig(tag=self.generate_tag))
|
|
437
|
-
probe_symbol[probe_queue[-1].tag] = 0
|
|
438
|
-
|
|
439
|
-
for cmd in cmdq:
|
|
440
|
-
if hasattr(cmd, 'frame_idx') and hasattr(cmd, 'frame'):
|
|
441
|
-
frame: QInsFrame = symbols.get(cmd.frame, None)
|
|
442
|
-
if frame is None:
|
|
443
|
-
raise AssemblyError(f'The frame tag required by instruction {cmd} '
|
|
444
|
-
f'does not exist in the instruction queue of channel {ch_id}')
|
|
445
|
-
cmd.frame_idx = frame.frame_idx
|
|
446
|
-
if hasattr(cmd, 'envelop_slice') and hasattr(cmd, 'envelop'):
|
|
447
|
-
envelop: QInsEnvelop = symbols.get(cmd.envelop, None)
|
|
448
|
-
if envelop is None:
|
|
449
|
-
raise AssemblyError(f'The envelop tag required by instruction {cmd} '
|
|
450
|
-
f'does not exist in the instruction queue of channel {ch_id}')
|
|
451
|
-
cmd.envelop_slice = envelop.envelop_slice
|
|
452
|
-
if isinstance(cmd, QInsCapture):
|
|
453
|
-
# 这里要保证占capture位置的playzero指令依然能被跳转
|
|
454
|
-
wait = QInsPlayZero(tag=cmd.tag, width=cmd.width + cmd.probe_delay)
|
|
455
|
-
cmd.tag = self.generate_tag
|
|
456
|
-
drive_queue.append(wait)
|
|
457
|
-
probe_queue.append(cmd)
|
|
458
|
-
else:
|
|
459
|
-
wait = _QInsPTrigDelay(tag=self.generate_tag, delay=cmd.overhead)
|
|
460
|
-
drive_queue.append(cmd)
|
|
461
|
-
probe_queue.append(wait)
|
|
462
|
-
drive_symbol[drive_queue[-1].tag] = len(drive_queue)-1
|
|
463
|
-
probe_symbol[probe_queue[-1].tag] = len(probe_queue)-1
|
|
464
|
-
probe_queue.append(_QInsPJump(target=probe_queue[0].tag, tag=self.generate_tag))
|
|
465
|
-
probe_symbol[probe_queue[-1].tag] = len(probe_queue)-1
|
|
466
|
-
return drive_queue, probe_queue, drive_symbol, probe_symbol
|
|
467
|
-
|
|
468
|
-
def _link_jump(self, drive_queue: List[NSQCommand], probe_queue, drive_symbol, probe_symbol):
|
|
469
|
-
"""!
|
|
470
|
-
连接指令队列中的跳转指令
|
|
471
|
-
|
|
472
|
-
@param drive_queue:
|
|
473
|
-
@param probe_queue:
|
|
474
|
-
@return:
|
|
475
|
-
"""
|
|
476
|
-
for cmd in drive_queue:
|
|
477
|
-
if hasattr(cmd, 'ins_idx') and hasattr(cmd, 'target'):
|
|
478
|
-
cmd.ins_idx = drive_symbol[cmd.target]
|
|
479
|
-
|
|
480
|
-
for cmd in probe_queue:
|
|
481
|
-
if hasattr(cmd, 'ins_idx') and hasattr(cmd, 'target'):
|
|
482
|
-
cmd.ins_idx = probe_symbol[cmd.target]
|
|
483
|
-
return drive_queue, probe_queue
|
|
484
|
-
|
|
485
|
-
def _optimize_probe_q(self, queue):
|
|
486
|
-
"""!
|
|
487
|
-
优化probe指令队列
|
|
488
|
-
|
|
489
|
-
@param queue:
|
|
490
|
-
@param symbol:
|
|
491
|
-
@return:
|
|
492
|
-
"""
|
|
493
|
-
symbol, idx = {}, 0
|
|
494
|
-
cache = []
|
|
495
|
-
res_q = []
|
|
496
|
-
# 合并所有相邻的_QInsProbeDelay,减少指令队列长度
|
|
497
|
-
for cmd in queue:
|
|
498
|
-
if isinstance(cmd, _QInsPTrigDelay):
|
|
499
|
-
cache.append(cmd)
|
|
500
|
-
else:
|
|
501
|
-
delay = _QInsPTrigDelay(tag=self.generate_tag, delay=sum(i.delay for i in cache))
|
|
502
|
-
if delay.delay != 0:
|
|
503
|
-
res_q.append(delay)
|
|
504
|
-
symbol[delay.tag] = idx
|
|
505
|
-
idx += 1
|
|
506
|
-
res_q.append(cmd)
|
|
507
|
-
symbol[cmd.tag] = idx
|
|
508
|
-
idx += 1
|
|
509
|
-
cache = []
|
|
510
|
-
return res_q, symbol
|
|
511
|
-
|
|
512
|
-
def _optimize_drive_q(self, queue, symbol):
|
|
513
|
-
"""!
|
|
514
|
-
优化drive指令队列
|
|
515
|
-
|
|
516
|
-
@param queue:
|
|
517
|
-
@param symbol:
|
|
518
|
-
@return:
|
|
519
|
-
"""
|
|
520
|
-
return queue, symbol
|
|
521
|
-
|
|
522
|
-
def assemble(self) -> "Tuple[dict, dict, dict]":
|
|
523
|
-
envelop_cache = {}
|
|
524
|
-
drive_cache = {}
|
|
525
|
-
for pch, dch_list in self.ch_map.items():
|
|
526
|
-
memory = np.zeros((10240 * 8 * len(dch_list), ), dtype=np.uint32).reshape((len(dch_list), 8*10240))
|
|
527
|
-
for idx, ch in enumerate(dch_list):
|
|
528
|
-
_data = np.frombuffer(b''.join(bytes(i) for i in self.drive_cmdq[ch]), dtype=np.int32)
|
|
529
|
-
memory[idx, :_data.size] = _data
|
|
530
|
-
drive_cache[pch] = memory
|
|
531
|
-
envelop_cache[pch] = np.array([self.envelop_cache[ch] for ch in dch_list], dtype=np.int16)
|
|
532
|
-
|
|
533
|
-
probe_cache = {}
|
|
534
|
-
for pch, queue in self.probe_cmdq.items():
|
|
535
|
-
memory = np.frombuffer(b''.join(bytes(i) for i in queue), dtype=np.int32)
|
|
536
|
-
probe_cache[pch] = memory
|
|
537
|
-
|
|
538
|
-
return envelop_cache, drive_cache, probe_cache
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: nsqdriver
|
|
3
|
-
Version: 0.3.0
|
|
4
|
-
Summary: Naishu Q series quantum measurement and control equipment driver interface
|
|
5
|
-
Home-page: https://g2hoyqcmh4.feishu.cn/wiki/wikcnzvyMd82DLZUe2NsI6HxsFc
|
|
6
|
-
Author: Naishu Technology
|
|
7
|
-
Author-email: jilianyi@naishu.tech
|
|
8
|
-
Classifier: Intended Audience :: Developers
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
-
Requires-Python: >=3.10
|
|
12
|
-
Requires-Dist: numpy (>=1.18)
|
|
13
|
-
Requires-Dist: waveforms
|
|
14
|
-
|
|
File without changes
|