forked from M-Labs/artiq
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.
|
- 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
|
- HVAMP_8CH 8 channel HV amplifier for Fastino / Zotinos
|
||||||
- Almazny mezzanine board for Mirny
|
- 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).
|
* Softcore targets now use the RISC-V architecture (VexRiscv) instead of OR1K (mor1kx).
|
||||||
* Gateware FPU is supported on KC705 and Kasli 2.0.
|
* Gateware FPU is supported on KC705 and Kasli 2.0.
|
||||||
* Faster compilation for large arrays/lists.
|
* Faster compilation for large arrays/lists.
|
||||||
|
|
|
@ -110,9 +110,10 @@ class CommMgmt:
|
||||||
return ty
|
return ty
|
||||||
|
|
||||||
def _read_expect(self, 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 {})".
|
raise IOError("Incorrect reply from device: {} (expected {})".
|
||||||
format(self._read_type, ty))
|
format(header, ty))
|
||||||
|
|
||||||
def _read_int32(self):
|
def _read_int32(self):
|
||||||
(value, ) = struct.unpack(self.endian + "l", self._read(4))
|
(value, ) = struct.unpack(self.endian + "l", self._read(4))
|
||||||
|
@ -159,7 +160,12 @@ class CommMgmt:
|
||||||
def config_read(self, key):
|
def config_read(self, key):
|
||||||
self._write_header(Request.ConfigRead)
|
self._write_header(Request.ConfigRead)
|
||||||
self._write_string(key)
|
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()
|
return self._read_string()
|
||||||
|
|
||||||
def config_write(self, key, value):
|
def config_write(self, key, value):
|
||||||
|
|
|
@ -170,11 +170,11 @@
|
||||||
},
|
},
|
||||||
"bank_direction_low": {
|
"bank_direction_low": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["input", "output"]
|
"enum": ["input", "output", "clkgen"]
|
||||||
},
|
},
|
||||||
"bank_direction_high": {
|
"bank_direction_high": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["input", "output"]
|
"enum": ["input", "output", "clkgen"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["ports", "bank_direction_low", "bank_direction_high"]
|
"required": ["ports", "bank_direction_low", "bank_direction_high"]
|
||||||
|
|
|
@ -14,6 +14,12 @@ from artiq.gui.scientific_spinbox import ScientificSpinBox
|
||||||
logger = logging.getLogger(__name__)
|
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):
|
class Editor(QtWidgets.QDialog):
|
||||||
def __init__(self, parent, dataset_ctl, key, value):
|
def __init__(self, parent, dataset_ctl, key, value):
|
||||||
QtWidgets.QDialog.__init__(self, parent=parent)
|
QtWidgets.QDialog.__init__(self, parent=parent)
|
||||||
|
@ -26,7 +32,11 @@ class Editor(QtWidgets.QDialog):
|
||||||
self.setLayout(grid)
|
self.setLayout(grid)
|
||||||
|
|
||||||
grid.addWidget(QtWidgets.QLabel("Name:"), 0, 0)
|
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(QtWidgets.QLabel("Value:"), 1, 0)
|
||||||
grid.addWidget(self.get_edit_widget(value), 1, 1)
|
grid.addWidget(self.get_edit_widget(value), 1, 1)
|
||||||
|
@ -39,8 +49,9 @@ class Editor(QtWidgets.QDialog):
|
||||||
buttons.rejected.connect(self.reject)
|
buttons.rejected.connect(self.reject)
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
|
newkey = self.name_widget.text()
|
||||||
value = self.initial_type(self.get_edit_widget_value())
|
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)
|
QtWidgets.QDialog.accept(self)
|
||||||
|
|
||||||
def get_edit_widget(self, initial_value):
|
def get_edit_widget(self, initial_value):
|
||||||
|
|
|
@ -203,6 +203,7 @@ _WidgetDesc = namedtuple("_WidgetDesc", "uid comment cls arguments")
|
||||||
|
|
||||||
def setup_from_ddb(ddb):
|
def setup_from_ddb(ddb):
|
||||||
mi_addr = None
|
mi_addr = None
|
||||||
|
mi_port = None
|
||||||
dds_sysclk = None
|
dds_sysclk = None
|
||||||
description = set()
|
description = set()
|
||||||
|
|
||||||
|
@ -235,14 +236,16 @@ def setup_from_ddb(ddb):
|
||||||
description.add(widget)
|
description.add(widget)
|
||||||
elif v["type"] == "controller" and k == "core_moninj":
|
elif v["type"] == "controller" and k == "core_moninj":
|
||||||
mi_addr = v["host"]
|
mi_addr = v["host"]
|
||||||
|
mi_port = v.get("port_proxy", 1383)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
return mi_addr, dds_sysclk, description
|
return mi_addr, mi_port, dds_sysclk, description
|
||||||
|
|
||||||
|
|
||||||
class _DeviceManager:
|
class _DeviceManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.mi_addr = None
|
self.mi_addr = None
|
||||||
|
self.mi_port = None
|
||||||
self.reconnect_mi = asyncio.Event()
|
self.reconnect_mi = asyncio.Event()
|
||||||
self.mi_connection = None
|
self.mi_connection = None
|
||||||
self.mi_connector_task = asyncio.ensure_future(self.mi_connector())
|
self.mi_connector_task = asyncio.ensure_future(self.mi_connector())
|
||||||
|
@ -264,10 +267,11 @@ class _DeviceManager:
|
||||||
return ddb
|
return ddb
|
||||||
|
|
||||||
def notify(self, mod):
|
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_addr = mi_addr
|
||||||
|
self.mi_port = mi_port
|
||||||
self.reconnect_mi.set()
|
self.reconnect_mi.set()
|
||||||
|
|
||||||
self.dds_sysclk = dds_sysclk
|
self.dds_sysclk = dds_sysclk
|
||||||
|
@ -397,7 +401,7 @@ class _DeviceManager:
|
||||||
new_mi_connection = CommMonInj(self.monitor_cb, self.injection_status_cb,
|
new_mi_connection = CommMonInj(self.monitor_cb, self.injection_status_cb,
|
||||||
self.disconnect_cb)
|
self.disconnect_cb)
|
||||||
try:
|
try:
|
||||||
await new_mi_connection.connect(self.mi_addr, 1383)
|
await new_mi_connection.connect(self.mi_addr, self.mi_port)
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger.info("cancelled connection to moninj")
|
logger.info("cancelled connection to moninj")
|
||||||
break
|
break
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub enum Error {
|
||||||
MissingSeparator { offset: usize },
|
MissingSeparator { offset: usize },
|
||||||
Utf8Error(str::Utf8Error),
|
Utf8Error(str::Utf8Error),
|
||||||
NoFlash,
|
NoFlash,
|
||||||
|
KeyNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
|
@ -28,6 +29,8 @@ impl fmt::Display for Error {
|
||||||
write!(f, "{}", err),
|
write!(f, "{}", err),
|
||||||
&Error::NoFlash =>
|
&Error::NoFlash =>
|
||||||
write!(f, "flash memory is not present"),
|
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| {
|
f(Lock::take().and_then(|lock| {
|
||||||
let mut iter = Iter::new(lock.data());
|
let mut iter = Iter::new(lock.data());
|
||||||
let mut value = &[][..];
|
let mut value = &[][..];
|
||||||
|
let mut found = false;
|
||||||
while let Some(result) = iter.next() {
|
while let Some(result) = iter.next() {
|
||||||
let (record_key, record_value) = result?;
|
let (record_key, record_value) = result?;
|
||||||
if key.as_bytes() == record_key {
|
if key.as_bytes() == record_key {
|
||||||
|
found = true;
|
||||||
// last write wins
|
// last write wins
|
||||||
value = record_value
|
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| {
|
config::read(config_key, |result| {
|
||||||
match result {
|
match result {
|
||||||
Ok(kernel) if kernel.len() > 0 => unsafe {
|
Ok(kernel) => unsafe {
|
||||||
// kernel CPU cannot access the SPI flash address space directly,
|
// kernel CPU cannot access the SPI flash address space directly,
|
||||||
// so make a copy.
|
// so make a copy.
|
||||||
kern_load(io, &mut session, Vec::from(kernel).as_ref())
|
kern_load(io, &mut session, Vec::from(kernel).as_ref())
|
||||||
|
|
|
@ -7,7 +7,10 @@ import socket
|
||||||
import ssl
|
import ssl
|
||||||
import io
|
import io
|
||||||
import zipfile
|
import zipfile
|
||||||
|
import json
|
||||||
|
from prettytable import PrettyTable
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
|
||||||
def get_artiq_cert():
|
def get_artiq_cert():
|
||||||
|
@ -54,19 +57,41 @@ class Client:
|
||||||
self.fsocket.write((" ".join(command) + "\n").encode())
|
self.fsocket.write((" ".join(command) + "\n").encode())
|
||||||
self.fsocket.flush()
|
self.fsocket.flush()
|
||||||
|
|
||||||
|
def read_line(self):
|
||||||
|
return self.fsocket.readline().decode("ascii")
|
||||||
|
|
||||||
def read_reply(self):
|
def read_reply(self):
|
||||||
return self.fsocket.readline().decode("ascii").split()
|
return self.fsocket.readline().decode("ascii").split()
|
||||||
|
|
||||||
|
def read_json(self):
|
||||||
|
return json.loads(self.fsocket.readline().decode("ascii"))
|
||||||
|
|
||||||
def login(self, username, password):
|
def login(self, username, password):
|
||||||
self.send_command("LOGIN", username, password)
|
self.send_command("LOGIN", username, password)
|
||||||
return self.read_reply() == ["HELLO"]
|
return self.read_reply() == ["HELLO"]
|
||||||
|
|
||||||
def build(self, 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)
|
self.send_command("BUILD", rev, variant)
|
||||||
reply = self.read_reply()[0]
|
reply = self.read_reply()[0]
|
||||||
if reply != "BUILDING":
|
if reply != "BUILDING":
|
||||||
return reply, None
|
return reply, None
|
||||||
print("Build in progress. This may take 10-15 minutes.")
|
print("Build in progress. This may take 10-15 minutes.")
|
||||||
|
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()
|
reply, status = self.read_reply()
|
||||||
if reply != "DONE":
|
if reply != "DONE":
|
||||||
raise ValueError("Unexpected server reply: expected 'DONE', got '{}'".format(reply))
|
raise ValueError("Unexpected server reply: expected 'DONE', got '{}'".format(reply))
|
||||||
|
@ -76,7 +101,15 @@ class Client:
|
||||||
reply, length = self.read_reply()
|
reply, length = self.read_reply()
|
||||||
if reply != "PRODUCT":
|
if reply != "PRODUCT":
|
||||||
raise ValueError("Unexpected server reply: expected 'PRODUCT', got '{}'".format(reply))
|
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.")
|
print("Download completed.")
|
||||||
return "OK", contents
|
return "OK", contents
|
||||||
|
|
||||||
|
@ -84,6 +117,13 @@ class Client:
|
||||||
self.send_command("PASSWD", password)
|
self.send_command("PASSWD", password)
|
||||||
return self.read_reply() == ["OK"]
|
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():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
@ -95,9 +135,11 @@ def main():
|
||||||
action.required = True
|
action.required = True
|
||||||
act_build = action.add_parser("build", help="build and download firmware")
|
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("--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("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_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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
cert = args.cert
|
cert = args.cert
|
||||||
|
@ -146,14 +188,22 @@ def main():
|
||||||
if rev is None:
|
if rev is None:
|
||||||
print("Unable to determine currently installed ARTIQ revision. Specify manually using --rev.")
|
print("Unable to determine currently installed ARTIQ revision. Specify manually using --rev.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
result, contents = client.build(rev, args.variant)
|
result, contents = client.build(rev, args.variant, args.log)
|
||||||
if result != "OK":
|
if result != "OK":
|
||||||
if result == "UNAUTHORIZED":
|
if result == "UNAUTHORIZED":
|
||||||
print("You are not authorized to build this variant. Your firmware subscription may have expired. Contact helpdesk\x40m-labs.hk.")
|
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:
|
else:
|
||||||
print("Build failed: {}".format(result))
|
print("Build failed: {}".format(result))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
zip_unarchive(contents, args.directory)
|
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:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -94,7 +94,8 @@ class PeripheralManager:
|
||||||
def process_dio(self, rtio_offset, peripheral, num_channels=8):
|
def process_dio(self, rtio_offset, peripheral, num_channels=8):
|
||||||
class_names = {
|
class_names = {
|
||||||
"input": "TTLInOut",
|
"input": "TTLInOut",
|
||||||
"output": "TTLOut"
|
"output": "TTLOut",
|
||||||
|
"clkgen": "TTLClockGen"
|
||||||
}
|
}
|
||||||
classes = [
|
classes = [
|
||||||
class_names[peripheral["bank_direction_low"]],
|
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))
|
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||||
|
|
||||||
pads = target.platform.request("urukul{}_dds_reset_sync_in".format(eem))
|
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
|
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.submodules += phy
|
||||||
target.rtio_channels.append(rtio.Channel.from_phy(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):
|
def peripheral_dio(module, peripheral, **kwargs):
|
||||||
ttl_classes = {
|
ttl_classes = {
|
||||||
"input": ttl_serdes_7series.InOut_8X,
|
"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:
|
if len(peripheral["ports"]) != 1:
|
||||||
raise ValueError("wrong number of ports")
|
raise ValueError("wrong number of ports")
|
||||||
|
|
|
@ -145,11 +145,16 @@ class InOut(Module):
|
||||||
|
|
||||||
|
|
||||||
class ClockGen(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))
|
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)
|
ftw = Signal(ftw_width)
|
||||||
acc = Signal(ftw_width)
|
acc = Signal(ftw_width)
|
||||||
self.sync.rio += If(self.rtlink.o.stb, ftw.eq(self.rtlink.o.data))
|
self.sync.rio += If(self.rtlink.o.stb, ftw.eq(self.rtlink.o.data))
|
||||||
|
@ -165,5 +170,5 @@ class ClockGen(Module):
|
||||||
acc.eq(0)
|
acc.eq(0)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
pad.eq(acc[-1])
|
pad_o.eq(acc[-1])
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue