mirror of https://github.com/m-labs/artiq.git
Merge branch 'master' into nac3
This commit is contained in:
commit
7cffa3f8ff
|
@ -68,6 +68,9 @@ Old syntax should be replaced with the form shown on the right.
|
|||
data[key][1] ==> value[key]
|
||||
data[key][2] ==> metadata[key]
|
||||
|
||||
* The ``ndecimals`` parameter in ``NumberValue`` and ``Scannable`` has been renamed to ``precision``.
|
||||
Parameters after and including ``scale`` in both constructors are now keyword-only.
|
||||
Refer to the updated ``no_hardware/arguments_demo.py`` example for current usage.
|
||||
|
||||
|
||||
ARTIQ-7
|
||||
|
|
|
@ -44,8 +44,8 @@ class AppletControlRPC:
|
|||
self.dataset_ctl = dataset_ctl
|
||||
self.background_tasks = set()
|
||||
|
||||
def _background(self, coro, *args):
|
||||
task = self.loop.create_task(coro(*args))
|
||||
def _background(self, coro, *args, **kwargs):
|
||||
task = self.loop.create_task(coro(*args, **kwargs))
|
||||
self.background_tasks.add(task)
|
||||
task.add_done_callback(self.background_tasks.discard)
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class AlmaznyLegacy:
|
|||
This applies to Almazny hardware v1.1 and earlier.
|
||||
Use :class:`artiq.coredevice.almazny.AlmaznyChannel` for Almazny v1.2 and later.
|
||||
|
||||
:param host_mirny - Mirny device Almazny is connected to
|
||||
:param host_mirny: Mirny device Almazny is connected to
|
||||
"""
|
||||
|
||||
core: KernelInvariant[Core]
|
||||
|
@ -74,9 +74,10 @@ class AlmaznyLegacy:
|
|||
def set_att(self, channel: int32, att: float, rf_switch: bool = True):
|
||||
"""
|
||||
Sets attenuators on chosen shift register (channel).
|
||||
:param channel - index of the register [0-3]
|
||||
:param att_mu - attenuation setting in dBm [0-31.5]
|
||||
:param rf_switch - rf switch (bool)
|
||||
|
||||
:param channel: index of the register [0-3]
|
||||
:param att: attenuation setting in dBm [0-31.5]
|
||||
:param rf_switch: rf switch (bool)
|
||||
"""
|
||||
self.set_att_mu(channel, self.att_to_mu(att), rf_switch)
|
||||
|
||||
|
@ -84,9 +85,10 @@ class AlmaznyLegacy:
|
|||
def set_att_mu(self, channel: int32, att_mu: int32, rf_switch: bool = True):
|
||||
"""
|
||||
Sets attenuators on chosen shift register (channel).
|
||||
:param channel - index of the register [0-3]
|
||||
:param att_mu - attenuation setting in machine units [0-63]
|
||||
:param rf_switch - rf switch (bool)
|
||||
|
||||
:param channel: index of the register [0-3]
|
||||
:param att_mu: attenuation setting in machine units [0-63]
|
||||
:param rf_switch: rf switch (bool)
|
||||
"""
|
||||
self.channel_sw[channel] = 1 if rf_switch else 0
|
||||
self.att_mu[channel] = att_mu
|
||||
|
@ -96,7 +98,8 @@ class AlmaznyLegacy:
|
|||
def output_toggle(self, oe: bool):
|
||||
"""
|
||||
Toggles output on all shift registers on or off.
|
||||
:param oe - toggle output enable (bool)
|
||||
|
||||
:param oe: toggle output enable (bool)
|
||||
"""
|
||||
self.output_enable = oe
|
||||
cfg_reg = self.mirny_cpld.read_reg(1)
|
||||
|
@ -142,6 +145,7 @@ class AlmaznyChannel:
|
|||
This driver requires Almazny hardware revision v1.2 or later
|
||||
and Mirny CPLD gateware v0.3 or later.
|
||||
Use :class:`artiq.coredevice.almazny.AlmaznyLegacy` for Almazny hardware v1.1 and earlier.
|
||||
|
||||
:param host_mirny: Mirny CPLD device name
|
||||
:param channel: channel index (0-3)
|
||||
"""
|
||||
|
@ -160,6 +164,7 @@ class AlmaznyChannel:
|
|||
"""
|
||||
Convert an attenuation in dB, RF switch state and LED state to machine
|
||||
units.
|
||||
|
||||
:param att: attenuator setting in dB (0-31.5)
|
||||
:param enable: RF switch state (bool)
|
||||
:param led: LED state (bool)
|
||||
|
@ -181,6 +186,7 @@ class AlmaznyChannel:
|
|||
def set_mu(self, mu: int32):
|
||||
"""
|
||||
Set channel state (machine units).
|
||||
|
||||
:param mu: channel state in machine units.
|
||||
"""
|
||||
self.mirny_cpld.write_ext(
|
||||
|
@ -190,6 +196,7 @@ class AlmaznyChannel:
|
|||
def set(self, att: float, enable: bool, led: bool = False):
|
||||
"""
|
||||
Set attenuation, RF switch, and LED state (SI units).
|
||||
|
||||
:param att: attenuator setting in dB (0-31.5)
|
||||
:param enable: RF switch state (bool)
|
||||
:param led: LED state (bool)
|
||||
|
|
|
@ -130,7 +130,7 @@
|
|||
},
|
||||
"hw_rev": {
|
||||
"type": "string",
|
||||
"enum": ["v1.0"]
|
||||
"enum": ["v1.0", "v1.1"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +142,7 @@
|
|||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["dio", "dio_spi", "urukul", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp"]
|
||||
"enum": ["dio", "dio_spi", "urukul", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp", "efc"]
|
||||
},
|
||||
"board": {
|
||||
"type": "string"
|
||||
|
@ -562,6 +562,28 @@
|
|||
},
|
||||
"required": ["ports"]
|
||||
}
|
||||
},{
|
||||
"title": "EFC",
|
||||
"if": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "efc"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"ports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
},
|
||||
"minItems": 1,
|
||||
"maxItems": 2
|
||||
}
|
||||
},
|
||||
"required": ["ports"]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,6 +168,7 @@ class I2CSwitch:
|
|||
@kernel
|
||||
def set(self, channel: int32):
|
||||
"""Enable one channel.
|
||||
|
||||
:param channel: channel number (0-7)
|
||||
"""
|
||||
i2c_switch_select(self.busno, self.address >> 1, 1 << channel)
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import numpy
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
from artiq.coredevice.rtio import rtio_output
|
||||
|
||||
|
||||
class Config:
|
||||
kernel_invariants = {"core", "channel", "target_o"}
|
||||
|
||||
def __init__(self, dmgr, channel, core_device="core"):
|
||||
self.core = dmgr.get(core_device)
|
||||
self.channel = channel
|
||||
self.target_o = channel << 8
|
||||
|
||||
@kernel
|
||||
def set_config(self, config):
|
||||
rtio_output(self.target_o, config)
|
||||
|
||||
|
||||
class Volt:
|
||||
kernel_invariants = {"core", "channel", "target_o"}
|
||||
|
||||
def __init__(self, dmgr, channel, core_device="core"):
|
||||
self.core = dmgr.get(core_device)
|
||||
self.channel = channel
|
||||
self.target_o = channel << 8
|
||||
|
||||
@kernel
|
||||
def set_waveform(self, a0: TInt32, a1: TInt32, a2: TInt64, a3: TInt64):
|
||||
pdq_words = [
|
||||
a0,
|
||||
a1,
|
||||
a1 >> 16,
|
||||
a2 & 0xFFFF,
|
||||
(a2 >> 16) & 0xFFFF,
|
||||
(a2 >> 32) & 0xFFFF,
|
||||
a3 & 0xFFFF,
|
||||
(a3 >> 16) & 0xFFFF,
|
||||
(a3 >> 32) & 0xFFFF,
|
||||
]
|
||||
|
||||
for i in range(len(pdq_words)):
|
||||
rtio_output(self.target_o | i, pdq_words[i])
|
||||
delay_mu(int64(self.core.ref_multiplier))
|
||||
|
||||
|
||||
class Dds:
|
||||
kernel_invariants = {"core", "channel", "target_o"}
|
||||
|
||||
def __init__(self, dmgr, channel, core_device="core"):
|
||||
self.core = dmgr.get(core_device)
|
||||
self.channel = channel
|
||||
self.target_o = channel << 8
|
||||
|
||||
@kernel
|
||||
def set_waveform(self, b0: TInt32, b1: TInt32, b2: TInt64, b3: TInt64,
|
||||
c0: TInt32, c1: TInt32, c2: TInt32):
|
||||
pdq_words = [
|
||||
b0,
|
||||
b1,
|
||||
b1 >> 16,
|
||||
b2 & 0xFFFF,
|
||||
(b2 >> 16) & 0xFFFF,
|
||||
(b2 >> 32) & 0xFFFF,
|
||||
b3 & 0xFFFF,
|
||||
(b3 >> 16) & 0xFFFF,
|
||||
(b3 >> 32) & 0xFFFF,
|
||||
c0,
|
||||
c1,
|
||||
c1 >> 16,
|
||||
c2,
|
||||
c2 >> 16,
|
||||
]
|
||||
|
||||
for i in range(len(pdq_words)):
|
||||
rtio_output(self.target_o | i, pdq_words[i])
|
||||
delay_mu(int64(self.core.ref_multiplier))
|
||||
|
||||
|
||||
class Trigger:
|
||||
kernel_invariants = {"core", "channel", "target_o"}
|
||||
|
||||
def __init__(self, dmgr, channel, core_device="core"):
|
||||
self.core = dmgr.get(core_device)
|
||||
self.channel = channel
|
||||
self.target_o = channel << 8
|
||||
|
||||
@kernel
|
||||
def trigger(self, trig_out):
|
||||
rtio_output(self.target_o, trig_out)
|
|
@ -286,9 +286,8 @@ class NRTSPIMaster:
|
|||
def set_config_mu(self, flags: int32 = 0, length: int32 = 8, div: int32 = 6, cs: int32 = 1):
|
||||
"""Set the ``config`` register.
|
||||
|
||||
Note that the non-realtime SPI cores are usually clocked by the system
|
||||
clock and not the RTIO clock. In many cases, the SPI configuration is
|
||||
already set by the firmware and you do not need to call this method.
|
||||
In many cases, the SPI configuration is already set by the firmware
|
||||
and you do not need to call this method.
|
||||
"""
|
||||
spi_set_config(self.busno, flags, length, div, cs)
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import numpy as np
|
|||
from PyQt5 import QtCore, QtWidgets
|
||||
from sipyco import pyon
|
||||
|
||||
from artiq.tools import short_format, exc_to_warning
|
||||
from artiq.tools import scale_from_metadata, short_format, exc_to_warning
|
||||
from artiq.gui.tools import LayoutWidget, QRecursiveFilterProxyModel
|
||||
from artiq.gui.models import DictSyncTreeSepModel
|
||||
from artiq.gui.scientific_spinbox import ScientificSpinBox
|
||||
|
@ -77,6 +77,12 @@ class CreateEditDialog(QtWidgets.QDialog):
|
|||
self.value_widget.setText(value)
|
||||
|
||||
if metadata is not None:
|
||||
scale = scale_from_metadata(metadata)
|
||||
decoded_value = pyon.decode(value)
|
||||
if scale == 1:
|
||||
self.value_widget.setText(value)
|
||||
else:
|
||||
self.value_widget.setText(pyon.encode(decoded_value / scale))
|
||||
self.unit_widget.setText(metadata.get('unit', ''))
|
||||
self.scale_widget.setText(str(metadata.get('scale', '')))
|
||||
self.precision_widget.setText(str(metadata.get('precision', '')))
|
||||
|
@ -97,10 +103,16 @@ class CreateEditDialog(QtWidgets.QDialog):
|
|||
metadata['scale'] = float(scale)
|
||||
if precision != "":
|
||||
metadata['precision'] = int(precision)
|
||||
scale = scale_from_metadata(metadata)
|
||||
value = pyon.decode(value)
|
||||
t = value.dtype if value is np.ndarray else type(value)
|
||||
is_floating = scale != 1 or np.issubdtype(t, np.floating)
|
||||
if is_floating:
|
||||
value = value * scale
|
||||
if self.key and self.key != key:
|
||||
asyncio.ensure_future(exc_to_warning(rename(self.key, key, pyon.decode(value), metadata, persist, self.dataset_ctl)))
|
||||
asyncio.ensure_future(exc_to_warning(rename(self.key, key, value, metadata, persist, self.dataset_ctl)))
|
||||
else:
|
||||
asyncio.ensure_future(exc_to_warning(self.dataset_ctl.set(key, pyon.decode(value), metadata=metadata, persist=persist)))
|
||||
asyncio.ensure_future(exc_to_warning(self.dataset_ctl.set(key, value, metadata=metadata, persist=persist)))
|
||||
self.key = key
|
||||
QtWidgets.QDialog.accept(self)
|
||||
|
||||
|
@ -197,12 +209,6 @@ class DatasetsDock(QtWidgets.QDockWidget):
|
|||
key = self.table_model.index_to_key(idx)
|
||||
if key is not None:
|
||||
persist, value, metadata = self.table_model.backing_store[key]
|
||||
t = type(value)
|
||||
if np.issubdtype(t, np.number) or np.issubdtype(t, np.bool_):
|
||||
value = str(value)
|
||||
elif np.issubdtype(t, np.unicode_):
|
||||
value = '"{}"'.format(str(value))
|
||||
else:
|
||||
value = pyon.encode(value)
|
||||
CreateEditDialog(self, self.dataset_ctl, key, value, metadata, persist).open()
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ class DDSSetter(EnvExperiment):
|
|||
"driver": self.get_device(k),
|
||||
"frequency": self.get_argument(
|
||||
"{}_frequency".format(k),
|
||||
NumberValue(100e6, scale=1e6, unit="MHz", ndecimals=6))
|
||||
NumberValue(100e6, scale=1e6, unit="MHz", precision=6))
|
||||
}
|
||||
|
||||
@kernel
|
||||
|
|
|
@ -31,8 +31,8 @@ class PhotonHistogram(EnvExperiment):
|
|||
self.setattr_device("bdd_sw")
|
||||
self.setattr_device("pmt")
|
||||
|
||||
self.setattr_argument("nbins", NumberValue(100, ndecimals=0, step=1))
|
||||
self.setattr_argument("repeats", NumberValue(100, ndecimals=0, step=1))
|
||||
self.setattr_argument("nbins", NumberValue(100, precision=0, step=1))
|
||||
self.setattr_argument("repeats", NumberValue(100, precision=0, step=1))
|
||||
|
||||
self.setattr_dataset("cool_f", 230*MHz)
|
||||
self.setattr_dataset("detect_f", 220*MHz)
|
||||
|
|
|
@ -97,7 +97,7 @@ class SpeedBenchmark(EnvExperiment):
|
|||
"CoreSend1MB",
|
||||
"CorePrimes"]))
|
||||
self.setattr_argument("nruns", NumberValue(10, min=1, max=1000,
|
||||
ndecimals=0, step=1))
|
||||
precision=0, step=1))
|
||||
self.setattr_device("core")
|
||||
self.setattr_device("scheduler")
|
||||
|
||||
|
|
|
@ -45,13 +45,13 @@ class ArgumentsDemo(EnvExperiment):
|
|||
PYONValue(self.get_dataset("foo", default=42)))
|
||||
self.setattr_argument("number", NumberValue(42e-6,
|
||||
unit="us",
|
||||
ndecimals=4))
|
||||
precision=4))
|
||||
self.setattr_argument("integer", NumberValue(42,
|
||||
step=1, ndecimals=0))
|
||||
step=1, precision=0))
|
||||
self.setattr_argument("string", StringValue("Hello World"))
|
||||
self.setattr_argument("scan", Scannable(global_max=400,
|
||||
default=NoScan(325),
|
||||
ndecimals=6))
|
||||
precision=6))
|
||||
self.setattr_argument("boolean", BooleanValue(True), "Group")
|
||||
self.setattr_argument("enum", EnumerationValue(
|
||||
["foo", "bar", "quux"], "foo"), "Group")
|
||||
|
|
|
@ -320,6 +320,7 @@ dependencies = [
|
|||
"build_misoc",
|
||||
"byteorder",
|
||||
"cslice",
|
||||
"dyld",
|
||||
"eh",
|
||||
"failure",
|
||||
"failure_derive",
|
||||
|
|
|
@ -17,7 +17,7 @@ void send_to_rtio_log(struct slice data);
|
|||
#define KERNELCPU_EXEC_ADDRESS 0x45000000
|
||||
#define KERNELCPU_PAYLOAD_ADDRESS 0x45060000
|
||||
#define KERNELCPU_LAST_ADDRESS 0x4fffffff
|
||||
#define KSUPPORT_HEADER_SIZE 0x80
|
||||
#define KSUPPORT_HEADER_SIZE 0x74
|
||||
|
||||
FILE *stderr;
|
||||
|
||||
|
|
|
@ -35,16 +35,6 @@ SECTIONS
|
|||
*(.text .text.*)
|
||||
} :text
|
||||
|
||||
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
|
||||
.got : {
|
||||
PROVIDE(_GLOBAL_OFFSET_TABLE_ = .);
|
||||
*(.got)
|
||||
} :text
|
||||
|
||||
.got.plt : {
|
||||
*(.got.plt)
|
||||
} :text
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata .rodata.*)
|
||||
|
|
|
@ -24,3 +24,4 @@ proto_artiq = { path = "../libproto_artiq" }
|
|||
|
||||
[features]
|
||||
uart_console = []
|
||||
alloc = []
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
use spi;
|
||||
use board_misoc::{csr, clock};
|
||||
|
||||
const DATA_CTRL_REG : u8 = 0x02;
|
||||
const IRCML_REG : u8 = 0x05;
|
||||
const QRCML_REG : u8 = 0x08;
|
||||
const CLKMODE_REG : u8 = 0x14;
|
||||
const VERSION_REG : u8 = 0x1F;
|
||||
|
||||
fn hard_reset() {
|
||||
unsafe {
|
||||
// Min Pulse Width: 50ns
|
||||
csr::dac_rst::out_write(1);
|
||||
clock::spin_us(1);
|
||||
csr::dac_rst::out_write(0);
|
||||
}
|
||||
}
|
||||
|
||||
fn spi_setup(dac_sel: u8, half_duplex: bool, end: bool) -> Result<(), &'static str> {
|
||||
// Clear the cs_polarity and cs config
|
||||
spi::set_config(0, 0, 8, 64, 0b1111)?;
|
||||
spi::set_config(0, 1 << 3, 8, 64, (7 - dac_sel) << 1)?;
|
||||
spi::set_config(0, (half_duplex as u8) << 7 | (end as u8) << 1, 8, 64, 0b0001)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(dac_sel: u8, reg: u8, val: u8) -> Result<(), &'static str> {
|
||||
spi_setup(dac_sel, false, false)?;
|
||||
spi::write(0, (reg as u32) << 24)?;
|
||||
spi_setup(dac_sel, false, true)?;
|
||||
spi::write(0, (val as u32) << 24)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(dac_sel: u8, reg: u8) -> Result<u8, &'static str> {
|
||||
spi_setup(dac_sel, false, false)?;
|
||||
spi::write(0, ((reg | 1 << 7) as u32) << 24)?;
|
||||
spi_setup(dac_sel, true, true)?;
|
||||
spi::write(0, 0)?;
|
||||
|
||||
Ok(spi::read(0)? as u8)
|
||||
}
|
||||
|
||||
pub fn init() -> Result<(), &'static str> {
|
||||
hard_reset();
|
||||
|
||||
for channel in 0..8 {
|
||||
let reg = read(channel, VERSION_REG)?;
|
||||
if reg != 0x0A {
|
||||
debug!("DAC AD9117 Channel {} has incorrect hardware version. VERSION reg: {:02x}", channel, reg);
|
||||
return Err("DAC AD9117 hardware version is not equal to 0x0A");
|
||||
}
|
||||
let reg = read(channel, CLKMODE_REG)?;
|
||||
if reg >> 4 & 1 != 0 {
|
||||
debug!("DAC AD9117 Channel {} retiming fails. CLKMODE reg: {:02x}", channel, reg);
|
||||
return Err("DAC AD9117 retiming failure");
|
||||
}
|
||||
|
||||
// Set the DACs input data format to be twos complement
|
||||
// Set IFIRST and IRISING to True
|
||||
write(channel, DATA_CTRL_REG, 1 << 7 | 1 << 5 | 1 << 4)?;
|
||||
|
||||
// Enable internal common mode resistors of both channels
|
||||
write(channel, IRCML_REG, 1 << 7)?;
|
||||
write(channel, QRCML_REG, 1 << 7)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
use board_misoc::{csr, clock, config};
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::format;
|
||||
|
||||
|
||||
struct SerdesConfig {
|
||||
pub delay: [u8; 4],
|
||||
}
|
||||
|
||||
impl SerdesConfig {
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
(self as *const SerdesConfig) as *const u8,
|
||||
core::mem::size_of::<SerdesConfig>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_lane(lane_no: u8) {
|
||||
unsafe {
|
||||
csr::eem_transceiver::lane_sel_write(lane_no);
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_delay(tap: u8) {
|
||||
unsafe {
|
||||
csr::eem_transceiver::dly_cnt_in_write(tap);
|
||||
csr::eem_transceiver::dly_ld_write(1);
|
||||
clock::spin_us(1);
|
||||
assert!(tap as u8 == csr::eem_transceiver::dly_cnt_out_read());
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_config(config: &SerdesConfig) {
|
||||
for lane_no in 0..4 {
|
||||
select_lane(lane_no as u8);
|
||||
apply_delay(config.delay[lane_no]);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn assign_delay() -> SerdesConfig {
|
||||
// Select an appropriate delay for lane 0
|
||||
select_lane(0);
|
||||
|
||||
let read_align = |dly: u8| -> f32 {
|
||||
apply_delay(dly);
|
||||
csr::eem_transceiver::counter_reset_write(1);
|
||||
|
||||
csr::eem_transceiver::counter_enable_write(1);
|
||||
clock::spin_us(2000);
|
||||
csr::eem_transceiver::counter_enable_write(0);
|
||||
|
||||
let (high, low) = (
|
||||
csr::eem_transceiver::counter_high_count_read(),
|
||||
csr::eem_transceiver::counter_low_count_read(),
|
||||
);
|
||||
if csr::eem_transceiver::counter_overflow_read() == 1 {
|
||||
panic!("Unexpected phase detector counter overflow");
|
||||
}
|
||||
|
||||
low as f32 / (low + high) as f32
|
||||
};
|
||||
|
||||
let mut best_dly = None;
|
||||
|
||||
loop {
|
||||
let mut prev = None;
|
||||
for curr_dly in 0..32 {
|
||||
let curr_low_rate = read_align(curr_dly);
|
||||
|
||||
if let Some(prev_low_rate) = prev {
|
||||
// This is potentially a crossover position
|
||||
if prev_low_rate <= curr_low_rate && curr_low_rate >= 0.5 {
|
||||
let prev_dev = 0.5 - prev_low_rate;
|
||||
let curr_dev = curr_low_rate - 0.5;
|
||||
let selected_idx = if prev_dev < curr_dev {
|
||||
curr_dly - 1
|
||||
} else {
|
||||
curr_dly
|
||||
};
|
||||
|
||||
// The setup setup/hold calibration timing (even with
|
||||
// tolerance) might be invalid in other lanes due to skew.
|
||||
// 5 taps is very conservative, generally it is 1 or 2
|
||||
if selected_idx < 5 {
|
||||
prev = None;
|
||||
continue;
|
||||
} else {
|
||||
best_dly = Some(selected_idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only rising slope from <= 0.5 can result in a rising low rate
|
||||
// crossover at 50%.
|
||||
if curr_low_rate <= 0.5 {
|
||||
prev = Some(curr_low_rate);
|
||||
}
|
||||
}
|
||||
|
||||
if best_dly.is_none() {
|
||||
error!("setup/hold timing calibration failed, retry in 1s...");
|
||||
clock::spin_us(1_000_000);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let best_dly = best_dly.unwrap();
|
||||
|
||||
apply_delay(best_dly);
|
||||
let mut delay_list = [best_dly; 4];
|
||||
|
||||
// Assign delay for other lanes
|
||||
for lane_no in 1..=3 {
|
||||
select_lane(lane_no as u8);
|
||||
|
||||
let mut min_deviation = 0.5;
|
||||
let mut min_idx = 0;
|
||||
for dly_delta in -3..=3 {
|
||||
let index = (best_dly as isize + dly_delta) as u8;
|
||||
let low_rate = read_align(index);
|
||||
// abs() from f32 is not available in core library
|
||||
let deviation = if low_rate < 0.5 {
|
||||
0.5 - low_rate
|
||||
} else {
|
||||
low_rate - 0.5
|
||||
};
|
||||
|
||||
if deviation < min_deviation {
|
||||
min_deviation = deviation;
|
||||
min_idx = index;
|
||||
}
|
||||
}
|
||||
|
||||
apply_delay(min_idx);
|
||||
delay_list[lane_no] = min_idx;
|
||||
}
|
||||
|
||||
debug!("setup/hold timing calibration: {:?}", delay_list);
|
||||
|
||||
SerdesConfig {
|
||||
delay: delay_list,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn align_comma() {
|
||||
loop {
|
||||
for slip in 1..=10 {
|
||||
// The soft transceiver has 2 8b10b decoders, which receives lane
|
||||
// 0/1 and lane 2/3 respectively. The decoder are time-multiplexed
|
||||
// to decode exactly 1 lane each sysclk cycle.
|
||||
//
|
||||
// The decoder decodes lane 0/2 data on odd sysclk cycles, buffer
|
||||
// on even cycles, and vice versa for lane 1/3. Data/Clock latency
|
||||
// could change timing. The extend bit flips the decoding timing,
|
||||
// so lane 0/2 data are decoded on even cycles, and lane 1/3 data
|
||||
// are decoded on odd cycles.
|
||||
//
|
||||
// This is needed because transmitting/receiving a 8b10b character
|
||||
// takes 2 sysclk cycles. Adjusting bitslip only via ISERDES
|
||||
// limits the range to 1 cycle. The wordslip bit extends the range
|
||||
// to 2 sysclk cycles.
|
||||
csr::eem_transceiver::wordslip_write((slip > 5) as u8);
|
||||
|
||||
// Apply a double bitslip since the ISERDES is 2x oversampled.
|
||||
// Bitslip is used for comma alignment purposes once setup/hold
|
||||
// timing is met.
|
||||
csr::eem_transceiver::bitslip_write(1);
|
||||
csr::eem_transceiver::bitslip_write(1);
|
||||
clock::spin_us(1);
|
||||
|
||||
csr::eem_transceiver::comma_align_reset_write(1);
|
||||
clock::spin_us(100);
|
||||
|
||||
if csr::eem_transceiver::comma_read() == 1 {
|
||||
debug!("comma alignment completed after {} bitslips", slip);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
error!("comma alignment failed, retrying in 1s...");
|
||||
clock::spin_us(1_000_000);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
for trx_no in 0..csr::CONFIG_EEM_DRTIO_COUNT {
|
||||
unsafe {
|
||||
csr::eem_transceiver::transceiver_sel_write(trx_no as u8);
|
||||
}
|
||||
|
||||
let key = format!("eem_drtio_delay{}", trx_no);
|
||||
config::read(&key, |r| {
|
||||
match r {
|
||||
Ok(record) => {
|
||||
info!("loading calibrated timing values from flash");
|
||||
unsafe {
|
||||
apply_config(&*(record.as_ptr() as *const SerdesConfig));
|
||||
}
|
||||
},
|
||||
|
||||
Err(_) => {
|
||||
info!("calibrating...");
|
||||
let config = unsafe { assign_delay() };
|
||||
config::write(&key, config.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
unsafe {
|
||||
align_comma();
|
||||
csr::eem_transceiver::rx_ready_write(1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,8 @@ extern crate log;
|
|||
extern crate io;
|
||||
extern crate board_misoc;
|
||||
extern crate proto_artiq;
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
pub mod spi;
|
||||
|
||||
|
@ -29,3 +31,9 @@ pub mod grabber;
|
|||
#[cfg(has_drtio)]
|
||||
pub mod drtioaux;
|
||||
pub mod drtio_routing;
|
||||
|
||||
#[cfg(all(has_drtio_eem, feature = "alloc"))]
|
||||
pub mod drtio_eem;
|
||||
|
||||
#[cfg(soc_platform = "efc")]
|
||||
pub mod ad9117;
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
mod imp {
|
||||
use board_misoc::csr;
|
||||
|
||||
pub fn set_config(busno: u8, flags: u8, length: u8, div: u8, cs: u8) -> Result<(), ()> {
|
||||
const INVALID_BUS: &'static str = "Invalid SPI bus";
|
||||
|
||||
pub fn set_config(busno: u8, flags: u8, length: u8, div: u8, cs: u8) -> Result<(), &'static str> {
|
||||
if busno != 0 {
|
||||
return Err(())
|
||||
return Err(INVALID_BUS)
|
||||
}
|
||||
unsafe {
|
||||
while csr::converter_spi::writable_read() == 0 {}
|
||||
|
@ -31,9 +33,9 @@ mod imp {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write(busno: u8, data: u32) -> Result<(), ()> {
|
||||
pub fn write(busno: u8, data: u32) -> Result<(), &'static str> {
|
||||
if busno != 0 {
|
||||
return Err(())
|
||||
return Err(INVALID_BUS)
|
||||
}
|
||||
unsafe {
|
||||
while csr::converter_spi::writable_read() == 0 {}
|
||||
|
@ -42,9 +44,9 @@ mod imp {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read(busno: u8) -> Result<u32, ()> {
|
||||
pub fn read(busno: u8) -> Result<u32, &'static str> {
|
||||
if busno != 0 {
|
||||
return Err(())
|
||||
return Err(INVALID_BUS)
|
||||
}
|
||||
Ok(unsafe {
|
||||
while csr::converter_spi::writable_read() == 0 {}
|
||||
|
|
|
@ -81,6 +81,31 @@ impl IoExpander {
|
|||
Ok(io_expander)
|
||||
}
|
||||
|
||||
#[cfg(soc_platform = "efc")]
|
||||
pub fn new() -> Result<Self, &'static str> {
|
||||
const VIRTUAL_LED_MAPPING: [(u8, u8, u8); 2] = [(0, 0, 5), (1, 0, 6)];
|
||||
|
||||
let io_expander = IoExpander {
|
||||
busno: 0,
|
||||
port: 1,
|
||||
address: 0x40,
|
||||
virtual_led_mapping: &VIRTUAL_LED_MAPPING,
|
||||
iodir: [0xff; 2],
|
||||
out_current: [0; 2],
|
||||
out_target: [0; 2],
|
||||
registers: Registers {
|
||||
iodira: 0x00,
|
||||
iodirb: 0x01,
|
||||
gpioa: 0x12,
|
||||
gpiob: 0x13,
|
||||
},
|
||||
};
|
||||
if !io_expander.check_ack()? {
|
||||
return Err("MCP23017 io expander not found.");
|
||||
};
|
||||
Ok(io_expander)
|
||||
}
|
||||
|
||||
#[cfg(soc_platform = "kasli")]
|
||||
fn select(&self) -> Result<(), &'static str> {
|
||||
let mask: u16 = 1 << self.port;
|
||||
|
@ -89,6 +114,13 @@ impl IoExpander {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(soc_platform = "efc")]
|
||||
fn select(&self) -> Result<(), &'static str> {
|
||||
let mask: u16 = 1 << self.port;
|
||||
i2c::switch_select(self.busno, 0x70, mask as u8)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, addr: u8, value: u8) -> Result<(), &'static str> {
|
||||
i2c::start(self.busno)?;
|
||||
i2c::write(self.busno, self.address)?;
|
||||
|
|
|
@ -40,7 +40,7 @@ pub mod ethmac;
|
|||
pub mod i2c;
|
||||
#[cfg(soc_platform = "kasli")]
|
||||
pub mod i2c_eeprom;
|
||||
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
|
||||
#[cfg(any(all(soc_platform = "kasli", hw_rev = "v2.0"), soc_platform = "efc"))]
|
||||
pub mod io_expander;
|
||||
#[cfg(all(has_ethmac, feature = "smoltcp"))]
|
||||
pub mod net_settings;
|
||||
|
|
|
@ -8,9 +8,6 @@ mod ddr {
|
|||
DFII_COMMAND_WRDATA, DFII_COMMAND_RDDATA};
|
||||
use sdram_phy::{DFII_NPHASES, DFII_PIX_DATA_SIZE, DFII_PIX_WRDATA_ADDR, DFII_PIX_RDDATA_ADDR};
|
||||
|
||||
#[cfg(kusddrphy)]
|
||||
const DDRPHY_MAX_DELAY: u16 = 512;
|
||||
#[cfg(not(kusddrphy))]
|
||||
const DDRPHY_MAX_DELAY: u16 = 32;
|
||||
|
||||
const DQS_SIGNAL_COUNT: usize = DFII_PIX_DATA_SIZE / 2;
|
||||
|
@ -35,17 +32,12 @@ mod ddr {
|
|||
|
||||
#[cfg(ddrphy_wlevel)]
|
||||
unsafe fn write_level_scan(logger: &mut Option<&mut dyn fmt::Write>) {
|
||||
#[cfg(kusddrphy)]
|
||||
log!(logger, "DQS initial delay: {} taps\n", ddrphy::wdly_dqs_taps_read());
|
||||
log!(logger, "Write leveling scan:\n");
|
||||
|
||||
enable_write_leveling(true);
|
||||
spin_cycles(100);
|
||||
|
||||
#[cfg(not(kusddrphy))]
|
||||
let ddrphy_max_delay : u16 = DDRPHY_MAX_DELAY;
|
||||
#[cfg(kusddrphy)]
|
||||
let ddrphy_max_delay : u16 = DDRPHY_MAX_DELAY - ddrphy::wdly_dqs_taps_read();
|
||||
|
||||
for n in 0..DQS_SIGNAL_COUNT {
|
||||
let dq_addr = dfii::PI0_RDDATA_ADDR
|
||||
|
@ -57,10 +49,6 @@ mod ddr {
|
|||
|
||||
ddrphy::wdly_dq_rst_write(1);
|
||||
ddrphy::wdly_dqs_rst_write(1);
|
||||
#[cfg(kusddrphy)]
|
||||
for _ in 0..ddrphy::wdly_dqs_taps_read() {
|
||||
ddrphy::wdly_dqs_inc_write(1);
|
||||
}
|
||||
|
||||
let mut dq;
|
||||
for _ in 0..ddrphy_max_delay {
|
||||
|
@ -88,17 +76,12 @@ mod ddr {
|
|||
unsafe fn write_level(logger: &mut Option<&mut dyn fmt::Write>,
|
||||
delay: &mut [u16; DQS_SIGNAL_COUNT],
|
||||
high_skew: &mut [bool; DQS_SIGNAL_COUNT]) -> bool {
|
||||
#[cfg(kusddrphy)]
|
||||
log!(logger, "DQS initial delay: {} taps\n", ddrphy::wdly_dqs_taps_read());
|
||||
log!(logger, "Write leveling: ");
|
||||
|
||||
enable_write_leveling(true);
|
||||
spin_cycles(100);
|
||||
|
||||
#[cfg(not(kusddrphy))]
|
||||
let ddrphy_max_delay : u16 = DDRPHY_MAX_DELAY;
|
||||
#[cfg(kusddrphy)]
|
||||
let ddrphy_max_delay : u16 = DDRPHY_MAX_DELAY - ddrphy::wdly_dqs_taps_read();
|
||||
|
||||
let mut failed = false;
|
||||
for n in 0..DQS_SIGNAL_COUNT {
|
||||
|
@ -112,10 +95,6 @@ mod ddr {
|
|||
|
||||
ddrphy::wdly_dq_rst_write(1);
|
||||
ddrphy::wdly_dqs_rst_write(1);
|
||||
#[cfg(kusddrphy)]
|
||||
for _ in 0..ddrphy::wdly_dqs_taps_read() {
|
||||
ddrphy::wdly_dqs_inc_write(1);
|
||||
}
|
||||
ddrphy::wlevel_strobe_write(1);
|
||||
spin_cycles(10);
|
||||
|
||||
|
@ -146,11 +125,6 @@ mod ddr {
|
|||
dq = ptr::read_volatile(dq_addr);
|
||||
}
|
||||
|
||||
// Get a bit further into the 0 zone
|
||||
#[cfg(kusddrphy)]
|
||||
for _ in 0..32 {
|
||||
incr_delay();
|
||||
}
|
||||
}
|
||||
|
||||
while dq == 0 {
|
||||
|
@ -191,9 +165,6 @@ mod ddr {
|
|||
if delay[n] > threshold {
|
||||
ddrphy::dly_sel_write(1 << n);
|
||||
|
||||
#[cfg(kusddrphy)]
|
||||
ddrphy::rdly_dq_bitslip_write(1);
|
||||
#[cfg(not(kusddrphy))]
|
||||
for _ in 0..3 {
|
||||
ddrphy::rdly_dq_bitslip_write(1);
|
||||
}
|
||||
|
@ -246,7 +217,7 @@ mod ddr {
|
|||
ddrphy::dly_sel_write(1 << (DQS_SIGNAL_COUNT - n - 1));
|
||||
|
||||
ddrphy::rdly_dq_rst_write(1);
|
||||
#[cfg(soc_platform = "kasli")]
|
||||
#[cfg(any(soc_platform = "kasli", soc_platform = "efc"))]
|
||||
{
|
||||
for _ in 0..3 {
|
||||
ddrphy::rdly_dq_bitslip_write(1);
|
||||
|
@ -337,7 +308,7 @@ mod ddr {
|
|||
let mut max_seen_valid = 0;
|
||||
|
||||
ddrphy::rdly_dq_rst_write(1);
|
||||
#[cfg(soc_platform = "kasli")]
|
||||
#[cfg(any(soc_platform = "kasli", soc_platform = "efc"))]
|
||||
{
|
||||
for _ in 0..3 {
|
||||
ddrphy::rdly_dq_bitslip_write(1);
|
||||
|
@ -400,7 +371,7 @@ mod ddr {
|
|||
|
||||
// Set delay to the middle
|
||||
ddrphy::rdly_dq_rst_write(1);
|
||||
#[cfg(soc_platform = "kasli")]
|
||||
#[cfg(any(soc_platform = "kasli", soc_platform = "efc"))]
|
||||
{
|
||||
for _ in 0..3 {
|
||||
ddrphy::rdly_dq_bitslip_write(1);
|
||||
|
@ -451,13 +422,9 @@ pub unsafe fn init(mut _logger: Option<&mut dyn fmt::Write>) -> bool {
|
|||
|
||||
#[cfg(has_ddrphy)]
|
||||
{
|
||||
#[cfg(kusddrphy)]
|
||||
csr::ddrphy::en_vtc_write(0);
|
||||
if !ddr::level(&mut _logger) {
|
||||
return false
|
||||
}
|
||||
#[cfg(kusddrphy)]
|
||||
csr::ddrphy::en_vtc_write(1);
|
||||
}
|
||||
|
||||
csr::dfii::control_write(sdram_phy::DFII_CONTROL_SEL);
|
||||
|
|
|
@ -5,7 +5,7 @@ use elf::*;
|
|||
|
||||
pub mod elf;
|
||||
|
||||
fn read_unaligned<T: Copy>(data: &[u8], offset: usize) -> Result<T, ()> {
|
||||
pub fn read_unaligned<T: Copy>(data: &[u8], offset: usize) -> Result<T, ()> {
|
||||
if data.len() < offset + mem::size_of::<T>() {
|
||||
Err(())
|
||||
} else {
|
||||
|
@ -75,6 +75,25 @@ impl<'a> fmt::Display for Error<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_elf_for_current_arch(ehdr: &Elf32_Ehdr, e_type: u16) -> bool {
|
||||
const IDENT: [u8; EI_NIDENT] = [
|
||||
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
|
||||
ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE,
|
||||
/* ABI version */ 0, /* padding */ 0, 0, 0, 0, 0, 0, 0
|
||||
];
|
||||
if ehdr.e_ident != IDENT { return false; }
|
||||
|
||||
if ehdr.e_type != e_type { return false; }
|
||||
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
const ARCH: u16 = EM_RISCV;
|
||||
#[cfg(not(target_arch = "riscv32"))]
|
||||
const ARCH: u16 = EM_NONE;
|
||||
if ehdr.e_machine != ARCH { return false; }
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Arch {
|
||||
RiscV,
|
||||
|
@ -268,25 +287,16 @@ impl<'a> Library<'a> {
|
|||
let ehdr = read_unaligned::<Elf32_Ehdr>(data, 0)
|
||||
.map_err(|()| "cannot read ELF header")?;
|
||||
|
||||
const IDENT: [u8; EI_NIDENT] = [
|
||||
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
|
||||
ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE,
|
||||
/* ABI version */ 0, /* padding */ 0, 0, 0, 0, 0, 0, 0
|
||||
];
|
||||
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
const ARCH: u16 = EM_RISCV;
|
||||
#[cfg(not(target_arch = "riscv32"))]
|
||||
const ARCH: u16 = EM_NONE;
|
||||
if !is_elf_for_current_arch(&ehdr, ET_DYN) {
|
||||
return Err("not a shared library for current architecture")?
|
||||
}
|
||||
|
||||
#[cfg(all(target_feature = "f", target_feature = "d"))]
|
||||
const FLAGS: u32 = EF_RISCV_FLOAT_ABI_DOUBLE;
|
||||
|
||||
#[cfg(not(all(target_feature = "f", target_feature = "d")))]
|
||||
const FLAGS: u32 = EF_RISCV_FLOAT_ABI_SOFT;
|
||||
|
||||
if ehdr.e_ident != IDENT || ehdr.e_type != ET_DYN || ehdr.e_machine != ARCH || ehdr.e_flags != FLAGS {
|
||||
return Err("not a shared library for current architecture")?
|
||||
if ehdr.e_flags != FLAGS {
|
||||
return Err("unexpected flags for shared library (wrong floating point ABI?)")?
|
||||
}
|
||||
|
||||
let mut dyn_off = None;
|
||||
|
|
|
@ -5,7 +5,10 @@ use dyld;
|
|||
pub const KERNELCPU_EXEC_ADDRESS: usize = 0x45000000;
|
||||
pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x45060000;
|
||||
pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff;
|
||||
pub const KSUPPORT_HEADER_SIZE: usize = 0x80;
|
||||
|
||||
// Must match the offset of the first (starting at KERNELCPU_EXEC_ADDRESS)
|
||||
// section in ksupport.elf.
|
||||
pub const KSUPPORT_HEADER_SIZE: usize = 0x74;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message<'a> {
|
||||
|
|
|
@ -112,8 +112,12 @@ unsafe fn recv_value<R, E>(reader: &mut R, tag: Tag, data: &mut *mut (),
|
|||
Tag::String | Tag::Bytes | Tag::ByteArray => {
|
||||
consume_value!(CMutSlice<u8>, |ptr| {
|
||||
let length = reader.read_u32()? as usize;
|
||||
if length > 0 {
|
||||
*ptr = CMutSlice::new(alloc(length)? as *mut u8, length);
|
||||
reader.read_exact((*ptr).as_mut())?;
|
||||
} else {
|
||||
*ptr = CMutSlice::new(core::ptr::NonNull::<u8>::dangling().as_ptr(), 0);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -19,13 +19,14 @@ byteorder = { version = "1.0", default-features = false }
|
|||
cslice = { version = "0.3" }
|
||||
log = { version = "0.4", default-features = false }
|
||||
managed = { version = "^0.7.1", default-features = false, features = ["alloc", "map"] }
|
||||
dyld = { path = "../libdyld" }
|
||||
eh = { path = "../libeh" }
|
||||
unwind_backtrace = { path = "../libunwind_backtrace" }
|
||||
io = { path = "../libio", features = ["byteorder"] }
|
||||
alloc_list = { path = "../liballoc_list" }
|
||||
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp"] }
|
||||
logger_artiq = { path = "../liblogger_artiq" }
|
||||
board_artiq = { path = "../libboard_artiq" }
|
||||
board_artiq = { path = "../libboard_artiq", features = ["alloc"] }
|
||||
proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] }
|
||||
riscv = { version = "0.6.0", features = ["inline-asm"] }
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use core::ptr;
|
||||
use board_misoc::csr;
|
||||
use core::{ptr, slice};
|
||||
use mailbox;
|
||||
use rpc_queue;
|
||||
|
||||
|
@ -13,15 +13,20 @@ pub unsafe fn start() {
|
|||
|
||||
stop();
|
||||
|
||||
extern {
|
||||
extern "C" {
|
||||
static _binary____ksupport_ksupport_elf_start: u8;
|
||||
static _binary____ksupport_ksupport_elf_end: u8;
|
||||
}
|
||||
let ksupport_start = &_binary____ksupport_ksupport_elf_start as *const _;
|
||||
let ksupport_end = &_binary____ksupport_ksupport_elf_end as *const _;
|
||||
ptr::copy_nonoverlapping(ksupport_start,
|
||||
(KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8,
|
||||
ksupport_end as usize - ksupport_start as usize);
|
||||
let ksupport_elf_start = &_binary____ksupport_ksupport_elf_start as *const u8;
|
||||
let ksupport_elf_end = &_binary____ksupport_ksupport_elf_end as *const u8;
|
||||
let ksupport_elf = slice::from_raw_parts(
|
||||
ksupport_elf_start,
|
||||
ksupport_elf_end as usize - ksupport_elf_start as usize,
|
||||
);
|
||||
|
||||
if let Err(msg) = load_image(&ksupport_elf) {
|
||||
panic!("failed to load kernel CPU image (ksupport.elf): {}", msg);
|
||||
}
|
||||
|
||||
csr::kernel_cpu::reset_write(0);
|
||||
|
||||
|
@ -41,6 +46,44 @@ pub unsafe fn stop() {
|
|||
rpc_queue::init();
|
||||
}
|
||||
|
||||
/// Loads the given image for execution on the kernel CPU.
|
||||
///
|
||||
/// The entire image including the headers is copied into memory for later use by libunwind, but
|
||||
/// placed such that the text section ends up at the right location in memory. Currently, we just
|
||||
/// hard-code the address range, but at least verify that this matches the ELF program header given
|
||||
/// in the image (avoids loading the – non-relocatable – code at the wrong address on toolchain/…
|
||||
/// changes).
|
||||
unsafe fn load_image(image: &[u8]) -> Result<(), &'static str> {
|
||||
use dyld::elf::*;
|
||||
use dyld::{is_elf_for_current_arch, read_unaligned};
|
||||
|
||||
let ehdr = read_unaligned::<Elf32_Ehdr>(image, 0).map_err(|()| "could not read ELF header")?;
|
||||
|
||||
// The check assumes the two CPUs share the same architecture. This is just to avoid inscrutable
|
||||
// errors; we do not functionally rely on this.
|
||||
if !is_elf_for_current_arch(&ehdr, ET_EXEC) {
|
||||
return Err("not an executable for kernel CPU architecture");
|
||||
}
|
||||
|
||||
// First program header should be the main text/… LOAD (see ksupport.ld).
|
||||
let phdr = read_unaligned::<Elf32_Phdr>(image, ehdr.e_phoff as usize)
|
||||
.map_err(|()| "could not read program header")?;
|
||||
if phdr.p_type != PT_LOAD {
|
||||
return Err("unexpected program header type");
|
||||
}
|
||||
if phdr.p_vaddr + phdr.p_memsz > KERNELCPU_LAST_ADDRESS as u32 {
|
||||
// This is a weak sanity check only; we also need to fit in the stack, etc.
|
||||
return Err("too large for kernel CPU address range");
|
||||
}
|
||||
const TARGET_ADDRESS: u32 = (KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as _;
|
||||
if phdr.p_vaddr - phdr.p_offset != TARGET_ADDRESS {
|
||||
return Err("unexpected load address/offset");
|
||||
}
|
||||
|
||||
ptr::copy_nonoverlapping(image.as_ptr(), TARGET_ADDRESS as *mut u8, image.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate(ptr: usize) -> bool {
|
||||
ptr >= KERNELCPU_EXEC_ADDRESS && ptr <= KERNELCPU_LAST_ADDRESS
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#![feature(lang_items, panic_info_message, const_btree_new, iter_advance_by)]
|
||||
#![no_std]
|
||||
|
||||
extern crate dyld;
|
||||
extern crate eh;
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
@ -39,6 +40,8 @@ use board_artiq::drtioaux;
|
|||
use board_artiq::drtio_routing;
|
||||
use board_artiq::{mailbox, rpc_queue};
|
||||
use proto_artiq::{mgmt_proto, moninj_proto, rpc_proto, session_proto, kernel_proto};
|
||||
#[cfg(has_drtio_eem)]
|
||||
use board_artiq::drtio_eem;
|
||||
#[cfg(has_rtio_analyzer)]
|
||||
use proto_artiq::analyzer_proto;
|
||||
|
||||
|
@ -125,6 +128,9 @@ fn startup() {
|
|||
}
|
||||
rtio_clocking::init();
|
||||
|
||||
#[cfg(has_drtio_eem)]
|
||||
drtio_eem::init();
|
||||
|
||||
let mut net_device = unsafe { ethmac::EthernetDevice::new() };
|
||||
net_device.reset_phy_if_any();
|
||||
|
||||
|
|
|
@ -259,14 +259,17 @@ pub fn init() {
|
|||
unsafe {
|
||||
// clock switch and reboot will begin after TX is initialized
|
||||
// and TX will be initialized after this
|
||||
csr::drtio_transceiver::stable_clkin_write(1);
|
||||
csr::gt_drtio::stable_clkin_write(1);
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
else {
|
||||
// enable TX after the reboot, with stable clock
|
||||
unsafe {
|
||||
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
|
||||
csr::gt_drtio::txenable_write(0xffffffffu32 as _);
|
||||
|
||||
#[cfg(has_drtio_eem)]
|
||||
csr::eem_transceiver::txenable_write(0xffffffffu32 as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ build_misoc = { path = "../libbuild_misoc" }
|
|||
[dependencies]
|
||||
log = { version = "0.4", default-features = false }
|
||||
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "log"] }
|
||||
board_artiq = { path = "../libboard_artiq" }
|
||||
board_artiq = { path = "../libboard_artiq", features = ["alloc"] }
|
||||
alloc_list = { path = "../liballoc_list" }
|
||||
riscv = { version = "0.6.0", features = ["inline-asm"] }
|
||||
proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] }
|
||||
|
|
|
@ -15,8 +15,12 @@ use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp};
|
|||
#[cfg(has_si5324)]
|
||||
use board_artiq::si5324;
|
||||
use board_artiq::{spi, drtioaux};
|
||||
#[cfg(soc_platform = "efc")]
|
||||
use board_artiq::ad9117;
|
||||
use board_artiq::drtio_routing;
|
||||
use proto_artiq::drtioaux_proto::ANALYZER_MAX_SIZE;
|
||||
#[cfg(has_drtio_eem)]
|
||||
use board_artiq::drtio_eem;
|
||||
use riscv::register::{mcause, mepc, mtval};
|
||||
use dma::Manager as DmaManager;
|
||||
use analyzer::Analyzer;
|
||||
|
@ -461,6 +465,7 @@ const SI5324_SETTINGS: si5324::FrequencySettings
|
|||
crystal_as_ckin2: true
|
||||
};
|
||||
|
||||
#[cfg(not(soc_platform = "efc"))]
|
||||
fn sysclk_setup() {
|
||||
let switched = unsafe {
|
||||
csr::crg::switch_done_read()
|
||||
|
@ -476,7 +481,7 @@ fn sysclk_setup() {
|
|||
// delay for clean UART log, wait until UART FIFO is empty
|
||||
clock::spin_us(1300);
|
||||
unsafe {
|
||||
csr::drtio_transceiver::stable_clkin_write(1);
|
||||
csr::gt_drtio::stable_clkin_write(1);
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
@ -527,14 +532,42 @@ pub extern fn main() -> i32 {
|
|||
io_expander1.service().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(not(soc_platform = "efc"))]
|
||||
sysclk_setup();
|
||||
|
||||
#[cfg(soc_platform = "efc")]
|
||||
let mut io_expander;
|
||||
#[cfg(soc_platform = "efc")]
|
||||
{
|
||||
io_expander = board_misoc::io_expander::IoExpander::new().unwrap();
|
||||
|
||||
// Enable LEDs
|
||||
io_expander.set_oe(0, 1 << 5 | 1 << 6 | 1 << 7).unwrap();
|
||||
|
||||
// Enable VADJ and P3V3_FMC
|
||||
io_expander.set_oe(1, 1 << 0 | 1 << 1).unwrap();
|
||||
|
||||
io_expander.set(1, 0, true);
|
||||
io_expander.set(1, 1, true);
|
||||
|
||||
io_expander.service().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(not(has_drtio_eem))]
|
||||
unsafe {
|
||||
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
|
||||
csr::gt_drtio::txenable_write(0xffffffffu32 as _);
|
||||
}
|
||||
|
||||
#[cfg(has_drtio_eem)]
|
||||
unsafe {
|
||||
csr::eem_transceiver::txenable_write(0xffffffffu32 as _);
|
||||
}
|
||||
|
||||
init_rtio_crg();
|
||||
|
||||
#[cfg(has_drtio_eem)]
|
||||
drtio_eem::init();
|
||||
|
||||
#[cfg(has_drtio_routing)]
|
||||
let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()];
|
||||
#[cfg(not(has_drtio_routing))]
|
||||
|
@ -547,6 +580,9 @@ pub extern fn main() -> i32 {
|
|||
|
||||
let mut hardware_tick_ts = 0;
|
||||
|
||||
#[cfg(soc_platform = "efc")]
|
||||
ad9117::init().expect("AD9117 initialization failed");
|
||||
|
||||
loop {
|
||||
while !drtiosat_link_rx_up() {
|
||||
drtiosat_process_errors();
|
||||
|
@ -558,6 +594,8 @@ pub extern fn main() -> i32 {
|
|||
io_expander0.service().expect("I2C I/O expander #0 service failed");
|
||||
io_expander1.service().expect("I2C I/O expander #1 service failed");
|
||||
}
|
||||
#[cfg(soc_platform = "efc")]
|
||||
io_expander.service().expect("I2C I/O expander service failed");
|
||||
hardware_tick(&mut hardware_tick_ts);
|
||||
}
|
||||
|
||||
|
@ -591,6 +629,8 @@ pub extern fn main() -> i32 {
|
|||
io_expander0.service().expect("I2C I/O expander #0 service failed");
|
||||
io_expander1.service().expect("I2C I/O expander #1 service failed");
|
||||
}
|
||||
#[cfg(soc_platform = "efc")]
|
||||
io_expander.service().expect("I2C I/O expander service failed");
|
||||
hardware_tick(&mut hardware_tick_ts);
|
||||
if drtiosat_tsc_loaded() {
|
||||
info!("TSC loaded from uplink");
|
||||
|
@ -621,6 +661,24 @@ pub extern fn main() -> i32 {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(soc_platform = "efc")]
|
||||
fn enable_error_led() {
|
||||
let mut io_expander = board_misoc::io_expander::IoExpander::new().unwrap();
|
||||
|
||||
// Keep LEDs enabled
|
||||
io_expander.set_oe(0, 1 << 5 | 1 << 6 | 1 << 7).unwrap();
|
||||
// Enable Error LED
|
||||
io_expander.set(0, 7, true);
|
||||
|
||||
// Keep VADJ and P3V3_FMC enabled
|
||||
io_expander.set_oe(1, 1 << 0 | 1 << 1).unwrap();
|
||||
|
||||
io_expander.set(1, 0, true);
|
||||
io_expander.set(1, 1, true);
|
||||
|
||||
io_expander.service().unwrap();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn exception(_regs: *const u32) {
|
||||
let pc = mepc::read();
|
||||
|
@ -660,8 +718,16 @@ pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
|
|||
|
||||
if let Some(location) = info.location() {
|
||||
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
|
||||
#[cfg(soc_platform = "efc")]
|
||||
{
|
||||
if location.file() != "libboard_misoc/io_expander.rs" {
|
||||
enable_error_led();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print!("panic at unknown location");
|
||||
#[cfg(soc_platform = "efc")]
|
||||
enable_error_led();
|
||||
}
|
||||
if let Some(message) = info.message() {
|
||||
println!(": {}", message);
|
||||
|
|
|
@ -14,6 +14,7 @@ import sys
|
|||
import os
|
||||
from operator import itemgetter
|
||||
from dateutil.parser import parse as parse_date
|
||||
import numpy as np
|
||||
|
||||
from prettytable import PrettyTable
|
||||
|
||||
|
@ -22,7 +23,7 @@ from sipyco.sync_struct import Subscriber
|
|||
from sipyco.broadcast import Receiver
|
||||
from sipyco import common_args, pyon
|
||||
|
||||
from artiq.tools import short_format, parse_arguments
|
||||
from artiq.tools import scale_from_metadata, short_format, parse_arguments
|
||||
from artiq import __version__ as artiq_version
|
||||
|
||||
|
||||
|
@ -187,7 +188,12 @@ def _action_set_dataset(remote, args):
|
|||
metadata["scale"] = args.scale
|
||||
if args.precision is not None:
|
||||
metadata["precision"] = args.precision
|
||||
remote.set(args.name, pyon.decode(args.value), persist, metadata)
|
||||
scale = scale_from_metadata(metadata)
|
||||
value = pyon.decode(args.value)
|
||||
t = type(value)
|
||||
if np.issubdtype(t, np.number) or t is np.ndarray:
|
||||
value = value * scale
|
||||
remote.set(args.name, value, persist, metadata)
|
||||
|
||||
|
||||
def _action_del_dataset(remote, args):
|
||||
|
|
|
@ -59,7 +59,7 @@ Prerequisites:
|
|||
help="SSH host to jump through")
|
||||
parser.add_argument("-t", "--target", default="kasli",
|
||||
help="target board, default: %(default)s, one of: "
|
||||
"kasli kc705")
|
||||
"kasli efc kc705")
|
||||
parser.add_argument("-I", "--preinit-command", default=[], action="append",
|
||||
help="add a pre-initialization OpenOCD command. "
|
||||
"Useful for selecting a board when several are connected.")
|
||||
|
@ -215,9 +215,28 @@ class ProgrammerXC7(Programmer):
|
|||
Programmer.__init__(self, client, preinit_script)
|
||||
self._proxy = proxy
|
||||
|
||||
if board != "efc":
|
||||
add_commands(self._board_script,
|
||||
"source {boardfile}",
|
||||
boardfile=self._transfer_script("board/{}.cfg".format(board)))
|
||||
else:
|
||||
add_commands(self._board_script,
|
||||
# OpenOCD does not have the efc board file so custom script is included.
|
||||
# To be used with Digilent-HS2 Programming Cable but the config in digilent-hs2.cfg is wrong
|
||||
# See digilent_jtag_smt2_nc.cfg for details
|
||||
"source [find interface/ftdi/digilent_jtag_smt2_nc.cfg]",
|
||||
|
||||
"ftdi tdo_sample_edge falling",
|
||||
|
||||
"reset_config none",
|
||||
"transport select jtag",
|
||||
"adapter speed 25000",
|
||||
|
||||
"source [find cpld/xilinx-xc7.cfg]",
|
||||
"source [find cpld/jtagspi.cfg]",
|
||||
"source [find fpga/xilinx-xadc.cfg]",
|
||||
"source [find fpga/xilinx-dna.cfg]"
|
||||
)
|
||||
self.add_flash_bank("spi0", "xc7", index=0)
|
||||
|
||||
add_commands(self._script, "xadc_report xc7.tap")
|
||||
|
@ -242,6 +261,13 @@ def main():
|
|||
"storage": ("spi0", 0x440000),
|
||||
"firmware": ("spi0", 0x450000),
|
||||
},
|
||||
"efc": {
|
||||
"programmer": partial(ProgrammerXC7, board="efc", proxy="bscan_spi_xc7a100t.bit"),
|
||||
"gateware": ("spi0", 0x000000),
|
||||
"bootloader": ("spi0", 0x400000),
|
||||
"storage": ("spi0", 0x440000),
|
||||
"firmware": ("spi0", 0x450000),
|
||||
},
|
||||
"kc705": {
|
||||
"programmer": partial(ProgrammerXC7, board="kc705", proxy="bscan_spi_xc7k325t.bit"),
|
||||
"gateware": ("spi0", 0x000000),
|
||||
|
|
|
@ -27,9 +27,10 @@ class ChannelInterface:
|
|||
|
||||
|
||||
class TransceiverInterface(AutoCSR):
|
||||
def __init__(self, channel_interfaces):
|
||||
def __init__(self, channel_interfaces, *, async_rx=True):
|
||||
self.stable_clkin = CSRStorage()
|
||||
self.txenable = CSRStorage(len(channel_interfaces))
|
||||
if async_rx:
|
||||
for i in range(len(channel_interfaces)):
|
||||
name = "rtio_rx" + str(i)
|
||||
setattr(self.clock_domains, "cd_"+name, ClockDomain(name=name))
|
||||
|
|
|
@ -2,6 +2,12 @@ from migen import *
|
|||
from migen.genlib.cdc import ElasticBuffer
|
||||
|
||||
|
||||
class NoRXSynchronizer:
|
||||
"""To be used when RX is already synchronous (e.g. IOSERDES based PHY)."""
|
||||
def resync(self, signal):
|
||||
return signal
|
||||
|
||||
|
||||
class GenericRXSynchronizer(Module):
|
||||
"""Simple RX synchronizer based on the portable Migen elastic buffer.
|
||||
|
||||
|
|
|
@ -0,0 +1,536 @@
|
|||
from migen import *
|
||||
from misoc.interconnect.csr import *
|
||||
from misoc.cores.code_8b10b import SingleEncoder, Decoder
|
||||
from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface
|
||||
|
||||
|
||||
class RXSerdes(Module):
|
||||
def __init__(self, i_pads):
|
||||
self.rxdata = [ Signal(10) for _ in range(4) ]
|
||||
self.ld = [ Signal() for _ in range(4) ]
|
||||
self.cnt_in = [ Signal(5) for _ in range(4) ]
|
||||
self.cnt_out = [ Signal(5) for _ in range(4) ]
|
||||
self.bitslip = [ Signal() for _ in range(4) ]
|
||||
self.o = [ Signal() for _ in range(4) ]
|
||||
|
||||
ser_in_no_dly = [ Signal() for _ in range(4) ]
|
||||
ser_in = [ Signal() for _ in range(4) ]
|
||||
shifts = [ Signal(2) for _ in range(4) ]
|
||||
|
||||
for i in range(4):
|
||||
self.specials += [
|
||||
# Master deserializer
|
||||
Instance("ISERDESE2",
|
||||
p_DATA_RATE="DDR",
|
||||
p_DATA_WIDTH=10,
|
||||
p_INTERFACE_TYPE="NETWORKING",
|
||||
p_NUM_CE=1,
|
||||
p_SERDES_MODE="MASTER",
|
||||
p_IOBDELAY="IFD",
|
||||
o_Q1=self.rxdata[i][9],
|
||||
o_Q2=self.rxdata[i][8],
|
||||
o_Q3=self.rxdata[i][7],
|
||||
o_Q4=self.rxdata[i][6],
|
||||
o_Q5=self.rxdata[i][5],
|
||||
o_Q6=self.rxdata[i][4],
|
||||
o_Q7=self.rxdata[i][3],
|
||||
o_Q8=self.rxdata[i][2],
|
||||
o_O=self.o[i],
|
||||
o_SHIFTOUT1=shifts[i][0],
|
||||
o_SHIFTOUT2=shifts[i][1],
|
||||
i_DDLY=ser_in[i],
|
||||
i_BITSLIP=self.bitslip[i],
|
||||
i_CLK=ClockSignal("sys5x"),
|
||||
i_CLKB=~ClockSignal("sys5x"),
|
||||
i_CE1=1,
|
||||
i_RST=ResetSignal(),
|
||||
i_CLKDIV=ClockSignal()),
|
||||
|
||||
# Slave deserializer
|
||||
Instance("ISERDESE2",
|
||||
p_DATA_RATE="DDR",
|
||||
p_DATA_WIDTH=10,
|
||||
p_INTERFACE_TYPE="NETWORKING",
|
||||
p_NUM_CE=1,
|
||||
p_SERDES_MODE="SLAVE",
|
||||
p_IOBDELAY="IFD",
|
||||
o_Q3=self.rxdata[i][1],
|
||||
o_Q4=self.rxdata[i][0],
|
||||
i_BITSLIP=self.bitslip[i],
|
||||
i_CLK=ClockSignal("sys5x"),
|
||||
i_CLKB=~ClockSignal("sys5x"),
|
||||
i_CE1=1,
|
||||
i_RST=ResetSignal(),
|
||||
i_CLKDIV=ClockSignal(),
|
||||
i_SHIFTIN1=shifts[i][0],
|
||||
i_SHIFTIN2=shifts[i][1]),
|
||||
|
||||
# Tunable delay
|
||||
# IDELAYCTRL is with the clocking
|
||||
Instance("IDELAYE2",
|
||||
p_DELAY_SRC="IDATAIN",
|
||||
p_SIGNAL_PATTERN="DATA",
|
||||
p_CINVCTRL_SEL="FALSE",
|
||||
p_HIGH_PERFORMANCE_MODE="TRUE",
|
||||
# REFCLK refers to the clock source of IDELAYCTRL
|
||||
p_REFCLK_FREQUENCY=200.0,
|
||||
p_PIPE_SEL="FALSE",
|
||||
p_IDELAY_TYPE="VAR_LOAD",
|
||||
p_IDELAY_VALUE=0,
|
||||
|
||||
i_C=ClockSignal(),
|
||||
i_LD=self.ld[i],
|
||||
i_CE=0,
|
||||
i_LDPIPEEN=0,
|
||||
i_INC=1, # Always increment
|
||||
|
||||
# Set the optimal delay tap via the aligner
|
||||
i_CNTVALUEIN=self.cnt_in[i],
|
||||
# Allow the aligner to check the tap value
|
||||
o_CNTVALUEOUT=self.cnt_out[i],
|
||||
|
||||
i_IDATAIN=ser_in_no_dly[i],
|
||||
o_DATAOUT=ser_in[i]
|
||||
),
|
||||
|
||||
# IOB
|
||||
Instance("IBUFDS",
|
||||
p_DIFF_TERM="TRUE",
|
||||
i_I=i_pads.p[i],
|
||||
i_IB=i_pads.n[i],
|
||||
o_O=ser_in_no_dly[i],
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class TXSerdes(Module):
|
||||
def __init__(self, o_pads):
|
||||
self.txdata = [ Signal(5) for _ in range(4) ]
|
||||
ser_out = [ Signal() for _ in range(4) ]
|
||||
t_out = [ Signal() for _ in range(4) ]
|
||||
|
||||
self.ext_rst = Signal()
|
||||
|
||||
for i in range(4):
|
||||
self.specials += [
|
||||
# Serializer
|
||||
Instance("OSERDESE2",
|
||||
p_DATA_RATE_OQ="SDR", p_DATA_RATE_TQ="BUF",
|
||||
p_DATA_WIDTH=5, p_TRISTATE_WIDTH=1,
|
||||
p_INIT_OQ=0b00000,
|
||||
o_OQ=ser_out[i],
|
||||
o_TQ=t_out[i],
|
||||
i_RST=ResetSignal() | self.ext_rst,
|
||||
i_CLK=ClockSignal("sys5x"),
|
||||
i_CLKDIV=ClockSignal(),
|
||||
i_D1=self.txdata[i][0],
|
||||
i_D2=self.txdata[i][1],
|
||||
i_D3=self.txdata[i][2],
|
||||
i_D4=self.txdata[i][3],
|
||||
i_D5=self.txdata[i][4],
|
||||
i_TCE=1, i_OCE=1,
|
||||
i_T1=0
|
||||
),
|
||||
|
||||
# IOB
|
||||
Instance("OBUFTDS",
|
||||
i_I=ser_out[i],
|
||||
o_O=o_pads.p[i],
|
||||
o_OB=o_pads.n[i],
|
||||
i_T=t_out[i],
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
# This module owns 2 8b10b encoders, each encoder route codewords to 2 lanes,
|
||||
# through time multiplexing. The scheduler releases 2 bytes every clock cycle,
|
||||
# and the encoders each encode 1 byte.
|
||||
#
|
||||
# Since each lane only transmits 5 bits per sysclk cycle, the encoder selects
|
||||
# a lane to first transmit the least significant word (LSW, 5 bits), and send
|
||||
# the rest in the next cycle using the same lane. It takes advantage of the
|
||||
# arrival sequence of bytes from the scrambler to achieve the transmission
|
||||
# pattern shown in the MultiDecoder module.
|
||||
class MultiEncoder(Module):
|
||||
def __init__(self):
|
||||
# Keep the link layer interface identical to standard encoders
|
||||
self.d = [ Signal(8) for _ in range(2) ]
|
||||
self.k = [ Signal() for _ in range(2) ]
|
||||
|
||||
# Output interface
|
||||
self.output = [ [ Signal(5) for _ in range(2) ] for _ in range(2) ]
|
||||
|
||||
# Clock enable signal
|
||||
# Alternate between sending encoded character to EEM 0/2 and EEM 1/3
|
||||
# every cycle
|
||||
self.clk_div2 = Signal()
|
||||
|
||||
# Intermediate registers for output and disparity
|
||||
# More significant bits are buffered due to channel geometry
|
||||
# Disparity bit is delayed. The same encoder is shared by 2 SERDES
|
||||
output_bufs = [ Signal(5) for _ in range(2) ]
|
||||
disp_bufs = [ Signal() for _ in range(2) ]
|
||||
|
||||
encoders = [ SingleEncoder() for _ in range(2) ]
|
||||
self.submodules += encoders
|
||||
|
||||
# Encoded characters are routed to the EEM pairs:
|
||||
# The first character goes through EEM 0/2
|
||||
# The second character goes through EEM 1/3, and repeat...
|
||||
# Lower order bits go first, so higher order bits are buffered and
|
||||
# transmitted in the next cycle.
|
||||
for d, k, output, output_buf, disp_buf, encoder in \
|
||||
zip(self.d, self.k, self.output, output_bufs, disp_bufs, encoders):
|
||||
self.comb += [
|
||||
encoder.d.eq(d),
|
||||
encoder.k.eq(k),
|
||||
|
||||
If(self.clk_div2,
|
||||
output[0].eq(encoder.output[0:5]),
|
||||
output[1].eq(output_buf),
|
||||
).Else(
|
||||
output[0].eq(output_buf),
|
||||
output[1].eq(encoder.output[0:5]),
|
||||
),
|
||||
]
|
||||
# Handle intermediate registers
|
||||
self.sync += [
|
||||
disp_buf.eq(encoder.disp_out),
|
||||
encoder.disp_in.eq(disp_buf),
|
||||
output_buf.eq(encoder.output[5:10]),
|
||||
]
|
||||
|
||||
|
||||
# Owns 2 8b10b decoders, each decodes data from lane 0/1 and lane 2/3class
|
||||
# respectively. The decoders are time multiplexed among the 2 lanes, and
|
||||
# each decoder decodes exactly 1 lane per sysclk cycle.
|
||||
#
|
||||
# The transmitter could send the following data pattern over the 4 lanes.
|
||||
# Capital letters denote the most significant word (MSW); The lowercase denote
|
||||
# the least significant word (LSW) of the same 8b10b character.
|
||||
#
|
||||
# Cycle \ Lane 0 1 2 3
|
||||
# 0 a Y b Z
|
||||
# 1 A c B d
|
||||
# 2 a' C b' D
|
||||
# 3 A' c' B' d'
|
||||
#
|
||||
# Lane 0/2 and lane 1/3 transmit word of different significance by design (see
|
||||
# MultiEncoder).
|
||||
#
|
||||
# This module buffers the LSW, and immediately send the whole 8b10b character
|
||||
# to the coresponding decoder once the MSW is also received.
|
||||
class MultiDecoder(Module):
|
||||
def __init__(self):
|
||||
self.raw_input = [ Signal(5) for _ in range(2) ]
|
||||
self.d = Signal(8)
|
||||
self.k = Signal()
|
||||
|
||||
# Clock enable signal
|
||||
# Alternate between decoding encoded character from EEM 0/2 and
|
||||
# EEM 1/3 every cycle
|
||||
self.clk_div2 = Signal()
|
||||
|
||||
# Extended bitslip mechanism. ISERDESE2 bitslip can only adjust bit
|
||||
# position by 5 bits (1 cycle). However, an encoded character takes 2
|
||||
# cycles to transmit/receive. The module needs to correctly reassemble
|
||||
# the 8b10b character. This is useful received waveform is the 1-cycle
|
||||
# delayed version of the above waveform. The same scheme would
|
||||
# incorrectly buffer words and create wrong symbols.
|
||||
#
|
||||
# Hence, wordslip put LSW as MSW and vice versa, effectively injects
|
||||
# an additional 5 bit positions worth of bitslips.
|
||||
self.wordslip = Signal()
|
||||
|
||||
# Intermediate register for input
|
||||
buffer = Signal(5)
|
||||
|
||||
self.submodules.decoder = Decoder()
|
||||
|
||||
# The decoder does the following actions:
|
||||
# - Process received characters from EEM 0/2
|
||||
# - Same, but from EEM 1/3
|
||||
#
|
||||
# Wordslipping is equivalent to swapping task between clock cycles.
|
||||
# (i.e. Swap processing target. Instead of processing EEM 0/2, process
|
||||
# EEM 1/3, and vice versa on the next cycle.) This effectively shifts
|
||||
# the processing time of any encoded character by 1 clock cycle (5
|
||||
# bitslip equivalent without considering oversampling, 10 otherwise).
|
||||
self.sync += [
|
||||
If(self.clk_div2 ^ self.wordslip,
|
||||
buffer.eq(self.raw_input[1])
|
||||
).Else(
|
||||
buffer.eq(self.raw_input[0])
|
||||
)
|
||||
]
|
||||
|
||||
self.comb += [
|
||||
If(self.clk_div2 ^ self.wordslip,
|
||||
self.decoder.input.eq(Cat(buffer, self.raw_input[0]))
|
||||
).Else(
|
||||
self.decoder.input.eq(Cat(buffer, self.raw_input[1]))
|
||||
)
|
||||
]
|
||||
|
||||
self.comb += [
|
||||
self.d.eq(self.decoder.d),
|
||||
self.k.eq(self.decoder.k),
|
||||
]
|
||||
|
||||
|
||||
class BangBangPhaseDetector(Module):
|
||||
def __init__(self):
|
||||
self.s = Signal(3)
|
||||
|
||||
self.high = Signal()
|
||||
self.low = Signal()
|
||||
|
||||
self.comb += If(~self.s[0] & self.s[2],
|
||||
self.high.eq(self.s[1]),
|
||||
self.low.eq(~self.s[1]),
|
||||
).Else(
|
||||
self.high.eq(0),
|
||||
self.low.eq(0),
|
||||
)
|
||||
|
||||
|
||||
class PhaseErrorCounter(Module, AutoCSR):
|
||||
def __init__(self):
|
||||
self.high_count = CSRStatus(18)
|
||||
self.low_count = CSRStatus(18)
|
||||
|
||||
# Odd indices are always oversampled bits
|
||||
self.rxdata = Signal(10)
|
||||
|
||||
# Measure setup/hold timing, count phase error in the following
|
||||
self.submodules.detector = BangBangPhaseDetector()
|
||||
self.comb += self.detector.s.eq(self.rxdata[:3])
|
||||
|
||||
self.reset = CSR()
|
||||
self.enable = CSRStorage()
|
||||
|
||||
self.overflow = CSRStatus()
|
||||
high_carry = Signal()
|
||||
low_carry = Signal()
|
||||
|
||||
self.sync += [
|
||||
If(self.reset.re,
|
||||
self.high_count.status.eq(0),
|
||||
self.low_count.status.eq(0),
|
||||
high_carry.eq(0),
|
||||
low_carry.eq(0),
|
||||
self.overflow.status.eq(0),
|
||||
).Elif(self.enable.storage,
|
||||
Cat(self.high_count.status, high_carry).eq(
|
||||
self.high_count.status + self.detector.high),
|
||||
Cat(self.low_count.status, low_carry).eq(
|
||||
self.low_count.status + self.detector.low),
|
||||
If(high_carry | low_carry, self.overflow.status.eq(1)),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class SerdesSingle(Module):
|
||||
def __init__(self, i_pads, o_pads):
|
||||
# Serdes modules
|
||||
self.submodules.rx_serdes = RXSerdes(i_pads)
|
||||
self.submodules.tx_serdes = TXSerdes(o_pads)
|
||||
|
||||
self.lane_sel = Signal(2)
|
||||
|
||||
self.bitslip = Signal()
|
||||
|
||||
for i in range(4):
|
||||
self.comb += self.rx_serdes.bitslip[i].eq(self.bitslip)
|
||||
|
||||
self.dly_cnt_in = Signal(5)
|
||||
self.dly_ld = Signal()
|
||||
|
||||
for i in range(4):
|
||||
self.comb += [
|
||||
self.rx_serdes.cnt_in[i].eq(self.dly_cnt_in),
|
||||
self.rx_serdes.ld[i].eq((self.lane_sel == i) & self.dly_ld),
|
||||
]
|
||||
|
||||
self.dly_cnt_out = Signal(5)
|
||||
|
||||
self.comb += Case(self.lane_sel, {
|
||||
idx: self.dly_cnt_out.eq(self.rx_serdes.cnt_out[idx]) for idx in range(4)
|
||||
})
|
||||
|
||||
self.wordslip = Signal()
|
||||
|
||||
# Encoder/Decoder interfaces
|
||||
self.submodules.encoder = MultiEncoder()
|
||||
self.submodules.decoders = decoders = Array(MultiDecoder() for _ in range(2))
|
||||
|
||||
self.comb += [
|
||||
decoders[0].wordslip.eq(self.wordslip),
|
||||
decoders[1].wordslip.eq(self.wordslip),
|
||||
]
|
||||
|
||||
# Route encoded symbols to TXSerdes, decoded symbols from RXSerdes
|
||||
for i in range(4):
|
||||
self.comb += [
|
||||
self.tx_serdes.txdata[i].eq(self.encoder.output[i//2][i%2]),
|
||||
decoders[i//2].raw_input[i%2].eq(self.rx_serdes.rxdata[i][0::2]),
|
||||
]
|
||||
|
||||
self.clk_div2 = Signal()
|
||||
self.comb += [
|
||||
self.encoder.clk_div2.eq(self.clk_div2),
|
||||
self.decoders[0].clk_div2.eq(self.clk_div2),
|
||||
self.decoders[1].clk_div2.eq(self.clk_div2),
|
||||
]
|
||||
|
||||
# Monitor lane 0 decoder output for bitslip alignment
|
||||
self.comma_align_reset = Signal()
|
||||
self.comma = Signal()
|
||||
|
||||
self.sync += If(self.comma_align_reset,
|
||||
self.comma.eq(0),
|
||||
).Elif(~self.comma,
|
||||
self.comma.eq(
|
||||
((decoders[0].d == 0x3C) | (decoders[0].d == 0xBC))
|
||||
& decoders[0].k))
|
||||
|
||||
|
||||
class OOBReset(Module):
|
||||
def __init__(self, iserdes_o):
|
||||
ce_counter = Signal(13)
|
||||
activity_ce = Signal()
|
||||
transition_ce = Signal()
|
||||
|
||||
self.sync.clk200 += Cat(ce_counter, activity_ce).eq(ce_counter + 1)
|
||||
self.comb += transition_ce.eq(ce_counter[0])
|
||||
|
||||
idle_low_meta = Signal()
|
||||
idle_high_meta = Signal()
|
||||
idle_low = Signal()
|
||||
idle_high = Signal()
|
||||
|
||||
idle = Signal()
|
||||
self.rst = Signal(reset=1)
|
||||
|
||||
# Detect the lack of transitions (idle) within 2 clk200 cycles
|
||||
self.specials += [
|
||||
Instance("FDCE", p_INIT=1, i_D=1, i_CLR=iserdes_o,
|
||||
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_low_meta,
|
||||
attr={"async_reg", "ars_ff1"}),
|
||||
Instance("FDCE", p_INIT=1, i_D=idle_low_meta, i_CLR=0,
|
||||
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_low,
|
||||
attr={"async_reg", "ars_ff2"}),
|
||||
|
||||
Instance("FDCE", p_INIT=1, i_D=1, i_CLR=~iserdes_o,
|
||||
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_high_meta,
|
||||
attr={"async_reg", "ars_ff1"}),
|
||||
Instance("FDCE", p_INIT=1, i_D=idle_high_meta, i_CLR=0,
|
||||
i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_high,
|
||||
attr={"async_reg", "ars_ff2"}),
|
||||
]
|
||||
|
||||
# Detect activity for the last 2**13 clk200 cycles
|
||||
# The 2**13 cycles are fully partitioned into 2**12 time segments of 2
|
||||
# cycles in duration. If there exists 2-cycle time segment without
|
||||
# signal level transition, rst is asserted.
|
||||
self.sync.clk200 += [
|
||||
If(activity_ce,
|
||||
idle.eq(0),
|
||||
self.rst.eq(idle),
|
||||
),
|
||||
If(idle_low | idle_high,
|
||||
idle.eq(1),
|
||||
self.rst.eq(1),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class EEMSerdes(Module, TransceiverInterface, AutoCSR):
|
||||
def __init__(self, platform, data_pads):
|
||||
self.rx_ready = CSRStorage()
|
||||
|
||||
self.transceiver_sel = CSRStorage(max(1, log2_int(len(data_pads))))
|
||||
self.lane_sel = CSRStorage(2)
|
||||
|
||||
self.bitslip = CSR()
|
||||
|
||||
self.dly_cnt_in = CSRStorage(5)
|
||||
self.dly_ld = CSR()
|
||||
self.dly_cnt_out = CSRStatus(5)
|
||||
|
||||
# Slide a word back/forward by 1 cycle, shared by all lanes of the
|
||||
# same transceiver. This is to determine if this cycle should decode
|
||||
# lane 0/2 or lane 1/3. See MultiEncoder/MultiDecoder for the full
|
||||
# scheme & timing.
|
||||
self.wordslip = CSRStorage()
|
||||
|
||||
# Monitor lane 0 decoder output for bitslip alignment
|
||||
self.comma_align_reset = CSR()
|
||||
self.comma = CSRStatus()
|
||||
|
||||
clk_div2 = Signal()
|
||||
self.sync += clk_div2.eq(~clk_div2)
|
||||
|
||||
channel_interfaces = []
|
||||
serdes_list = []
|
||||
for i_pads, o_pads in data_pads:
|
||||
serdes = SerdesSingle(i_pads, o_pads)
|
||||
self.comb += serdes.clk_div2.eq(clk_div2)
|
||||
serdes_list.append(serdes)
|
||||
|
||||
chan_if = ChannelInterface(serdes.encoder, serdes.decoders)
|
||||
self.comb += chan_if.rx_ready.eq(self.rx_ready.storage)
|
||||
channel_interfaces.append(chan_if)
|
||||
|
||||
# Route CSR signals using transceiver_sel
|
||||
self.comb += Case(self.transceiver_sel.storage, {
|
||||
trx_no: [
|
||||
serdes.bitslip.eq(self.bitslip.re),
|
||||
serdes.dly_ld.eq(self.dly_ld.re),
|
||||
|
||||
self.dly_cnt_out.status.eq(serdes.dly_cnt_out),
|
||||
self.comma.status.eq(serdes.comma),
|
||||
] for trx_no, serdes in enumerate(serdes_list)
|
||||
})
|
||||
|
||||
# Wordslip needs to be latched. It needs to hold when calibrating
|
||||
# other transceivers and/or after calibration.
|
||||
self.sync += If(self.wordslip.re,
|
||||
Case(self.transceiver_sel.storage, {
|
||||
trx_no: [
|
||||
serdes.wordslip.eq(self.wordslip.storage)
|
||||
] for trx_no, serdes in enumerate(serdes_list)
|
||||
})
|
||||
)
|
||||
|
||||
for serdes in serdes_list:
|
||||
self.comb += [
|
||||
# Delay counter write only comes into effect after dly_ld
|
||||
# So, just MUX dly_ld instead.
|
||||
serdes.dly_cnt_in.eq(self.dly_cnt_in.storage),
|
||||
|
||||
# Comma align reset & lane selection can be broadcasted
|
||||
# without MUXing. Transceivers are aligned one-by-one
|
||||
serdes.lane_sel.eq(self.lane_sel.storage),
|
||||
serdes.comma_align_reset.eq(self.comma_align_reset.re),
|
||||
]
|
||||
|
||||
# Setup/hold timing calibration module
|
||||
self.submodules.counter = PhaseErrorCounter()
|
||||
self.comb += Case(self.transceiver_sel.storage, {
|
||||
trx_no: Case(self.lane_sel.storage, {
|
||||
lane_idx: self.counter.rxdata.eq(serdes.rx_serdes.rxdata[lane_idx])
|
||||
for lane_idx in range(4)
|
||||
}) for trx_no, serdes in enumerate(serdes_list)
|
||||
})
|
||||
|
||||
self.submodules += serdes_list
|
||||
|
||||
self.submodules.oob_reset = OOBReset(serdes_list[0].rx_serdes.o[0])
|
||||
self.rst = self.oob_reset.rst
|
||||
self.rst.attr.add("no_retiming")
|
||||
|
||||
TransceiverInterface.__init__(self, channel_interfaces, async_rx=False)
|
||||
|
||||
for tx_en, serdes in zip(self.txenable.storage, serdes_list):
|
||||
self.comb += serdes.tx_serdes.ext_rst.eq(~tx_en)
|
|
@ -25,9 +25,13 @@ def default_iostandard(eem):
|
|||
|
||||
class _EEM:
|
||||
@classmethod
|
||||
def add_extension(cls, target, eem, *args, **kwargs):
|
||||
def add_extension(cls, target, eem, *args, is_drtio_over_eem=False, **kwargs):
|
||||
name = cls.__name__
|
||||
target.platform.add_extension(cls.io(eem, *args, **kwargs))
|
||||
if is_drtio_over_eem:
|
||||
print("{} (EEM{}) starting at DRTIO channel 0x{:06x}"
|
||||
.format(name, eem, (len(target.gt_drtio.channels) + len(target.eem_drtio_channels) + 1) << 16))
|
||||
else:
|
||||
print("{} (EEM{}) starting at RTIO channel 0x{:06x}"
|
||||
.format(name, eem, len(target.rtio_channels)))
|
||||
|
||||
|
@ -710,3 +714,36 @@ class HVAmp(_EEM):
|
|||
phy = ttl_out_cls(pads.p, pads.n)
|
||||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
|
||||
class EFC(_EEM):
|
||||
@staticmethod
|
||||
def io(eem, iostandard=default_iostandard):
|
||||
# Master: Pair 0~3 data IN, 4~7 OUT
|
||||
data_in = ("efc{}_drtio_rx".format(eem), 0,
|
||||
Subsignal("p", Pins("{} {} {} {}".format(*[
|
||||
_eem_pin(eem, i, "p") for i in range(4)
|
||||
]))),
|
||||
Subsignal("n", Pins("{} {} {} {}".format(*[
|
||||
_eem_pin(eem, i, "n") for i in range(4)
|
||||
]))),
|
||||
iostandard(eem),
|
||||
Misc("DIFF_TERM=TRUE"),
|
||||
)
|
||||
|
||||
data_out = ("efc{}_drtio_tx".format(eem), 0,
|
||||
Subsignal("p", Pins("{} {} {} {}".format(*[
|
||||
_eem_pin(eem, i, "p") for i in range(4, 8)
|
||||
]))),
|
||||
Subsignal("n", Pins("{} {} {} {}".format(*[
|
||||
_eem_pin(eem, i, "n") for i in range(4, 8)
|
||||
]))),
|
||||
iostandard(eem),
|
||||
)
|
||||
|
||||
return [data_in, data_out]
|
||||
|
||||
@classmethod
|
||||
def add_std(cls, target, eem, eem_aux, iostandard=default_iostandard):
|
||||
cls.add_extension(target, eem, is_drtio_over_eem=True, iostandard=iostandard)
|
||||
target.eem_drtio_channels.append((target.platform.request("efc{}_drtio_rx".format(eem), 0), target.platform.request("efc{}_drtio_tx".format(eem), 0)))
|
||||
|
|
|
@ -126,6 +126,15 @@ def peripheral_hvamp(module, peripheral, **kwargs):
|
|||
eem.HVAmp.add_std(module, peripheral["ports"][0],
|
||||
ttl_simple.Output, **kwargs)
|
||||
|
||||
def peripheral_efc(module, peripheral, **kwargs):
|
||||
if len(peripheral["ports"]) == 1:
|
||||
port = peripheral["ports"][0]
|
||||
port_aux = None
|
||||
elif len(peripheral["ports"]) == 2:
|
||||
port, port_aux = peripheral["ports"]
|
||||
else:
|
||||
raise ValueError("wrong number of ports")
|
||||
eem.EFC.add_std(module, port, port_aux)
|
||||
|
||||
peripheral_processors = {
|
||||
"dio": peripheral_dio,
|
||||
|
@ -139,6 +148,7 @@ peripheral_processors = {
|
|||
"fastino": peripheral_fastino,
|
||||
"phaser": peripheral_phaser,
|
||||
"hvamp": peripheral_hvamp,
|
||||
"efc": peripheral_efc,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,298 @@
|
|||
# Copyright 2013-2017 Robert Jordens <jordens@gmail.com>
|
||||
#
|
||||
# pdq is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pdq is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pdq. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from collections import namedtuple
|
||||
from operator import add
|
||||
|
||||
from migen import *
|
||||
from misoc.interconnect.stream import Endpoint
|
||||
from misoc.interconnect.csr import *
|
||||
from misoc.cores.cordic import Cordic
|
||||
from artiq.gateware.rtio import rtlink
|
||||
|
||||
class DacInterface(Module, AutoCSR):
|
||||
def __init__(self, pads):
|
||||
bit_width = len(pads[0].data)
|
||||
|
||||
self.data = [[Signal(bit_width) for _ in range(2)] for _ in range(8)]
|
||||
|
||||
self.ddr_clk_phase_shift = CSR()
|
||||
self.ddr_clk_phase_shift_done = CSRStatus(reset=1)
|
||||
|
||||
mmcm_ps_fb = Signal()
|
||||
mmcm_ps_output = Signal()
|
||||
mmcm_ps_psdone = Signal()
|
||||
ddr_clk = Signal()
|
||||
|
||||
# Generate DAC DDR CLK
|
||||
# 125MHz to 125MHz with controllable phase shift,
|
||||
# VCO @ 1000MHz.
|
||||
# Phase is shifted by 45 degree by default
|
||||
self.specials += \
|
||||
Instance("MMCME2_ADV",
|
||||
p_CLKIN1_PERIOD=8.0,
|
||||
i_CLKIN1=ClockSignal(),
|
||||
i_RST=ResetSignal(),
|
||||
i_CLKINSEL=1,
|
||||
|
||||
p_CLKFBOUT_MULT_F=8.0,
|
||||
p_CLKOUT0_DIVIDE_F=8.0,
|
||||
p_DIVCLK_DIVIDE=1,
|
||||
p_CLKOUT0_PHASE=45.0,
|
||||
|
||||
o_CLKFBOUT=mmcm_ps_fb, i_CLKFBIN=mmcm_ps_fb,
|
||||
|
||||
p_CLKOUT0_USE_FINE_PS="TRUE",
|
||||
o_CLKOUT0=mmcm_ps_output,
|
||||
|
||||
i_PSCLK=ClockSignal(),
|
||||
i_PSEN=self.ddr_clk_phase_shift.re,
|
||||
i_PSINCDEC=self.ddr_clk_phase_shift.r,
|
||||
o_PSDONE=mmcm_ps_psdone,
|
||||
)
|
||||
|
||||
self.sync += [
|
||||
If(self.ddr_clk_phase_shift.re, self.ddr_clk_phase_shift_done.status.eq(0)),
|
||||
If(mmcm_ps_psdone, self.ddr_clk_phase_shift_done.status.eq(1))
|
||||
]
|
||||
|
||||
# din.clk pads locate at multiple clock regions/IO banks
|
||||
self.specials += [
|
||||
Instance("BUFG", i_I=mmcm_ps_output, o_O=ddr_clk),
|
||||
]
|
||||
|
||||
for i, din in enumerate(pads):
|
||||
self.specials += Instance("ODDR",
|
||||
i_C=ddr_clk,
|
||||
i_CE=1,
|
||||
i_D1=1,
|
||||
i_D2=0,
|
||||
o_Q=din.clk,
|
||||
p_DDR_CLK_EDGE="SAME_EDGE")
|
||||
self.specials += [
|
||||
Instance("ODDR",
|
||||
i_C=ClockSignal(),
|
||||
i_CE=1,
|
||||
i_D1=self.data[i][0][bit], # DDR CLK Rising Edge
|
||||
i_D2=self.data[i][1][bit], # DDR CLK Falling Edge
|
||||
o_Q=din.data[bit],
|
||||
p_DDR_CLK_EDGE="SAME_EDGE")
|
||||
for bit in range(bit_width)]
|
||||
|
||||
class Dac(Module):
|
||||
"""Output module.
|
||||
|
||||
Holds the two output line executors.
|
||||
|
||||
Attributes:
|
||||
data (Signal[16]): Output value to be send to the DAC.
|
||||
clear (Signal): Clear accumulated phase offset when loading a new
|
||||
waveform. Input.
|
||||
i (Endpoint[]): Coefficients of the output lines.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.clear = Signal()
|
||||
self.data = Signal(16)
|
||||
|
||||
###
|
||||
|
||||
subs = [
|
||||
Volt(),
|
||||
Dds(self.clear),
|
||||
]
|
||||
|
||||
self.sync.rio += [
|
||||
self.data.eq(reduce(add, [sub.data for sub in subs])),
|
||||
]
|
||||
|
||||
self.i = [ sub.i for sub in subs ]
|
||||
self.submodules += subs
|
||||
|
||||
|
||||
class Volt(Module):
|
||||
"""DC bias spline interpolator.
|
||||
|
||||
The line data is interpreted as a concatenation of:
|
||||
|
||||
* 16 bit amplitude offset
|
||||
* 32 bit amplitude first order derivative
|
||||
* 48 bit amplitude second order derivative
|
||||
* 48 bit amplitude third order derivative
|
||||
|
||||
Attributes:
|
||||
data (Signal[16]): Output data from this spline.
|
||||
i (Endpoint): Coefficients of the DC bias spline, along with its
|
||||
latency compensation.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.data = Signal(16)
|
||||
self.i = Endpoint([("data", 144)])
|
||||
self.i.latency = 17
|
||||
|
||||
###
|
||||
|
||||
v = [Signal(48) for i in range(4)] # amp, damp, ddamp, dddamp
|
||||
|
||||
# Increase latency of stb by 17 cycles to compensate CORDIC latency
|
||||
stb_r = [ Signal() for _ in range(17) ]
|
||||
self.sync.rio += [
|
||||
stb_r[0].eq(self.i.stb),
|
||||
]
|
||||
for idx in range(16):
|
||||
self.sync.rio += stb_r[idx+1].eq(stb_r[idx])
|
||||
|
||||
self.sync.rio += [
|
||||
v[0].eq(v[0] + v[1]),
|
||||
v[1].eq(v[1] + v[2]),
|
||||
v[2].eq(v[2] + v[3]),
|
||||
If(stb_r[16],
|
||||
v[0].eq(0),
|
||||
v[1].eq(0),
|
||||
Cat(v[0][32:], v[1][16:], v[2], v[3]).eq(self.i.payload.raw_bits()),
|
||||
)
|
||||
]
|
||||
self.comb += self.data.eq(v[0][32:])
|
||||
|
||||
|
||||
class Dds(Module):
|
||||
"""DDS spline interpolator.
|
||||
|
||||
The line data is interpreted as:
|
||||
|
||||
* 16 bit amplitude offset
|
||||
* 32 bit amplitude first order derivative
|
||||
* 48 bit amplitude second order derivative
|
||||
* 48 bit amplitude third order derivative
|
||||
* 16 bit phase offset
|
||||
* 32 bit frequency word
|
||||
* 32 bit chirp
|
||||
|
||||
Args:
|
||||
line (Record[line_layout]): Next line to be executed. Input.
|
||||
clear (Signal): Clear accumulated phase offset when loading a new
|
||||
waveform. Input.
|
||||
|
||||
Attributes:
|
||||
data (Signal[16]): Output data from this spline.
|
||||
i (Endpoint): Coefficients of the DDS spline, along with its latency
|
||||
compensation.
|
||||
"""
|
||||
def __init__(self, clear):
|
||||
self.data = Signal(16)
|
||||
self.i = Endpoint([("data", 224)])
|
||||
|
||||
###
|
||||
|
||||
self.submodules.cordic = Cordic(width=16, eval_mode="pipelined",
|
||||
guard=None)
|
||||
|
||||
za = Signal(32)
|
||||
z = [Signal(32) for i in range(3)] # phase, dphase, ddphase
|
||||
x = [Signal(48) for i in range(4)] # amp, damp, ddamp, dddamp
|
||||
self.comb += [
|
||||
self.cordic.xi.eq(x[0][32:]),
|
||||
self.cordic.yi.eq(0),
|
||||
self.cordic.zi.eq(za[16:] + z[0][16:]),
|
||||
self.data.eq(self.cordic.xo),
|
||||
]
|
||||
|
||||
self.sync.rio += [
|
||||
za.eq(za + z[1]),
|
||||
x[0].eq(x[0] + x[1]),
|
||||
x[1].eq(x[1] + x[2]),
|
||||
x[2].eq(x[2] + x[3]),
|
||||
z[1].eq(z[1] + z[2]),
|
||||
If(self.i.stb,
|
||||
x[0].eq(0),
|
||||
x[1].eq(0),
|
||||
Cat(x[0][32:], x[1][16:], x[2], x[3], z[0][16:], z[1], z[2]
|
||||
).eq(self.i.payload.raw_bits()),
|
||||
If(clear,
|
||||
za.eq(0),
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class Config(Module):
|
||||
def __init__(self):
|
||||
self.clr = Signal(16, reset=0xFFFF)
|
||||
self.i = Endpoint([("data", 16)])
|
||||
|
||||
# This introduces 1 extra latency to everything in config
|
||||
# See the latency/delay attributes in Volt & DDS Endpoints/rtlinks
|
||||
self.sync.rio += If(self.i.stb, self.clr.eq(self.i.data))
|
||||
|
||||
|
||||
Phy = namedtuple("Phy", "rtlink probes overrides")
|
||||
|
||||
|
||||
class Shuttler(Module, AutoCSR):
|
||||
"""Shuttler module.
|
||||
|
||||
Used both in functional simulation and final gateware.
|
||||
|
||||
Holds the DACs and the configuration register. The DAC and Config are
|
||||
collected and adapted into RTIO interface.
|
||||
|
||||
Attributes:
|
||||
phys (list): List of Endpoints.
|
||||
"""
|
||||
def __init__(self, pads):
|
||||
NUM_OF_DACS = 16
|
||||
|
||||
self.submodules.dac_interface = DacInterface(pads)
|
||||
|
||||
self.phys = []
|
||||
|
||||
self.submodules.cfg = Config()
|
||||
cfg_rtl_iface = rtlink.Interface(rtlink.OInterface(
|
||||
data_width=len(self.cfg.i.data),
|
||||
enable_replace=False))
|
||||
|
||||
self.comb += [
|
||||
self.cfg.i.stb.eq(cfg_rtl_iface.o.stb),
|
||||
self.cfg.i.data.eq(cfg_rtl_iface.o.data),
|
||||
]
|
||||
self.phys.append(Phy(cfg_rtl_iface, [], []))
|
||||
|
||||
trigger_iface = rtlink.Interface(rtlink.OInterface(
|
||||
data_width=NUM_OF_DACS,
|
||||
enable_replace=False))
|
||||
self.phys.append(Phy(trigger_iface, [], []))
|
||||
|
||||
for idx in range(NUM_OF_DACS):
|
||||
dac = Dac()
|
||||
self.comb += [
|
||||
dac.clear.eq(self.cfg.clr[idx]),
|
||||
self.dac_interface.data[idx // 2][idx % 2].eq(dac.data)
|
||||
]
|
||||
|
||||
for i in dac.i:
|
||||
delay = getattr(i, "latency", 0)
|
||||
rtl_iface = rtlink.Interface(rtlink.OInterface(
|
||||
data_width=16, address_width=4, delay=delay))
|
||||
array = Array(i.data[wi: wi+16] for wi in range(0, len(i.data), 16))
|
||||
|
||||
self.sync.rio += [
|
||||
i.stb.eq(trigger_iface.o.data[idx] & trigger_iface.o.stb),
|
||||
If(rtl_iface.o.stb,
|
||||
array[rtl_iface.o.address].eq(rtl_iface.o.data),
|
||||
),
|
||||
]
|
||||
|
||||
self.phys.append(Phy(rtl_iface, [], []))
|
||||
|
||||
self.submodules += dac
|
|
@ -0,0 +1,223 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
|
||||
from migen import *
|
||||
from migen.build.generic_platform import *
|
||||
|
||||
from misoc.cores import gpio, spi2
|
||||
from misoc.targets.efc import BaseSoC
|
||||
from misoc.integration.builder import builder_args, builder_argdict
|
||||
|
||||
from artiq.gateware.amp import AMPSoC
|
||||
from artiq.gateware import rtio
|
||||
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
|
||||
from artiq.gateware.rtio.phy import ttl_simple
|
||||
from artiq.gateware.drtio.transceiver import eem_serdes
|
||||
from artiq.gateware.drtio.rx_synchronizer import NoRXSynchronizer
|
||||
from artiq.gateware.drtio import *
|
||||
from artiq.gateware.shuttler import Shuttler
|
||||
from artiq.build_soc import *
|
||||
|
||||
|
||||
class Satellite(BaseSoC, AMPSoC):
|
||||
mem_map = {
|
||||
"rtio": 0x20000000,
|
||||
"drtioaux": 0x50000000,
|
||||
"mailbox": 0x70000000
|
||||
}
|
||||
mem_map.update(BaseSoC.mem_map)
|
||||
|
||||
def __init__(self, gateware_identifier_str=None, **kwargs):
|
||||
BaseSoC.__init__(self,
|
||||
cpu_type="vexriscv",
|
||||
cpu_bus_width=64,
|
||||
sdram_controller_type="minicon",
|
||||
l2_size=128*1024,
|
||||
clk_freq=125e6,
|
||||
**kwargs)
|
||||
AMPSoC.__init__(self)
|
||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
||||
|
||||
platform = self.platform
|
||||
|
||||
drtio_eem_io = [
|
||||
("drtio_tx", 0,
|
||||
Subsignal("p", Pins("eem0:d0_cc_p eem0:d1_p eem0:d2_p eem0:d3_p")),
|
||||
Subsignal("n", Pins("eem0:d0_cc_n eem0:d1_n eem0:d2_n eem0:d3_n")),
|
||||
IOStandard("LVDS_25"),
|
||||
),
|
||||
("drtio_rx", 0,
|
||||
Subsignal("p", Pins("eem0:d4_p eem0:d5_p eem0:d6_p eem0:d7_p")),
|
||||
Subsignal("n", Pins("eem0:d4_n eem0:d5_n eem0:d6_n eem0:d7_n")),
|
||||
IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE"),
|
||||
),
|
||||
]
|
||||
|
||||
platform.add_extension(drtio_eem_io)
|
||||
data_pads = [
|
||||
(platform.request("drtio_rx"), platform.request("drtio_tx"))
|
||||
]
|
||||
|
||||
# Disable SERVMOD, hardwire it to ground to enable EEM 0
|
||||
servmod = self.platform.request("servmod")
|
||||
self.comb += servmod.eq(0)
|
||||
|
||||
self.submodules.eem_transceiver = eem_serdes.EEMSerdes(self.platform, data_pads)
|
||||
self.csr_devices.append("eem_transceiver")
|
||||
self.config["HAS_DRTIO_EEM"] = None
|
||||
self.config["EEM_DRTIO_COUNT"] = 1
|
||||
|
||||
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
|
||||
|
||||
cdr = ClockDomainsRenamer({"rtio_rx": "sys"})
|
||||
core = cdr(DRTIOSatellite(
|
||||
self.rtio_tsc, self.eem_transceiver.channels[0],
|
||||
NoRXSynchronizer()))
|
||||
self.submodules.drtiosat = core
|
||||
self.csr_devices.append("drtiosat")
|
||||
|
||||
self.submodules.drtioaux0 = cdr(DRTIOAuxController(
|
||||
core.link_layer, self.cpu_dw))
|
||||
self.csr_devices.append("drtioaux0")
|
||||
|
||||
memory_address = self.mem_map["drtioaux"]
|
||||
self.add_wb_slave(memory_address, 0x800, self.drtioaux0.bus)
|
||||
self.add_memory_region("drtioaux0_mem", memory_address | self.shadow_base, 0x800)
|
||||
|
||||
self.config["HAS_DRTIO"] = None
|
||||
self.add_csr_group("drtioaux", ["drtioaux0"])
|
||||
self.add_memory_group("drtioaux_mem", ["drtioaux0_mem"])
|
||||
|
||||
# Async reset gateware if data lane is idle
|
||||
self.comb += self.crg.reset.eq(self.eem_transceiver.rst)
|
||||
|
||||
i2c = self.platform.request("fpga_i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
self.csr_devices.append("i2c")
|
||||
self.config["I2C_BUS_COUNT"] = 1
|
||||
|
||||
# Enable I2C
|
||||
i2c_reset = self.platform.request("i2c_mux_rst_n")
|
||||
self.comb += i2c_reset.eq(1)
|
||||
|
||||
fix_serdes_timing_path(platform)
|
||||
|
||||
self.config["DRTIO_ROLE"] = "satellite"
|
||||
self.config["RTIO_FREQUENCY"] = "125.0"
|
||||
|
||||
shuttler_io = [
|
||||
('dac_spi', 0,
|
||||
Subsignal('clk', Pins('fmc0:HB16_N')),
|
||||
Subsignal('mosi', Pins('fmc0:HB06_CC_N')),
|
||||
Subsignal('cs_n', Pins('fmc0:LA31_N fmc0:LA31_P fmc0:HB19_P fmc0:LA30_P')),
|
||||
IOStandard("LVCMOS18")),
|
||||
('dac_rst', 0, Pins('fmc0:HB16_P'), IOStandard("LVCMOS18")),
|
||||
('dac_din', 0,
|
||||
Subsignal('data', Pins('fmc0:HA06_N fmc0:HA06_P fmc0:HA07_N fmc0:HA02_N fmc0:HA07_P fmc0:HA02_P fmc0:HA03_N fmc0:HA03_P fmc0:HA04_N fmc0:HA04_P fmc0:HA05_N fmc0:HA05_P fmc0:HA00_CC_N fmc0:HA01_CC_N')),
|
||||
Subsignal('clk', Pins('fmc0:HA00_CC_P')),
|
||||
IOStandard('LVCMOS18')),
|
||||
('dac_din', 1,
|
||||
Subsignal('data', Pins('fmc0:LA09_P fmc0:LA09_N fmc0:LA07_N fmc0:LA08_N fmc0:LA07_P fmc0:LA08_P fmc0:LA05_N fmc0:LA04_N fmc0:LA05_P fmc0:LA06_N fmc0:LA04_P fmc0:LA03_N fmc0:LA03_P fmc0:LA06_P')),
|
||||
Subsignal('clk', Pins('fmc0:LA00_CC_P')),
|
||||
IOStandard('LVCMOS18')),
|
||||
('dac_din', 2,
|
||||
Subsignal('data', Pins('fmc0:HA14_N fmc0:HA14_P fmc0:HA12_N fmc0:HA12_P fmc0:HA13_N fmc0:HA10_N fmc0:HA10_P fmc0:HA11_N fmc0:HA11_P fmc0:HA13_P fmc0:HA08_N fmc0:HA08_P fmc0:HA09_N fmc0:HA09_P')),
|
||||
Subsignal('clk', Pins('fmc0:HA01_CC_P')),
|
||||
IOStandard('LVCMOS18')),
|
||||
('dac_din', 3,
|
||||
Subsignal('data', Pins('fmc0:LA14_N fmc0:LA15_N fmc0:LA16_N fmc0:LA15_P fmc0:LA14_P fmc0:LA13_N fmc0:LA16_P fmc0:LA13_P fmc0:LA11_N fmc0:LA12_N fmc0:LA11_P fmc0:LA12_P fmc0:LA10_N fmc0:LA10_P')),
|
||||
Subsignal('clk', Pins('fmc0:LA01_CC_P')),
|
||||
IOStandard('LVCMOS18')),
|
||||
('dac_din', 4,
|
||||
Subsignal('data', Pins('fmc0:HA22_N fmc0:HA19_N fmc0:HA22_P fmc0:HA21_N fmc0:HA21_P fmc0:HA19_P fmc0:HA18_CC_N fmc0:HA20_N fmc0:HA20_P fmc0:HA18_CC_P fmc0:HA15_N fmc0:HA15_P fmc0:HA16_N fmc0:HA16_P')),
|
||||
Subsignal('clk', Pins('fmc0:HA17_CC_P')),
|
||||
IOStandard('LVCMOS18')),
|
||||
('dac_din', 5,
|
||||
Subsignal('data', Pins('fmc0:LA24_N fmc0:LA25_N fmc0:LA24_P fmc0:LA25_P fmc0:LA21_N fmc0:LA21_P fmc0:LA22_N fmc0:LA22_P fmc0:LA23_N fmc0:LA23_P fmc0:LA19_N fmc0:LA19_P fmc0:LA20_N fmc0:LA20_P')),
|
||||
Subsignal('clk', Pins('fmc0:LA17_CC_P')),
|
||||
IOStandard('LVCMOS18')),
|
||||
('dac_din', 6,
|
||||
Subsignal('data', Pins('fmc0:HB08_N fmc0:HB08_P fmc0:HB07_N fmc0:HB07_P fmc0:HB04_N fmc0:HB04_P fmc0:HB01_N fmc0:HB05_N fmc0:HB01_P fmc0:HB05_P fmc0:HB02_N fmc0:HB02_P fmc0:HB03_N fmc0:HB03_P')),
|
||||
Subsignal('clk', Pins('fmc0:HB00_CC_P')),
|
||||
IOStandard('LVCMOS18')),
|
||||
('dac_din', 7,
|
||||
Subsignal('data', Pins('fmc0:HB13_N fmc0:HB12_N fmc0:HB13_P fmc0:HB12_P fmc0:HB15_N fmc0:HB15_P fmc0:HB11_N fmc0:HB09_N fmc0:HB09_P fmc0:HB14_N fmc0:HB14_P fmc0:HB10_N fmc0:HB10_P fmc0:HB11_P')),
|
||||
Subsignal('clk', Pins('fmc0:HB06_CC_P')),
|
||||
IOStandard('LVCMOS18')),
|
||||
]
|
||||
|
||||
platform.add_extension(shuttler_io)
|
||||
|
||||
self.submodules.converter_spi = spi2.SPIMaster(spi2.SPIInterface(self.platform.request("dac_spi", 0)))
|
||||
self.csr_devices.append("converter_spi")
|
||||
self.config["HAS_CONVERTER_SPI"] = None
|
||||
|
||||
self.submodules.dac_rst = gpio.GPIOOut(self.platform.request("dac_rst"))
|
||||
self.csr_devices.append("dac_rst")
|
||||
|
||||
self.rtio_channels = []
|
||||
|
||||
for i in range(2):
|
||||
phy = ttl_simple.Output(self.virtual_leds.get(i))
|
||||
self.submodules += phy
|
||||
self.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
||||
self.submodules.shuttler = Shuttler([platform.request("dac_din", i) for i in range(8)])
|
||||
self.csr_devices.append("shuttler")
|
||||
self.rtio_channels.extend(rtio.Channel.from_phy(phy) for phy in self.shuttler.phys)
|
||||
|
||||
self.config["HAS_RTIO_LOG"] = None
|
||||
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
|
||||
self.rtio_channels.append(rtio.LogChannel())
|
||||
|
||||
self.add_rtio(self.rtio_channels)
|
||||
|
||||
def add_rtio(self, rtio_channels, sed_lanes=8):
|
||||
# Only add MonInj core if there is anything to monitor
|
||||
if any([len(c.probes) for c in rtio_channels]):
|
||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
||||
self.csr_devices.append("rtio_moninj")
|
||||
|
||||
# satellite (master-controlled) RTIO
|
||||
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels, lane_count=sed_lanes)
|
||||
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
|
||||
|
||||
# subkernel RTIO
|
||||
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc)
|
||||
self.register_kernel_cpu_csrdevice("rtio")
|
||||
|
||||
self.submodules.rtio_dma = rtio.DMA(self.get_native_sdram_if(), self.cpu_dw)
|
||||
self.csr_devices.append("rtio_dma")
|
||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||
[self.drtiosat.cri, self.rtio_dma.cri],
|
||||
[self.local_io.cri],
|
||||
enable_routing=True)
|
||||
self.csr_devices.append("cri_con")
|
||||
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
|
||||
self.csr_devices.append("routing_table")
|
||||
|
||||
self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio_tsc, self.local_io.cri,
|
||||
self.get_native_sdram_if(), cpu_dw=self.cpu_dw)
|
||||
self.csr_devices.append("rtio_analyzer")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="ARTIQ device binary builder for EEM FMC Carrier systems")
|
||||
builder_args(parser)
|
||||
parser.set_defaults(output_dir="artiq_efc")
|
||||
parser.add_argument("-V", "--variant", default="shuttler")
|
||||
parser.add_argument("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
args = parser.parse_args()
|
||||
|
||||
argdict = dict()
|
||||
argdict["gateware_identifier_str"] = args.gateware_identifier_str
|
||||
|
||||
soc = Satellite(**argdict)
|
||||
build_artiq_soc(soc, builder_argdict(args))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -19,7 +19,7 @@ from artiq.gateware import rtio
|
|||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, edge_counter
|
||||
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
|
||||
from artiq.gateware import eem
|
||||
from artiq.gateware.drtio.transceiver import gtp_7series
|
||||
from artiq.gateware.drtio.transceiver import gtp_7series, eem_serdes
|
||||
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
||||
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
|
||||
from artiq.gateware.drtio import *
|
||||
|
@ -268,17 +268,17 @@ class MasterBase(MiniSoC, AMPSoC):
|
|||
sfp_ctls = [platform.request("sfp_ctl", i) for i in range(1, 3)]
|
||||
self.comb += [sc.tx_disable.eq(0) for sc in sfp_ctls]
|
||||
|
||||
self.submodules.drtio_transceiver = gtp_7series.GTP(
|
||||
self.submodules.gt_drtio = gtp_7series.GTP(
|
||||
qpll_channel=self.drtio_qpll_channel,
|
||||
data_pads=drtio_data_pads,
|
||||
sys_clk_freq=self.clk_freq,
|
||||
rtio_clk_freq=rtio_clk_freq)
|
||||
self.csr_devices.append("drtio_transceiver")
|
||||
self.csr_devices.append("gt_drtio")
|
||||
|
||||
if enable_sata:
|
||||
sfp_channels = self.drtio_transceiver.channels[1:]
|
||||
sfp_channels = self.gt_drtio.channels[1:]
|
||||
else:
|
||||
sfp_channels = self.drtio_transceiver.channels
|
||||
sfp_channels = self.gt_drtio.channels
|
||||
if self.platform.hw_rev in ("v1.0", "v1.1"):
|
||||
self.comb += [sfp_ctl.led.eq(channel.rx_ready)
|
||||
for sfp_ctl, channel in zip(sfp_ctls, sfp_channels)]
|
||||
|
@ -288,21 +288,21 @@ class MasterBase(MiniSoC, AMPSoC):
|
|||
|
||||
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
|
||||
|
||||
drtio_csr_group = []
|
||||
drtioaux_csr_group = []
|
||||
drtioaux_memory_group = []
|
||||
self.drtio_csr_group = []
|
||||
self.drtioaux_csr_group = []
|
||||
self.drtioaux_memory_group = []
|
||||
self.drtio_cri = []
|
||||
for i in range(len(self.drtio_transceiver.channels)):
|
||||
for i in range(len(self.gt_drtio.channels)):
|
||||
core_name = "drtio" + str(i)
|
||||
coreaux_name = "drtioaux" + str(i)
|
||||
memory_name = "drtioaux" + str(i) + "_mem"
|
||||
drtio_csr_group.append(core_name)
|
||||
drtioaux_csr_group.append(coreaux_name)
|
||||
drtioaux_memory_group.append(memory_name)
|
||||
self.drtio_csr_group.append(core_name)
|
||||
self.drtioaux_csr_group.append(coreaux_name)
|
||||
self.drtioaux_memory_group.append(memory_name)
|
||||
|
||||
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
|
||||
|
||||
core = cdr(DRTIOMaster(self.rtio_tsc, self.drtio_transceiver.channels[i]))
|
||||
core = cdr(DRTIOMaster(self.rtio_tsc, self.gt_drtio.channels[i]))
|
||||
setattr(self.submodules, core_name, core)
|
||||
self.drtio_cri.append(core.cri)
|
||||
self.csr_devices.append(core_name)
|
||||
|
@ -317,12 +317,9 @@ class MasterBase(MiniSoC, AMPSoC):
|
|||
self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800)
|
||||
self.config["HAS_DRTIO"] = None
|
||||
self.config["HAS_DRTIO_ROUTING"] = None
|
||||
self.add_csr_group("drtio", drtio_csr_group)
|
||||
self.add_csr_group("drtioaux", drtioaux_csr_group)
|
||||
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
||||
|
||||
rtio_clk_period = 1e9/rtio_clk_freq
|
||||
gtp = self.drtio_transceiver.gtps[0]
|
||||
gtp = self.gt_drtio.gtps[0]
|
||||
|
||||
txout_buf = Signal()
|
||||
self.specials += Instance("BUFG", i_I=gtp.txoutclk, o_O=txout_buf)
|
||||
|
@ -334,7 +331,7 @@ class MasterBase(MiniSoC, AMPSoC):
|
|||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk,
|
||||
gtp.txoutclk, gtp.rxoutclk)
|
||||
for gtp in self.drtio_transceiver.gtps[1:]:
|
||||
for gtp in self.gt_drtio.gtps[1:]:
|
||||
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, gtp.rxoutclk)
|
||||
|
@ -367,6 +364,43 @@ class MasterBase(MiniSoC, AMPSoC):
|
|||
self.get_native_sdram_if(), cpu_dw=self.cpu_dw)
|
||||
self.csr_devices.append("rtio_analyzer")
|
||||
|
||||
def add_eem_drtio(self, eem_drtio_channels):
|
||||
# Must be called before invoking add_rtio() to construct the CRI
|
||||
# interconnect properly
|
||||
self.submodules.eem_transceiver = eem_serdes.EEMSerdes(self.platform, eem_drtio_channels)
|
||||
self.csr_devices.append("eem_transceiver")
|
||||
self.config["HAS_DRTIO_EEM"] = None
|
||||
self.config["EEM_DRTIO_COUNT"] = len(eem_drtio_channels)
|
||||
|
||||
cdr = ClockDomainsRenamer({"rtio_rx": "sys"})
|
||||
for i in range(len(self.eem_transceiver.channels)):
|
||||
channel = i + len(self.gt_drtio.channels)
|
||||
core_name = "drtio" + str(channel)
|
||||
coreaux_name = "drtioaux" + str(channel)
|
||||
memory_name = "drtioaux" + str(channel) + "_mem"
|
||||
self.drtio_csr_group.append(core_name)
|
||||
self.drtioaux_csr_group.append(coreaux_name)
|
||||
self.drtioaux_memory_group.append(memory_name)
|
||||
|
||||
core = cdr(DRTIOMaster(self.rtio_tsc, self.eem_transceiver.channels[i]))
|
||||
setattr(self.submodules, core_name, core)
|
||||
self.drtio_cri.append(core.cri)
|
||||
self.csr_devices.append(core_name)
|
||||
|
||||
coreaux = cdr(DRTIOAuxController(core.link_layer, self.cpu_dw))
|
||||
setattr(self.submodules, coreaux_name, coreaux)
|
||||
self.csr_devices.append(coreaux_name)
|
||||
|
||||
memory_address = self.mem_map["drtioaux"] + 0x800*channel
|
||||
self.add_wb_slave(memory_address, 0x800,
|
||||
coreaux.bus)
|
||||
self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800)
|
||||
|
||||
def add_drtio_cpuif_groups(self):
|
||||
self.add_csr_group("drtio", self.drtio_csr_group)
|
||||
self.add_csr_group("drtioaux", self.drtioaux_csr_group)
|
||||
self.add_memory_group("drtioaux_mem", self.drtioaux_memory_group)
|
||||
|
||||
# Never running out of stupid features, GTs on A7 make you pack
|
||||
# unrelated transceiver PLLs into one GTPE2_COMMON yourself.
|
||||
def create_qpll(self):
|
||||
|
@ -472,17 +506,17 @@ class SatelliteBase(BaseSoC, AMPSoC):
|
|||
if self.platform.hw_rev in ("v1.0", "v1.1"):
|
||||
sfp_ctls = [platform.request("sfp_ctl", i) for i in range(3)]
|
||||
self.comb += [sc.tx_disable.eq(0) for sc in sfp_ctls]
|
||||
self.submodules.drtio_transceiver = gtp_7series.GTP(
|
||||
self.submodules.gt_drtio = gtp_7series.GTP(
|
||||
qpll_channel=qpll.channels[0],
|
||||
data_pads=drtio_data_pads,
|
||||
sys_clk_freq=self.clk_freq,
|
||||
rtio_clk_freq=rtio_clk_freq)
|
||||
self.csr_devices.append("drtio_transceiver")
|
||||
self.csr_devices.append("gt_drtio")
|
||||
|
||||
if enable_sata:
|
||||
sfp_channels = self.drtio_transceiver.channels[1:]
|
||||
sfp_channels = self.gt_drtio.channels[1:]
|
||||
else:
|
||||
sfp_channels = self.drtio_transceiver.channels
|
||||
sfp_channels = self.gt_drtio.channels
|
||||
if self.platform.hw_rev in ("v1.0", "v1.1"):
|
||||
self.comb += [sfp_ctl.led.eq(channel.rx_ready)
|
||||
for sfp_ctl, channel in zip(sfp_ctls, sfp_channels)]
|
||||
|
@ -496,7 +530,7 @@ class SatelliteBase(BaseSoC, AMPSoC):
|
|||
drtioaux_memory_group = []
|
||||
drtiorep_csr_group = []
|
||||
self.drtio_cri = []
|
||||
for i in range(len(self.drtio_transceiver.channels)):
|
||||
for i in range(len(self.gt_drtio.channels)):
|
||||
coreaux_name = "drtioaux" + str(i)
|
||||
memory_name = "drtioaux" + str(i) + "_mem"
|
||||
drtioaux_csr_group.append(coreaux_name)
|
||||
|
@ -507,7 +541,7 @@ class SatelliteBase(BaseSoC, AMPSoC):
|
|||
if i == 0:
|
||||
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
|
||||
core = cdr(DRTIOSatellite(
|
||||
self.rtio_tsc, self.drtio_transceiver.channels[i],
|
||||
self.rtio_tsc, self.gt_drtio.channels[i],
|
||||
self.rx_synchronizer))
|
||||
self.submodules.drtiosat = core
|
||||
self.csr_devices.append("drtiosat")
|
||||
|
@ -516,7 +550,7 @@ class SatelliteBase(BaseSoC, AMPSoC):
|
|||
drtiorep_csr_group.append(corerep_name)
|
||||
|
||||
core = cdr(DRTIORepeater(
|
||||
self.rtio_tsc, self.drtio_transceiver.channels[i]))
|
||||
self.rtio_tsc, self.gt_drtio.channels[i]))
|
||||
setattr(self.submodules, corerep_name, core)
|
||||
self.drtio_cri.append(core.cri)
|
||||
self.csr_devices.append(corerep_name)
|
||||
|
@ -555,7 +589,7 @@ class SatelliteBase(BaseSoC, AMPSoC):
|
|||
self.config["HAS_SI5324"] = None
|
||||
self.config["SI5324_SOFT_RESET"] = None
|
||||
|
||||
gtp = self.drtio_transceiver.gtps[0]
|
||||
gtp = self.gt_drtio.gtps[0]
|
||||
txout_buf = Signal()
|
||||
self.specials += Instance("BUFG", i_I=gtp.txoutclk, o_O=txout_buf)
|
||||
self.crg.configure(txout_buf, clk_sw=gtp.tx_init.done)
|
||||
|
@ -565,7 +599,7 @@ class SatelliteBase(BaseSoC, AMPSoC):
|
|||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk,
|
||||
gtp.txoutclk, gtp.rxoutclk)
|
||||
for gtp in self.drtio_transceiver.gtps[1:]:
|
||||
for gtp in self.gt_drtio.gtps[1:]:
|
||||
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, gtp.rxoutclk)
|
||||
|
|
|
@ -71,10 +71,12 @@ class GenericMaster(MasterBase):
|
|||
if hw_rev is None:
|
||||
hw_rev = description["hw_rev"]
|
||||
self.class_name_override = description["variant"]
|
||||
has_drtio_over_eem = any(peripheral["type"] == "efc" for peripheral in description["peripherals"])
|
||||
MasterBase.__init__(self,
|
||||
hw_rev=hw_rev,
|
||||
rtio_clk_freq=description["rtio_frequency"],
|
||||
enable_sata=description["enable_sata_drtio"],
|
||||
enable_sys5x=has_drtio_over_eem,
|
||||
**kwargs)
|
||||
self.config["DRTIO_ROLE"] = description["drtio_role"]
|
||||
if "ext_ref_frequency" in description:
|
||||
|
@ -85,6 +87,8 @@ class GenericMaster(MasterBase):
|
|||
# EEM clock fan-out from Si5324, not MMCX
|
||||
self.comb += self.platform.request("clk_sel").eq(1)
|
||||
|
||||
if has_drtio_over_eem:
|
||||
self.eem_drtio_channels = []
|
||||
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||
if has_grabber:
|
||||
self.grabber_csr_group = []
|
||||
|
@ -95,13 +99,18 @@ class GenericMaster(MasterBase):
|
|||
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
|
||||
self.rtio_channels.append(rtio.LogChannel())
|
||||
|
||||
if has_drtio_over_eem:
|
||||
self.add_eem_drtio(self.eem_drtio_channels)
|
||||
self.add_drtio_cpuif_groups()
|
||||
|
||||
self.add_rtio(self.rtio_channels, sed_lanes=description["sed_lanes"])
|
||||
|
||||
if has_grabber:
|
||||
self.config["HAS_GRABBER"] = None
|
||||
self.add_csr_group("grabber", self.grabber_csr_group)
|
||||
for grabber in self.grabber_csr_group:
|
||||
self.platform.add_false_path_constraints(
|
||||
self.drtio_transceiver.gtps[0].txoutclk, getattr(self, grabber).deserializer.cd_cl.clk)
|
||||
self.gt_drtio.gtps[0].txoutclk, getattr(self, grabber).deserializer.cd_cl.clk)
|
||||
|
||||
|
||||
class GenericSatellite(SatelliteBase):
|
||||
|
@ -135,7 +144,7 @@ class GenericSatellite(SatelliteBase):
|
|||
self.add_csr_group("grabber", self.grabber_csr_group)
|
||||
for grabber in self.grabber_csr_group:
|
||||
self.platform.add_false_path_constraints(
|
||||
self.drtio_transceiver.gtps[0].txoutclk, getattr(self, grabber).deserializer.cd_cl.clk)
|
||||
self.gt_drtio.gtps[0].txoutclk, getattr(self, grabber).deserializer.cd_cl.clk)
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -168,6 +177,10 @@ def main():
|
|||
else:
|
||||
raise ValueError("Invalid DRTIO role")
|
||||
|
||||
has_efc = any(peripheral["type"] == "efc" for peripheral in description["peripherals"])
|
||||
if has_efc and (description["drtio_role"] == "standalone"):
|
||||
raise ValueError("EFC requires DRTIO, please switch role to master")
|
||||
|
||||
soc = cls(description, gateware_identifier_str=args.gateware_identifier_str, **soc_kasli_argdict(args))
|
||||
args.variant = description["variant"]
|
||||
build_artiq_soc(soc, builder_argdict(args))
|
||||
|
|
|
@ -215,11 +215,11 @@ class _MasterBase(MiniSoC, AMPSoC):
|
|||
]
|
||||
|
||||
# 1000BASE_BX10 Ethernet compatible, 100/125MHz RTIO clock
|
||||
self.submodules.drtio_transceiver = gtx_7series.GTX(
|
||||
self.submodules.gt_drtio = gtx_7series.GTX(
|
||||
clock_pads=platform.request("si5324_clkout"),
|
||||
pads=data_pads,
|
||||
clk_freq=self.clk_freq)
|
||||
self.csr_devices.append("drtio_transceiver")
|
||||
self.csr_devices.append("gt_drtio")
|
||||
|
||||
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
|
||||
|
||||
|
@ -227,7 +227,7 @@ class _MasterBase(MiniSoC, AMPSoC):
|
|||
drtioaux_csr_group = []
|
||||
drtioaux_memory_group = []
|
||||
self.drtio_cri = []
|
||||
for i in range(len(self.drtio_transceiver.channels)):
|
||||
for i in range(len(self.gt_drtio.channels)):
|
||||
core_name = "drtio" + str(i)
|
||||
coreaux_name = "drtioaux" + str(i)
|
||||
memory_name = "drtioaux" + str(i) + "_mem"
|
||||
|
@ -238,7 +238,7 @@ class _MasterBase(MiniSoC, AMPSoC):
|
|||
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
|
||||
|
||||
core = cdr(DRTIOMaster(
|
||||
self.rtio_tsc, self.drtio_transceiver.channels[i]))
|
||||
self.rtio_tsc, self.gt_drtio.channels[i]))
|
||||
setattr(self.submodules, core_name, core)
|
||||
self.drtio_cri.append(core.cri)
|
||||
self.csr_devices.append(core_name)
|
||||
|
@ -257,7 +257,7 @@ class _MasterBase(MiniSoC, AMPSoC):
|
|||
self.add_csr_group("drtioaux", drtioaux_csr_group)
|
||||
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
||||
|
||||
self.config["RTIO_FREQUENCY"] = str(self.drtio_transceiver.rtio_clk_freq/1e6)
|
||||
self.config["RTIO_FREQUENCY"] = str(self.gt_drtio.rtio_clk_freq/1e6)
|
||||
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n, reset_out=1)
|
||||
self.csr_devices.append("si5324_rst_n")
|
||||
i2c = self.platform.request("i2c")
|
||||
|
@ -266,10 +266,10 @@ class _MasterBase(MiniSoC, AMPSoC):
|
|||
self.config["I2C_BUS_COUNT"] = 1
|
||||
self.config["HAS_SI5324"] = None
|
||||
|
||||
rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
|
||||
rtio_clk_period = 1e9/self.gt_drtio.rtio_clk_freq
|
||||
# Constrain TX & RX timing for the first transceiver channel
|
||||
# (First channel acts as master for phase alignment for all channels' TX)
|
||||
gtx0 = self.drtio_transceiver.gtxs[0]
|
||||
gtx0 = self.gt_drtio.gtxs[0]
|
||||
|
||||
txout_buf = Signal()
|
||||
self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf)
|
||||
|
@ -286,7 +286,7 @@ class _MasterBase(MiniSoC, AMPSoC):
|
|||
self.crg.cd_sys.clk, gtx0.rxoutclk)
|
||||
# Constrain RX timing for the each transceiver channel
|
||||
# (Each channel performs single-lane phase alignment for RX)
|
||||
for gtx in self.drtio_transceiver.gtxs[1:]:
|
||||
for gtx in self.gt_drtio.gtxs[1:]:
|
||||
platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, gtx.rxoutclk)
|
||||
|
@ -363,11 +363,11 @@ class _SatelliteBase(BaseSoC, AMPSoC):
|
|||
rtio_clk_freq = clk_freq
|
||||
|
||||
# 1000BASE_BX10 Ethernet compatible, 100/125MHz RTIO clock
|
||||
self.submodules.drtio_transceiver = gtx_7series.GTX(
|
||||
self.submodules.gt_drtio = gtx_7series.GTX(
|
||||
clock_pads=platform.request("si5324_clkout"),
|
||||
pads=data_pads,
|
||||
clk_freq=self.clk_freq)
|
||||
self.csr_devices.append("drtio_transceiver")
|
||||
self.csr_devices.append("gt_drtio")
|
||||
|
||||
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
|
||||
|
||||
|
@ -375,7 +375,7 @@ class _SatelliteBase(BaseSoC, AMPSoC):
|
|||
drtioaux_memory_group = []
|
||||
drtiorep_csr_group = []
|
||||
self.drtio_cri = []
|
||||
for i in range(len(self.drtio_transceiver.channels)):
|
||||
for i in range(len(self.gt_drtio.channels)):
|
||||
coreaux_name = "drtioaux" + str(i)
|
||||
memory_name = "drtioaux" + str(i) + "_mem"
|
||||
drtioaux_csr_group.append(coreaux_name)
|
||||
|
@ -387,7 +387,7 @@ class _SatelliteBase(BaseSoC, AMPSoC):
|
|||
if i == 0:
|
||||
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
|
||||
core = cdr(DRTIOSatellite(
|
||||
self.rtio_tsc, self.drtio_transceiver.channels[0], self.rx_synchronizer))
|
||||
self.rtio_tsc, self.gt_drtio.channels[0], self.rx_synchronizer))
|
||||
self.submodules.drtiosat = core
|
||||
self.csr_devices.append("drtiosat")
|
||||
# Repeaters
|
||||
|
@ -395,7 +395,7 @@ class _SatelliteBase(BaseSoC, AMPSoC):
|
|||
corerep_name = "drtiorep" + str(i-1)
|
||||
drtiorep_csr_group.append(corerep_name)
|
||||
core = cdr(DRTIORepeater(
|
||||
self.rtio_tsc, self.drtio_transceiver.channels[i]))
|
||||
self.rtio_tsc, self.gt_drtio.channels[i]))
|
||||
setattr(self.submodules, corerep_name, core)
|
||||
self.drtio_cri.append(core.cri)
|
||||
self.csr_devices.append(corerep_name)
|
||||
|
@ -414,14 +414,14 @@ class _SatelliteBase(BaseSoC, AMPSoC):
|
|||
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
||||
self.add_csr_group("drtiorep", drtiorep_csr_group)
|
||||
|
||||
self.config["RTIO_FREQUENCY"] = str(self.drtio_transceiver.rtio_clk_freq/1e6)
|
||||
self.config["RTIO_FREQUENCY"] = str(self.gt_drtio.rtio_clk_freq/1e6)
|
||||
# Si5324 Phaser
|
||||
self.submodules.siphaser = SiPhaser7Series(
|
||||
si5324_clkin=platform.request("si5324_clkin_33"),
|
||||
rx_synchronizer=self.rx_synchronizer,
|
||||
ref_clk=ClockSignal("bootstrap"),
|
||||
ultrascale=False,
|
||||
rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq)
|
||||
rtio_clk_freq=self.gt_drtio.rtio_clk_freq)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||
self.csr_devices.append("siphaser")
|
||||
|
@ -433,10 +433,10 @@ class _SatelliteBase(BaseSoC, AMPSoC):
|
|||
self.config["I2C_BUS_COUNT"] = 1
|
||||
self.config["HAS_SI5324"] = None
|
||||
|
||||
rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
|
||||
rtio_clk_period = 1e9/self.gt_drtio.rtio_clk_freq
|
||||
# Constrain TX & RX timing for the first transceiver channel
|
||||
# (First channel acts as master for phase alignment for all channels' TX)
|
||||
gtx0 = self.drtio_transceiver.gtxs[0]
|
||||
gtx0 = self.gt_drtio.gtxs[0]
|
||||
|
||||
txout_buf = Signal()
|
||||
self.specials += Instance("BUFG", i_I=gtx0.txoutclk, o_O=txout_buf)
|
||||
|
@ -451,7 +451,7 @@ class _SatelliteBase(BaseSoC, AMPSoC):
|
|||
platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period)
|
||||
# Constrain RX timing for the each transceiver channel
|
||||
# (Each channel performs single-lane phase alignment for RX)
|
||||
for gtx in self.drtio_transceiver.gtxs[1:]:
|
||||
for gtx in self.gt_drtio.gtxs[1:]:
|
||||
platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, gtx.rxoutclk)
|
||||
|
|
|
@ -120,8 +120,8 @@ class NumberEntryFloat(ScientificSpinBox):
|
|||
disable_scroll_wheel(self)
|
||||
procdesc = argument["desc"]
|
||||
scale = procdesc["scale"]
|
||||
self.setDecimals(procdesc["ndecimals"])
|
||||
self.setPrecision()
|
||||
self.setDecimals(procdesc["precision"])
|
||||
self.setSigFigs()
|
||||
self.setSingleStep(procdesc["step"]/scale)
|
||||
self.setRelativeStep()
|
||||
if procdesc["min"] is not None:
|
||||
|
@ -159,8 +159,8 @@ class _NoScan(LayoutWidget):
|
|||
scale = procdesc["scale"]
|
||||
self.value = ScientificSpinBox()
|
||||
disable_scroll_wheel(self.value)
|
||||
self.value.setDecimals(procdesc["ndecimals"])
|
||||
self.value.setPrecision()
|
||||
self.value.setDecimals(procdesc["precision"])
|
||||
self.value.setSigFigs()
|
||||
if procdesc["global_min"] is not None:
|
||||
self.value.setMinimum(procdesc["global_min"]/scale)
|
||||
else:
|
||||
|
@ -202,7 +202,7 @@ class _RangeScan(LayoutWidget):
|
|||
scale = procdesc["scale"]
|
||||
|
||||
def apply_properties(widget):
|
||||
widget.setDecimals(procdesc["ndecimals"])
|
||||
widget.setDecimals(procdesc["precision"])
|
||||
if procdesc["global_min"] is not None:
|
||||
widget.setMinimum(procdesc["global_min"]/scale)
|
||||
else:
|
||||
|
@ -244,10 +244,10 @@ class _RangeScan(LayoutWidget):
|
|||
self.addWidget(randomize, 3, 1)
|
||||
|
||||
apply_properties(start)
|
||||
start.setPrecision()
|
||||
start.setSigFigs()
|
||||
start.setRelativeStep()
|
||||
apply_properties(stop)
|
||||
stop.setPrecision()
|
||||
stop.setSigFigs()
|
||||
stop.setRelativeStep()
|
||||
apply_properties(scanner)
|
||||
|
||||
|
@ -293,7 +293,7 @@ class _CenterScan(LayoutWidget):
|
|||
scale = procdesc["scale"]
|
||||
|
||||
def apply_properties(widget):
|
||||
widget.setDecimals(procdesc["ndecimals"])
|
||||
widget.setDecimals(procdesc["precision"])
|
||||
if procdesc["global_min"] is not None:
|
||||
widget.setMinimum(procdesc["global_min"]/scale)
|
||||
else:
|
||||
|
@ -310,7 +310,7 @@ class _CenterScan(LayoutWidget):
|
|||
center = ScientificSpinBox()
|
||||
disable_scroll_wheel(center)
|
||||
apply_properties(center)
|
||||
center.setPrecision()
|
||||
center.setSigFigs()
|
||||
center.setRelativeStep()
|
||||
center.setValue(state["center"]/scale)
|
||||
self.addWidget(center, 0, 1)
|
||||
|
@ -319,7 +319,7 @@ class _CenterScan(LayoutWidget):
|
|||
span = ScientificSpinBox()
|
||||
disable_scroll_wheel(span)
|
||||
apply_properties(span)
|
||||
span.setPrecision()
|
||||
span.setSigFigs()
|
||||
span.setRelativeStep()
|
||||
span.setMinimum(0)
|
||||
span.setValue(state["span"]/scale)
|
||||
|
@ -329,7 +329,7 @@ class _CenterScan(LayoutWidget):
|
|||
step = ScientificSpinBox()
|
||||
disable_scroll_wheel(step)
|
||||
apply_properties(step)
|
||||
step.setPrecision()
|
||||
step.setSigFigs()
|
||||
step.setRelativeStep()
|
||||
step.setMinimum(0)
|
||||
step.setValue(state["step"]/scale)
|
||||
|
@ -469,7 +469,7 @@ class ScanEntry(LayoutWidget):
|
|||
def procdesc_to_entry(procdesc):
|
||||
ty = procdesc["ty"]
|
||||
if ty == "NumberValue":
|
||||
is_int = (procdesc["ndecimals"] == 0
|
||||
is_int = (procdesc["precision"] == 0
|
||||
and int(procdesc["step"]) == procdesc["step"]
|
||||
and procdesc["scale"] == 1)
|
||||
if is_int:
|
||||
|
|
|
@ -20,21 +20,21 @@ class ScientificSpinBox(QtWidgets.QDoubleSpinBox):
|
|||
self.setCorrectionMode(self.CorrectToPreviousValue)
|
||||
# singleStep: resolution for step, buttons, accelerators
|
||||
# decimals: absolute rounding granularity
|
||||
# precision: number of significant digits shown, %g precision
|
||||
self.setPrecision()
|
||||
# sigFigs: number of significant digits shown
|
||||
self.setSigFigs()
|
||||
self.setRelativeStep()
|
||||
self.setRange(-inf, inf)
|
||||
self.setValue(0)
|
||||
# self.setKeyboardTracking(False)
|
||||
|
||||
def setPrecision(self, d=None):
|
||||
def setSigFigs(self, d=None):
|
||||
if d is None:
|
||||
d = self.decimals() + 3
|
||||
self._precision = max(1, int(d))
|
||||
self._fmt = "{{:.{}g}}".format(self._precision)
|
||||
self._sig_figs = max(1, int(d))
|
||||
self._fmt = "{{:.{}g}}".format(self._sig_figs)
|
||||
|
||||
def precision(self):
|
||||
return self._precision
|
||||
def sigFigs(self):
|
||||
return self._sig_figs
|
||||
|
||||
def setRelativeStep(self, s=None):
|
||||
if s is None:
|
||||
|
|
|
@ -100,7 +100,7 @@ class NumberValue(_SimpleArgProcessor):
|
|||
"""An argument that can take a numerical value.
|
||||
|
||||
If ``type=="auto"``, the result will be a ``float`` unless
|
||||
ndecimals = 0, scale = 1 and step is an integer. Setting ``type`` to
|
||||
precision = 0, scale = 1 and step is an integer. Setting ``type`` to
|
||||
``int`` will also result in an error unless these conditions are met.
|
||||
|
||||
When ``scale`` is not specified, and the unit is a common one (i.e.
|
||||
|
@ -123,14 +123,17 @@ class NumberValue(_SimpleArgProcessor):
|
|||
buttons in a UI. The default is the scale divided by 10.
|
||||
:param min: The minimum value of the argument.
|
||||
:param max: The maximum value of the argument.
|
||||
:param ndecimals: The number of decimals a UI should use.
|
||||
:param precision: The maximum number of decimals a UI should use.
|
||||
:param type: Type of this number. Accepts ``"float"``, ``"int"`` or
|
||||
``"auto"``. Defaults to ``"auto"``.
|
||||
"""
|
||||
valid_types = ["auto", "float", "int"]
|
||||
|
||||
def __init__(self, default=NoDefault, unit="", scale=None,
|
||||
step=None, min=None, max=None, ndecimals=2, type="auto"):
|
||||
def __init__(self, default=NoDefault, unit="", *, scale=None,
|
||||
step=None, min=None, max=None, precision=2, type="auto", ndecimals=None):
|
||||
if ndecimals is not None:
|
||||
print("DeprecationWarning: 'ndecimals' is deprecated. Please use 'precision' instead.")
|
||||
precision = ndecimals
|
||||
if scale is None:
|
||||
if unit == "":
|
||||
scale = 1.0
|
||||
|
@ -147,7 +150,7 @@ class NumberValue(_SimpleArgProcessor):
|
|||
self.step = step
|
||||
self.min = min
|
||||
self.max = max
|
||||
self.ndecimals = ndecimals
|
||||
self.precision = precision
|
||||
self.type = type
|
||||
|
||||
if self.type not in NumberValue.valid_types:
|
||||
|
@ -155,7 +158,7 @@ class NumberValue(_SimpleArgProcessor):
|
|||
|
||||
if self.type == "int" and not self._is_int_compatible():
|
||||
raise ValueError(("Value marked as integer but settings are "
|
||||
"not compatible. Please set ndecimals = 0, "
|
||||
"not compatible. Please set precision = 0, "
|
||||
"scale = 1 and step to an integer"))
|
||||
|
||||
super().__init__(default)
|
||||
|
@ -165,7 +168,7 @@ class NumberValue(_SimpleArgProcessor):
|
|||
Are the settings other than `type` compatible with this being
|
||||
an integer?
|
||||
'''
|
||||
return (self.ndecimals == 0
|
||||
return (self.precision == 0
|
||||
and int(self.step) == self.step
|
||||
and self.scale == 1)
|
||||
|
||||
|
@ -191,7 +194,7 @@ class NumberValue(_SimpleArgProcessor):
|
|||
d["step"] = self.step
|
||||
d["min"] = self.min
|
||||
d["max"] = self.max
|
||||
d["ndecimals"] = self.ndecimals
|
||||
d["precision"] = self.precision
|
||||
d["type"] = self.type
|
||||
return d
|
||||
|
||||
|
|
|
@ -83,8 +83,7 @@ class RangeScan(ScanObject):
|
|||
self.sequence = [i*dx + start for i in range(npoints)]
|
||||
|
||||
if randomize:
|
||||
rng = random.Random(seed)
|
||||
random.shuffle(self.sequence, rng.random)
|
||||
random.Random(seed).shuffle(self.sequence)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.sequence)
|
||||
|
@ -120,8 +119,7 @@ class CenterScan(ScanObject):
|
|||
for i in range(n) for sign in [-1, 1]][1:]
|
||||
|
||||
if randomize:
|
||||
rng = random.Random(seed)
|
||||
random.shuffle(self.sequence, rng.random)
|
||||
random.Random(seed).shuffle(self.sequence)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.sequence)
|
||||
|
@ -191,11 +189,14 @@ class Scannable:
|
|||
:param unit: A string representing the unit of the scanned variable.
|
||||
:param scale: A numerical scaling factor by which the displayed values
|
||||
are multiplied when referenced in the experiment.
|
||||
:param ndecimals: The number of decimals a UI should use.
|
||||
:param precision: The maximum number of decimals a UI should use.
|
||||
"""
|
||||
def __init__(self, default=NoDefault, unit="", scale=None,
|
||||
def __init__(self, default=NoDefault, unit="", *, scale=None,
|
||||
global_step=None, global_min=None, global_max=None,
|
||||
ndecimals=2):
|
||||
precision=2, ndecimals=None):
|
||||
if ndecimals is not None:
|
||||
print("DeprecationWarning: 'ndecimals' is deprecated. Please use 'precision' instead.")
|
||||
precision = ndecimals
|
||||
if scale is None:
|
||||
if unit == "":
|
||||
scale = 1.0
|
||||
|
@ -216,7 +217,7 @@ class Scannable:
|
|||
self.global_step = global_step
|
||||
self.global_min = global_min
|
||||
self.global_max = global_max
|
||||
self.ndecimals = ndecimals
|
||||
self.precision = precision
|
||||
|
||||
def default(self):
|
||||
if not hasattr(self, "default_values"):
|
||||
|
@ -240,7 +241,7 @@ class Scannable:
|
|||
d["global_step"] = self.global_step
|
||||
d["global_min"] = self.global_min
|
||||
d["global_max"] = self.global_max
|
||||
d["ndecimals"] = self.ndecimals
|
||||
d["precision"] = self.precision
|
||||
return d
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# RUN: %python -m artiq.compiler.testbench.embedding +diag %s 2>%t
|
||||
# RUN: OutputCheck %s --file-to-check=%t
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
|
||||
@kernel
|
||||
def modify(x):
|
||||
# CHECK-L: ${LINE:+1}: error: cannot assign to a tuple element
|
||||
x[0] = 2
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
modify((1, "foo", True))
|
||||
modify((2, "bar", False))
|
|
@ -0,0 +1,16 @@
|
|||
# RUN: env ARTIQ_DUMP_LLVM=%t %python -m artiq.compiler.testbench.embedding +compile %s
|
||||
# RUN: OutputCheck %s --file-to-check=%t.ll
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
|
||||
# CHECK-L: void @_Z16testbench.unpackzz({ i32, { i8*, i32 }, i1 } %ARG.x)
|
||||
|
||||
@kernel
|
||||
def unpack(x):
|
||||
print(x[0])
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
unpack((1, "foo", True))
|
||||
unpack((2, "bar", False))
|
|
@ -0,0 +1,14 @@
|
|||
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||
# RUN: OutputCheck %s --file-to-check=%t
|
||||
|
||||
i = 0
|
||||
x = (1, "foo", True)
|
||||
|
||||
# CHECK-L: ${LINE:+1}: error: tuples can only be indexed by a constant
|
||||
x[i]
|
||||
|
||||
# CHECK-L: ${LINE:+1}: error: tuples can only be indexed by a constant
|
||||
x[0:2]
|
||||
|
||||
# CHECK-L: ${LINE:+1}: error: index 3 is out of range for tuple of size 3
|
||||
x[3]
|
|
@ -0,0 +1,22 @@
|
|||
# RUN: %python -m artiq.compiler.testbench.jit %s
|
||||
# RUN: %python %s
|
||||
|
||||
# Basic indexing
|
||||
a = (1, "xyz", True)
|
||||
|
||||
assert a[0] == 1
|
||||
assert a[1] == "xyz"
|
||||
assert a[2] == True
|
||||
|
||||
# Nested indexing
|
||||
b = (a, 2, (3, "abc", a))
|
||||
|
||||
assert b[0][0] == 1
|
||||
assert b[1] == 2
|
||||
assert b[2][2][0] == 1
|
||||
|
||||
# Usage on the LHS of an assignment
|
||||
c = (1, 2, [1, 2, 3])
|
||||
|
||||
c[2][0] = 456
|
||||
assert c[2][0] == 456
|
|
@ -9,7 +9,7 @@ from artiq.language.environment import BooleanValue, EnumerationValue, \
|
|||
class NumberValueCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.default_value = NumberValue()
|
||||
self.int_value = NumberValue(42, step=1, ndecimals=0)
|
||||
self.int_value = NumberValue(42, step=1, precision=0)
|
||||
self.float_value = NumberValue(42)
|
||||
|
||||
def test_invalid_default(self):
|
||||
|
|
|
@ -18,7 +18,8 @@ from artiq.language.environment import is_public_experiment
|
|||
from artiq.language import units
|
||||
|
||||
|
||||
__all__ = ["parse_arguments", "elide", "short_format", "file_import",
|
||||
__all__ = ["parse_arguments", "elide", "scale_from_metadata",
|
||||
"short_format", "file_import",
|
||||
"get_experiment",
|
||||
"exc_to_warning", "asyncio_wait_or_cancel",
|
||||
"get_windows_drives", "get_user_config_dir"]
|
||||
|
@ -54,12 +55,15 @@ def elide(s, maxlen):
|
|||
s += "..."
|
||||
return s
|
||||
|
||||
def scale_from_metadata(metadata):
|
||||
unit = metadata.get("unit", "")
|
||||
default_scale = getattr(units, unit, 1)
|
||||
return metadata.get("scale", default_scale)
|
||||
|
||||
def short_format(v, metadata={}):
|
||||
m = metadata
|
||||
unit = m.get("unit", "")
|
||||
default_scale = getattr(units, unit, 1)
|
||||
scale = m.get("scale", default_scale)
|
||||
scale = scale_from_metadata(m)
|
||||
precision = m.get("precision", None)
|
||||
if v is None:
|
||||
return "None"
|
||||
|
@ -68,6 +72,7 @@ def short_format(v, metadata={}):
|
|||
v_t = np.divide(v, scale)
|
||||
v_str = np.format_float_positional(v_t,
|
||||
precision=precision,
|
||||
trim='-',
|
||||
unique=True)
|
||||
v_str += " " + unit if unit else ""
|
||||
return v_str
|
||||
|
|
|
@ -46,6 +46,8 @@ Datasets are values (possibly arrays) that are read and written by experiments a
|
|||
|
||||
A dataset may be broadcasted, that is, distributed to all clients connected to the master. For example, the ARTIQ GUI may plot it while the experiment is in progress to give rapid feedback to the user. Broadcasted datasets live in a global key-value store; experiments should use distinctive real-time result names in order to avoid conflicts. Broadcasted datasets may be used to communicate values across experiments; for example, a periodic calibration experiment may update a dataset read by payload experiments. Broadcasted datasets are replaced when a new dataset with the same key (name) is produced.
|
||||
|
||||
Datasets can attach metadata for numerical formatting with the ``unit``, ``scale`` and ``precision`` parameters. In experiment code, values are assumed to be in the SI unit. In code, setting a dataset with a value of `1000` and the unit `kV` represents the quantity `1 kV`. It is recommended to use the globals defined by `artiq.language.units` and write `1*kV` instead of `1000` for the value. In dashboards and clients these globals are not available. There, setting a dataset with a value of `1` and the unit `kV` simply represents the quantity `1 kV`. ``precision`` refers to the max number of decimal places to display. This parameter does not affect the underlying value, and is only used for display purposes.
|
||||
|
||||
Broadcasted datasets may be persistent: the master stores them in a file typically called ``dataset_db.pyon`` so they are saved across master restarts.
|
||||
|
||||
Datasets produced by an experiment run may be archived in the HDF5 output for that run.
|
||||
|
|
|
@ -53,7 +53,7 @@ Experiments may have arguments whose values can be set in the dashboard and used
|
|||
|
||||
|
||||
def build(self):
|
||||
self.setattr_argument("count", NumberValue(ndecimals=0, step=1))
|
||||
self.setattr_argument("count", NumberValue(precision=0, step=1))
|
||||
|
||||
def run(self):
|
||||
for i in range(self.count):
|
||||
|
|
30
flake.nix
30
flake.nix
|
@ -126,6 +126,8 @@
|
|||
migen = pkgs.python3Packages.buildPythonPackage rec {
|
||||
name = "migen";
|
||||
src = src-migen;
|
||||
format = "pyproject";
|
||||
nativeBuildInputs = [ pkgs.python3Packages.setuptools ];
|
||||
propagatedBuildInputs = [ pkgs.python3Packages.colorama ];
|
||||
};
|
||||
|
||||
|
@ -211,16 +213,20 @@
|
|||
'';
|
||||
installPhase =
|
||||
''
|
||||
TARGET_DIR=$out
|
||||
mkdir -p $TARGET_DIR
|
||||
cp artiq_${target}/${variant}/gateware/top.bit $TARGET_DIR
|
||||
mkdir $out
|
||||
cp artiq_${target}/${variant}/gateware/top.bit $out
|
||||
if [ -e artiq_${target}/${variant}/software/bootloader/bootloader.bin ]
|
||||
then cp artiq_${target}/${variant}/software/bootloader/bootloader.bin $TARGET_DIR
|
||||
then cp artiq_${target}/${variant}/software/bootloader/bootloader.bin $out
|
||||
fi
|
||||
if [ -e artiq_${target}/${variant}/software/runtime ]
|
||||
then cp artiq_${target}/${variant}/software/runtime/runtime.{elf,fbi} $TARGET_DIR
|
||||
else cp artiq_${target}/${variant}/software/satman/satman.{elf,fbi} $TARGET_DIR
|
||||
then cp artiq_${target}/${variant}/software/runtime/runtime.{elf,fbi} $out
|
||||
else cp artiq_${target}/${variant}/software/satman/satman.{elf,fbi} $out
|
||||
fi
|
||||
|
||||
mkdir $out/nix-support
|
||||
for i in $out/*.*; do
|
||||
echo file binary-dist $i >> $out/nix-support/hydra-build-products
|
||||
done
|
||||
'';
|
||||
# don't mangle ELF files as they are not for NixOS
|
||||
dontFixup = true;
|
||||
|
@ -291,6 +297,10 @@
|
|||
target = "kc705";
|
||||
variant = "nist_clock";
|
||||
};
|
||||
artiq-board-efc-shuttler = makeArtiqBoardPackage {
|
||||
target = "efc";
|
||||
variant = "shuttler";
|
||||
};
|
||||
inherit sphinxcontrib-wavedrom latex-artiq-manual;
|
||||
artiq-manual-html = pkgs.stdenvNoCC.mkDerivation rec {
|
||||
name = "artiq-manual-html-${version}";
|
||||
|
@ -374,9 +384,9 @@
|
|||
(pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ migen misoc artiq ps.packaging ]))
|
||||
rust
|
||||
pkgs.cargo-xbuild
|
||||
pkgs.llvmPackages_11.clang-unwrapped
|
||||
pkgs.llvm_11
|
||||
pkgs.lld_11
|
||||
pkgs.llvmPackages_14.clang-unwrapped
|
||||
pkgs.llvm_14
|
||||
pkgs.lld_14
|
||||
packages.x86_64-linux.vivado
|
||||
packages.x86_64-linux.openocd-bscanspi
|
||||
];
|
||||
|
@ -387,7 +397,7 @@
|
|||
};
|
||||
|
||||
hydraJobs = {
|
||||
inherit (packages.x86_64-linux) artiq artiq-board-kc705-nist_clock openocd-bscanspi;
|
||||
inherit (packages.x86_64-linux) artiq artiq-board-kc705-nist_clock artiq-board-efc-shuttler openocd-bscanspi;
|
||||
sipyco-msys2-pkg = packages.x86_64-w64-mingw32.sipyco-pkg;
|
||||
artiq-comtools-msys2-pkg = packages.x86_64-w64-mingw32.artiq-comtools-pkg;
|
||||
artiq-msys2-pkg = packages.x86_64-w64-mingw32.artiq-pkg;
|
||||
|
|
Loading…
Reference in New Issue