mirror of https://github.com/m-labs/artiq.git
Merge branch 'master' into nac3
This commit is contained in:
commit
45edb9e0f7
|
@ -12,6 +12,7 @@ Highlights:
|
|||
- Kasli-SoC, a new EEM carrier based on a Zynq SoC, enabling much faster kernel execution.
|
||||
- HVAMP_8CH 8 channel HV amplifier for Fastino / Zotinos
|
||||
- Almazny mezzanine board for Mirny
|
||||
* TTL device output can be now configured to work as a clock generator.
|
||||
* Softcore targets now use the RISC-V architecture (VexRiscv) instead of OR1K (mor1kx).
|
||||
* Gateware FPU is supported on KC705 and Kasli 2.0.
|
||||
* Faster compilation for large arrays/lists.
|
||||
|
|
|
@ -110,9 +110,10 @@ class CommMgmt:
|
|||
return ty
|
||||
|
||||
def _read_expect(self, ty):
|
||||
if self._read_header() != ty:
|
||||
header = self._read_header()
|
||||
if header != ty:
|
||||
raise IOError("Incorrect reply from device: {} (expected {})".
|
||||
format(self._read_type, ty))
|
||||
format(header, ty))
|
||||
|
||||
def _read_int32(self):
|
||||
(value, ) = struct.unpack(self.endian + "l", self._read(4))
|
||||
|
@ -159,7 +160,12 @@ class CommMgmt:
|
|||
def config_read(self, key):
|
||||
self._write_header(Request.ConfigRead)
|
||||
self._write_string(key)
|
||||
self._read_expect(Reply.ConfigData)
|
||||
ty = self._read_header()
|
||||
if ty == Reply.Error:
|
||||
raise IOError("Device failed to read config. The key may not exist.")
|
||||
elif ty != Reply.ConfigData:
|
||||
raise IOError("Incorrect reply from device: {} (expected {})".
|
||||
format(ty, Reply.ConfigData))
|
||||
return self._read_string()
|
||||
|
||||
def config_write(self, key, value):
|
||||
|
|
|
@ -170,11 +170,11 @@
|
|||
},
|
||||
"bank_direction_low": {
|
||||
"type": "string",
|
||||
"enum": ["input", "output"]
|
||||
"enum": ["input", "output", "clkgen"]
|
||||
},
|
||||
"bank_direction_high": {
|
||||
"type": "string",
|
||||
"enum": ["input", "output"]
|
||||
"enum": ["input", "output", "clkgen"]
|
||||
}
|
||||
},
|
||||
"required": ["ports", "bank_direction_low", "bank_direction_high"]
|
||||
|
|
|
@ -14,6 +14,12 @@ from artiq.gui.scientific_spinbox import ScientificSpinBox
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def rename(key, newkey, value, dataset_ctl):
|
||||
if key != newkey:
|
||||
await dataset_ctl.delete(key)
|
||||
await dataset_ctl.set(newkey, value)
|
||||
|
||||
|
||||
class Editor(QtWidgets.QDialog):
|
||||
def __init__(self, parent, dataset_ctl, key, value):
|
||||
QtWidgets.QDialog.__init__(self, parent=parent)
|
||||
|
@ -26,7 +32,11 @@ class Editor(QtWidgets.QDialog):
|
|||
self.setLayout(grid)
|
||||
|
||||
grid.addWidget(QtWidgets.QLabel("Name:"), 0, 0)
|
||||
grid.addWidget(QtWidgets.QLabel(key), 0, 1)
|
||||
|
||||
self.name_widget = QtWidgets.QLineEdit()
|
||||
self.name_widget.setText(key)
|
||||
|
||||
grid.addWidget(self.name_widget, 0, 1)
|
||||
|
||||
grid.addWidget(QtWidgets.QLabel("Value:"), 1, 0)
|
||||
grid.addWidget(self.get_edit_widget(value), 1, 1)
|
||||
|
@ -39,8 +49,9 @@ class Editor(QtWidgets.QDialog):
|
|||
buttons.rejected.connect(self.reject)
|
||||
|
||||
def accept(self):
|
||||
newkey = self.name_widget.text()
|
||||
value = self.initial_type(self.get_edit_widget_value())
|
||||
asyncio.ensure_future(self.dataset_ctl.set(self.key, value))
|
||||
asyncio.ensure_future(rename(self.key, newkey, value, self.dataset_ctl))
|
||||
QtWidgets.QDialog.accept(self)
|
||||
|
||||
def get_edit_widget(self, initial_value):
|
||||
|
|
|
@ -203,6 +203,7 @@ _WidgetDesc = namedtuple("_WidgetDesc", "uid comment cls arguments")
|
|||
|
||||
def setup_from_ddb(ddb):
|
||||
mi_addr = None
|
||||
mi_port = None
|
||||
dds_sysclk = None
|
||||
description = set()
|
||||
|
||||
|
@ -235,14 +236,16 @@ def setup_from_ddb(ddb):
|
|||
description.add(widget)
|
||||
elif v["type"] == "controller" and k == "core_moninj":
|
||||
mi_addr = v["host"]
|
||||
mi_port = v.get("port_proxy", 1383)
|
||||
except KeyError:
|
||||
pass
|
||||
return mi_addr, dds_sysclk, description
|
||||
return mi_addr, mi_port, dds_sysclk, description
|
||||
|
||||
|
||||
class _DeviceManager:
|
||||
def __init__(self):
|
||||
self.mi_addr = None
|
||||
self.mi_port = None
|
||||
self.reconnect_mi = asyncio.Event()
|
||||
self.mi_connection = None
|
||||
self.mi_connector_task = asyncio.ensure_future(self.mi_connector())
|
||||
|
@ -264,10 +267,11 @@ class _DeviceManager:
|
|||
return ddb
|
||||
|
||||
def notify(self, mod):
|
||||
mi_addr, dds_sysclk, description = setup_from_ddb(self.ddb)
|
||||
mi_addr, mi_port, dds_sysclk, description = setup_from_ddb(self.ddb)
|
||||
|
||||
if mi_addr != self.mi_addr:
|
||||
if (mi_addr, mi_port) != (self.mi_addr, self.mi_port):
|
||||
self.mi_addr = mi_addr
|
||||
self.mi_port = mi_port
|
||||
self.reconnect_mi.set()
|
||||
|
||||
self.dds_sysclk = dds_sysclk
|
||||
|
@ -397,7 +401,7 @@ class _DeviceManager:
|
|||
new_mi_connection = CommMonInj(self.monitor_cb, self.injection_status_cb,
|
||||
self.disconnect_cb)
|
||||
try:
|
||||
await new_mi_connection.connect(self.mi_addr, 1383)
|
||||
await new_mi_connection.connect(self.mi_addr, self.mi_port)
|
||||
except asyncio.CancelledError:
|
||||
logger.info("cancelled connection to moninj")
|
||||
break
|
||||
|
|
|
@ -9,6 +9,7 @@ pub enum Error {
|
|||
MissingSeparator { offset: usize },
|
||||
Utf8Error(str::Utf8Error),
|
||||
NoFlash,
|
||||
KeyNotFound
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
|
@ -28,6 +29,8 @@ impl fmt::Display for Error {
|
|||
write!(f, "{}", err),
|
||||
&Error::NoFlash =>
|
||||
write!(f, "flash memory is not present"),
|
||||
&Error::KeyNotFound =>
|
||||
write!(f, "key not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,14 +159,16 @@ mod imp {
|
|||
f(Lock::take().and_then(|lock| {
|
||||
let mut iter = Iter::new(lock.data());
|
||||
let mut value = &[][..];
|
||||
let mut found = false;
|
||||
while let Some(result) = iter.next() {
|
||||
let (record_key, record_value) = result?;
|
||||
if key.as_bytes() == record_key {
|
||||
found = true;
|
||||
// last write wins
|
||||
value = record_value
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
if found { Ok(value) } else { Err(Error::KeyNotFound) }
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
@ -529,7 +529,7 @@ fn flash_kernel_worker(io: &Io, aux_mutex: &Mutex,
|
|||
|
||||
config::read(config_key, |result| {
|
||||
match result {
|
||||
Ok(kernel) if kernel.len() > 0 => unsafe {
|
||||
Ok(kernel) => unsafe {
|
||||
// kernel CPU cannot access the SPI flash address space directly,
|
||||
// so make a copy.
|
||||
kern_load(io, &mut session, Vec::from(kernel).as_ref())
|
||||
|
|
|
@ -7,7 +7,10 @@ import socket
|
|||
import ssl
|
||||
import io
|
||||
import zipfile
|
||||
import json
|
||||
from prettytable import PrettyTable
|
||||
from getpass import getpass
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
def get_artiq_cert():
|
||||
|
@ -54,20 +57,42 @@ class Client:
|
|||
self.fsocket.write((" ".join(command) + "\n").encode())
|
||||
self.fsocket.flush()
|
||||
|
||||
def read_line(self):
|
||||
return self.fsocket.readline().decode("ascii")
|
||||
|
||||
def read_reply(self):
|
||||
return self.fsocket.readline().decode("ascii").split()
|
||||
|
||||
def read_json(self):
|
||||
return json.loads(self.fsocket.readline().decode("ascii"))
|
||||
|
||||
def login(self, username, password):
|
||||
self.send_command("LOGIN", username, password)
|
||||
return self.read_reply() == ["HELLO"]
|
||||
|
||||
def build(self, rev, variant):
|
||||
self.send_command("BUILD", rev, variant)
|
||||
def build(self, rev, variant, log):
|
||||
if not variant:
|
||||
variants = self.get_variants()
|
||||
if len(variants) != 1:
|
||||
raise ValueError("User can build more than 1 variant - need to specify")
|
||||
variant = variants[0][0]
|
||||
print("Building variant: {}".format(variant))
|
||||
if log:
|
||||
self.send_command("BUILD", rev, variant, "LOG_ENABLE")
|
||||
else:
|
||||
self.send_command("BUILD", rev, variant)
|
||||
reply = self.read_reply()[0]
|
||||
if reply != "BUILDING":
|
||||
return reply, None
|
||||
print("Build in progress. This may take 10-15 minutes.")
|
||||
reply, status = self.read_reply()
|
||||
if log:
|
||||
line = self.read_line()
|
||||
while line != "" and line.startswith("LOG"):
|
||||
print(line[4:], end="")
|
||||
line = self.read_line()
|
||||
reply, status = line.split()
|
||||
else:
|
||||
reply, status = self.read_reply()
|
||||
if reply != "DONE":
|
||||
raise ValueError("Unexpected server reply: expected 'DONE', got '{}'".format(reply))
|
||||
if status != "done":
|
||||
|
@ -76,13 +101,28 @@ class Client:
|
|||
reply, length = self.read_reply()
|
||||
if reply != "PRODUCT":
|
||||
raise ValueError("Unexpected server reply: expected 'PRODUCT', got '{}'".format(reply))
|
||||
contents = self.fsocket.read(int(length))
|
||||
length = int(length)
|
||||
contents = bytearray()
|
||||
with tqdm(total=length, unit="iB", unit_scale=True, unit_divisor=1024) as progress_bar:
|
||||
total = 0
|
||||
while total != length:
|
||||
chunk_len = min(4096, length-total)
|
||||
contents += self.fsocket.read(chunk_len)
|
||||
total += chunk_len
|
||||
progress_bar.update(chunk_len)
|
||||
print("Download completed.")
|
||||
return "OK", contents
|
||||
|
||||
def passwd(self, password):
|
||||
self.send_command("PASSWD", password)
|
||||
return self.read_reply() == ["OK"]
|
||||
|
||||
def get_variants(self):
|
||||
self.send_command("GET_VARIANTS")
|
||||
reply = self.read_reply()[0]
|
||||
if reply != "OK":
|
||||
raise ValueError("Unexpected server reply: expected 'OK', got '{}'".format(reply))
|
||||
return self.read_json()
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -95,9 +135,11 @@ def main():
|
|||
action.required = True
|
||||
act_build = action.add_parser("build", help="build and download firmware")
|
||||
act_build.add_argument("--rev", default=None, help="revision to build (default: currently installed ARTIQ revision)")
|
||||
act_build.add_argument("variant", help="variant to build")
|
||||
act_build.add_argument("--log", action="store_true", help="Display the build log")
|
||||
act_build.add_argument("directory", help="output directory")
|
||||
act_build.add_argument("variant", nargs="?", default=None, help="variant to build (can be omitted if user is authorised to build only one)")
|
||||
act_passwd = action.add_parser("passwd", help="change password")
|
||||
act_get_variants = action.add_parser("get_variants", help="get available variants and expiry dates")
|
||||
args = parser.parse_args()
|
||||
|
||||
cert = args.cert
|
||||
|
@ -146,14 +188,22 @@ def main():
|
|||
if rev is None:
|
||||
print("Unable to determine currently installed ARTIQ revision. Specify manually using --rev.")
|
||||
sys.exit(1)
|
||||
result, contents = client.build(rev, args.variant)
|
||||
result, contents = client.build(rev, args.variant, args.log)
|
||||
if result != "OK":
|
||||
if result == "UNAUTHORIZED":
|
||||
print("You are not authorized to build this variant. Your firmware subscription may have expired. Contact helpdesk\x40m-labs.hk.")
|
||||
elif result == "TOOMANY":
|
||||
print("Too many builds in a queue. Please wait for others to finish.")
|
||||
else:
|
||||
print("Build failed: {}".format(result))
|
||||
sys.exit(1)
|
||||
zip_unarchive(contents, args.directory)
|
||||
elif args.action == "get_variants":
|
||||
data = client.get_variants()
|
||||
table = PrettyTable()
|
||||
table.field_names = ["Variant", "Expiry date"]
|
||||
table.add_rows(data)
|
||||
print(table)
|
||||
else:
|
||||
raise ValueError
|
||||
finally:
|
||||
|
|
|
@ -94,7 +94,8 @@ class PeripheralManager:
|
|||
def process_dio(self, rtio_offset, peripheral, num_channels=8):
|
||||
class_names = {
|
||||
"input": "TTLInOut",
|
||||
"output": "TTLOut"
|
||||
"output": "TTLOut",
|
||||
"clkgen": "TTLClockGen"
|
||||
}
|
||||
classes = [
|
||||
class_names[peripheral["bank_direction_low"]],
|
||||
|
|
|
@ -231,10 +231,8 @@ class Urukul(_EEM):
|
|||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||
|
||||
pads = target.platform.request("urukul{}_dds_reset_sync_in".format(eem))
|
||||
pad = Signal(reset=0)
|
||||
target.specials += DifferentialOutput(pad, pads.p, pads.n)
|
||||
if sync_gen_cls is not None: # AD9910 variant and SYNC_IN from EEM
|
||||
phy = sync_gen_cls(pad, ftw_width=4)
|
||||
phy = sync_gen_cls(pad=pads.p, pad_n=pads.n, ftw_width=4)
|
||||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, edge_counter
|
|||
def peripheral_dio(module, peripheral, **kwargs):
|
||||
ttl_classes = {
|
||||
"input": ttl_serdes_7series.InOut_8X,
|
||||
"output": ttl_serdes_7series.Output_8X
|
||||
"output": ttl_serdes_7series.Output_8X,
|
||||
"clkgen": ttl_simple.ClockGen
|
||||
}
|
||||
if len(peripheral["ports"]) != 1:
|
||||
raise ValueError("wrong number of ports")
|
||||
|
|
|
@ -145,11 +145,16 @@ class InOut(Module):
|
|||
|
||||
|
||||
class ClockGen(Module):
|
||||
def __init__(self, pad, ftw_width=24):
|
||||
def __init__(self, pad, pad_n=None, ftw_width=24, dci=False):
|
||||
self.rtlink = rtlink.Interface(rtlink.OInterface(ftw_width))
|
||||
|
||||
# # #
|
||||
|
||||
pad_o = Signal()
|
||||
if pad_n is None:
|
||||
self.comb += pad.eq(pad_o)
|
||||
else:
|
||||
self.specials += DifferentialOutput(pad_o, pad, pad_n)
|
||||
ftw = Signal(ftw_width)
|
||||
acc = Signal(ftw_width)
|
||||
self.sync.rio += If(self.rtlink.o.stb, ftw.eq(self.rtlink.o.data))
|
||||
|
@ -165,5 +170,5 @@ class ClockGen(Module):
|
|||
acc.eq(0)
|
||||
)
|
||||
),
|
||||
pad.eq(acc[-1])
|
||||
pad_o.eq(acc[-1])
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue