Merge branch 'master' into nac3

This commit is contained in:
Sebastien Bourdeauducq 2021-11-03 21:37:18 +08:00
commit 977543e05a
25 changed files with 495 additions and 154 deletions

View File

@ -24,6 +24,10 @@ Highlights:
- HVAMP_8CH 8 channel HV amplifier for Fastino / Zotino - HVAMP_8CH 8 channel HV amplifier for Fastino / Zotino
* ``artiq_ddb_template`` generates edge-counter keys that start with the key of the corresponding * ``artiq_ddb_template`` generates edge-counter keys that start with the key of the corresponding
TTL device (e.g. ``"ttl_0_counter"`` for the edge counter on TTL device``"ttl_0"``) TTL device (e.g. ``"ttl_0_counter"`` for the edge counter on TTL device``"ttl_0"``)
* ``artiq_master`` now has an ``--experiment-subdir`` option to scan only a subdirectory of the
repository when building the list of experiments.
* The configuration entry ``rtio_clock`` supports multiple clocking settings, deprecating the usage
of compile-time options.
Breaking changes: Breaking changes:

View File

@ -3,7 +3,7 @@ streaming DAC.
""" """
from numpy import int32 from numpy import int32
from artiq.language.core import kernel, portable, delay from artiq.language.core import kernel, portable, delay, delay_mu
from artiq.coredevice.rtio import (rtio_output, rtio_output_wide, from artiq.coredevice.rtio import (rtio_output, rtio_output_wide,
rtio_input_data) rtio_input_data)
from artiq.language.units import us from artiq.language.units import us
@ -191,3 +191,82 @@ class Fastino:
green LED. green LED.
""" """
self.write(0x23, leds) self.write(0x23, leds)
@kernel
def set_continuous(self, channel_mask):
"""Enable continuous DAC updates on channels regardless of new data
being submitted.
"""
self.write(0x25, channel_mask)
@kernel
def stage_cic_mu(self, rate_mantissa, rate_exponent, gain_exponent):
"""Stage machine unit CIC interpolator configuration.
"""
if rate_mantissa < 0 or rate_mantissa >= 1 << 6:
raise ValueError("rate_mantissa out of bounds")
if rate_exponent < 0 or rate_exponent >= 1 << 4:
raise ValueError("rate_exponent out of bounds")
if gain_exponent < 0 or gain_exponent >= 1 << 6:
raise ValueError("gain_exponent out of bounds")
config = rate_mantissa | (rate_exponent << 6) | (gain_exponent << 10)
self.write(0x26, config)
@kernel
def stage_cic(self, rate) -> TInt32:
"""Compute and stage interpolator configuration.
This method approximates the desired interpolation rate using a 10 bit
floating point representation (6 bit mantissa, 4 bit exponent) and
then determines an optimal interpolation gain compensation exponent
to avoid clipping. Gains for rates that are powers of two are accurately
compensated. Other rates lead to overall less than unity gain (but more
than 0.5 gain).
The overall gain including gain compensation is
`actual_rate**order/2**ceil(log2(actual_rate**order))`
where `order = 3`.
Returns the actual interpolation rate.
"""
if rate <= 0 or rate > 1 << 16:
raise ValueError("rate out of bounds")
rate_mantissa = rate
rate_exponent = 0
while rate_mantissa > 1 << 6:
rate_exponent += 1
rate_mantissa >>= 1
order = 3
gain = 1
for i in range(order):
gain *= rate_mantissa
gain_exponent = 0
while gain > 1 << gain_exponent:
gain_exponent += 1
gain_exponent += order*rate_exponent
assert gain_exponent <= order*16
self.stage_cic_mu(rate_mantissa - 1, rate_exponent, gain_exponent)
return rate_mantissa << rate_exponent
@kernel
def apply_cic(self, channel_mask):
"""Apply the staged interpolator configuration on the specified channels.
Each Fastino channel includes a fourth order (cubic) CIC interpolator with
variable rate change and variable output gain compensation (see
:meth:`stage_cic`).
Channels using non-unity interpolation rate should have
continous DAC updates enabled (see :meth:`set_continuous`) unless
their output is supposed to be constant.
This method resets and settles the affected interpolators. There will be
no output updates for the next `order = 3` input samples.
Affected channels will only accept one input sample per input sample
period. This method synchronizes the input sample period to the current
frame on the affected channels.
If application of new interpolator settings results in a change of the
overall gain, there will be a corresponding output step.
"""
self.write(0x27, channel_mask)

View File

@ -203,6 +203,7 @@ class Phaser:
self.measure_frame_timestamp() self.measure_frame_timestamp()
if self.frame_tstamp < 0: if self.frame_tstamp < 0:
raise ValueError("frame timestamp measurement timed out") raise ValueError("frame timestamp measurement timed out")
delay(.1*ms)
# reset # reset
self.set_cfg(dac_resetb=0, dac_sleep=1, dac_txena=0, self.set_cfg(dac_resetb=0, dac_sleep=1, dac_txena=0,

View File

@ -163,7 +163,7 @@ dependencies = [
[[package]] [[package]]
name = "fringe" name = "fringe"
version = "1.2.1" version = "1.2.1"
source = "git+https://git.m-labs.hk/M-Labs/libfringe.git?rev=9748bb#9748bb8af86c131d45be1238ea4d5f965a974630" source = "git+https://git.m-labs.hk/M-Labs/libfringe.git?rev=3ecbe5#3ecbe53f7644b18ee46ebd5b2ca12c9cbceec43a"
dependencies = [ dependencies = [
"libc 0.2.101", "libc 0.2.101",
] ]

View File

@ -17,7 +17,7 @@ use board_misoc::slave_fpga;
#[cfg(has_ethmac)] #[cfg(has_ethmac)]
use board_misoc::{clock, ethmac, net_settings}; use board_misoc::{clock, ethmac, net_settings};
use board_misoc::uart_console::Console; use board_misoc::uart_console::Console;
use riscv::register::{mcause, mepc}; use riscv::register::{mcause, mepc, mtval};
fn check_integrity() -> bool { fn check_integrity() -> bool {
extern { extern {
@ -522,7 +522,8 @@ pub extern fn main() -> i32 {
pub extern fn exception(_regs: *const u32) { pub extern fn exception(_regs: *const u32) {
let pc = mepc::read(); let pc = mepc::read();
let cause = mcause::read().cause(); let cause = mcause::read().cause();
panic!("{:?} at PC {:#08x}", cause, u32::try_from(pc).unwrap()) let mtval = mtval::read();
panic!("{:?} at PC {:#08x}, trap value {:#08x}", cause, u32::try_from(pc).unwrap(), mtval);
} }
#[no_mangle] #[no_mangle]

View File

@ -23,7 +23,7 @@ use proto_artiq::{kernel_proto, rpc_proto};
use kernel_proto::*; use kernel_proto::*;
#[cfg(has_rtio_dma)] #[cfg(has_rtio_dma)]
use board_misoc::csr; use board_misoc::csr;
use riscv::register::{mcause, mepc}; use riscv::register::{mcause, mepc, mtval};
fn send(request: &Message) { fn send(request: &Message) {
unsafe { mailbox::send(request as *const _ as usize) } unsafe { mailbox::send(request as *const _ as usize) }
@ -493,11 +493,13 @@ pub unsafe fn main() {
let _end = library.lookup(b"_end").unwrap(); let _end = library.lookup(b"_end").unwrap();
let __modinit__ = library.lookup(b"__modinit__").unwrap(); let __modinit__ = library.lookup(b"__modinit__").unwrap();
let typeinfo = library.lookup(b"typeinfo"); let typeinfo = library.lookup(b"typeinfo");
let _sstack_guard = library.lookup(b"_sstack_guard").unwrap();
LIBRARY = Some(library); LIBRARY = Some(library);
ptr::write_bytes(__bss_start as *mut u8, 0, (_end - __bss_start) as usize); ptr::write_bytes(__bss_start as *mut u8, 0, (_end - __bss_start) as usize);
board_misoc::pmp::init_stack_guard(_sstack_guard as usize);
board_misoc::cache::flush_cpu_dcache(); board_misoc::cache::flush_cpu_dcache();
board_misoc::cache::flush_cpu_icache(); board_misoc::cache::flush_cpu_icache();
@ -530,7 +532,8 @@ pub unsafe fn main() {
pub extern fn exception(_regs: *const u32) { pub extern fn exception(_regs: *const u32) {
let pc = mepc::read(); let pc = mepc::read();
let cause = mcause::read().cause(); let cause = mcause::read().cause();
panic!("{:?} at PC {:#08x}", cause, u32::try_from(pc).unwrap()) let mtval = mtval::read();
panic!("{:?} at PC {:#08x}, trap value {:#08x}", cause, u32::try_from(pc).unwrap(), mtval);
} }
#[no_mangle] #[no_mangle]

View File

@ -1,4 +1,5 @@
use super::cache; use super::{cache, pmp};
use riscv::register::*;
pub unsafe fn reset() -> ! { pub unsafe fn reset() -> ! {
llvm_asm!(r#" llvm_asm!(r#"
@ -16,3 +17,14 @@ pub unsafe fn jump(addr: usize) -> ! {
"# : : "r"(addr) : : "volatile"); "# : : "r"(addr) : : "volatile");
loop {} loop {}
} }
pub unsafe fn start_user(addr: usize) -> ! {
pmp::enable_user_memory();
mstatus::set_mpp(mstatus::MPP::User);
mepc::write(addr);
llvm_asm!(
"mret"
: : : : "volatile"
);
unreachable!()
}

View File

@ -1,2 +1,3 @@
pub mod cache; pub mod cache;
pub mod boot; pub mod boot;
pub mod pmp;

View File

@ -0,0 +1,55 @@
use riscv::register::{pmpaddr0, pmpaddr1, pmpaddr2, pmpaddr3, pmpcfg0};
static mut THREAD_DEPTH: u8 = 0;
const PMP_L : usize = 0b10000000;
const PMP_NAPOT: usize = 0b00011000;
const PMP_X : usize = 0b00000100;
const PMP_W : usize = 0b00000010;
const PMP_R : usize = 0b00000001;
const PMP_OFF : usize = 0b00000000;
#[inline(always)]
pub unsafe fn init_stack_guard(guard_base: usize) {
pmpaddr2::write((guard_base >> 2) | ((0x1000 - 1) >> 3));
pmpcfg0::write((PMP_L | PMP_NAPOT) << 16);
}
#[inline(always)]
pub fn enable_user_memory() {
pmpaddr3::write((0x80000000 - 1) >> 3);
pmpcfg0::write((PMP_L | PMP_NAPOT | PMP_X | PMP_W | PMP_R) << 24);
}
#[inline(always)]
pub unsafe fn push_pmp_region(addr: usize) {
let pmp_addr = (addr >> 2) | ((0x1000 - 1) >> 3);
match THREAD_DEPTH {
// Activate PMP0 when switching from main stack to thread
0 => {
pmpaddr0::write(pmp_addr);
pmpcfg0::write(PMP_NAPOT);
}
// Temporarily activate PMP1 when spawning a thread from a thread
// The thread should swap back to the main stack very soon after init
1 => {
pmpaddr1::write(pmp_addr);
pmpcfg0::write(PMP_NAPOT << 8 | PMP_NAPOT);
}
// Thread *running* another thread should not be possible
_ => unreachable!()
}
THREAD_DEPTH += 1;
}
#[inline(always)]
pub unsafe fn pop_pmp_region() {
THREAD_DEPTH -= 1;
match THREAD_DEPTH {
0 => pmpcfg0::write(PMP_OFF),
1 => pmpcfg0::write(PMP_NAPOT),
_ => unreachable!()
}
}

View File

@ -32,6 +32,6 @@ riscv = { version = "0.6.0", features = ["inline-asm"] }
[dependencies.fringe] [dependencies.fringe]
git = "https://git.m-labs.hk/M-Labs/libfringe.git" git = "https://git.m-labs.hk/M-Labs/libfringe.git"
rev = "9748bb" rev = "3ecbe5"
default-features = false default-features = false
features = ["alloc"] features = ["alloc"]

View File

@ -29,7 +29,7 @@ use core::cell::RefCell;
use core::convert::TryFrom; use core::convert::TryFrom;
use smoltcp::wire::IpCidr; use smoltcp::wire::IpCidr;
use board_misoc::{csr, ident, clock, spiflash, config, net_settings}; use board_misoc::{csr, ident, clock, spiflash, config, net_settings, pmp, boot};
#[cfg(has_ethmac)] #[cfg(has_ethmac)]
use board_misoc::ethmac; use board_misoc::ethmac;
#[cfg(has_drtio)] #[cfg(has_drtio)]
@ -40,7 +40,7 @@ use proto_artiq::{mgmt_proto, moninj_proto, rpc_proto, session_proto, kernel_pro
#[cfg(has_rtio_analyzer)] #[cfg(has_rtio_analyzer)]
use proto_artiq::analyzer_proto; use proto_artiq::analyzer_proto;
use riscv::register::{mcause, mepc}; use riscv::register::{mcause, mepc, mtval};
mod rtio_clocking; mod rtio_clocking;
mod rtio_mgt; mod rtio_mgt;
@ -247,10 +247,15 @@ pub extern fn main() -> i32 {
extern { extern {
static mut _fheap: u8; static mut _fheap: u8;
static mut _eheap: u8; static mut _eheap: u8;
static mut _sstack_guard: u8;
} }
ALLOC.add_range(&mut _fheap, &mut _eheap); ALLOC.add_range(&mut _fheap, &mut _eheap);
logger_artiq::BufferLogger::new(&mut LOG_BUFFER[..]).register(startup); pmp::init_stack_guard(&_sstack_guard as *const u8 as usize);
logger_artiq::BufferLogger::new(&mut LOG_BUFFER[..]).register(||
boot::start_user(startup as usize)
);
0 0
} }
@ -285,6 +290,18 @@ pub extern fn exception(regs: *const TrapFrame) {
mcause::Trap::Interrupt(source) => { mcause::Trap::Interrupt(source) => {
info!("Called interrupt with {:?}", source); info!("Called interrupt with {:?}", source);
}, },
mcause::Trap::Exception(mcause::Exception::UserEnvCall) => {
unsafe {
if (*regs).a7 == 0 {
pmp::pop_pmp_region()
} else {
pmp::push_pmp_region((*regs).a7)
}
}
mepc::write(pc + 4);
},
mcause::Trap::Exception(e) => { mcause::Trap::Exception(e) => {
println!("Trap frame: {:x?}", unsafe { *regs }); println!("Trap frame: {:x?}", unsafe { *regs });
@ -302,7 +319,8 @@ pub extern fn exception(regs: *const TrapFrame) {
} }
hexdump(u32::try_from(pc).unwrap()); hexdump(u32::try_from(pc).unwrap());
panic!("exception {:?} at PC 0x{:x}", e, u32::try_from(pc).unwrap()) let mtval = mtval::read();
panic!("exception {:?} at PC 0x{:x}, trap value 0x{:x}", e, u32::try_from(pc).unwrap(), mtval)
} }
} }
} }

View File

@ -4,29 +4,66 @@ use board_artiq::si5324;
#[cfg(has_drtio)] #[cfg(has_drtio)]
use board_misoc::{csr, clock}; use board_misoc::{csr, clock};
#[derive(Debug)] #[derive(Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum RtioClock { pub enum RtioClock {
Internal = 0, Default,
External = 1 Int_125,
Int_100,
Int_150,
Ext0_Bypass,
Ext0_Synth0_10to125,
Ext0_Synth0_100to125,
Ext0_Synth0_125to125,
} }
#[allow(unreachable_code)]
fn get_rtio_clock_cfg() -> RtioClock { fn get_rtio_clock_cfg() -> RtioClock {
config::read("rtio_clock", |result| { config::read_str("rtio_clock", |result| {
match result { let res = match result {
Ok(b"i") => { Ok("int_125") => RtioClock::Int_125,
info!("using internal RTIO clock"); Ok("int_100") => RtioClock::Int_100,
RtioClock::Internal Ok("int_150") => RtioClock::Int_150,
Ok("ext0_bypass") => RtioClock::Ext0_Bypass,
Ok("ext0_bypass_125") => RtioClock::Ext0_Bypass,
Ok("ext0_bypass_100") => RtioClock::Ext0_Bypass,
Ok("ext0_synth0_10to125") => RtioClock::Ext0_Synth0_10to125,
Ok("ext0_synth0_100to125") => RtioClock::Ext0_Synth0_100to125,
Ok("ext0_synth0_125to125") => RtioClock::Ext0_Synth0_125to125,
Ok("i") => {
warn!("Using legacy rtio_clock setting ('i'). Falling back to default. This will be deprecated.");
RtioClock::Default
}, },
Ok(b"e") => { Ok("e") => {
info!("using external RTIO clock"); warn!("Using legacy rtio_clock setting ('e'). This will be deprecated.");
RtioClock::External RtioClock::Ext0_Bypass
}, },
_ => { _ => {
info!("using internal RTIO clock (by default)"); warn!("rtio_clock setting not recognised. Falling back to default.");
RtioClock::Internal RtioClock::Default
},
} }
};
if res == RtioClock::Default {
#[cfg(any(si5324_ext_ref, ext_ref_frequency))]
warn!("si5324_ext_ref and ext_ref_frequency compile-time options are deprecated. Please use the rtio_clock coreconfig settings instead.");
#[cfg(all(rtio_frequency = "125.0", si5324_ext_ref, ext_ref_frequency = "10.0"))]
return RtioClock::Ext0_Synth0_10to125;
#[cfg(all(rtio_frequency = "125.0", si5324_ext_ref, ext_ref_frequency = "100.0"))]
return RtioClock::Ext0_Synth0_100to125;
#[cfg(all(rtio_frequency = "125.0", si5324_ext_ref, ext_ref_frequency = "125.0"))]
return RtioClock::Ext0_Synth0_125to125;
#[cfg(all(rtio_frequency = "125.0", not(si5324_ext_ref)))]
return RtioClock::Int_125;
#[cfg(all(rtio_frequency = "150.0", not(si5324_ext_ref)))]
return RtioClock::Int_150;
#[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref)))]
return RtioClock::Int_100;
//in case nothing is set
return RtioClock::Int_125;
}
res
}) })
} }
#[cfg(has_rtio_crg)] #[cfg(has_rtio_crg)]
@ -41,9 +78,23 @@ pub mod crg {
#[cfg(has_rtio_clock_switch)] #[cfg(has_rtio_clock_switch)]
pub fn init(clk: RtioClock) -> bool { pub fn init(clk: RtioClock) -> bool {
let clk_sel: u8 = match clk {
RtioClock::Ext0_Bypass => {
info!("Using external clock");
1
},
RtioClock::Int_125 => {
info!("Using internal RTIO clock");
0
},
_ => {
warn!("rtio_clock setting '{:?}' is not supported. Using default internal RTIO clock instead", clk);
0
}
};
unsafe { unsafe {
csr::rtio_crg::pll_reset_write(1); csr::rtio_crg::pll_reset_write(1);
csr::rtio_crg::clock_sel_write(clk as u8); csr::rtio_crg::clock_sel_write(clk_sel);
csr::rtio_crg::pll_reset_write(0); csr::rtio_crg::pll_reset_write(0);
} }
clock::spin_us(150); clock::spin_us(150);
@ -52,6 +103,7 @@ pub mod crg {
#[cfg(not(has_rtio_clock_switch))] #[cfg(not(has_rtio_clock_switch))]
pub fn init() -> bool { pub fn init() -> bool {
info!("Using internal RTIO clock");
unsafe { unsafe {
csr::rtio_crg::pll_reset_write(0); csr::rtio_crg::pll_reset_write(0);
} }
@ -66,11 +118,11 @@ pub mod crg {
} }
#[cfg(si5324_as_synthesizer)] #[cfg(si5324_as_synthesizer)]
fn setup_si5324_as_synthesizer() { fn setup_si5324_as_synthesizer(cfg: RtioClock) {
// 125 MHz output from 10 MHz CLKINx reference, 504 Hz BW let si5324_settings = match cfg {
#[cfg(all(rtio_frequency = "125.0", si5324_ext_ref, ext_ref_frequency = "10.0"))] RtioClock::Ext0_Synth0_10to125 => { // 125 MHz output from 10 MHz CLKINx reference, 504 Hz BW
const SI5324_SETTINGS: si5324::FrequencySettings info!("using 10MHz reference to make 125MHz RTIO clock with PLL");
= si5324::FrequencySettings { si5324::FrequencySettings {
n1_hs : 10, n1_hs : 10,
nc1_ls : 4, nc1_ls : 4,
n2_hs : 10, n2_hs : 10,
@ -79,11 +131,11 @@ fn setup_si5324_as_synthesizer() {
n32 : 6, n32 : 6,
bwsel : 4, bwsel : 4,
crystal_ref: false crystal_ref: false
}; }
// 125MHz output, from 100MHz CLKINx reference, 586 Hz loop bandwidth },
#[cfg(all(rtio_frequency = "125.0", si5324_ext_ref, ext_ref_frequency = "100.0"))] RtioClock::Ext0_Synth0_100to125 => { // 125MHz output, from 100MHz CLKINx reference, 586 Hz loop bandwidth
const SI5324_SETTINGS: si5324::FrequencySettings info!("using 10MHz reference to make 125MHz RTIO clock with PLL");
= si5324::FrequencySettings { si5324::FrequencySettings {
n1_hs : 10, n1_hs : 10,
nc1_ls : 4, nc1_ls : 4,
n2_hs : 10, n2_hs : 10,
@ -92,11 +144,11 @@ fn setup_si5324_as_synthesizer() {
n32 : 52, n32 : 52,
bwsel : 4, bwsel : 4,
crystal_ref: false crystal_ref: false
}; }
// 125MHz output, from 125MHz CLKINx reference, 606 Hz loop bandwidth },
#[cfg(all(rtio_frequency = "125.0", si5324_ext_ref, ext_ref_frequency = "125.0"))] RtioClock::Ext0_Synth0_125to125 => { // 125MHz output, from 125MHz CLKINx reference, 606 Hz loop bandwidth
const SI5324_SETTINGS: si5324::FrequencySettings info!("using 10MHz reference to make 125MHz RTIO clock with PLL");
= si5324::FrequencySettings { si5324::FrequencySettings {
n1_hs : 5, n1_hs : 5,
nc1_ls : 8, nc1_ls : 8,
n2_hs : 7, n2_hs : 7,
@ -105,24 +157,11 @@ fn setup_si5324_as_synthesizer() {
n32 : 63, n32 : 63,
bwsel : 4, bwsel : 4,
crystal_ref: false crystal_ref: false
}; }
// 125MHz output, from crystal, 7 Hz },
#[cfg(all(rtio_frequency = "125.0", not(si5324_ext_ref)))] RtioClock::Int_150 => { // 150MHz output, from crystal
const SI5324_SETTINGS: si5324::FrequencySettings info!("using internal 150MHz RTIO clock");
= si5324::FrequencySettings { si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 19972,
n31 : 4565,
n32 : 4565,
bwsel : 4,
crystal_ref: true
};
// 150MHz output, from crystal
#[cfg(all(rtio_frequency = "150.0", not(si5324_ext_ref)))]
const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings {
n1_hs : 9, n1_hs : 9,
nc1_ls : 4, nc1_ls : 4,
n2_hs : 10, n2_hs : 10,
@ -131,11 +170,11 @@ fn setup_si5324_as_synthesizer() {
n32 : 7139, n32 : 7139,
bwsel : 3, bwsel : 3,
crystal_ref: true crystal_ref: true
}; }
// 100MHz output, from crystal. Also used as reference for Sayma HMC830. },
#[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref)))] RtioClock::Int_100 => { // 100MHz output, from crystal. Also used as reference for Sayma HMC830.
const SI5324_SETTINGS: si5324::FrequencySettings info!("using internal 100MHz RTIO clock");
= si5324::FrequencySettings { si5324::FrequencySettings {
n1_hs : 9, n1_hs : 9,
nc1_ls : 6, nc1_ls : 6,
n2_hs : 10, n2_hs : 10,
@ -144,6 +183,34 @@ fn setup_si5324_as_synthesizer() {
n32 : 7139, n32 : 7139,
bwsel : 3, bwsel : 3,
crystal_ref: true crystal_ref: true
}
},
RtioClock::Int_125 => { // 125MHz output, from crystal, 7 Hz
info!("using internal 125MHz RTIO clock");
si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 19972,
n31 : 4565,
n32 : 4565,
bwsel : 4,
crystal_ref: true
}
}
_ => { // 125MHz output like above, default (if chosen option is not supported)
warn!("rtio_clock setting '{:?}' is not supported. Falling back to default internal 125MHz RTIO clock.", cfg);
si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 19972,
n31 : 4565,
n32 : 4565,
bwsel : 4,
crystal_ref: true
}
}
}; };
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0", not(si5324_ext_ref)))] #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0", not(si5324_ext_ref)))]
let si5324_ref_input = si5324::Input::Ckin2; let si5324_ref_input = si5324::Input::Ckin2;
@ -155,10 +222,11 @@ fn setup_si5324_as_synthesizer() {
let si5324_ref_input = si5324::Input::Ckin2; let si5324_ref_input = si5324::Input::Ckin2;
#[cfg(soc_platform = "kc705")] #[cfg(soc_platform = "kc705")]
let si5324_ref_input = si5324::Input::Ckin2; let si5324_ref_input = si5324::Input::Ckin2;
si5324::setup(&SI5324_SETTINGS, si5324_ref_input).expect("cannot initialize Si5324"); si5324::setup(&si5324_settings, si5324_ref_input).expect("cannot initialize Si5324");
} }
pub fn init() { pub fn init() {
let clock_cfg = get_rtio_clock_cfg();
#[cfg(si5324_as_synthesizer)] #[cfg(si5324_as_synthesizer)]
{ {
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
@ -169,9 +237,12 @@ pub fn init() {
let si5324_ext_input = si5324::Input::Ckin2; let si5324_ext_input = si5324::Input::Ckin2;
#[cfg(soc_platform = "kc705")] #[cfg(soc_platform = "kc705")]
let si5324_ext_input = si5324::Input::Ckin2; let si5324_ext_input = si5324::Input::Ckin2;
match get_rtio_clock_cfg() { match clock_cfg {
RtioClock::Internal => setup_si5324_as_synthesizer(), RtioClock::Ext0_Bypass => {
RtioClock::External => si5324::bypass(si5324_ext_input).expect("cannot bypass Si5324") info!("using external RTIO clock with PLL bypass");
si5324::bypass(si5324_ext_input).expect("cannot bypass Si5324")
},
_ => setup_si5324_as_synthesizer(clock_cfg),
} }
} }
@ -189,7 +260,7 @@ pub fn init() {
#[cfg(has_rtio_crg)] #[cfg(has_rtio_crg)]
{ {
#[cfg(has_rtio_clock_switch)] #[cfg(has_rtio_clock_switch)]
let result = crg::init(get_rtio_clock_cfg()); let result = crg::init(clock_cfg);
#[cfg(not(has_rtio_clock_switch))] #[cfg(not(has_rtio_clock_switch))]
let result = crg::init(); let result = crg::init();
if !result { if !result {

View File

@ -69,8 +69,11 @@ SECTIONS
_ebss = .; _ebss = .;
} > runtime } > runtime
.stack (NOLOAD) : ALIGN(16) .stack (NOLOAD) : ALIGN(0x1000)
{ {
_sstack_guard = .;
. += 0x1000;
_estack = .;
. += 0x10000; . += 0x10000;
_fstack = . - 16; _fstack = . - 16;
} > runtime } > runtime

View File

@ -61,7 +61,8 @@ impl Thread {
let spawned = io.spawned.clone(); let spawned = io.spawned.clone();
let sockets = io.sockets.clone(); let sockets = io.sockets.clone();
let stack = OwnedStack::new(stack_size); // Add a 4k stack guard to the stack of any new threads
let stack = OwnedStack::new(stack_size + 4096);
ThreadHandle::new(Thread { ThreadHandle::new(Thread {
generator: Generator::unsafe_new(stack, |yielder, _| { generator: Generator::unsafe_new(stack, |yielder, _| {
f(Io { f(Io {

View File

@ -9,7 +9,7 @@ extern crate board_artiq;
extern crate riscv; extern crate riscv;
use core::convert::TryFrom; use core::convert::TryFrom;
use board_misoc::{csr, ident, clock, uart_logger, i2c}; use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp};
#[cfg(has_si5324)] #[cfg(has_si5324)]
use board_artiq::si5324; use board_artiq::si5324;
#[cfg(has_wrpll)] #[cfg(has_wrpll)]
@ -18,7 +18,7 @@ use board_artiq::{spi, drtioaux};
use board_artiq::drtio_routing; use board_artiq::drtio_routing;
#[cfg(has_hmc830_7043)] #[cfg(has_hmc830_7043)]
use board_artiq::hmc830_7043; use board_artiq::hmc830_7043;
use riscv::register::{mcause, mepc}; use riscv::register::{mcause, mepc, mtval};
mod repeater; mod repeater;
#[cfg(has_jdcg)] #[cfg(has_jdcg)]
@ -449,6 +449,14 @@ const SI5324_SETTINGS: si5324::FrequencySettings
#[no_mangle] #[no_mangle]
pub extern fn main() -> i32 { pub extern fn main() -> i32 {
extern {
static mut _sstack_guard: u8;
}
unsafe {
pmp::init_stack_guard(&_sstack_guard as *const u8 as usize);
}
clock::init(); clock::init();
uart_logger::ConsoleLogger::register(); uart_logger::ConsoleLogger::register();
@ -662,7 +670,8 @@ pub extern fn exception(_regs: *const u32) {
} }
hexdump(u32::try_from(pc).unwrap()); hexdump(u32::try_from(pc).unwrap());
panic!("exception {:?} at PC 0x{:x}", cause, u32::try_from(pc).unwrap()) let mtval = mtval::read();
panic!("exception {:?} at PC 0x{:x}, trap value 0x{:x}", cause, u32::try_from(pc).unwrap(), mtval)
} }
#[no_mangle] #[no_mangle]

View File

@ -58,8 +58,10 @@ SECTIONS
_ebss = .; _ebss = .;
} > main_ram } > main_ram
.stack (NOLOAD) : ALIGN(16) .stack (NOLOAD) : ALIGN(0x1000)
{ {
_sstack_guard = .;
. += 0x1000;
_estack = .; _estack = .;
. += 0x10000; . += 0x10000;
_fstack = . - 16; _fstack = . - 16;

View File

@ -418,8 +418,8 @@ def main():
magic = 0x5352544d # "SRTM", see sayma_rtm target magic = 0x5352544d # "SRTM", see sayma_rtm target
length = bin_file.tell() - 8 length = bin_file.tell() - 8
bin_file.seek(0) bin_file.seek(0)
bin_file.write(magic.to_bytes(4, byteorder="big")) bin_file.write(magic.to_bytes(4, byteorder="little"))
bin_file.write(length.to_bytes(4, byteorder="big")) bin_file.write(length.to_bytes(4, byteorder="little"))
atexit.register(lambda: os.unlink(bin_filename)) atexit.register(lambda: os.unlink(bin_filename))
return bin_filename return bin_filename

View File

@ -50,6 +50,10 @@ def get_argparser():
group.add_argument( group.add_argument(
"-r", "--repository", default="repository", "-r", "--repository", default="repository",
help="path to the repository (default: '%(default)s')") help="path to the repository (default: '%(default)s')")
group.add_argument(
"--experiment-subdir", default="",
help=("path to the experiment folder from the repository root "
"(default: '%(default)s')"))
log_args(parser) log_args(parser)
@ -104,7 +108,8 @@ def main():
repo_backend = GitBackend(args.repository) repo_backend = GitBackend(args.repository)
else: else:
repo_backend = FilesystemBackend(args.repository) repo_backend = FilesystemBackend(args.repository)
experiment_db = ExperimentDB(repo_backend, worker_handlers) experiment_db = ExperimentDB(
repo_backend, worker_handlers, args.experiment_subdir)
atexit.register(experiment_db.close) atexit.register(experiment_db.close)
scheduler = Scheduler(RIDCounter(), worker_handlers, experiment_db) scheduler = Scheduler(RIDCounter(), worker_handlers, experiment_db)

View File

@ -27,16 +27,16 @@ class Fastino(Module):
# dac data words # dac data words
dacs = [Signal(16) for i in range(32)] dacs = [Signal(16) for i in range(32)]
header = Record([ header = Record([
("cfg", 4), ("cfg", 4),
("leds", 8), ("leds", 8),
("reserved", 8), ("typ", 1),
("reserved", 7),
("addr", 4), ("addr", 4),
("enable", len(dacs)), ("enable", len(dacs)),
]) ])
body = Cat(header.raw_bits(), dacs) assert len(Cat(header.raw_bits(), dacs)) == len(self.serializer.payload)
assert len(body) == len(self.serializer.payload)
self.comb += self.serializer.payload.eq(body)
# # # # # #
@ -62,38 +62,61 @@ class Fastino(Module):
# address space is sparse. # address space is sparse.
hold = Signal.like(header.enable) hold = Signal.like(header.enable)
continuous = Signal.like(header.enable)
cic_config = Signal(16)
read_regs = Array([Signal.like(self.serializer.readback) read_regs = Array([Signal.like(self.serializer.readback)
for _ in range(1 << len(header.addr))]) for _ in range(1 << len(header.addr))])
cases = { cases = {
# update # update
0x20: header.enable.eq(header.enable | self.rtlink.o.data), 0x20: [
header.enable.eq(self.rtlink.o.data),
header.typ.eq(0),
],
# hold # hold
0x21: hold.eq(self.rtlink.o.data), 0x21: hold.eq(self.rtlink.o.data),
# cfg # cfg
0x22: header.cfg.eq(self.rtlink.o.data), 0x22: header.cfg.eq(self.rtlink.o.data),
# leds # leds
0x23: header.leds.eq(self.rtlink.o.data), 0x23: header.leds.eq(self.rtlink.o.data),
# reserved # reserved bits
0x24: header.reserved.eq(self.rtlink.o.data), 0x24: header.reserved.eq(self.rtlink.o.data),
# force continuous DAC updates
0x25: continuous.eq(self.rtlink.o.data),
# interpolator configuration stage
0x26: cic_config.eq(self.rtlink.o.data),
# interpolator update flags
0x27: [
header.enable.eq(self.rtlink.o.data),
header.typ.eq(1),
],
} }
for i in range(0, len(dacs), width): for i in range(0, len(dacs), width):
cases[i] = [ cases[i] = [
Cat(dacs[i:i + width]).eq(self.rtlink.o.data), Cat(dacs[i:i + width]).eq(self.rtlink.o.data),
[If(~hold[i + j], [If(~hold[i + j] & (header.typ == 0),
header.enable[i + j].eq(1), header.enable[i + j].eq(1),
) for j in range(width)] ) for j in range(width)]
] ]
self.comb += [
If(header.typ == 0,
self.serializer.payload.eq(Cat(header.raw_bits(), dacs)),
).Else(
self.serializer.payload.eq(Cat(header.raw_bits(), Replicate(cic_config, len(dacs)))),
),
]
self.sync.rio_phy += [ self.sync.rio_phy += [
If(self.serializer.stb, If(self.serializer.stb,
header.enable.eq(0), header.typ.eq(0),
header.enable.eq(continuous),
read_regs[header.addr].eq(self.serializer.readback), read_regs[header.addr].eq(self.serializer.readback),
header.addr.eq(header.addr + 1), header.addr.eq(header.addr + 1),
), ),
If(self.rtlink.o.stb & ~self.rtlink.o.address[-1], If(self.rtlink.o.stb,
Case(self.rtlink.o.address[:-1], cases), Case(self.rtlink.o.address, cases),
), ),
] ]

View File

@ -240,7 +240,7 @@ class SatmanSoCBuilder(Builder):
"satman.bin") "satman.bin")
with open(satman, "rb") as boot_file: with open(satman, "rb") as boot_file:
boot_data = [] boot_data = []
unpack_endian = ">I" unpack_endian = "<I"
while True: while True:
w = boot_file.read(4) w = boot_file.read(4)
if not w: if not w:

View File

@ -74,19 +74,20 @@ class _RepoScanner:
entry_dict.update(entries) entry_dict.update(entries)
return entry_dict return entry_dict
async def scan(self, root): async def scan(self, root, subdir=""):
self.worker = Worker(self.worker_handlers) self.worker = Worker(self.worker_handlers)
try: try:
r = await self._scan(root) r = await self._scan(root, subdir)
finally: finally:
await self.worker.close() await self.worker.close()
return r return r
class ExperimentDB: class ExperimentDB:
def __init__(self, repo_backend, worker_handlers): def __init__(self, repo_backend, worker_handlers, experiment_subdir=""):
self.repo_backend = repo_backend self.repo_backend = repo_backend
self.worker_handlers = worker_handlers self.worker_handlers = worker_handlers
self.experiment_subdir = experiment_subdir
self.cur_rev = self.repo_backend.get_head_rev() self.cur_rev = self.repo_backend.get_head_rev()
self.repo_backend.request_rev(self.cur_rev) self.repo_backend.request_rev(self.cur_rev)
@ -115,7 +116,8 @@ class ExperimentDB:
self.cur_rev = new_cur_rev self.cur_rev = new_cur_rev
self.status["cur_rev"] = new_cur_rev self.status["cur_rev"] = new_cur_rev
t1 = time.monotonic() t1 = time.monotonic()
new_explist = await _RepoScanner(self.worker_handlers).scan(wd) new_explist = await _RepoScanner(self.worker_handlers).scan(
wd, self.experiment_subdir)
logger.info("repository scan took %d seconds", time.monotonic()-t1) logger.info("repository scan took %d seconds", time.monotonic()-t1)
update_from_dict(self.explist, new_explist) update_from_dict(self.explist, new_explist)
finally: finally:

View File

@ -164,6 +164,19 @@ See :mod:`artiq.coredevice.i2c` for more details.
Clocking Clocking
++++++++ ++++++++
The KC705 supports an internal 125 MHz RTIO clock (based on its crystal oscillator) and an external clock, that can be selected using the ``rtio_clock`` configuration entry. Valid values are ``i`` and ``e``, and the default is ``i``. The selected option can be observed in the core device boot logs. The KC705 in standalone variants supports an internal 125 MHz RTIO clock (based on its crystal oscillator, or external reference for PLL for DRTIO variants) and an external clock, that can be selected using the ``rtio_clock`` configuration entry. Valid values are:
* ``int_125`` - internal crystal oscillator, 125 MHz output (default),
* ``ext0_bypass`` - external clock.
On Kasli, ``rtio_clock=i`` is the default and generates the RTIO clock using a PLL locked either to an internal crystal or to an external frequency reference (variant-dependent). ``rtio_clock=e`` bypasses that PLL and the user must supply the RTIO clock (typically 125 MHz) at the Kasli front panel SMA input. Bypassing the PLL ensures the skews between input clock, Kasli downstream clock outputs, and RTIO clock are deterministic accross reboots of the system. This is useful when phase determinism is required in situtations where the reference clock fans out to other devices before reaching Kasli. KC705 in DRTIO variants and Kasli generates the RTIO clock using a PLL locked either to an internal crystal or to an external frequency reference. Valid values are:
* ``int_125`` - internal crystal oscillator using PLL, 125 MHz output (default),
* ``int_100`` - internal crystal oscillator using PLL, 100 MHz output,
* ``int_150`` - internal crystal oscillator using PLL, 150 MHz output,
* ``ext0_synth0_10to125`` - external 10 MHz reference using PLL, 125 MHz output,
* ``ext0_synth0_100to125`` - external 100 MHz reference using PLL, 125 MHz output,
* ``ext0_synth0_125to125`` - external 125 MHz reference using PLL, 125 MHz output,
* ``ext0_bypass``, ``ext0_bypass_125``, ``ext0_bypass_100`` - external clock - with explicit aliases available.
The selected option can be observed in the core device boot logs.
Options ``rtio_clock=int_XXX`` and ``rtio_clock=ext0_synth0_XXXXX`` generate the RTIO clock using a PLL locked either to an internal crystal or to an external frequency reference (depending on exact option). ``rtio_clock=ext0_bypass`` bypasses that PLL and the user must supply the RTIO clock (typically 125 MHz) at the Kasli front panel SMA input. Bypassing the PLL ensures the skews between input clock, Kasli downstream clock outputs, and RTIO clock are deterministic accross reboots of the system. This is useful when phase determinism is required in situtations where the reference clock fans out to other devices before reaching Kasli.

View File

@ -288,7 +288,17 @@ If you are using DRTIO and the default routing table (for a star topology) is no
* Select the RTIO clock source (KC705 and Kasli) * Select the RTIO clock source (KC705 and Kasli)
The KC705 may use either an external clock signal or its internal clock. The clock is selected at power-up. For Kasli, setting the RTIO clock source to "external" would bypass the Si5324 synthesiser, requiring that an input clock be present. To select the source, use one of these commands: :: The KC705 may use either an external clock signal, or its internal clock with external frequency or internal crystal reference. The clock is selected at power-up. Setting the RTIO clock source to "ext0_bypass" would bypass the Si5324 synthesiser, requiring that an input clock be present. To select the source, use one of these commands: ::
$ artiq_coremgmt config write -s rtio_clock i # internal clock (default) $ artiq_coremgmt config write -s rtio_clock int_125 # internal 125MHz clock (default)
$ artiq_coremgmt config write -s rtio_clock e # external clock $ artiq_coremgmt config write -s rtio_clock ext0_bypass # external clock (bypass)
Other options include:
- ``ext0_synth0_10to125`` - external 10MHz reference clock used by Si5324 to synthesize a 125MHz RTIO clock,
- ``ext0_synth0_100to125`` - exteral 100MHz reference clock used by Si5324 to synthesize a 125MHz RTIO clock,
- ``ext0_synth0_125to125`` - exteral 125MHz reference clock used by Si5324 to synthesize a 125MHz RTIO clock,
- ``int_100`` - internal crystal reference is used by Si5324 to synthesize a 100MHz RTIO clock,
- ``int_150`` - internal crystal reference is used by Si5324 to synthesize a 150MHz RTIO clock.
- ``ext0_bypass_125`` and ``ext0_bypass_100`` - explicit aliases for ``ext0_bypass``.
Availability of these options depends on the board and their configuration - specific setting may or may not be supported.

View File

@ -18,11 +18,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1633621759, "lastModified": 1634758644,
"narHash": "sha256-Mw29zuYjOozICTWsc9RvjuR7hW5D0H83onQh/WoVrMs=", "narHash": "sha256-H3UW/msC6wadg28lcgZv2Ge/P7dWxesL6i37a0GOeyM=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "6755a884b24038a73dd4c8022dbb05375feef0a7", "rev": "70904d4a9927a4d6e05c72c4aaac4370e05107f3",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -44,11 +44,11 @@
"src-migen": { "src-migen": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1633615575, "lastModified": 1634182166,
"narHash": "sha256-frh+WVOkEKMpVEMhdE/GZKSj82XnSC8OF8SWNluk/Yg=", "narHash": "sha256-Iw2d8fCgwuuIevkugSqd8Iplj6N+2nR1pn+or5E38Fk=",
"owner": "m-labs", "owner": "m-labs",
"repo": "migen", "repo": "migen",
"rev": "6e3f8e565704b4293174aedfb15b3470d233f528", "rev": "7507a2bb16dd2cac63535175ce67fb30dfdae1c0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -60,11 +60,11 @@
"src-misoc": { "src-misoc": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1631240866, "lastModified": 1634799783,
"narHash": "sha256-dal999XLFvS8Ol1hZnQjx7q/UfAXkzSMhAWcZKCDPx4=", "narHash": "sha256-CbeXsLTwwYBWb5cfrVVYkcQYV207gi9+CQhzfeZbXGc=",
"ref": "master", "ref": "master",
"rev": "c9572e777febf7abcfbebf624e0323d82600f267", "rev": "855914deace34880c69589022c52a8921f431063",
"revCount": 2371, "revCount": 2375,
"submodules": true, "submodules": true,
"type": "git", "type": "git",
"url": "https://github.com/m-labs/misoc.git" "url": "https://github.com/m-labs/misoc.git"

View File

@ -42,6 +42,8 @@
xorg.libXext xorg.libXext
xorg.libXtst xorg.libXtst
xorg.libXi xorg.libXi
freetype
fontconfig
]; ];
sipyco = pkgs.python3Packages.buildPythonPackage { sipyco = pkgs.python3Packages.buildPythonPackage {
@ -125,6 +127,30 @@
propagatedBuildInputs = with pkgs.python3Packages; [ jinja2 numpy migen pyserial asyncserial ]; propagatedBuildInputs = with pkgs.python3Packages; [ jinja2 numpy migen pyserial asyncserial ];
}; };
jesd204b = pkgs.python3Packages.buildPythonPackage rec {
pname = "jesd204b";
version = "unstable-2021-05-05";
src = pkgs.fetchFromGitHub {
owner = "m-labs";
repo = "jesd204b";
rev = "bf1cd9014c8b7a9db67609f653634daaf3bcd39b";
sha256 = "sha256-wyYOCRIPANReeCl+KaIpiAStsn2mzfMlK+cSrUzVrAw=";
};
propagatedBuildInputs = with pkgs.python3Packages; [ migen misoc ];
};
microscope = pkgs.python3Packages.buildPythonPackage rec {
pname = "microscope";
version = "unstable-2020-12-28";
src = pkgs.fetchFromGitHub {
owner = "m-labs";
repo = "microscope";
rev = "c21afe7a53258f05bde57e5ebf2e2761f3d495e4";
sha256 = "sha256-jzyiLRuEf7p8LdhmZvOQj/dyQx8eUE8p6uRlwoiT8vg=";
};
propagatedBuildInputs = with pkgs.python3Packages; [ pyserial prettytable msgpack migen ];
};
cargo-xbuild = rustPlatform.buildRustPackage rec { cargo-xbuild = rustPlatform.buildRustPackage rec {
pname = "cargo-xbuild"; pname = "cargo-xbuild";
version = "0.6.5"; version = "0.6.5";
@ -147,7 +173,7 @@
vivado = pkgs.buildFHSUserEnv { vivado = pkgs.buildFHSUserEnv {
name = "vivado"; name = "vivado";
targetPkgs = vivadoDeps; targetPkgs = vivadoDeps;
profile = "source /opt/Xilinx/Vivado/2020.1/settings64.sh"; profile = "source /opt/Xilinx/Vivado/2021.1/settings64.sh";
runScript = "vivado"; runScript = "vivado";
}; };
@ -158,7 +184,7 @@
cargoDeps = rustPlatform.fetchCargoTarball { cargoDeps = rustPlatform.fetchCargoTarball {
name = "artiq-firmware-cargo-deps"; name = "artiq-firmware-cargo-deps";
src = "${self}/artiq/firmware"; src = "${self}/artiq/firmware";
sha256 = "0hh9x34gs81a8g15abka6a0z1wlankra13rbap5j7ba2r8cz4962"; sha256 = "sha256-Lf6M4M/jdRiO5MsWSoqtOSNfRIhbze+qvg4kaiiBWW4=";
}; };
nativeBuildInputs = [ nativeBuildInputs = [
(pkgs.python3.withPackages(ps: [ migen misoc artiq ])) (pkgs.python3.withPackages(ps: [ migen misoc artiq ]))
@ -180,7 +206,9 @@
export TARGET_AR=llvm-ar export TARGET_AR=llvm-ar
${buildCommand} ${buildCommand}
''; '';
checkPhase = '' doCheck = true;
checkPhase =
''
# Search for PCREs in the Vivado output to check for errors # Search for PCREs in the Vivado output to check for errors
check_log() { check_log() {
grep -Pe "$1" artiq_${target}/${variant}/gateware/vivado.log && exit 1 || true grep -Pe "$1" artiq_${target}/${variant}/gateware/vivado.log && exit 1 || true
@ -277,7 +305,7 @@
devShell.x86_64-linux = pkgs.mkShell { devShell.x86_64-linux = pkgs.mkShell {
name = "artiq-dev-shell"; name = "artiq-dev-shell";
buildInputs = [ buildInputs = [
(pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ migen misoc artiq ps.paramiko ps.jsonschema ])) (pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ migen misoc jesd204b artiq ps.paramiko ps.jsonschema microscope ]))
rustPlatform.rust.rustc rustPlatform.rust.rustc
rustPlatform.rust.cargo rustPlatform.rust.cargo
cargo-xbuild cargo-xbuild