Merge branch 'master' into nac3

This commit is contained in:
Sebastien Bourdeauducq 2022-05-24 11:12:20 +08:00
commit 45edb9e0f7
12 changed files with 108 additions and 26 deletions

View File

@ -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.

View File

@ -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):

View File

@ -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"]

View File

@ -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):

View File

@ -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

View File

@ -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) }
}))
}

View File

@ -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())

View File

@ -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:

View File

@ -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"]],

View File

@ -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))

View File

@ -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")

View File

@ -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])
]