mirror of https://github.com/m-labs/artiq.git
Compare commits
11 Commits
Author | SHA1 | Date |
---|---|---|
Sebastien Bourdeauducq | de6f44467f | |
Sebastien Bourdeauducq | 59dfb9e902 | |
Sebastien Bourdeauducq | 2e05a1bd0d | |
Sebastien Bourdeauducq | d622fb8db7 | |
Sebastien Bourdeauducq | c4a9fa78ee | |
Sebastien Bourdeauducq | 3adcf37625 | |
Sebastien Bourdeauducq | 9941ee3d2a | |
Sebastien Bourdeauducq | 542a5f934f | |
Sebastien Bourdeauducq | f1b2a7041a | |
Sebastien Bourdeauducq | 93e82e2201 | |
Sebastien Bourdeauducq | 7b72c9e915 |
|
@ -240,12 +240,6 @@ version = "0.7.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
|
@ -327,7 +321,7 @@ dependencies = [
|
|||
"io",
|
||||
"log",
|
||||
"logger_artiq",
|
||||
"managed 0.7.2",
|
||||
"managed",
|
||||
"proto_artiq",
|
||||
"riscv",
|
||||
"smoltcp",
|
||||
|
@ -371,13 +365,13 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||
|
||||
[[package]]
|
||||
name = "smoltcp"
|
||||
version = "0.8.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2308a1657c8db1f5b4993bab4e620bdbe5623bd81f254cf60326767bb243237"
|
||||
checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"managed 0.8.0",
|
||||
"managed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -16,5 +16,5 @@ build_misoc = { path = "../libbuild_misoc" }
|
|||
byteorder = { version = "1.0", default-features = false }
|
||||
crc = { version = "1.7", default-features = false }
|
||||
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp"] }
|
||||
smoltcp = { version = "0.8.0", default-features = false, features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] }
|
||||
smoltcp = { version = "0.6.0", default-features = false, features = ["ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] }
|
||||
riscv = { version = "0.6.0", features = ["inline-asm"] }
|
||||
|
|
|
@ -18,8 +18,6 @@ use board_misoc::slave_fpga;
|
|||
use board_misoc::{clock, ethmac, net_settings};
|
||||
use board_misoc::uart_console::Console;
|
||||
use riscv::register::{mcause, mepc, mtval};
|
||||
use smoltcp::iface::SocketStorage;
|
||||
use smoltcp::wire::{HardwareAddress, IpAddress, Ipv4Address};
|
||||
|
||||
fn check_integrity() -> bool {
|
||||
extern {
|
||||
|
@ -398,9 +396,6 @@ fn network_boot() {
|
|||
|
||||
println!("Initializing network...");
|
||||
|
||||
// Assuming only one socket is ever needed by the bootloader.
|
||||
// The smoltcp reuses the listening socket when the connection is established.
|
||||
let mut sockets = [SocketStorage::EMPTY];
|
||||
let mut net_device = unsafe { ethmac::EthernetDevice::new() };
|
||||
net_device.reset_phy_if_any();
|
||||
|
||||
|
@ -410,25 +405,22 @@ fn network_boot() {
|
|||
let net_addresses = net_settings::get_adresses();
|
||||
println!("Network addresses: {}", net_addresses);
|
||||
let mut ip_addrs = [
|
||||
IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0),
|
||||
IpCidr::new(net_addresses.ipv4_addr, 0),
|
||||
IpCidr::new(net_addresses.ipv6_ll_addr, 0),
|
||||
IpCidr::new(net_addresses.ipv6_ll_addr, 0)
|
||||
];
|
||||
if let net_settings::Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr {
|
||||
ip_addrs[0] = IpCidr::new(IpAddress::Ipv4(ipv4), 0);
|
||||
}
|
||||
let mut interface = match net_addresses.ipv6_addr {
|
||||
Some(addr) => {
|
||||
ip_addrs[2] = IpCidr::new(addr, 0);
|
||||
smoltcp::iface::InterfaceBuilder::new(net_device, &mut sockets[..])
|
||||
.hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr))
|
||||
smoltcp::iface::EthernetInterfaceBuilder::new(net_device)
|
||||
.ethernet_addr(net_addresses.hardware_addr)
|
||||
.ip_addrs(&mut ip_addrs[..])
|
||||
.neighbor_cache(neighbor_cache)
|
||||
.finalize()
|
||||
}
|
||||
None =>
|
||||
smoltcp::iface::InterfaceBuilder::new(net_device, &mut sockets[..])
|
||||
.hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr))
|
||||
smoltcp::iface::EthernetInterfaceBuilder::new(net_device)
|
||||
.ethernet_addr(net_addresses.hardware_addr)
|
||||
.ip_addrs(&mut ip_addrs[..2])
|
||||
.neighbor_cache(neighbor_cache)
|
||||
.finalize()
|
||||
|
@ -437,10 +429,14 @@ fn network_boot() {
|
|||
let mut rx_storage = [0; 4096];
|
||||
let mut tx_storage = [0; 128];
|
||||
|
||||
let mut socket_set_entries: [_; 1] = Default::default();
|
||||
let mut sockets =
|
||||
smoltcp::socket::SocketSet::new(&mut socket_set_entries[..]);
|
||||
|
||||
let tcp_rx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]);
|
||||
let tcp_tx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut tx_storage[..]);
|
||||
let tcp_socket = smoltcp::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
|
||||
let tcp_handle = interface.add_socket(tcp_socket);
|
||||
let tcp_handle = sockets.add(tcp_socket);
|
||||
|
||||
let mut net_conn = NetConn::new();
|
||||
let mut boot_time = None;
|
||||
|
@ -450,7 +446,7 @@ fn network_boot() {
|
|||
loop {
|
||||
let timestamp = clock::get_ms() as i64;
|
||||
{
|
||||
let socket = &mut *interface.get_socket::<smoltcp::socket::TcpSocket>(tcp_handle);
|
||||
let socket = &mut *sockets.get::<smoltcp::socket::TcpSocket>(tcp_handle);
|
||||
|
||||
match boot_time {
|
||||
None => {
|
||||
|
@ -479,7 +475,7 @@ fn network_boot() {
|
|||
}
|
||||
}
|
||||
|
||||
match interface.poll(smoltcp::time::Instant::from_millis(timestamp)) {
|
||||
match interface.poll(&mut sockets, smoltcp::time::Instant::from_millis(timestamp)) {
|
||||
Ok(_) => (),
|
||||
Err(smoltcp::Error::Unrecognized) => (),
|
||||
Err(err) => println!("Network error: {}", err)
|
||||
|
|
|
@ -22,8 +22,6 @@ pub mod rpc_queue;
|
|||
|
||||
#[cfg(has_si5324)]
|
||||
pub mod si5324;
|
||||
#[cfg(has_wrpll)]
|
||||
pub mod wrpll;
|
||||
|
||||
#[cfg(has_hmc830_7043)]
|
||||
pub mod hmc830_7043;
|
||||
|
|
|
@ -1,538 +0,0 @@
|
|||
use board_misoc::{csr, clock};
|
||||
|
||||
mod i2c {
|
||||
use board_misoc::{csr, clock};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Dcxo {
|
||||
Main,
|
||||
Helper
|
||||
}
|
||||
|
||||
fn half_period() { clock::spin_us(1) }
|
||||
const SDA_MASK: u8 = 2;
|
||||
const SCL_MASK: u8 = 1;
|
||||
|
||||
fn sda_i(dcxo: Dcxo) -> bool {
|
||||
let reg = match dcxo {
|
||||
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_in_read() },
|
||||
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_in_read() },
|
||||
};
|
||||
reg & SDA_MASK != 0
|
||||
}
|
||||
|
||||
fn sda_oe(dcxo: Dcxo, oe: bool) {
|
||||
let reg = match dcxo {
|
||||
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_read() },
|
||||
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_read() },
|
||||
};
|
||||
let reg = if oe { reg | SDA_MASK } else { reg & !SDA_MASK };
|
||||
match dcxo {
|
||||
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_write(reg) },
|
||||
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_write(reg) }
|
||||
}
|
||||
}
|
||||
|
||||
fn sda_o(dcxo: Dcxo, o: bool) {
|
||||
let reg = match dcxo {
|
||||
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_read() },
|
||||
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_read() },
|
||||
};
|
||||
let reg = if o { reg | SDA_MASK } else { reg & !SDA_MASK };
|
||||
match dcxo {
|
||||
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_write(reg) },
|
||||
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_write(reg) }
|
||||
}
|
||||
}
|
||||
|
||||
fn scl_oe(dcxo: Dcxo, oe: bool) {
|
||||
let reg = match dcxo {
|
||||
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_read() },
|
||||
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_read() },
|
||||
};
|
||||
let reg = if oe { reg | SCL_MASK } else { reg & !SCL_MASK };
|
||||
match dcxo {
|
||||
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_write(reg) },
|
||||
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_write(reg) }
|
||||
}
|
||||
}
|
||||
|
||||
fn scl_o(dcxo: Dcxo, o: bool) {
|
||||
let reg = match dcxo {
|
||||
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_read() },
|
||||
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_read() },
|
||||
};
|
||||
let reg = if o { reg | SCL_MASK } else { reg & !SCL_MASK };
|
||||
match dcxo {
|
||||
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_write(reg) },
|
||||
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_write(reg) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(dcxo: Dcxo) -> Result<(), &'static str> {
|
||||
// Set SCL as output, and high level
|
||||
scl_o(dcxo, true);
|
||||
scl_oe(dcxo, true);
|
||||
// Prepare a zero level on SDA so that sda_oe pulls it down
|
||||
sda_o(dcxo, false);
|
||||
// Release SDA
|
||||
sda_oe(dcxo, false);
|
||||
|
||||
// Check the I2C bus is ready
|
||||
half_period();
|
||||
half_period();
|
||||
if !sda_i(dcxo) {
|
||||
// Try toggling SCL a few times
|
||||
for _bit in 0..8 {
|
||||
scl_o(dcxo, false);
|
||||
half_period();
|
||||
scl_o(dcxo, true);
|
||||
half_period();
|
||||
}
|
||||
}
|
||||
|
||||
if !sda_i(dcxo) {
|
||||
return Err("SDA is stuck low and doesn't get unstuck");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start(dcxo: Dcxo) {
|
||||
// Set SCL high then SDA low
|
||||
scl_o(dcxo, true);
|
||||
half_period();
|
||||
sda_oe(dcxo, true);
|
||||
half_period();
|
||||
}
|
||||
|
||||
pub fn stop(dcxo: Dcxo) {
|
||||
// First, make sure SCL is low, so that the target releases the SDA line
|
||||
scl_o(dcxo, false);
|
||||
half_period();
|
||||
// Set SCL high then SDA high
|
||||
sda_oe(dcxo, true);
|
||||
scl_o(dcxo, true);
|
||||
half_period();
|
||||
sda_oe(dcxo, false);
|
||||
half_period();
|
||||
}
|
||||
|
||||
pub fn write(dcxo: Dcxo, data: u8) -> bool {
|
||||
// MSB first
|
||||
for bit in (0..8).rev() {
|
||||
// Set SCL low and set our bit on SDA
|
||||
scl_o(dcxo, false);
|
||||
sda_oe(dcxo, data & (1 << bit) == 0);
|
||||
half_period();
|
||||
// Set SCL high ; data is shifted on the rising edge of SCL
|
||||
scl_o(dcxo, true);
|
||||
half_period();
|
||||
}
|
||||
// Check ack
|
||||
// Set SCL low, then release SDA so that the I2C target can respond
|
||||
scl_o(dcxo, false);
|
||||
half_period();
|
||||
sda_oe(dcxo, false);
|
||||
// Set SCL high and check for ack
|
||||
scl_o(dcxo, true);
|
||||
half_period();
|
||||
// returns true if acked (I2C target pulled SDA low)
|
||||
!sda_i(dcxo)
|
||||
}
|
||||
|
||||
pub fn read(dcxo: Dcxo, ack: bool) -> u8 {
|
||||
// Set SCL low first, otherwise setting SDA as input may cause a transition
|
||||
// on SDA with SCL high which will be interpreted as START/STOP condition.
|
||||
scl_o(dcxo, false);
|
||||
half_period(); // make sure SCL has settled low
|
||||
sda_oe(dcxo, false);
|
||||
|
||||
let mut data: u8 = 0;
|
||||
|
||||
// MSB first
|
||||
for bit in (0..8).rev() {
|
||||
scl_o(dcxo, false);
|
||||
half_period();
|
||||
// Set SCL high and shift data
|
||||
scl_o(dcxo, true);
|
||||
half_period();
|
||||
if sda_i(dcxo) { data |= 1 << bit }
|
||||
}
|
||||
// Send ack
|
||||
// Set SCL low and pull SDA low when acking
|
||||
scl_o(dcxo, false);
|
||||
if ack { sda_oe(dcxo, true) }
|
||||
half_period();
|
||||
// then set SCL high
|
||||
scl_o(dcxo, true);
|
||||
half_period();
|
||||
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
mod si549 {
|
||||
use board_misoc::clock;
|
||||
use super::i2c;
|
||||
|
||||
#[cfg(any(soc_platform = "metlino", soc_platform = "sayma_amc", soc_platform = "sayma_rtm"))]
|
||||
pub const ADDRESS: u8 = 0x55;
|
||||
#[cfg(soc_platform = "kasli")]
|
||||
pub const ADDRESS: u8 = 0x67;
|
||||
|
||||
pub fn write(dcxo: i2c::Dcxo, reg: u8, val: u8) -> Result<(), &'static str> {
|
||||
i2c::start(dcxo);
|
||||
if !i2c::write(dcxo, ADDRESS << 1) {
|
||||
return Err("Si549 failed to ack write address")
|
||||
}
|
||||
if !i2c::write(dcxo, reg) {
|
||||
return Err("Si549 failed to ack register")
|
||||
}
|
||||
if !i2c::write(dcxo, val) {
|
||||
return Err("Si549 failed to ack value")
|
||||
}
|
||||
i2c::stop(dcxo);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_no_ack_value(dcxo: i2c::Dcxo, reg: u8, val: u8) -> Result<(), &'static str> {
|
||||
i2c::start(dcxo);
|
||||
if !i2c::write(dcxo, ADDRESS << 1) {
|
||||
return Err("Si549 failed to ack write address")
|
||||
}
|
||||
if !i2c::write(dcxo, reg) {
|
||||
return Err("Si549 failed to ack register")
|
||||
}
|
||||
i2c::write(dcxo, val);
|
||||
i2c::stop(dcxo);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read(dcxo: i2c::Dcxo, reg: u8) -> Result<u8, &'static str> {
|
||||
i2c::start(dcxo);
|
||||
if !i2c::write(dcxo, ADDRESS << 1) {
|
||||
return Err("Si549 failed to ack write address")
|
||||
}
|
||||
if !i2c::write(dcxo, reg) {
|
||||
return Err("Si549 failed to ack register")
|
||||
}
|
||||
i2c::stop(dcxo);
|
||||
|
||||
i2c::start(dcxo);
|
||||
if !i2c::write(dcxo, (ADDRESS << 1) | 1) {
|
||||
return Err("Si549 failed to ack read address")
|
||||
}
|
||||
let val = i2c::read(dcxo, false);
|
||||
i2c::stop(dcxo);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
pub fn program(dcxo: i2c::Dcxo, hsdiv: u16, lsdiv: u8, fbdiv: u64) -> Result<(), &'static str> {
|
||||
i2c::init(dcxo)?;
|
||||
|
||||
write(dcxo, 255, 0x00)?; // PAGE
|
||||
write_no_ack_value(dcxo, 7, 0x80)?; // RESET
|
||||
clock::spin_us(100_000); // required? not specified in datasheet.
|
||||
|
||||
write(dcxo, 255, 0x00)?; // PAGE
|
||||
write(dcxo, 69, 0x00)?; // Disable FCAL override.
|
||||
// Note: Value 0x00 from Table 5.6 is inconsistent with Table 5.7,
|
||||
// which shows bit 0 as reserved and =1.
|
||||
write(dcxo, 17, 0x00)?; // Synchronously disable output
|
||||
|
||||
// The Si549 has no ID register, so we check that it responds correctly
|
||||
// by writing values to a RAM-like register and reading them back.
|
||||
for test_value in 0..255 {
|
||||
write(dcxo, 23, test_value)?;
|
||||
let readback = read(dcxo, 23)?;
|
||||
if readback != test_value {
|
||||
return Err("Si549 detection failed");
|
||||
}
|
||||
}
|
||||
|
||||
write(dcxo, 23, hsdiv as u8)?;
|
||||
write(dcxo, 24, (hsdiv >> 8) as u8 | (lsdiv << 4))?;
|
||||
write(dcxo, 26, fbdiv as u8)?;
|
||||
write(dcxo, 27, (fbdiv >> 8) as u8)?;
|
||||
write(dcxo, 28, (fbdiv >> 16) as u8)?;
|
||||
write(dcxo, 29, (fbdiv >> 24) as u8)?;
|
||||
write(dcxo, 30, (fbdiv >> 32) as u8)?;
|
||||
write(dcxo, 31, (fbdiv >> 40) as u8)?;
|
||||
|
||||
write(dcxo, 7, 0x08)?; // Start FCAL
|
||||
write(dcxo, 17, 0x01)?; // Synchronously enable output
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Si549 digital frequency trim ("all-digital PLL" register)
|
||||
// ∆ f_out = adpll * 0.0001164e-6 (0.1164 ppb/lsb)
|
||||
// max trim range is +- 950 ppm
|
||||
pub fn set_adpll(dcxo: i2c::Dcxo, adpll: i32) -> Result<(), &'static str> {
|
||||
write(dcxo, 231, adpll as u8)?;
|
||||
write(dcxo, 232, (adpll >> 8) as u8)?;
|
||||
write(dcxo, 233, (adpll >> 16) as u8)?;
|
||||
clock::spin_us(100);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_adpll(dcxo: i2c::Dcxo) -> Result<i32, &'static str> {
|
||||
let b1 = read(dcxo, 231)? as i32;
|
||||
let b2 = read(dcxo, 232)? as i32;
|
||||
let b3 = read(dcxo, 233)? as i8 as i32;
|
||||
Ok(b3 << 16 | b2 << 8 | b1)
|
||||
}
|
||||
}
|
||||
|
||||
// to do: load from gateware config
|
||||
const DDMTD_COUNTER_N: u32 = 15;
|
||||
const DDMTD_COUNTER_M: u32 = (1 << DDMTD_COUNTER_N);
|
||||
const F_SYS: f64 = csr::CONFIG_CLOCK_FREQUENCY as f64;
|
||||
|
||||
const F_MAIN: f64 = 125.0e6;
|
||||
const F_HELPER: f64 = F_MAIN * DDMTD_COUNTER_M as f64 / (DDMTD_COUNTER_M + 1) as f64;
|
||||
const F_BEAT: f64 = F_MAIN - F_HELPER;
|
||||
const TIME_STEP: f32 = 1./F_BEAT as f32;
|
||||
|
||||
fn ddmtd_tag_to_s(mu: f32) -> f32 {
|
||||
return (mu as f32)*TIME_STEP;
|
||||
}
|
||||
|
||||
fn get_frequencies() -> (u32, u32, u32) {
|
||||
unsafe {
|
||||
csr::wrpll::frequency_counter_update_en_write(1);
|
||||
// wait for at least one full update cycle (> 2 timer periods)
|
||||
clock::spin_us(200_000);
|
||||
csr::wrpll::frequency_counter_update_en_write(0);
|
||||
let helper = csr::wrpll::frequency_counter_counter_helper_read();
|
||||
let main = csr::wrpll::frequency_counter_counter_rtio_read();
|
||||
let cdr = csr::wrpll::frequency_counter_counter_rtio_rx0_read();
|
||||
(helper, main, cdr)
|
||||
}
|
||||
}
|
||||
|
||||
fn log_frequencies() -> (u32, u32, u32) {
|
||||
let (f_helper, f_main, f_cdr) = get_frequencies();
|
||||
let conv_khz = |f| 4*(f as u64)*(csr::CONFIG_CLOCK_FREQUENCY as u64)/(1000*(1 << 23));
|
||||
info!("helper clock frequency: {}kHz ({})", conv_khz(f_helper), f_helper);
|
||||
info!("main clock frequency: {}kHz ({})", conv_khz(f_main), f_main);
|
||||
info!("CDR clock frequency: {}kHz ({})", conv_khz(f_cdr), f_cdr);
|
||||
(f_helper, f_main, f_cdr)
|
||||
}
|
||||
|
||||
fn get_tags() -> (i32, i32, u16, u16) {
|
||||
unsafe {
|
||||
csr::wrpll::tag_arm_write(1);
|
||||
while csr::wrpll::tag_arm_read() != 0 {}
|
||||
|
||||
let main_diff = csr::wrpll::main_diff_tag_read() as i32;
|
||||
let helper_diff = csr::wrpll::helper_diff_tag_read() as i32;
|
||||
let ref_tag = csr::wrpll::ref_tag_read();
|
||||
let main_tag = csr::wrpll::main_tag_read();
|
||||
(main_diff, helper_diff, ref_tag, main_tag)
|
||||
}
|
||||
}
|
||||
|
||||
fn print_tags() {
|
||||
const NUM_TAGS: usize = 30;
|
||||
let mut main_diffs = [0; NUM_TAGS]; // input to main loop filter
|
||||
let mut helper_diffs = [0; NUM_TAGS]; // input to helper loop filter
|
||||
let mut ref_tags = [0; NUM_TAGS];
|
||||
let mut main_tags = [0; NUM_TAGS];
|
||||
let mut jitter = [0 as f32; NUM_TAGS];
|
||||
|
||||
for i in 0..NUM_TAGS {
|
||||
let (main_diff, helper_diff, ref_tag, main_tag) = get_tags();
|
||||
main_diffs[i] = main_diff;
|
||||
helper_diffs[i] = helper_diff;
|
||||
ref_tags[i] = ref_tag;
|
||||
main_tags[i] = main_tag;
|
||||
}
|
||||
info!("DDMTD ref tags: {:?}", ref_tags);
|
||||
info!("DDMTD main tags: {:?}", main_tags);
|
||||
info!("DDMTD main diffs: {:?}", main_diffs);
|
||||
info!("DDMTD helper diffs: {:?}", helper_diffs);
|
||||
|
||||
// look at the difference between the main DCXO and reference...
|
||||
let t0 = main_diffs[0];
|
||||
main_diffs.iter_mut().for_each(|x| *x -= t0);
|
||||
|
||||
// crude estimate of the max difference across our sample set (assumes no unwrapping issues...)
|
||||
let delta = main_diffs[main_diffs.len()-1] as f32 / (main_diffs.len()-1) as f32;
|
||||
info!("detla: {:?} tags", delta);
|
||||
let delta_f: f32 = delta/DDMTD_COUNTER_M as f32 * F_BEAT as f32;
|
||||
info!("MAIN <-> ref frequency difference: {:?} Hz ({:?} ppm)", delta_f, delta_f/F_HELPER as f32 * 1e6);
|
||||
|
||||
jitter.iter_mut().enumerate().for_each(|(i, x)| *x = main_diffs[i] as f32 - delta*(i as f32));
|
||||
info!("jitter: {:?} tags", jitter);
|
||||
|
||||
let var = jitter.iter().map(|x| x*x).fold(0 as f32, |acc, x| acc + x as f32) / NUM_TAGS as f32;
|
||||
info!("variance: {:?} tags^2", var);
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
info!("initializing WR PLL...");
|
||||
|
||||
unsafe { csr::wrpll::helper_reset_write(1); }
|
||||
|
||||
unsafe {
|
||||
csr::wrpll::helper_dcxo_i2c_address_write(si549::ADDRESS);
|
||||
csr::wrpll::main_dcxo_i2c_address_write(si549::ADDRESS);
|
||||
}
|
||||
|
||||
#[cfg(rtio_frequency = "125.0")]
|
||||
let (h_hsdiv, h_lsdiv, h_fbdiv) = (0x05c, 0, 0x04b5badb98a);
|
||||
#[cfg(rtio_frequency = "125.0")]
|
||||
let (m_hsdiv, m_lsdiv, m_fbdiv) = (0x05c, 0, 0x04b5c447213);
|
||||
|
||||
si549::program(i2c::Dcxo::Main, m_hsdiv, m_lsdiv, m_fbdiv)
|
||||
.expect("cannot initialize main Si549");
|
||||
si549::program(i2c::Dcxo::Helper, h_hsdiv, h_lsdiv, h_fbdiv)
|
||||
.expect("cannot initialize helper Si549");
|
||||
// Si549 Settling Time for Large Frequency Change.
|
||||
// Datasheet said 10ms but it lied.
|
||||
clock::spin_us(50_000);
|
||||
|
||||
unsafe { csr::wrpll::helper_reset_write(0); }
|
||||
clock::spin_us(1);
|
||||
}
|
||||
|
||||
pub fn diagnostics() {
|
||||
info!("WRPLL diagnostics...");
|
||||
info!("Untrimmed oscillator frequencies:");
|
||||
log_frequencies();
|
||||
|
||||
info!("Increase helper DCXO frequency by +10ppm (1.25kHz):");
|
||||
si549::set_adpll(i2c::Dcxo::Helper, 85911).expect("ADPLL write failed");
|
||||
// to do: add check on frequency?
|
||||
log_frequencies();
|
||||
}
|
||||
|
||||
fn trim_dcxos(f_helper: u32, f_main: u32, f_cdr: u32) -> Result<(i32, i32), &'static str> {
|
||||
info!("Trimming oscillator frequencies...");
|
||||
const DCXO_STEP: i64 = (1.0e6/0.0001164) as i64;
|
||||
const ADPLL_MAX: i64 = (950.0/0.0001164) as i64;
|
||||
|
||||
const TIMER_WIDTH: u32 = 23;
|
||||
const COUNTER_DIV: u32 = 2;
|
||||
|
||||
// how many counts we expect to measure
|
||||
const SYS_COUNTS: i64 = (1 << (TIMER_WIDTH - COUNTER_DIV)) as i64;
|
||||
const EXP_MAIN_COUNTS: i64 = ((SYS_COUNTS as f64) * (F_MAIN/F_SYS)) as i64;
|
||||
const EXP_HELPER_COUNTS: i64 = ((SYS_COUNTS as f64) * (F_HELPER/F_SYS)) as i64;
|
||||
|
||||
// calibrate the SYS clock to the CDR clock and correct the measured counts
|
||||
// assume frequency errors are small so we can make an additive correction
|
||||
// positive error means sys clock is too fast
|
||||
let sys_err: i64 = EXP_MAIN_COUNTS - (f_cdr as i64);
|
||||
let main_err: i64 = EXP_MAIN_COUNTS - (f_main as i64) - sys_err;
|
||||
let helper_err: i64 = EXP_HELPER_COUNTS - (f_helper as i64) - sys_err;
|
||||
|
||||
info!("sys count err {}", sys_err);
|
||||
info!("main counts err {}", main_err);
|
||||
info!("helper counts err {}", helper_err);
|
||||
|
||||
// calculate required adjustment to the ADPLL register see
|
||||
// https://www.silabs.com/documents/public/data-sheets/si549-datasheet.pdf
|
||||
// section 5.6
|
||||
let helper_adpll: i64 = helper_err*DCXO_STEP/EXP_HELPER_COUNTS;
|
||||
let main_adpll: i64 = main_err*DCXO_STEP/EXP_MAIN_COUNTS;
|
||||
if helper_adpll.abs() > ADPLL_MAX {
|
||||
return Err("helper DCXO offset too large");
|
||||
}
|
||||
if main_adpll.abs() > ADPLL_MAX {
|
||||
return Err("main DCXO offset too large");
|
||||
}
|
||||
|
||||
info!("ADPLL offsets: helper={} main={}", helper_adpll, main_adpll);
|
||||
Ok((helper_adpll as i32, main_adpll as i32))
|
||||
}
|
||||
|
||||
fn statistics(data: &[u16]) -> (f32, f32) {
|
||||
let sum = data.iter().fold(0 as u32, |acc, x| acc + *x as u32);
|
||||
let mean = sum as f32 / data.len() as f32;
|
||||
|
||||
let squared_sum = data.iter().fold(0 as u32, |acc, x| acc + (*x as u32).pow(2));
|
||||
let variance = (squared_sum as f32 / data.len() as f32) - mean;
|
||||
return (mean, variance)
|
||||
}
|
||||
|
||||
fn select_recovered_clock_int(rc: bool) -> Result<(), &'static str> {
|
||||
info!("Untrimmed oscillator frequencies:");
|
||||
let (f_helper, f_main, f_cdr) = log_frequencies();
|
||||
if rc {
|
||||
let (helper_adpll, main_adpll) = trim_dcxos(f_helper, f_main, f_cdr)?;
|
||||
// to do: add assertion on max frequency shift here?
|
||||
si549::set_adpll(i2c::Dcxo::Helper, helper_adpll).expect("ADPLL write failed");
|
||||
si549::set_adpll(i2c::Dcxo::Main, main_adpll).expect("ADPLL write failed");
|
||||
|
||||
log_frequencies();
|
||||
clock::spin_us(100_000); // TO DO: remove/reduce!
|
||||
print_tags();
|
||||
|
||||
info!("increasing main DCXO by 1ppm (125Hz):");
|
||||
si549::set_adpll(i2c::Dcxo::Main, main_adpll + 8591).expect("ADPLL write failed");
|
||||
clock::spin_us(100_000);
|
||||
print_tags();
|
||||
|
||||
si549::set_adpll(i2c::Dcxo::Main, main_adpll).expect("ADPLL write failed");
|
||||
|
||||
unsafe {
|
||||
csr::wrpll::adpll_offset_helper_write(helper_adpll as u32);
|
||||
csr::wrpll::adpll_offset_main_write(main_adpll as u32);
|
||||
csr::wrpll::helper_dcxo_gpio_enable_write(0);
|
||||
csr::wrpll::main_dcxo_gpio_enable_write(0);
|
||||
csr::wrpll::helper_dcxo_errors_write(0xff);
|
||||
csr::wrpll::main_dcxo_errors_write(0xff);
|
||||
csr::wrpll::collector_reset_write(0);
|
||||
}
|
||||
clock::spin_us(1_000); // wait for the collector to produce meaningful output
|
||||
unsafe {
|
||||
csr::wrpll::filter_reset_write(0);
|
||||
}
|
||||
|
||||
clock::spin_us(100_000);
|
||||
|
||||
print_tags();
|
||||
// let mut tags = [0; 10];
|
||||
// for i in 0..tags.len() {
|
||||
// tags[i] = get_ddmtd_helper_tag();
|
||||
// }
|
||||
// info!("DDMTD helper tags: {:?}", tags);
|
||||
|
||||
unsafe {
|
||||
csr::wrpll::filter_reset_write(1);
|
||||
csr::wrpll::collector_reset_write(1);
|
||||
}
|
||||
clock::spin_us(50_000);
|
||||
unsafe {
|
||||
csr::wrpll::helper_dcxo_gpio_enable_write(1);
|
||||
csr::wrpll::main_dcxo_gpio_enable_write(1);
|
||||
}
|
||||
unsafe {
|
||||
info!("error {} {}",
|
||||
csr::wrpll::helper_dcxo_errors_read(),
|
||||
csr::wrpll::main_dcxo_errors_read());
|
||||
}
|
||||
info!("new ADPLL: {} {}",
|
||||
si549::get_adpll(i2c::Dcxo::Helper)?,
|
||||
si549::get_adpll(i2c::Dcxo::Main)?);
|
||||
} else {
|
||||
si549::set_adpll(i2c::Dcxo::Helper, 0).expect("ADPLL write failed");
|
||||
si549::set_adpll(i2c::Dcxo::Main, 0).expect("ADPLL write failed");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_recovered_clock(rc: bool) {
|
||||
if rc {
|
||||
info!("switching to recovered clock");
|
||||
} else {
|
||||
info!("switching to local XO clock");
|
||||
}
|
||||
match select_recovered_clock_int(rc) {
|
||||
Ok(()) => info!("clock transition completed"),
|
||||
Err(e) => error!("clock transition failed: {}", e)
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ build_misoc = { path = "../libbuild_misoc" }
|
|||
[dependencies]
|
||||
byteorder = { version = "1.0", default-features = false }
|
||||
log = { version = "0.4", default-features = false, optional = true }
|
||||
smoltcp = { version = "0.8.0", default-features = false, optional = true }
|
||||
smoltcp = { version = "0.6.0", default-features = false, optional = true }
|
||||
riscv = { version = "0.6.0", features = ["inline-asm"] }
|
||||
|
||||
[features]
|
||||
|
|
|
@ -1,43 +1,15 @@
|
|||
use core::fmt;
|
||||
use core::fmt::{Display, Formatter};
|
||||
use core::str::FromStr;
|
||||
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, Ipv4Address};
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress};
|
||||
|
||||
use config;
|
||||
#[cfg(soc_platform = "kasli")]
|
||||
use i2c_eeprom;
|
||||
|
||||
pub enum Ipv4AddrConfig {
|
||||
UseDhcp,
|
||||
Static(Ipv4Address),
|
||||
}
|
||||
|
||||
impl FromStr for Ipv4AddrConfig {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(if s == "use_dhcp" {
|
||||
Ipv4AddrConfig::UseDhcp
|
||||
} else {
|
||||
Ipv4AddrConfig::Static(Ipv4Address::from_str(s)?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Ipv4AddrConfig {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Ipv4AddrConfig::UseDhcp => write!(f, "use_dhcp"),
|
||||
Ipv4AddrConfig::Static(ipv4) => write!(f, "{}", ipv4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct NetAddresses {
|
||||
pub hardware_addr: EthernetAddress,
|
||||
pub ipv4_addr: Ipv4AddrConfig,
|
||||
pub ipv4_addr: IpAddress,
|
||||
pub ipv6_ll_addr: IpAddress,
|
||||
pub ipv6_addr: Option<IpAddress>
|
||||
}
|
||||
|
@ -79,7 +51,16 @@ pub fn get_adresses() -> NetAddresses {
|
|||
let ipv4_addr;
|
||||
match config::read_str("ip", |r| r.map(|s| s.parse())) {
|
||||
Ok(Ok(addr)) => ipv4_addr = addr,
|
||||
_ => ipv4_addr = Ipv4AddrConfig::UseDhcp,
|
||||
_ => {
|
||||
#[cfg(soc_platform = "kasli")]
|
||||
{ ipv4_addr = IpAddress::v4(192, 168, 1, 70); }
|
||||
#[cfg(soc_platform = "sayma_amc")]
|
||||
{ ipv4_addr = IpAddress::v4(192, 168, 1, 60); }
|
||||
#[cfg(soc_platform = "metlino")]
|
||||
{ ipv4_addr = IpAddress::v4(192, 168, 1, 65); }
|
||||
#[cfg(soc_platform = "kc705")]
|
||||
{ ipv4_addr = IpAddress::v4(192, 168, 1, 50); }
|
||||
}
|
||||
}
|
||||
|
||||
let ipv6_ll_addr = IpAddress::v6(
|
||||
|
|
|
@ -27,13 +27,9 @@ board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp
|
|||
logger_artiq = { path = "../liblogger_artiq" }
|
||||
board_artiq = { path = "../libboard_artiq" }
|
||||
proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] }
|
||||
smoltcp = { version = "0.6.0", default-features = false, features = ["alloc", "ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] }
|
||||
riscv = { version = "0.6.0", features = ["inline-asm"] }
|
||||
|
||||
[dependencies.smoltcp]
|
||||
version = "0.8.0"
|
||||
default-features = false
|
||||
features = ["alloc", "medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp", "socket-dhcpv4"]
|
||||
|
||||
[dependencies.fringe]
|
||||
git = "https://git.m-labs.hk/M-Labs/libfringe.git"
|
||||
rev = "3ecbe5"
|
||||
|
|
|
@ -79,7 +79,5 @@ pub fn thread(io: Io) {
|
|||
Ok(()) => (),
|
||||
Err(err) => error!("analyzer aborted: {}", err)
|
||||
}
|
||||
|
||||
stream.close().expect("analyzer: close socket")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
use board_misoc::clock;
|
||||
use sched;
|
||||
use sched::Dhcpv4Socket;
|
||||
use smoltcp::socket::Dhcpv4Event;
|
||||
use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
|
||||
|
||||
pub fn dhcp_thread(io: sched::Io) {
|
||||
let mut socket = Dhcpv4Socket::new(&io);
|
||||
let mut last_ip: Option<Ipv4Cidr> = None;
|
||||
let mut done_reset = false;
|
||||
let start_time = clock::get_ms();
|
||||
|
||||
loop {
|
||||
// A significant amount of the time our first discover isn't received
|
||||
// by the server. This is likely to be because the ethernet device isn't quite
|
||||
// ready at the point that it is sent. The following makes recovery from
|
||||
// that faster.
|
||||
if !done_reset && last_ip.is_none() && start_time + 1000 < clock::get_ms() {
|
||||
info!("Didn't get initial IP in first second. Assuming packet loss, trying a reset.");
|
||||
socket.reset();
|
||||
done_reset = true;
|
||||
}
|
||||
if let Some(event) = socket.poll() {
|
||||
match event {
|
||||
Dhcpv4Event::Configured(config) => {
|
||||
// Only compare the ip address in the config with previous config because we
|
||||
// ignore the rest of the config i.e. we don't do any DNS or require a default
|
||||
// gateway.
|
||||
let changed = if let Some(last_ip) = last_ip {
|
||||
if config.address != last_ip {
|
||||
info!("IP address changed {} -> {}", last_ip, config.address);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
info!("Acquired IP address: None -> {}", config.address);
|
||||
true
|
||||
};
|
||||
if changed {
|
||||
last_ip = Some(config.address);
|
||||
io.set_ipv4_address(&config.address);
|
||||
}
|
||||
}
|
||||
Dhcpv4Event::Deconfigured => {
|
||||
if let Some(ip) = last_ip {
|
||||
info!("Lost IP address {} -> None", ip);
|
||||
io.set_ipv4_address(&Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
|
||||
last_ip = None;
|
||||
}
|
||||
// We always get one of these events at the start, ignore that one
|
||||
}
|
||||
}
|
||||
}
|
||||
// We want to poll after every poll of the interface. So we need to
|
||||
// do a minimal yield here.
|
||||
io.relinquish().unwrap();
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
use smoltcp::iface::{Interface, InterfaceBuilder};
|
||||
use smoltcp::phy::Device;
|
||||
use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr};
|
||||
use board_misoc::net_settings::{Ipv4AddrConfig, NetAddresses};
|
||||
|
||||
|
||||
const IPV4_INDEX: usize = 0;
|
||||
const IPV6_LL_INDEX: usize = 1;
|
||||
const IPV6_INDEX: usize = 2;
|
||||
const IP_ADDRESS_STORAGE_SIZE: usize = 3;
|
||||
|
||||
pub trait InterfaceBuilderEx {
|
||||
fn init_ip_addrs(self, net_addresses: &NetAddresses) -> Self;
|
||||
}
|
||||
|
||||
impl<'a, DeviceT: for<'d> Device<'d>> InterfaceBuilderEx for InterfaceBuilder<'a, DeviceT> {
|
||||
fn init_ip_addrs(self, net_addresses: &NetAddresses) -> Self {
|
||||
let mut storage = [
|
||||
IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); IP_ADDRESS_STORAGE_SIZE
|
||||
];
|
||||
if let Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr {
|
||||
storage[IPV4_INDEX] = IpCidr::new(IpAddress::Ipv4(ipv4), 0);
|
||||
}
|
||||
storage[IPV6_LL_INDEX] = IpCidr::new(net_addresses.ipv6_ll_addr, 0);
|
||||
if let Some(ipv6) = net_addresses.ipv6_addr {
|
||||
storage[IPV6_INDEX] = IpCidr::new(ipv6, 0);
|
||||
}
|
||||
self.ip_addrs(storage)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InterfaceEx {
|
||||
fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr);
|
||||
}
|
||||
|
||||
impl<'a, DeviceT: for<'d> Device<'d>> InterfaceEx for Interface<'a, DeviceT> {
|
||||
fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr) {
|
||||
self.update_ip_addrs(|storage| storage[IPV4_INDEX] = IpCidr::Ipv4(*addr))
|
||||
}
|
||||
}
|
|
@ -27,12 +27,11 @@ extern crate riscv;
|
|||
|
||||
use core::cell::RefCell;
|
||||
use core::convert::TryFrom;
|
||||
use smoltcp::wire::HardwareAddress;
|
||||
use smoltcp::wire::IpCidr;
|
||||
|
||||
use board_misoc::{csr, ident, clock, spiflash, config, net_settings, pmp, boot};
|
||||
#[cfg(has_ethmac)]
|
||||
use board_misoc::ethmac;
|
||||
use board_misoc::net_settings::{Ipv4AddrConfig};
|
||||
#[cfg(has_drtio)]
|
||||
use board_artiq::drtioaux;
|
||||
use board_artiq::drtio_routing;
|
||||
|
@ -42,7 +41,6 @@ use proto_artiq::{mgmt_proto, moninj_proto, rpc_proto, session_proto, kernel_pro
|
|||
use proto_artiq::analyzer_proto;
|
||||
|
||||
use riscv::register::{mcause, mepc, mtval};
|
||||
use ip_addr_storage::InterfaceBuilderEx;
|
||||
|
||||
mod rtio_clocking;
|
||||
mod rtio_mgt;
|
||||
|
@ -60,8 +58,6 @@ mod session;
|
|||
mod moninj;
|
||||
#[cfg(has_rtio_analyzer)]
|
||||
mod analyzer;
|
||||
mod dhcp;
|
||||
mod ip_addr_storage;
|
||||
|
||||
#[cfg(has_grabber)]
|
||||
fn grabber_thread(io: sched::Io) {
|
||||
|
@ -127,35 +123,54 @@ fn startup() {
|
|||
net_device.reset_phy_if_any();
|
||||
|
||||
let net_device = {
|
||||
use smoltcp::phy::Tracer;
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::PrettyPrinter;
|
||||
use smoltcp::wire::EthernetFrame;
|
||||
|
||||
// We can't create the function pointer as a separate variable here because the type of
|
||||
// the packet argument Packet isn't accessible and rust's type inference isn't sufficient
|
||||
// to propagate in to a local var.
|
||||
match config::read_str("net_trace", |r| r.map(|s| s == "1")) {
|
||||
Ok(true) => Tracer::new(net_device, |timestamp, packet| {
|
||||
print!("\x1b[37m[{:6}.{:03}s]\n{}\x1b[0m\n",
|
||||
timestamp.secs(), timestamp.millis(), packet)
|
||||
}),
|
||||
_ => Tracer::new(net_device, |_, _| {}),
|
||||
fn net_trace_writer(timestamp: Instant, printer: PrettyPrinter<EthernetFrame<&[u8]>>) {
|
||||
print!("\x1b[37m[{:6}.{:03}s]\n{}\x1b[0m\n",
|
||||
timestamp.secs(), timestamp.millis(), printer)
|
||||
}
|
||||
|
||||
fn net_trace_silent(_timestamp: Instant, _printer: PrettyPrinter<EthernetFrame<&[u8]>>) {}
|
||||
|
||||
let net_trace_fn: fn(Instant, PrettyPrinter<EthernetFrame<&[u8]>>);
|
||||
match config::read_str("net_trace", |r| r.map(|s| s == "1")) {
|
||||
Ok(true) => net_trace_fn = net_trace_writer,
|
||||
_ => net_trace_fn = net_trace_silent
|
||||
}
|
||||
smoltcp::phy::EthernetTracer::new(net_device, net_trace_fn)
|
||||
};
|
||||
|
||||
let neighbor_cache =
|
||||
smoltcp::iface::NeighborCache::new(alloc::collections::btree_map::BTreeMap::new());
|
||||
let net_addresses = net_settings::get_adresses();
|
||||
info!("network addresses: {}", net_addresses);
|
||||
let use_dhcp = if matches!(net_addresses.ipv4_addr, Ipv4AddrConfig::UseDhcp) {
|
||||
info!("Will try to acquire an IPv4 address with DHCP");
|
||||
true
|
||||
} else {
|
||||
false
|
||||
let mut interface = match net_addresses.ipv6_addr {
|
||||
Some(addr) => {
|
||||
let ip_addrs = [
|
||||
IpCidr::new(net_addresses.ipv4_addr, 0),
|
||||
IpCidr::new(net_addresses.ipv6_ll_addr, 0),
|
||||
IpCidr::new(addr, 0)
|
||||
];
|
||||
smoltcp::iface::EthernetInterfaceBuilder::new(net_device)
|
||||
.ethernet_addr(net_addresses.hardware_addr)
|
||||
.ip_addrs(ip_addrs)
|
||||
.neighbor_cache(neighbor_cache)
|
||||
.finalize()
|
||||
}
|
||||
None => {
|
||||
let ip_addrs = [
|
||||
IpCidr::new(net_addresses.ipv4_addr, 0),
|
||||
IpCidr::new(net_addresses.ipv6_ll_addr, 0)
|
||||
];
|
||||
smoltcp::iface::EthernetInterfaceBuilder::new(net_device)
|
||||
.ethernet_addr(net_addresses.hardware_addr)
|
||||
.ip_addrs(ip_addrs)
|
||||
.neighbor_cache(neighbor_cache)
|
||||
.finalize()
|
||||
}
|
||||
};
|
||||
let interface = smoltcp::iface::InterfaceBuilder::new(net_device, vec![])
|
||||
.hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr))
|
||||
.init_ip_addrs(&net_addresses)
|
||||
.neighbor_cache(neighbor_cache)
|
||||
.finalize();
|
||||
|
||||
#[cfg(has_drtio)]
|
||||
let drtio_routing_table = urc::Urc::new(RefCell::new(
|
||||
|
@ -169,13 +184,9 @@ fn startup() {
|
|||
drtio_routing::interconnect_disable_all();
|
||||
let aux_mutex = sched::Mutex::new();
|
||||
|
||||
let mut scheduler = sched::Scheduler::new(interface);
|
||||
let mut scheduler = sched::Scheduler::new();
|
||||
let io = scheduler.io();
|
||||
|
||||
if use_dhcp {
|
||||
io.spawn(4096, dhcp::dhcp_thread);
|
||||
}
|
||||
|
||||
rtio_mgt::startup(&io, &aux_mutex, &drtio_routing_table, &up_destinations);
|
||||
|
||||
io.spawn(4096, mgmt::thread);
|
||||
|
@ -200,7 +211,19 @@ fn startup() {
|
|||
let mut net_stats = ethmac::EthernetStatistics::new();
|
||||
loop {
|
||||
scheduler.run();
|
||||
scheduler.run_network();
|
||||
|
||||
{
|
||||
let sockets = &mut *scheduler.sockets().borrow_mut();
|
||||
loop {
|
||||
let timestamp = smoltcp::time::Instant::from_millis(clock::get_ms() as i64);
|
||||
match interface.poll(sockets, timestamp) {
|
||||
Ok(true) => (),
|
||||
Ok(false) => break,
|
||||
Err(smoltcp::Error::Unrecognized) => (),
|
||||
Err(err) => debug!("network error: {}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(_net_stats_diff) = net_stats.update() {
|
||||
debug!("ethernet mac:{}", ethmac::EthernetStatistics::new());
|
||||
|
|
|
@ -131,7 +131,6 @@ pub fn thread(io: Io) {
|
|||
Err(Error::Io(IoError::UnexpectedEnd)) => (),
|
||||
Err(err) => error!("aborted: {}", err)
|
||||
}
|
||||
stream.close().expect("mgmt: close socket");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,7 +214,6 @@ pub fn thread(io: Io, aux_mutex: &Mutex, routing_table: &Urc<RefCell<drtio_routi
|
|||
Ok(()) => {},
|
||||
Err(err) => error!("moninj aborted: {}", err)
|
||||
}
|
||||
stream.close().expect("moninj: close socket");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,21 +2,18 @@
|
|||
|
||||
use core::mem;
|
||||
use core::result;
|
||||
use core::cell::{Cell, RefCell, RefMut};
|
||||
use core::cell::{Cell, RefCell};
|
||||
use alloc::vec::Vec;
|
||||
use fringe::OwnedStack;
|
||||
use fringe::generator::{Generator, Yielder, State as GeneratorState};
|
||||
use smoltcp::time::Duration;
|
||||
use smoltcp::Error as NetworkError;
|
||||
use smoltcp::wire::{IpEndpoint, Ipv4Cidr};
|
||||
use smoltcp::iface::{Interface, SocketHandle};
|
||||
use smoltcp::wire::IpEndpoint;
|
||||
use smoltcp::socket::{SocketHandle, SocketRef};
|
||||
|
||||
use io::{Read, Write};
|
||||
use board_misoc::clock;
|
||||
use urc::Urc;
|
||||
use board_misoc::ethmac::EthernetDevice;
|
||||
use smoltcp::phy::Tracer;
|
||||
use ip_addr_storage::InterfaceEx;
|
||||
|
||||
#[derive(Fail, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -34,6 +31,8 @@ impl From<NetworkError> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
type SocketSet = ::smoltcp::socket::SocketSet<'static, 'static, 'static>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WaitRequest {
|
||||
event: Option<*mut dyn FnMut() -> bool>,
|
||||
|
@ -60,7 +59,7 @@ impl Thread {
|
|||
unsafe fn new<F>(io: &Io, stack_size: usize, f: F) -> ThreadHandle
|
||||
where F: 'static + FnOnce(Io) + Send {
|
||||
let spawned = io.spawned.clone();
|
||||
let network = io.network.clone();
|
||||
let sockets = io.sockets.clone();
|
||||
|
||||
// Add a 4k stack guard to the stack of any new threads
|
||||
let stack = OwnedStack::new(stack_size + 4096);
|
||||
|
@ -68,8 +67,8 @@ impl Thread {
|
|||
generator: Generator::unsafe_new(stack, |yielder, _| {
|
||||
f(Io {
|
||||
yielder: Some(yielder),
|
||||
spawned,
|
||||
network
|
||||
spawned: spawned,
|
||||
sockets: sockets
|
||||
})
|
||||
}),
|
||||
waiting_for: WaitRequest {
|
||||
|
@ -116,21 +115,19 @@ impl ThreadHandle {
|
|||
}
|
||||
}
|
||||
|
||||
type Network = Interface<'static, Tracer<EthernetDevice>>;
|
||||
|
||||
pub struct Scheduler {
|
||||
threads: Vec<ThreadHandle>,
|
||||
spawned: Urc<RefCell<Vec<ThreadHandle>>>,
|
||||
network: Urc<RefCell<Network>>,
|
||||
sockets: Urc<RefCell<SocketSet>>,
|
||||
run_idx: usize,
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
pub fn new(network: Network) -> Scheduler {
|
||||
pub fn new() -> Scheduler {
|
||||
Scheduler {
|
||||
threads: Vec::new(),
|
||||
spawned: Urc::new(RefCell::new(Vec::new())),
|
||||
network: Urc::new(RefCell::new(network)),
|
||||
sockets: Urc::new(RefCell::new(SocketSet::new(Vec::new()))),
|
||||
run_idx: 0,
|
||||
}
|
||||
}
|
||||
|
@ -139,11 +136,13 @@ impl Scheduler {
|
|||
Io {
|
||||
yielder: None,
|
||||
spawned: self.spawned.clone(),
|
||||
network: self.network.clone()
|
||||
sockets: self.sockets.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
self.sockets.borrow_mut().prune();
|
||||
|
||||
self.threads.append(&mut *self.spawned.borrow_mut());
|
||||
if self.threads.len() == 0 { return }
|
||||
|
||||
|
@ -189,17 +188,8 @@ impl Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run_network(&mut self) {
|
||||
let mut interface = self.network.borrow_mut();
|
||||
loop {
|
||||
let timestamp = smoltcp::time::Instant::from_millis(clock::get_ms() as i64);
|
||||
match interface.poll(timestamp) {
|
||||
Ok(true) => (),
|
||||
Ok(false) => break,
|
||||
Err(smoltcp::Error::Unrecognized) => (),
|
||||
Err(err) => debug!("network error: {}", err)
|
||||
}
|
||||
}
|
||||
pub fn sockets(&self) -> &RefCell<SocketSet> {
|
||||
&*self.sockets
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,7 +197,7 @@ impl Scheduler {
|
|||
pub struct Io<'a> {
|
||||
yielder: Option<&'a Yielder<WaitResult, WaitRequest>>,
|
||||
spawned: Urc<RefCell<Vec<ThreadHandle>>>,
|
||||
network: Urc<RefCell<Network>>,
|
||||
sockets: Urc<RefCell<SocketSet>>,
|
||||
}
|
||||
|
||||
impl<'a> Io<'a> {
|
||||
|
@ -274,10 +264,6 @@ impl<'a> Io<'a> {
|
|||
pub fn join(&self, handle: ThreadHandle) -> Result<(), Error> {
|
||||
self.until(move || handle.terminated())
|
||||
}
|
||||
|
||||
pub fn set_ipv4_address(&self, addr: &Ipv4Cidr) {
|
||||
self.network.borrow_mut().update_ipv4_addr(addr)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -305,10 +291,10 @@ impl<'a> Drop for MutexGuard<'a> {
|
|||
|
||||
macro_rules! until {
|
||||
($socket:expr, $ty:ty, |$var:ident| $cond:expr) => ({
|
||||
let (network, handle) = ($socket.io.network.clone(), $socket.handle);
|
||||
let (sockets, handle) = ($socket.io.sockets.clone(), $socket.handle);
|
||||
$socket.io.until(move || {
|
||||
let mut network = network.borrow_mut();
|
||||
let $var = network.get_socket::<$ty>(handle);
|
||||
let mut sockets = sockets.borrow_mut();
|
||||
let $var = sockets.get::<$ty>(handle);
|
||||
$cond
|
||||
})
|
||||
})
|
||||
|
@ -330,9 +316,9 @@ impl<'a> TcpListener<'a> {
|
|||
fn new_lower(io: &'a Io<'a>, buffer_size: usize) -> SocketHandle {
|
||||
let rx_buffer = vec![0; buffer_size];
|
||||
let tx_buffer = vec![0; buffer_size];
|
||||
io.network
|
||||
io.sockets
|
||||
.borrow_mut()
|
||||
.add_socket(TcpSocketLower::new(
|
||||
.add(TcpSocketLower::new(
|
||||
TcpSocketBuffer::new(rx_buffer),
|
||||
TcpSocketBuffer::new(tx_buffer)))
|
||||
}
|
||||
|
@ -347,9 +333,9 @@ impl<'a> TcpListener<'a> {
|
|||
}
|
||||
|
||||
fn with_lower<F, R>(&self, f: F) -> R
|
||||
where F: FnOnce(&mut TcpSocketLower) -> R {
|
||||
let mut network = self.io.network.borrow_mut();
|
||||
let result = f(network.get_socket(self.handle.get()));
|
||||
where F: FnOnce(SocketRef<TcpSocketLower>) -> R {
|
||||
let mut sockets = self.io.sockets.borrow_mut();
|
||||
let result = f(sockets.get(self.handle.get()));
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -367,7 +353,7 @@ impl<'a> TcpListener<'a> {
|
|||
|
||||
pub fn listen<T: Into<IpEndpoint>>(&self, endpoint: T) -> Result<(), Error> {
|
||||
let endpoint = endpoint.into();
|
||||
self.with_lower(|s| s.listen(endpoint))
|
||||
self.with_lower(|mut s| s.listen(endpoint))
|
||||
.map(|()| {
|
||||
self.endpoint.set(endpoint);
|
||||
()
|
||||
|
@ -375,18 +361,14 @@ impl<'a> TcpListener<'a> {
|
|||
.map_err(|err| err.into())
|
||||
}
|
||||
|
||||
/// Accept a TCP connection
|
||||
///
|
||||
/// When the returned TcpStream is dropped it is immediately forgotten about. In order to
|
||||
/// ensure that pending data is sent and the far end is notified, `close` must be called.
|
||||
pub fn accept(&self) -> Result<TcpStream<'a>, Error> {
|
||||
// We're waiting until at least one half of the connection becomes open.
|
||||
// This handles the case where a remote socket immediately sends a FIN--
|
||||
// that still counts as accepting even though nothing may be sent.
|
||||
let (network, handle) = (self.io.network.clone(), self.handle.get());
|
||||
let (sockets, handle) = (self.io.sockets.clone(), self.handle.get());
|
||||
self.io.until(move || {
|
||||
let mut network = network.borrow_mut();
|
||||
let socket = network.get_socket::<TcpSocketLower>(handle);
|
||||
let mut sockets = sockets.borrow_mut();
|
||||
let socket = sockets.get::<TcpSocketLower>(handle);
|
||||
socket.may_send() || socket.may_recv()
|
||||
})?;
|
||||
|
||||
|
@ -403,14 +385,14 @@ impl<'a> TcpListener<'a> {
|
|||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
self.with_lower(|s| s.close())
|
||||
self.with_lower(|mut s| s.close())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for TcpListener<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.with_lower(|s| s.close());
|
||||
self.io.network.borrow_mut().remove_socket(self.handle.get());
|
||||
self.with_lower(|mut s| s.close());
|
||||
self.io.sockets.borrow_mut().release(self.handle.get())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,9 +416,9 @@ impl<'a> TcpStream<'a> {
|
|||
}
|
||||
|
||||
fn with_lower<F, R>(&self, f: F) -> R
|
||||
where F: FnOnce(&mut TcpSocketLower) -> R {
|
||||
let mut network = self.io.network.borrow_mut();
|
||||
let result = f(network.get_socket(self.handle));
|
||||
where F: FnOnce(SocketRef<TcpSocketLower>) -> R {
|
||||
let mut sockets = self.io.sockets.borrow_mut();
|
||||
let result = f(sockets.get(self.handle));
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -473,7 +455,7 @@ impl<'a> TcpStream<'a> {
|
|||
}
|
||||
|
||||
pub fn set_timeout(&self, value: Option<u64>) {
|
||||
self.with_lower(|s| s.set_timeout(value.map(Duration::from_millis)))
|
||||
self.with_lower(|mut s| s.set_timeout(value.map(Duration::from_millis)))
|
||||
}
|
||||
|
||||
pub fn keep_alive(&self) -> Option<u64> {
|
||||
|
@ -481,11 +463,11 @@ impl<'a> TcpStream<'a> {
|
|||
}
|
||||
|
||||
pub fn set_keep_alive(&self, value: Option<u64>) {
|
||||
self.with_lower(|s| s.set_keep_alive(value.map(Duration::from_millis)))
|
||||
self.with_lower(|mut s| s.set_keep_alive(value.map(Duration::from_millis)))
|
||||
}
|
||||
|
||||
pub fn close(&self) -> Result<(), Error> {
|
||||
self.with_lower(|s| s.close());
|
||||
self.with_lower(|mut s| s.close());
|
||||
until!(self, TcpSocketLower, |s| !s.is_open())?;
|
||||
// right now the socket may be in TIME-WAIT state. if we don't give it a chance to send
|
||||
// a packet, and the user code executes a loop { s.listen(); s.read(); s.close(); }
|
||||
|
@ -499,33 +481,23 @@ impl<'a> Read for TcpStream<'a> {
|
|||
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::ReadError> {
|
||||
// Only borrow the underlying socket for the span of the next statement.
|
||||
let result = self.with_lower(|s| s.recv_slice(buf));
|
||||
let result = self.with_lower(|mut s| s.recv_slice(buf));
|
||||
match result {
|
||||
// Slow path: we need to block until buffer is non-empty.
|
||||
Ok(0) => {
|
||||
until!(self, TcpSocketLower, |s| s.can_recv() || !s.may_recv())?;
|
||||
match self.with_lower(|s| s.recv_slice(buf)) {
|
||||
match self.with_lower(|mut s| s.recv_slice(buf)) {
|
||||
Ok(length) => Ok(length),
|
||||
Err(NetworkError::Finished) |
|
||||
Err(NetworkError::Illegal) => Ok(0),
|
||||
Err(e) => {
|
||||
panic!("Unexpected error from smoltcp: {}", e);
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
// Fast path: we had data in buffer.
|
||||
Ok(length) => Ok(length),
|
||||
// We've received a fin.
|
||||
Err(NetworkError::Finished) |
|
||||
// Error path: the receive half of the socket is not open.
|
||||
Err(NetworkError::Illegal) => Ok(0),
|
||||
// No other error may be returned.
|
||||
Err(e) => {
|
||||
// This could return Err(Error::Network(e)) rather than panic,
|
||||
// but I expect that'll just cause a panic later perhaps with
|
||||
// less interesting context.
|
||||
panic!("Unexpected error from smoltcp: {}", e);
|
||||
}
|
||||
Err(_) => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -536,12 +508,12 @@ impl<'a> Write for TcpStream<'a> {
|
|||
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::WriteError> {
|
||||
// Only borrow the underlying socket for the span of the next statement.
|
||||
let result = self.with_lower(|s| s.send_slice(buf));
|
||||
let result = self.with_lower(|mut s| s.send_slice(buf));
|
||||
match result {
|
||||
// Slow path: we need to block until buffer is non-full.
|
||||
Ok(0) => {
|
||||
until!(self, TcpSocketLower, |s| s.can_send() || !s.may_send())?;
|
||||
match self.with_lower(|s| s.send_slice(buf)) {
|
||||
match self.with_lower(|mut s| s.send_slice(buf)) {
|
||||
Ok(length) => Ok(length),
|
||||
Err(NetworkError::Illegal) => Ok(0),
|
||||
_ => unreachable!()
|
||||
|
@ -568,62 +540,7 @@ impl<'a> Write for TcpStream<'a> {
|
|||
|
||||
impl<'a> Drop for TcpStream<'a> {
|
||||
fn drop(&mut self) {
|
||||
// There's no point calling the lower close here unless we also defer the removal of the
|
||||
// socket from smoltcp until it's had a chance to process the event
|
||||
let (unsent_bytes, is_open) = self.with_lower(
|
||||
|s| (s.send_queue(), s.is_open())
|
||||
);
|
||||
if is_open {
|
||||
warn!(
|
||||
"Dropping open TcpStream in state {}, with {} unsent bytes",
|
||||
self.with_lower(|s| s.state()), unsent_bytes
|
||||
)
|
||||
} else if unsent_bytes != 0 {
|
||||
debug!("Dropping socket with {} bytes unsent", unsent_bytes)
|
||||
}
|
||||
|
||||
self.io.network.borrow_mut().remove_socket(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dhcpv4Socket<'a> {
|
||||
io: &'a Io<'a>,
|
||||
handle: SocketHandle,
|
||||
}
|
||||
|
||||
impl<'a> Dhcpv4Socket<'a> {
|
||||
|
||||
fn new_lower(io: &'a Io<'a>) -> SocketHandle {
|
||||
let socket = smoltcp::socket::Dhcpv4Socket::new();
|
||||
io.network.borrow_mut().add_socket(socket)
|
||||
}
|
||||
|
||||
pub fn new(io: &'a Io<'a>) -> Self {
|
||||
Self {
|
||||
io,
|
||||
handle: Self::new_lower(io)
|
||||
}
|
||||
}
|
||||
|
||||
fn lower(&mut self) -> RefMut<smoltcp::socket::Dhcpv4Socket> {
|
||||
RefMut::map(
|
||||
self.io.network.borrow_mut(),
|
||||
|network| network.get_socket::<smoltcp::socket::Dhcpv4Socket>(self.handle),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn poll(&mut self) -> Option<smoltcp::socket::Dhcpv4Event> {
|
||||
self.lower().poll()
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.lower().reset()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for Dhcpv4Socket<'a> {
|
||||
fn drop(&mut self) {
|
||||
let mut network = self.io.network.borrow_mut();
|
||||
network.remove_socket(self.handle);
|
||||
self.with_lower(|mut s| s.close());
|
||||
self.io.sockets.borrow_mut().release(self.handle)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -650,7 +650,6 @@ pub fn thread(io: Io, aux_mutex: &Mutex,
|
|||
error!("session aborted: {}", err);
|
||||
}
|
||||
}
|
||||
stream.close().expect("session: close socket");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@ use core::convert::TryFrom;
|
|||
use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp};
|
||||
#[cfg(has_si5324)]
|
||||
use board_artiq::si5324;
|
||||
#[cfg(has_wrpll)]
|
||||
use board_artiq::wrpll;
|
||||
use board_artiq::{spi, drtioaux};
|
||||
use board_artiq::drtio_routing;
|
||||
#[cfg(has_hmc830_7043)]
|
||||
|
@ -492,17 +490,6 @@ pub extern fn main() -> i32 {
|
|||
io_expander1 = board_misoc::io_expander::IoExpander::new(1);
|
||||
io_expander0.init().expect("I2C I/O expander #0 initialization failed");
|
||||
io_expander1.init().expect("I2C I/O expander #1 initialization failed");
|
||||
#[cfg(has_wrpll)]
|
||||
{
|
||||
io_expander0.set_oe(1, 1 << 7).unwrap();
|
||||
io_expander0.set(1, 7, true);
|
||||
io_expander0.service().unwrap();
|
||||
io_expander1.set_oe(0, 1 << 7).unwrap();
|
||||
io_expander1.set_oe(1, 1 << 7).unwrap();
|
||||
io_expander1.set(0, 7, true);
|
||||
io_expander1.set(1, 7, true);
|
||||
io_expander1.service().unwrap();
|
||||
}
|
||||
|
||||
// Actively drive TX_DISABLE to false on SFP0..3
|
||||
io_expander0.set_oe(0, 1 << 1).unwrap();
|
||||
|
@ -519,8 +506,6 @@ pub extern fn main() -> i32 {
|
|||
|
||||
#[cfg(has_si5324)]
|
||||
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
|
||||
#[cfg(has_wrpll)]
|
||||
wrpll::init();
|
||||
|
||||
unsafe {
|
||||
csr::drtio_transceiver::stable_clkin_write(1);
|
||||
|
@ -530,8 +515,6 @@ pub extern fn main() -> i32 {
|
|||
unsafe {
|
||||
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
|
||||
}
|
||||
#[cfg(has_wrpll)]
|
||||
wrpll::diagnostics();
|
||||
init_rtio_crg();
|
||||
|
||||
#[cfg(has_hmc830_7043)]
|
||||
|
@ -582,8 +565,6 @@ pub extern fn main() -> i32 {
|
|||
si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks");
|
||||
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
|
||||
}
|
||||
#[cfg(has_wrpll)]
|
||||
wrpll::select_recovered_clock(true);
|
||||
|
||||
drtioaux::reset(0);
|
||||
drtiosat_reset(false);
|
||||
|
@ -632,7 +613,7 @@ pub extern fn main() -> i32 {
|
|||
if is_up && !was_up {
|
||||
/*
|
||||
* One side of the JESD204 elastic buffer is clocked by the jitter filter
|
||||
* (Si5324 or WRPLL), the other by the RTM.
|
||||
* (Si5324), the other by the RTM.
|
||||
* The elastic buffer can operate only when those two clocks are derived from
|
||||
* the same oscillator.
|
||||
* This is the case when either of those conditions is true:
|
||||
|
@ -664,8 +645,6 @@ pub extern fn main() -> i32 {
|
|||
info!("uplink is down, switching to local oscillator clock");
|
||||
#[cfg(has_si5324)]
|
||||
si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks");
|
||||
#[cfg(has_wrpll)]
|
||||
wrpll::select_recovered_clock(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
from artiq.gateware.drtio.wrpll.core import WRPLL
|
||||
from artiq.gateware.drtio.wrpll.ddmtd import DDMTDSamplerExtFF, DDMTDSamplerGTP
|
|
@ -1,156 +0,0 @@
|
|||
from migen import *
|
||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||
from migen.genlib.cdc import MultiReg, PulseSynchronizer
|
||||
from misoc.interconnect.csr import *
|
||||
|
||||
from artiq.gateware.drtio.wrpll.si549 import Si549
|
||||
from artiq.gateware.drtio.wrpll.ddmtd import DDMTD, Collector
|
||||
from artiq.gateware.drtio.wrpll import thls, filters
|
||||
|
||||
|
||||
class FrequencyCounter(Module, AutoCSR):
|
||||
def __init__(self, timer_width=23, counter_width=23, domains=["helper", "rtio", "rtio_rx0"]):
|
||||
for domain in domains:
|
||||
name = "counter_" + domain
|
||||
counter = CSRStatus(counter_width, name=name)
|
||||
setattr(self, name, counter)
|
||||
self.update_en = CSRStorage()
|
||||
|
||||
timer = Signal(timer_width)
|
||||
timer_tick = Signal()
|
||||
self.sync += Cat(timer, timer_tick).eq(timer + 1)
|
||||
|
||||
for domain in domains:
|
||||
sync_domain = getattr(self.sync, domain)
|
||||
divider = Signal(2)
|
||||
sync_domain += divider.eq(divider + 1)
|
||||
|
||||
divided = Signal()
|
||||
divided.attr.add("no_retiming")
|
||||
sync_domain += divided.eq(divider[-1])
|
||||
divided_sys = Signal()
|
||||
self.specials += MultiReg(divided, divided_sys)
|
||||
|
||||
divided_sys_r = Signal()
|
||||
divided_tick = Signal()
|
||||
self.sync += divided_sys_r.eq(divided_sys)
|
||||
self.comb += divided_tick.eq(divided_sys & ~divided_sys_r)
|
||||
|
||||
counter = Signal(counter_width)
|
||||
counter_csr = getattr(self, "counter_" + domain)
|
||||
self.sync += [
|
||||
If(timer_tick,
|
||||
If(self.update_en.storage, counter_csr.status.eq(counter)),
|
||||
counter.eq(0),
|
||||
).Else(
|
||||
If(divided_tick, counter.eq(counter + 1))
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class WRPLL(Module, AutoCSR):
|
||||
def __init__(self, helper_clk_pads, main_dcxo_i2c, helper_dxco_i2c, ddmtd_inputs, N=15):
|
||||
self.helper_reset = CSRStorage(reset=1)
|
||||
self.collector_reset = CSRStorage(reset=1)
|
||||
self.filter_reset = CSRStorage(reset=1)
|
||||
self.adpll_offset_helper = CSRStorage(24)
|
||||
self.adpll_offset_main = CSRStorage(24)
|
||||
|
||||
self.tag_arm = CSR()
|
||||
self.main_diff_tag = CSRStatus(32)
|
||||
self.helper_diff_tag = CSRStatus(32)
|
||||
self.ref_tag = CSRStatus(N)
|
||||
self.main_tag = CSRStatus(N)
|
||||
|
||||
main_diff_tag_32 = Signal((32, True))
|
||||
helper_diff_tag_32 = Signal((32, True))
|
||||
self.comb += [
|
||||
self.main_diff_tag.status.eq(main_diff_tag_32),
|
||||
self.helper_diff_tag.status.eq(helper_diff_tag_32)
|
||||
]
|
||||
|
||||
self.clock_domains.cd_helper = ClockDomain()
|
||||
self.clock_domains.cd_collector = ClockDomain()
|
||||
self.clock_domains.cd_filter = ClockDomain()
|
||||
self.helper_reset.storage.attr.add("no_retiming")
|
||||
self.filter_reset.storage.attr.add("no_retiming")
|
||||
self.specials += Instance("IBUFGDS",
|
||||
i_I=helper_clk_pads.p, i_IB=helper_clk_pads.n,
|
||||
o_O=self.cd_helper.clk)
|
||||
self.comb += [
|
||||
self.cd_collector.clk.eq(self.cd_collector.clk),
|
||||
self.cd_filter.clk.eq(self.cd_helper.clk),
|
||||
]
|
||||
self.specials += [
|
||||
AsyncResetSynchronizer(self.cd_helper, self.helper_reset.storage),
|
||||
AsyncResetSynchronizer(self.cd_collector, self.collector_reset.storage),
|
||||
AsyncResetSynchronizer(self.cd_filter, self.filter_reset.storage)
|
||||
]
|
||||
|
||||
self.submodules.helper_dcxo = Si549(helper_dxco_i2c)
|
||||
self.submodules.main_dcxo = Si549(main_dcxo_i2c)
|
||||
|
||||
# for diagnostics and PLL initialization
|
||||
self.submodules.frequency_counter = FrequencyCounter()
|
||||
|
||||
ddmtd_counter = Signal(N)
|
||||
self.sync.helper += ddmtd_counter.eq(ddmtd_counter + 1)
|
||||
self.submodules.ddmtd_ref = DDMTD(ddmtd_counter, ddmtd_inputs.rec_clk)
|
||||
self.submodules.ddmtd_main = DDMTD(ddmtd_counter, ddmtd_inputs.main_xo)
|
||||
|
||||
collector_cd = ClockDomainsRenamer("collector")
|
||||
filter_cd = ClockDomainsRenamer("filter")
|
||||
self.submodules.collector = collector_cd(Collector(N))
|
||||
self.submodules.filter_helper = filter_cd(
|
||||
thls.make(filters.helper, data_width=48))
|
||||
self.submodules.filter_main = filter_cd(
|
||||
thls.make(filters.main, data_width=48))
|
||||
|
||||
self.comb += [
|
||||
self.collector.tag_ref.eq(self.ddmtd_ref.h_tag),
|
||||
self.collector.ref_stb.eq(self.ddmtd_ref.h_tag_update),
|
||||
self.collector.tag_main.eq(self.ddmtd_main.h_tag),
|
||||
self.collector.main_stb.eq(self.ddmtd_main.h_tag_update)
|
||||
]
|
||||
|
||||
collector_stb_ps = PulseSynchronizer("helper", "sys")
|
||||
self.submodules += collector_stb_ps
|
||||
self.sync.helper += collector_stb_ps.i.eq(self.collector.out_stb)
|
||||
collector_stb_sys = Signal()
|
||||
self.sync += collector_stb_sys.eq(collector_stb_ps.o)
|
||||
|
||||
main_diff_tag_sys = Signal((N+2, True))
|
||||
helper_diff_tag_sys = Signal((N+2, True))
|
||||
ref_tag_sys = Signal(N)
|
||||
main_tag_sys = Signal(N)
|
||||
self.specials += MultiReg(self.collector.out_main, main_diff_tag_sys)
|
||||
self.specials += MultiReg(self.collector.out_helper, helper_diff_tag_sys)
|
||||
self.specials += MultiReg(self.collector.tag_ref, ref_tag_sys)
|
||||
self.specials += MultiReg(self.collector.tag_main, main_tag_sys)
|
||||
|
||||
self.sync += [
|
||||
If(self.tag_arm.re & self.tag_arm.r, self.tag_arm.w.eq(1)),
|
||||
If(collector_stb_sys,
|
||||
self.tag_arm.w.eq(0),
|
||||
If(self.tag_arm.w,
|
||||
main_diff_tag_32.eq(main_diff_tag_sys),
|
||||
helper_diff_tag_32.eq(helper_diff_tag_sys),
|
||||
self.ref_tag.status.eq(ref_tag_sys),
|
||||
self.main_tag.status.eq(main_tag_sys)
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
self.comb += [
|
||||
self.filter_helper.input.eq(self.collector.out_helper << 22),
|
||||
self.filter_helper.input_stb.eq(self.collector.out_stb),
|
||||
self.filter_main.input.eq(self.collector.out_main),
|
||||
self.filter_main.input_stb.eq(self.collector.out_stb)
|
||||
]
|
||||
|
||||
self.sync.helper += [
|
||||
self.helper_dcxo.adpll_stb.eq(self.filter_helper.output_stb),
|
||||
self.helper_dcxo.adpll.eq(self.filter_helper.output + self.adpll_offset_helper.storage),
|
||||
self.main_dcxo.adpll_stb.eq(self.filter_main.output_stb),
|
||||
self.main_dcxo.adpll.eq(self.filter_main.output + self.adpll_offset_main.storage)
|
||||
]
|
|
@ -1,221 +0,0 @@
|
|||
from migen import *
|
||||
from migen.genlib.cdc import PulseSynchronizer, MultiReg
|
||||
from migen.genlib.fsm import FSM
|
||||
from misoc.interconnect.csr import *
|
||||
|
||||
|
||||
class DDMTDSamplerExtFF(Module):
|
||||
def __init__(self, ddmtd_inputs):
|
||||
self.rec_clk = Signal()
|
||||
self.main_xo = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# TODO: s/h timing at FPGA pads
|
||||
if hasattr(ddmtd_inputs, "rec_clk"):
|
||||
rec_clk_1 = ddmtd_inputs.rec_clk
|
||||
else:
|
||||
rec_clk_1 = Signal()
|
||||
self.specials += Instance("IBUFDS",
|
||||
i_I=ddmtd_inputs.rec_clk_p, i_IB=ddmtd_inputs.rec_clk_n,
|
||||
o_O=rec_clk_1)
|
||||
if hasattr(ddmtd_inputs, "main_xo"):
|
||||
main_xo_1 = ddmtd_inputs.main_xo
|
||||
else:
|
||||
main_xo_1 = Signal()
|
||||
self.specials += Instance("IBUFDS",
|
||||
i_I=ddmtd_inputs.main_xo_p, i_IB=ddmtd_inputs.main_xo_n,
|
||||
o_O=main_xo_1)
|
||||
self.specials += [
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=rec_clk_1, o_Q=self.rec_clk,
|
||||
attr={("IOB", "TRUE")}),
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=main_xo_1, o_Q=self.main_xo,
|
||||
attr={("IOB", "TRUE")}),
|
||||
]
|
||||
|
||||
|
||||
class DDMTDSamplerGTP(Module):
|
||||
def __init__(self, gtp, main_xo_pads):
|
||||
self.rec_clk = Signal()
|
||||
self.main_xo = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
# Getting the main XO signal from IBUFDS_GTE2 is problematic because
|
||||
# the transceiver PLL craps out if an improper clock signal is applied,
|
||||
# so we are disabling the buffer until the clock is stable.
|
||||
main_xo_se = Signal()
|
||||
rec_clk_1 = Signal()
|
||||
main_xo_1 = Signal()
|
||||
self.specials += [
|
||||
Instance("IBUFDS",
|
||||
i_I=main_xo_pads.p, i_IB=main_xo_pads.n,
|
||||
o_O=main_xo_se),
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=gtp.cd_rtio_rx0.clk, o_Q=rec_clk_1,
|
||||
attr={("DONT_TOUCH", "TRUE")}),
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=rec_clk_1, o_Q=self.rec_clk,
|
||||
attr={("DONT_TOUCH", "TRUE")}),
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=main_xo_se, o_Q=main_xo_1,
|
||||
attr={("IOB", "TRUE")}),
|
||||
Instance("FD", i_C=ClockSignal("helper"),
|
||||
i_D=main_xo_1, o_Q=self.main_xo,
|
||||
attr={("DONT_TOUCH", "TRUE")}),
|
||||
]
|
||||
|
||||
|
||||
class DDMTDDeglitcherFirstEdge(Module):
|
||||
def __init__(self, input_signal, blind_period=128):
|
||||
self.detect = Signal()
|
||||
self.tag_correction = 0
|
||||
|
||||
rising = Signal()
|
||||
input_signal_r = Signal()
|
||||
self.sync.helper += [
|
||||
input_signal_r.eq(input_signal),
|
||||
rising.eq(input_signal & ~input_signal_r)
|
||||
]
|
||||
|
||||
blind_counter = Signal(max=blind_period)
|
||||
self.sync.helper += [
|
||||
If(blind_counter != 0, blind_counter.eq(blind_counter - 1)),
|
||||
If(input_signal_r, blind_counter.eq(blind_period - 1)),
|
||||
self.detect.eq(rising & (blind_counter == 0))
|
||||
]
|
||||
|
||||
|
||||
class DDMTD(Module):
|
||||
def __init__(self, counter, input_signal):
|
||||
|
||||
# in helper clock domain
|
||||
self.h_tag = Signal(len(counter))
|
||||
self.h_tag_update = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
deglitcher = DDMTDDeglitcherFirstEdge(input_signal)
|
||||
self.submodules += deglitcher
|
||||
|
||||
self.sync.helper += [
|
||||
self.h_tag_update.eq(0),
|
||||
If(deglitcher.detect,
|
||||
self.h_tag_update.eq(1),
|
||||
self.h_tag.eq(counter + deglitcher.tag_correction)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class Collector(Module):
|
||||
"""Generates loop filter inputs from DDMTD outputs.
|
||||
|
||||
The input to the main DCXO lock loop filter is the difference between the
|
||||
reference and main tags after unwrapping (see below).
|
||||
|
||||
The input to the helper DCXO lock loop filter is the difference between the
|
||||
current reference tag and the previous reference tag after unwrapping.
|
||||
|
||||
When the WR PLL is locked, the following ideally (no noise/jitter) obtain:
|
||||
- f_main = f_ref
|
||||
- f_helper = f_ref * 2^N/(2^N+1)
|
||||
- f_beat = f_ref - f_helper = f_ref / (2^N + 1) (cycle time is: dt=1/f_beat)
|
||||
- the reference and main DCXO tags are equal to each other at every cycle
|
||||
(the main DCXO lock drives this difference to 0)
|
||||
- the reference and main DCXO tags both have the same value at each cycle
|
||||
(the tag difference for each DDMTD is given by
|
||||
f_helper*dt = f_helper/f_beat = 2^N, which causes the N-bit DDMTD counter
|
||||
to wrap around and come back to its previous value)
|
||||
|
||||
Note that we currently lock the frequency of the helper DCXO to the
|
||||
reference clock, not it's phase. As a result, while the tag differences are
|
||||
controlled, their absolute values are arbitrary. We could consider moving
|
||||
the helper lock to a phase lock at some point in the future...
|
||||
|
||||
Since the DDMTD counter is only N bits, it is possible for tag values to
|
||||
wrap around. This will happen frequently if the locked tags happens to be
|
||||
near the edges of the counter, so that jitter can easily cause a phase wrap.
|
||||
But, it can also easily happen during lock acquisition or other transients.
|
||||
To avoid glitches in the output, we unwrap the tag differences. Currently
|
||||
we do this in hardware, but we should consider extending the processor to
|
||||
allow us to do it inside the filters. Since the processor uses wider
|
||||
signals, this would significantly extend the overall glitch-free
|
||||
range of the PLL and may aid lock acquisition.
|
||||
"""
|
||||
def __init__(self, N):
|
||||
self.ref_stb = Signal()
|
||||
self.main_stb = Signal()
|
||||
self.tag_ref = Signal(N)
|
||||
self.tag_main = Signal(N)
|
||||
|
||||
self.out_stb = Signal()
|
||||
self.out_main = Signal((N+2, True))
|
||||
self.out_helper = Signal((N+2, True))
|
||||
self.out_tag_ref = Signal(N)
|
||||
self.out_tag_main = Signal(N)
|
||||
|
||||
tag_ref_r = Signal(N)
|
||||
tag_main_r = Signal(N)
|
||||
main_tag_diff = Signal((N+2, True))
|
||||
helper_tag_diff = Signal((N+2, True))
|
||||
|
||||
# # #
|
||||
|
||||
fsm = FSM(reset_state="IDLE")
|
||||
self.submodules += fsm
|
||||
|
||||
fsm.act("IDLE",
|
||||
NextValue(self.out_stb, 0),
|
||||
If(self.ref_stb & self.main_stb,
|
||||
NextValue(tag_ref_r, self.tag_ref),
|
||||
NextValue(tag_main_r, self.tag_main),
|
||||
NextState("DIFF")
|
||||
).Elif(self.ref_stb,
|
||||
NextValue(tag_ref_r, self.tag_ref),
|
||||
NextState("WAITMAIN")
|
||||
).Elif(self.main_stb,
|
||||
NextValue(tag_main_r, self.tag_main),
|
||||
NextState("WAITREF")
|
||||
)
|
||||
)
|
||||
fsm.act("WAITREF",
|
||||
If(self.ref_stb,
|
||||
NextValue(tag_ref_r, self.tag_ref),
|
||||
NextState("DIFF")
|
||||
)
|
||||
)
|
||||
fsm.act("WAITMAIN",
|
||||
If(self.main_stb,
|
||||
NextValue(tag_main_r, self.tag_main),
|
||||
NextState("DIFF")
|
||||
)
|
||||
)
|
||||
fsm.act("DIFF",
|
||||
NextValue(main_tag_diff, tag_main_r - tag_ref_r),
|
||||
NextValue(helper_tag_diff, tag_ref_r - self.out_tag_ref),
|
||||
NextState("UNWRAP")
|
||||
)
|
||||
fsm.act("UNWRAP",
|
||||
If(main_tag_diff - self.out_main > 2**(N-1),
|
||||
NextValue(main_tag_diff, main_tag_diff - 2**N)
|
||||
).Elif(self.out_main - main_tag_diff > 2**(N-1),
|
||||
NextValue(main_tag_diff, main_tag_diff + 2**N)
|
||||
),
|
||||
|
||||
If(helper_tag_diff - self.out_helper > 2**(N-1),
|
||||
NextValue(helper_tag_diff, helper_tag_diff - 2**N)
|
||||
).Elif(self.out_helper - helper_tag_diff > 2**(N-1),
|
||||
NextValue(helper_tag_diff, helper_tag_diff + 2**N)
|
||||
),
|
||||
NextState("OUTPUT")
|
||||
)
|
||||
fsm.act("OUTPUT",
|
||||
NextValue(self.out_tag_ref, tag_ref_r),
|
||||
NextValue(self.out_tag_main, tag_main_r),
|
||||
NextValue(self.out_main, main_tag_diff),
|
||||
NextValue(self.out_helper, helper_tag_diff),
|
||||
NextValue(self.out_stb, 1),
|
||||
NextState("IDLE")
|
||||
)
|
|
@ -1,61 +0,0 @@
|
|||
helper_xn1 = 0
|
||||
helper_xn2 = 0
|
||||
helper_yn0 = 0
|
||||
helper_yn1 = 0
|
||||
helper_yn2 = 0
|
||||
helper_out = 0
|
||||
|
||||
main_xn1 = 0
|
||||
main_xn2 = 0
|
||||
main_yn0 = 0
|
||||
main_yn1 = 0
|
||||
main_yn2 = 0
|
||||
|
||||
|
||||
def helper(tag_diff):
|
||||
global helper_xn1, helper_xn2, helper_yn0, \
|
||||
helper_yn1, helper_yn2, helper_out
|
||||
|
||||
helper_xn0 = 0 - tag_diff # *(2**22)
|
||||
|
||||
helper_yr = 4294967296
|
||||
|
||||
helper_yn2 = helper_yn1
|
||||
helper_yn1 = helper_yn0
|
||||
|
||||
helper_yn0 = (284885690 * (helper_xn0
|
||||
+ (217319150 * helper_xn1 >> 44)
|
||||
- (17591968725108 * helper_xn2 >> 44)
|
||||
) >> 44
|
||||
) + (35184372088832*helper_yn1 >> 44) - helper_yn2
|
||||
|
||||
helper_xn2 = helper_xn1
|
||||
helper_xn1 = helper_xn0
|
||||
|
||||
helper_out = 268435456*helper_yn0 >> 44
|
||||
helper_out = min(helper_out, helper_yr)
|
||||
helper_out = max(helper_out, 0 - helper_yr)
|
||||
|
||||
return helper_out
|
||||
|
||||
|
||||
def main(main_xn0):
|
||||
global main_xn1, main_xn2, main_yn0, main_yn1, main_yn2
|
||||
|
||||
main_yr = 4294967296
|
||||
|
||||
main_yn2 = main_yn1
|
||||
main_yn1 = main_yn0
|
||||
main_yn0 = (
|
||||
((133450380908*(((35184372088832*main_xn0) >> 44) +
|
||||
((17592186044417*main_xn1) >> 44))) >> 44) +
|
||||
((29455872930889*main_yn1) >> 44) -
|
||||
((12673794781453*main_yn2) >> 44))
|
||||
|
||||
main_xn2 = main_xn1
|
||||
main_xn1 = main_xn0
|
||||
|
||||
main_yn0 = min(main_yn0, main_yr)
|
||||
main_yn0 = max(main_yn0, 0 - main_yr)
|
||||
|
||||
return main_yn0
|
|
@ -1,340 +0,0 @@
|
|||
from migen import *
|
||||
from migen.genlib.fsm import *
|
||||
from migen.genlib.cdc import MultiReg, PulseSynchronizer, BlindTransfer
|
||||
|
||||
from misoc.interconnect.csr import *
|
||||
|
||||
|
||||
class I2CClockGen(Module):
|
||||
def __init__(self, width):
|
||||
self.load = Signal(width)
|
||||
self.clk2x = Signal()
|
||||
|
||||
cnt = Signal.like(self.load)
|
||||
self.comb += [
|
||||
self.clk2x.eq(cnt == 0),
|
||||
]
|
||||
self.sync += [
|
||||
If(self.clk2x,
|
||||
cnt.eq(self.load),
|
||||
).Else(
|
||||
cnt.eq(cnt - 1),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class I2CMasterMachine(Module):
|
||||
def __init__(self, clock_width):
|
||||
self.scl = Signal(reset=1)
|
||||
self.sda_o = Signal(reset=1)
|
||||
self.sda_i = Signal()
|
||||
|
||||
self.submodules.cg = CEInserter()(I2CClockGen(clock_width))
|
||||
self.start = Signal()
|
||||
self.stop = Signal()
|
||||
self.write = Signal()
|
||||
self.ack = Signal()
|
||||
self.data = Signal(8)
|
||||
self.ready = Signal()
|
||||
|
||||
###
|
||||
|
||||
bits = Signal(4)
|
||||
data = Signal(8)
|
||||
|
||||
fsm = CEInserter()(FSM("IDLE"))
|
||||
self.submodules += fsm
|
||||
|
||||
fsm.act("IDLE",
|
||||
self.ready.eq(1),
|
||||
If(self.start,
|
||||
NextState("START0"),
|
||||
).Elif(self.stop,
|
||||
NextState("STOP0"),
|
||||
).Elif(self.write,
|
||||
NextValue(bits, 8),
|
||||
NextValue(data, self.data),
|
||||
NextState("WRITE0")
|
||||
)
|
||||
)
|
||||
|
||||
fsm.act("START0",
|
||||
NextValue(self.scl, 1),
|
||||
NextState("START1")
|
||||
)
|
||||
fsm.act("START1",
|
||||
NextValue(self.sda_o, 0),
|
||||
NextState("IDLE")
|
||||
)
|
||||
|
||||
fsm.act("STOP0",
|
||||
NextValue(self.scl, 0),
|
||||
NextState("STOP1")
|
||||
)
|
||||
fsm.act("STOP1",
|
||||
NextValue(self.sda_o, 0),
|
||||
NextState("STOP2")
|
||||
)
|
||||
fsm.act("STOP2",
|
||||
NextValue(self.scl, 1),
|
||||
NextState("STOP3")
|
||||
)
|
||||
fsm.act("STOP3",
|
||||
NextValue(self.sda_o, 1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
|
||||
fsm.act("WRITE0",
|
||||
NextValue(self.scl, 0),
|
||||
NextState("WRITE1")
|
||||
)
|
||||
fsm.act("WRITE1",
|
||||
If(bits == 0,
|
||||
NextValue(self.sda_o, 1),
|
||||
NextState("READACK0"),
|
||||
).Else(
|
||||
NextValue(self.sda_o, data[7]),
|
||||
NextState("WRITE2"),
|
||||
)
|
||||
)
|
||||
fsm.act("WRITE2",
|
||||
NextValue(self.scl, 1),
|
||||
NextValue(data[1:], data[:-1]),
|
||||
NextValue(bits, bits - 1),
|
||||
NextState("WRITE0"),
|
||||
)
|
||||
fsm.act("READACK0",
|
||||
NextValue(self.scl, 1),
|
||||
NextState("READACK1"),
|
||||
)
|
||||
fsm.act("READACK1",
|
||||
NextValue(self.ack, ~self.sda_i),
|
||||
NextState("IDLE")
|
||||
)
|
||||
|
||||
run = Signal()
|
||||
idle = Signal()
|
||||
self.comb += [
|
||||
run.eq((self.start | self.stop | self.write) & self.ready),
|
||||
idle.eq(~run & fsm.ongoing("IDLE")),
|
||||
self.cg.ce.eq(~idle),
|
||||
fsm.ce.eq(run | self.cg.clk2x),
|
||||
]
|
||||
|
||||
|
||||
class ADPLLProgrammer(Module):
|
||||
def __init__(self):
|
||||
self.i2c_divider = Signal(16)
|
||||
self.i2c_address = Signal(7)
|
||||
|
||||
self.adpll = Signal(24)
|
||||
self.stb = Signal()
|
||||
self.busy = Signal()
|
||||
self.nack = Signal()
|
||||
|
||||
self.scl = Signal()
|
||||
self.sda_i = Signal()
|
||||
self.sda_o = Signal()
|
||||
|
||||
self.scl.attr.add("no_retiming")
|
||||
self.sda_o.attr.add("no_retiming")
|
||||
|
||||
# # #
|
||||
|
||||
master = I2CMasterMachine(16)
|
||||
self.submodules += master
|
||||
|
||||
self.comb += [
|
||||
master.cg.load.eq(self.i2c_divider),
|
||||
self.scl.eq(master.scl),
|
||||
master.sda_i.eq(self.sda_i),
|
||||
self.sda_o.eq(master.sda_o)
|
||||
]
|
||||
|
||||
fsm = FSM()
|
||||
self.submodules += fsm
|
||||
|
||||
adpll = Signal.like(self.adpll)
|
||||
|
||||
fsm.act("IDLE",
|
||||
If(self.stb,
|
||||
NextValue(adpll, self.adpll),
|
||||
NextState("START")
|
||||
)
|
||||
)
|
||||
fsm.act("START",
|
||||
master.start.eq(1),
|
||||
If(master.ready, NextState("DEVADDRESS"))
|
||||
)
|
||||
fsm.act("DEVADDRESS",
|
||||
master.data.eq(self.i2c_address << 1),
|
||||
master.write.eq(1),
|
||||
If(master.ready, NextState("REGADRESS"))
|
||||
)
|
||||
fsm.act("REGADRESS",
|
||||
master.data.eq(231),
|
||||
master.write.eq(1),
|
||||
If(master.ready,
|
||||
If(master.ack,
|
||||
NextState("DATA0")
|
||||
).Else(
|
||||
self.nack.eq(1),
|
||||
NextState("STOP")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("DATA0",
|
||||
master.data.eq(adpll[0:8]),
|
||||
master.write.eq(1),
|
||||
If(master.ready,
|
||||
If(master.ack,
|
||||
NextState("DATA1")
|
||||
).Else(
|
||||
self.nack.eq(1),
|
||||
NextState("STOP")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("DATA1",
|
||||
master.data.eq(adpll[8:16]),
|
||||
master.write.eq(1),
|
||||
If(master.ready,
|
||||
If(master.ack,
|
||||
NextState("DATA2")
|
||||
).Else(
|
||||
self.nack.eq(1),
|
||||
NextState("STOP")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("DATA2",
|
||||
master.data.eq(adpll[16:24]),
|
||||
master.write.eq(1),
|
||||
If(master.ready,
|
||||
If(~master.ack, self.nack.eq(1)),
|
||||
NextState("STOP")
|
||||
)
|
||||
)
|
||||
fsm.act("STOP",
|
||||
master.stop.eq(1),
|
||||
If(master.ready,
|
||||
If(~master.ack, self.nack.eq(1)),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
||||
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
|
||||
|
||||
|
||||
def simulate_programmer():
|
||||
from migen.sim.core import run_simulation
|
||||
|
||||
dut = ADPLLProgrammer()
|
||||
|
||||
def generator():
|
||||
yield dut.i2c_divider.eq(4)
|
||||
yield dut.i2c_address.eq(0x55)
|
||||
yield
|
||||
yield dut.adpll.eq(0x123456)
|
||||
yield dut.stb.eq(1)
|
||||
yield
|
||||
yield dut.stb.eq(0)
|
||||
yield
|
||||
while (yield dut.busy):
|
||||
yield
|
||||
for _ in range(20):
|
||||
yield
|
||||
|
||||
run_simulation(dut, generator(), vcd_name="tb.vcd")
|
||||
|
||||
|
||||
class Si549(Module, AutoCSR):
|
||||
def __init__(self, pads):
|
||||
self.gpio_enable = CSRStorage(reset=1)
|
||||
self.gpio_in = CSRStatus(2)
|
||||
self.gpio_out = CSRStorage(2)
|
||||
self.gpio_oe = CSRStorage(2)
|
||||
|
||||
self.i2c_divider = CSRStorage(16, reset=75)
|
||||
self.i2c_address = CSRStorage(7)
|
||||
self.errors = CSR(2)
|
||||
|
||||
# in helper clock domain
|
||||
self.adpll = Signal(24)
|
||||
self.adpll_stb = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
programmer = ClockDomainsRenamer("helper")(ADPLLProgrammer())
|
||||
self.submodules += programmer
|
||||
|
||||
self.i2c_divider.storage.attr.add("no_retiming")
|
||||
self.i2c_address.storage.attr.add("no_retiming")
|
||||
self.specials += [
|
||||
MultiReg(self.i2c_divider.storage, programmer.i2c_divider, "helper"),
|
||||
MultiReg(self.i2c_address.storage, programmer.i2c_address, "helper")
|
||||
]
|
||||
self.comb += [
|
||||
programmer.adpll.eq(self.adpll),
|
||||
programmer.stb.eq(self.adpll_stb)
|
||||
]
|
||||
|
||||
self.gpio_enable.storage.attr.add("no_retiming")
|
||||
self.gpio_out.storage.attr.add("no_retiming")
|
||||
self.gpio_oe.storage.attr.add("no_retiming")
|
||||
|
||||
# SCL GPIO and mux
|
||||
ts_scl = TSTriple(1)
|
||||
self.specials += ts_scl.get_tristate(pads.scl)
|
||||
|
||||
status = Signal()
|
||||
self.comb += self.gpio_in.status[0].eq(status)
|
||||
|
||||
self.specials += MultiReg(ts_scl.i, status)
|
||||
self.comb += [
|
||||
If(self.gpio_enable.storage,
|
||||
ts_scl.o.eq(self.gpio_out.storage[0]),
|
||||
ts_scl.oe.eq(self.gpio_oe.storage[0])
|
||||
).Else(
|
||||
ts_scl.o.eq(0),
|
||||
ts_scl.oe.eq(~programmer.scl)
|
||||
)
|
||||
]
|
||||
|
||||
# SDA GPIO and mux
|
||||
ts_sda = TSTriple(1)
|
||||
self.specials += ts_sda.get_tristate(pads.sda)
|
||||
|
||||
status = Signal()
|
||||
self.comb += self.gpio_in.status[1].eq(status)
|
||||
|
||||
self.specials += MultiReg(ts_sda.i, status)
|
||||
self.comb += [
|
||||
If(self.gpio_enable.storage,
|
||||
ts_sda.o.eq(self.gpio_out.storage[1]),
|
||||
ts_sda.oe.eq(self.gpio_oe.storage[1])
|
||||
).Else(
|
||||
ts_sda.o.eq(0),
|
||||
ts_sda.oe.eq(~programmer.sda_o)
|
||||
)
|
||||
]
|
||||
self.specials += MultiReg(ts_sda.i, programmer.sda_i, "helper")
|
||||
|
||||
# Error reporting
|
||||
collision_cdc = BlindTransfer("helper", "sys")
|
||||
self.submodules += collision_cdc
|
||||
self.comb += collision_cdc.i.eq(programmer.stb & programmer.busy)
|
||||
|
||||
nack_cdc = PulseSynchronizer("helper", "sys")
|
||||
self.submodules += nack_cdc
|
||||
self.comb += nack_cdc.i.eq(programmer.nack)
|
||||
|
||||
for n, trig in enumerate([collision_cdc.o, nack_cdc.o]):
|
||||
self.sync += [
|
||||
If(self.errors.re & self.errors.r[n], self.errors.w[n].eq(0)),
|
||||
If(trig, self.errors.w[n].eq(1))
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
simulate_programmer()
|
|
@ -1,618 +0,0 @@
|
|||
import inspect
|
||||
import ast
|
||||
from copy import copy
|
||||
import operator
|
||||
from functools import reduce
|
||||
from collections import OrderedDict
|
||||
|
||||
from migen import *
|
||||
from migen.genlib.fsm import *
|
||||
|
||||
|
||||
class Isn:
|
||||
def __init__(self, immediate=None, inputs=None, outputs=None):
|
||||
if inputs is None:
|
||||
inputs = []
|
||||
if outputs is None:
|
||||
outputs = []
|
||||
self.immediate = immediate
|
||||
self.inputs = inputs
|
||||
self.outputs = outputs
|
||||
|
||||
def __repr__(self):
|
||||
r = "<"
|
||||
r += self.__class__.__name__
|
||||
if self.immediate is not None:
|
||||
r += " (" + str(self.immediate) + ")"
|
||||
for inp in self.inputs:
|
||||
r += " r" + str(inp)
|
||||
if self.outputs:
|
||||
r += " ->"
|
||||
for outp in self.outputs:
|
||||
r += " r" + str(outp)
|
||||
r += ">"
|
||||
return r
|
||||
|
||||
|
||||
class NopIsn(Isn):
|
||||
opcode = 0
|
||||
|
||||
class AddIsn(Isn):
|
||||
opcode = 1
|
||||
|
||||
class SubIsn(Isn):
|
||||
opcode = 2
|
||||
|
||||
class MulShiftIsn(Isn):
|
||||
opcode = 3
|
||||
|
||||
# opcode = 4: MulShift with alternate shift
|
||||
|
||||
class MinIsn(Isn):
|
||||
opcode = 5
|
||||
|
||||
class MaxIsn(Isn):
|
||||
opcode = 6
|
||||
|
||||
class CopyIsn(Isn):
|
||||
opcode = 7
|
||||
|
||||
class InputIsn(Isn):
|
||||
opcode = 8
|
||||
|
||||
class OutputIsn(Isn):
|
||||
opcode = 9
|
||||
|
||||
class EndIsn(Isn):
|
||||
opcode = 10
|
||||
|
||||
|
||||
class ASTCompiler:
|
||||
def __init__(self):
|
||||
self.program = []
|
||||
self.data = []
|
||||
self.next_ssa_reg = -1
|
||||
self.constants = dict()
|
||||
self.names = dict()
|
||||
self.globals = OrderedDict()
|
||||
|
||||
def get_ssa_reg(self):
|
||||
r = self.next_ssa_reg
|
||||
self.next_ssa_reg -= 1
|
||||
return r
|
||||
|
||||
def add_global(self, name):
|
||||
if name not in self.globals:
|
||||
r = len(self.data)
|
||||
self.data.append(0)
|
||||
self.names[name] = r
|
||||
self.globals[name] = r
|
||||
|
||||
def input(self, name):
|
||||
target = self.get_ssa_reg()
|
||||
self.program.append(InputIsn(outputs=[target]))
|
||||
self.names[name] = target
|
||||
|
||||
def emit(self, node):
|
||||
if isinstance(node, ast.BinOp):
|
||||
if isinstance(node.op, ast.RShift):
|
||||
if not isinstance(node.left, ast.BinOp) or not isinstance(node.left.op, ast.Mult):
|
||||
raise NotImplementedError
|
||||
if not isinstance(node.right, ast.Num):
|
||||
raise NotImplementedError
|
||||
left = self.emit(node.left.left)
|
||||
right = self.emit(node.left.right)
|
||||
cons = lambda **kwargs: MulShiftIsn(immediate=node.right.n, **kwargs)
|
||||
else:
|
||||
left = self.emit(node.left)
|
||||
right = self.emit(node.right)
|
||||
if isinstance(node.op, ast.Add):
|
||||
cons = AddIsn
|
||||
elif isinstance(node.op, ast.Sub):
|
||||
cons = SubIsn
|
||||
elif isinstance(node.op, ast.Mult):
|
||||
cons = lambda **kwargs: MulShiftIsn(immediate=0, **kwargs)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
output = self.get_ssa_reg()
|
||||
self.program.append(cons(inputs=[left, right], outputs=[output]))
|
||||
return output
|
||||
elif isinstance(node, ast.Call):
|
||||
if not isinstance(node.func, ast.Name):
|
||||
raise NotImplementedError
|
||||
funcname = node.func.id
|
||||
if node.keywords:
|
||||
raise NotImplementedError
|
||||
inputs = [self.emit(x) for x in node.args]
|
||||
if funcname == "min":
|
||||
cons = MinIsn
|
||||
elif funcname == "max":
|
||||
cons = MaxIsn
|
||||
else:
|
||||
raise NotImplementedError
|
||||
output = self.get_ssa_reg()
|
||||
self.program.append(cons(inputs=inputs, outputs=[output]))
|
||||
return output
|
||||
elif isinstance(node, (ast.Num, ast.UnaryOp)):
|
||||
if isinstance(node, ast.UnaryOp):
|
||||
if not isinstance(node.operand, ast.Num):
|
||||
raise NotImplementedError
|
||||
if isinstance(node.op, ast.UAdd):
|
||||
transform = lambda x: x
|
||||
elif isinstance(node.op, ast.USub):
|
||||
transform = operator.neg
|
||||
elif isinstance(node.op, ast.Invert):
|
||||
transform = operator.invert
|
||||
else:
|
||||
raise NotImplementedError
|
||||
node = node.operand
|
||||
else:
|
||||
transform = lambda x: x
|
||||
n = transform(node.n)
|
||||
if n in self.constants:
|
||||
return self.constants[n]
|
||||
else:
|
||||
r = len(self.data)
|
||||
self.data.append(n)
|
||||
self.constants[n] = r
|
||||
return r
|
||||
elif isinstance(node, ast.Name):
|
||||
return self.names[node.id]
|
||||
elif isinstance(node, ast.Assign):
|
||||
output = self.emit(node.value)
|
||||
for target in node.targets:
|
||||
assert isinstance(target, ast.Name)
|
||||
self.names[target.id] = output
|
||||
elif isinstance(node, ast.Return):
|
||||
value = self.emit(node.value)
|
||||
self.program.append(OutputIsn(inputs=[value]))
|
||||
elif isinstance(node, ast.Global):
|
||||
pass
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Processor:
|
||||
def __init__(self, data_width=32, multiplier_stages=2):
|
||||
self.data_width = data_width
|
||||
self.multiplier_stages = multiplier_stages
|
||||
self.multiplier_shifts = []
|
||||
self.program_rom_size = None
|
||||
self.data_ram_size = None
|
||||
self.opcode_bits = 4
|
||||
self.reg_bits = None
|
||||
|
||||
def get_instruction_latency(self, isn):
|
||||
return {
|
||||
AddIsn: 2,
|
||||
SubIsn: 2,
|
||||
MulShiftIsn: 1 + self.multiplier_stages,
|
||||
MinIsn: 2,
|
||||
MaxIsn: 2,
|
||||
CopyIsn: 1,
|
||||
InputIsn: 1
|
||||
}[isn.__class__]
|
||||
|
||||
def encode_instruction(self, isn, exit):
|
||||
opcode = isn.opcode
|
||||
if isn.immediate is not None and not isinstance(isn, MulShiftIsn):
|
||||
r0 = isn.immediate
|
||||
if len(isn.inputs) >= 1:
|
||||
r1 = isn.inputs[0]
|
||||
else:
|
||||
r1 = 0
|
||||
else:
|
||||
if len(isn.inputs) >= 1:
|
||||
r0 = isn.inputs[0]
|
||||
else:
|
||||
r0 = 0
|
||||
if len(isn.inputs) >= 2:
|
||||
r1 = isn.inputs[1]
|
||||
else:
|
||||
r1 = 0
|
||||
r = 0
|
||||
for value, bits in ((exit, self.reg_bits), (r1, self.reg_bits), (r0, self.reg_bits), (opcode, self.opcode_bits)):
|
||||
r <<= bits
|
||||
r |= value
|
||||
return r
|
||||
|
||||
def instruction_bits(self):
|
||||
return 3*self.reg_bits + self.opcode_bits
|
||||
|
||||
def implement(self, program, data):
|
||||
return ProcessorImpl(self, program, data)
|
||||
|
||||
|
||||
class Scheduler:
|
||||
def __init__(self, processor, reserved_data, program):
|
||||
self.processor = processor
|
||||
self.reserved_data = reserved_data
|
||||
self.used_registers = set(range(self.reserved_data))
|
||||
self.exits = dict()
|
||||
self.program = program
|
||||
self.remaining = copy(program)
|
||||
self.output = []
|
||||
|
||||
def allocate_register(self):
|
||||
r = min(set(range(max(self.used_registers) + 2)) - self.used_registers)
|
||||
self.used_registers.add(r)
|
||||
return r
|
||||
|
||||
def free_register(self, r):
|
||||
assert r >= self.reserved_data
|
||||
self.used_registers.discard(r)
|
||||
|
||||
def find_inputs(self, cycle, isn):
|
||||
mapped_inputs = []
|
||||
for inp in isn.inputs:
|
||||
if inp >= 0:
|
||||
mapped_inputs.append(inp)
|
||||
else:
|
||||
found = False
|
||||
for i in range(cycle):
|
||||
if i in self.exits:
|
||||
r, rm = self.exits[i]
|
||||
if r == inp:
|
||||
mapped_inputs.append(rm)
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
return None
|
||||
return mapped_inputs
|
||||
|
||||
def schedule_one(self, isn):
|
||||
cycle = len(self.output)
|
||||
mapped_inputs = self.find_inputs(cycle, isn)
|
||||
if mapped_inputs is None:
|
||||
return False
|
||||
|
||||
if isn.outputs:
|
||||
# check that exit slot is free
|
||||
latency = self.processor.get_instruction_latency(isn)
|
||||
exit = cycle + latency
|
||||
if exit in self.exits:
|
||||
return False
|
||||
|
||||
# avoid RAW hazard with global writeback
|
||||
for output in isn.outputs:
|
||||
if output >= 0:
|
||||
for risn in self.remaining:
|
||||
for inp in risn.inputs:
|
||||
if inp == output:
|
||||
return False
|
||||
|
||||
# Instruction can be scheduled
|
||||
|
||||
self.remaining.remove(isn)
|
||||
|
||||
for inp, minp in zip(isn.inputs, mapped_inputs):
|
||||
can_free = inp < 0 and all(inp != rinp for risn in self.remaining for rinp in risn.inputs)
|
||||
if can_free:
|
||||
self.free_register(minp)
|
||||
|
||||
if isn.outputs:
|
||||
assert len(isn.outputs) == 1
|
||||
if isn.outputs[0] < 0:
|
||||
output = self.allocate_register()
|
||||
else:
|
||||
output = isn.outputs[0]
|
||||
self.exits[exit] = (isn.outputs[0], output)
|
||||
self.output.append(isn.__class__(immediate=isn.immediate, inputs=mapped_inputs))
|
||||
|
||||
return True
|
||||
|
||||
def schedule(self):
|
||||
while self.remaining:
|
||||
success = False
|
||||
for isn in self.remaining:
|
||||
if self.schedule_one(isn):
|
||||
success = True
|
||||
break
|
||||
if not success:
|
||||
self.output.append(NopIsn())
|
||||
self.output += [NopIsn()]*(max(self.exits.keys()) - len(self.output) + 1)
|
||||
return self.output
|
||||
|
||||
|
||||
class CompiledProgram:
|
||||
def __init__(self, processor, program, exits, data, glbs):
|
||||
self.processor = processor
|
||||
self.program = program
|
||||
self.exits = exits
|
||||
self.data = data
|
||||
self.globals = glbs
|
||||
|
||||
def pretty_print(self):
|
||||
for cycle, isn in enumerate(self.program):
|
||||
l = "{:4d} {:15}".format(cycle, str(isn))
|
||||
if cycle in self.exits:
|
||||
l += " -> r{}".format(self.exits[cycle])
|
||||
print(l)
|
||||
|
||||
def dimension_processor(self):
|
||||
self.processor.program_rom_size = len(self.program)
|
||||
self.processor.data_ram_size = len(self.data)
|
||||
self.processor.reg_bits = (self.processor.data_ram_size - 1).bit_length()
|
||||
for isn in self.program:
|
||||
if isinstance(isn, MulShiftIsn) and isn.immediate not in self.processor.multiplier_shifts:
|
||||
self.processor.multiplier_shifts.append(isn.immediate)
|
||||
|
||||
def encode(self):
|
||||
r = []
|
||||
for i, isn in enumerate(self.program):
|
||||
exit = self.exits.get(i, 0)
|
||||
r.append(self.processor.encode_instruction(isn, exit))
|
||||
return r
|
||||
|
||||
|
||||
def compile(processor, function):
|
||||
node = ast.parse(inspect.getsource(function))
|
||||
assert isinstance(node, ast.Module)
|
||||
assert len(node.body) == 1
|
||||
node = node.body[0]
|
||||
assert isinstance(node, ast.FunctionDef)
|
||||
assert len(node.args.args) == 1
|
||||
arg = node.args.args[0].arg
|
||||
body = node.body
|
||||
|
||||
astcompiler = ASTCompiler()
|
||||
for node in body:
|
||||
if isinstance(node, ast.Global):
|
||||
for name in node.names:
|
||||
astcompiler.add_global(name)
|
||||
arg_r = astcompiler.input(arg)
|
||||
for node in body:
|
||||
astcompiler.emit(node)
|
||||
if isinstance(node, ast.Return):
|
||||
break
|
||||
for glbl, location in astcompiler.globals.items():
|
||||
new_location = astcompiler.names[glbl]
|
||||
if new_location != location:
|
||||
astcompiler.program.append(CopyIsn(inputs=[new_location], outputs=[location]))
|
||||
|
||||
scheduler = Scheduler(processor, len(astcompiler.data), astcompiler.program)
|
||||
scheduler.schedule()
|
||||
|
||||
program = copy(scheduler.output)
|
||||
program.append(EndIsn())
|
||||
|
||||
max_reg = max(max(max(isn.inputs + [0]) for isn in program), max(v[1] for k, v in scheduler.exits.items()))
|
||||
|
||||
return CompiledProgram(
|
||||
processor=processor,
|
||||
program=program,
|
||||
exits={k: v[1] for k, v in scheduler.exits.items()},
|
||||
data=astcompiler.data + [0]*(max_reg - len(astcompiler.data) + 1),
|
||||
glbs=astcompiler.globals)
|
||||
|
||||
|
||||
class BaseUnit(Module):
|
||||
def __init__(self, data_width):
|
||||
self.stb_i = Signal()
|
||||
self.i0 = Signal((data_width, True))
|
||||
self.i1 = Signal((data_width, True))
|
||||
self.stb_o = Signal()
|
||||
self.o = Signal((data_width, True))
|
||||
|
||||
|
||||
class NopUnit(BaseUnit):
|
||||
pass
|
||||
|
||||
|
||||
class OpUnit(BaseUnit):
|
||||
def __init__(self, op, data_width, stages, op_data_width=None):
|
||||
BaseUnit.__init__(self, data_width)
|
||||
# work around Migen's mishandling of Verilog's cretinous operator width rules
|
||||
if op_data_width is None:
|
||||
op_data_width = data_width
|
||||
|
||||
if stages > 1:
|
||||
# Vivado backward retiming for DSP does not work correctly if DSP inputs
|
||||
# are not registered.
|
||||
i0 = Signal.like(self.i0)
|
||||
i1 = Signal.like(self.i1)
|
||||
stb_i = Signal()
|
||||
self.sync += [
|
||||
i0.eq(self.i0),
|
||||
i1.eq(self.i1),
|
||||
stb_i.eq(self.stb_i)
|
||||
]
|
||||
output_stages = stages - 1
|
||||
else:
|
||||
i0, i1, stb_i = self.i0, self.i1, self.stb_i
|
||||
output_stages = stages
|
||||
|
||||
o = Signal((op_data_width, True))
|
||||
self.comb += o.eq(op(i0, i1))
|
||||
stb_o = stb_i
|
||||
for i in range(output_stages):
|
||||
n_o = Signal((data_width, True))
|
||||
if stages > 1:
|
||||
n_o.attr.add(("retiming_backward", 1))
|
||||
n_stb_o = Signal()
|
||||
self.sync += [
|
||||
n_o.eq(o),
|
||||
n_stb_o.eq(stb_o)
|
||||
]
|
||||
o = n_o
|
||||
stb_o = n_stb_o
|
||||
self.comb += [
|
||||
self.o.eq(o),
|
||||
self.stb_o.eq(stb_o)
|
||||
]
|
||||
|
||||
|
||||
class SelectUnit(BaseUnit):
|
||||
def __init__(self, op, data_width):
|
||||
BaseUnit.__init__(self, data_width)
|
||||
|
||||
self.sync += [
|
||||
self.stb_o.eq(self.stb_i),
|
||||
If(op(self.i0, self.i1),
|
||||
self.o.eq(self.i0)
|
||||
).Else(
|
||||
self.o.eq(self.i1)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class CopyUnit(BaseUnit):
|
||||
def __init__(self, data_width):
|
||||
BaseUnit.__init__(self, data_width)
|
||||
|
||||
self.comb += [
|
||||
self.stb_o.eq(self.stb_i),
|
||||
self.o.eq(self.i0)
|
||||
]
|
||||
|
||||
|
||||
class InputUnit(BaseUnit):
|
||||
def __init__(self, data_width, input_stb, input):
|
||||
BaseUnit.__init__(self, data_width)
|
||||
self.buffer = Signal(data_width)
|
||||
|
||||
self.comb += [
|
||||
self.stb_o.eq(self.stb_i),
|
||||
self.o.eq(self.buffer)
|
||||
]
|
||||
|
||||
|
||||
class OutputUnit(BaseUnit):
|
||||
def __init__(self, data_width, output_stb, output):
|
||||
BaseUnit.__init__(self, data_width)
|
||||
|
||||
self.sync += [
|
||||
output_stb.eq(self.stb_i),
|
||||
output.eq(self.i0)
|
||||
]
|
||||
|
||||
|
||||
class ProcessorImpl(Module):
|
||||
def __init__(self, pd, program, data):
|
||||
self.input_stb = Signal()
|
||||
self.input = Signal((pd.data_width, True))
|
||||
|
||||
self.output_stb = Signal()
|
||||
self.output = Signal((pd.data_width, True))
|
||||
|
||||
self.busy = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
program_mem = Memory(pd.instruction_bits(), pd.program_rom_size, init=program)
|
||||
data_mem0 = Memory(pd.data_width, pd.data_ram_size, init=data)
|
||||
data_mem1 = Memory(pd.data_width, pd.data_ram_size, init=data)
|
||||
self.specials += program_mem, data_mem0, data_mem1
|
||||
|
||||
pc = Signal(pd.instruction_bits())
|
||||
pc_next = Signal.like(pc)
|
||||
pc_en = Signal()
|
||||
self.sync += pc.eq(pc_next)
|
||||
self.comb += [
|
||||
If(pc_en,
|
||||
pc_next.eq(pc + 1)
|
||||
).Else(
|
||||
pc_next.eq(0)
|
||||
)
|
||||
]
|
||||
program_mem_port = program_mem.get_port()
|
||||
self.specials += program_mem_port
|
||||
self.comb += program_mem_port.adr.eq(pc_next)
|
||||
|
||||
s = 0
|
||||
opcode = Signal(pd.opcode_bits)
|
||||
self.comb += opcode.eq(program_mem_port.dat_r[s:s+pd.opcode_bits])
|
||||
s += pd.opcode_bits
|
||||
r0 = Signal(pd.reg_bits)
|
||||
self.comb += r0.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
|
||||
s += pd.reg_bits
|
||||
r1 = Signal(pd.reg_bits)
|
||||
self.comb += r1.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
|
||||
s += pd.reg_bits
|
||||
exit = Signal(pd.reg_bits)
|
||||
self.comb += exit.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
|
||||
|
||||
data_read_port0 = data_mem0.get_port()
|
||||
data_read_port1 = data_mem1.get_port()
|
||||
self.specials += data_read_port0, data_read_port1
|
||||
self.comb += [
|
||||
data_read_port0.adr.eq(r0),
|
||||
data_read_port1.adr.eq(r1)
|
||||
]
|
||||
|
||||
data_write_port = data_mem0.get_port(write_capable=True)
|
||||
data_write_port_dup = data_mem1.get_port(write_capable=True)
|
||||
self.specials += data_write_port, data_write_port_dup
|
||||
self.comb += [
|
||||
data_write_port_dup.we.eq(data_write_port.we),
|
||||
data_write_port_dup.adr.eq(data_write_port.adr),
|
||||
data_write_port_dup.dat_w.eq(data_write_port.dat_w),
|
||||
data_write_port.adr.eq(exit)
|
||||
]
|
||||
|
||||
nop = NopUnit(pd.data_width)
|
||||
adder = OpUnit(operator.add, pd.data_width, 1)
|
||||
subtractor = OpUnit(operator.sub, pd.data_width, 1)
|
||||
if pd.multiplier_shifts:
|
||||
if len(pd.multiplier_shifts) != 1:
|
||||
raise NotImplementedError
|
||||
multiplier = OpUnit(lambda a, b: a * b >> pd.multiplier_shifts[0],
|
||||
pd.data_width, pd.multiplier_stages, op_data_width=2*pd.data_width)
|
||||
else:
|
||||
multiplier = NopUnit(pd.data_width)
|
||||
minu = SelectUnit(operator.lt, pd.data_width)
|
||||
maxu = SelectUnit(operator.gt, pd.data_width)
|
||||
copier = CopyUnit(pd.data_width)
|
||||
inu = InputUnit(pd.data_width, self.input_stb, self.input)
|
||||
outu = OutputUnit(pd.data_width, self.output_stb, self.output)
|
||||
units = [nop, adder, subtractor, multiplier, minu, maxu, copier, inu, outu]
|
||||
self.submodules += units
|
||||
|
||||
for unit in units:
|
||||
self.sync += unit.stb_i.eq(0)
|
||||
self.comb += [
|
||||
unit.i0.eq(data_read_port0.dat_r),
|
||||
unit.i1.eq(data_read_port1.dat_r),
|
||||
If(unit.stb_o,
|
||||
data_write_port.we.eq(1),
|
||||
data_write_port.dat_w.eq(unit.o)
|
||||
)
|
||||
]
|
||||
|
||||
decode_table = [
|
||||
(NopIsn.opcode, nop),
|
||||
(AddIsn.opcode, adder),
|
||||
(SubIsn.opcode, subtractor),
|
||||
(MulShiftIsn.opcode, multiplier),
|
||||
(MulShiftIsn.opcode + 1, multiplier),
|
||||
(MinIsn.opcode, minu),
|
||||
(MaxIsn.opcode, maxu),
|
||||
(CopyIsn.opcode, copier),
|
||||
(InputIsn.opcode, inu),
|
||||
(OutputIsn.opcode, outu)
|
||||
]
|
||||
for allocated_opcode, unit in decode_table:
|
||||
self.sync += If(pc_en & (opcode == allocated_opcode), unit.stb_i.eq(1))
|
||||
|
||||
fsm = FSM()
|
||||
self.submodules += fsm
|
||||
fsm.act("IDLE",
|
||||
pc_en.eq(0),
|
||||
NextValue(inu.buffer, self.input),
|
||||
If(self.input_stb, NextState("PROCESSING"))
|
||||
)
|
||||
fsm.act("PROCESSING",
|
||||
self.busy.eq(1),
|
||||
pc_en.eq(1),
|
||||
If(opcode == EndIsn.opcode,
|
||||
pc_en.eq(0),
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def make(function, **kwargs):
|
||||
proc = Processor(**kwargs)
|
||||
cp = compile(proc, function)
|
||||
cp.dimension_processor()
|
||||
return proc.implement(cp.encode(), cp.data)
|
|
@ -21,7 +21,6 @@ from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_
|
|||
from artiq.gateware import eem
|
||||
from artiq.gateware.drtio.transceiver import gtp_7series
|
||||
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
||||
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerGTP
|
||||
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
|
||||
from artiq.gateware.drtio import *
|
||||
from artiq.build_soc import *
|
||||
|
@ -443,7 +442,7 @@ class SatelliteBase(BaseSoC):
|
|||
}
|
||||
mem_map.update(BaseSoC.mem_map)
|
||||
|
||||
def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, with_wrpll=False, gateware_identifier_str=None, hw_rev="v2.0", **kwargs):
|
||||
def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, gateware_identifier_str=None, hw_rev="v2.0", **kwargs):
|
||||
if hw_rev in ("v1.0", "v1.1"):
|
||||
cpu_bus_width = 32
|
||||
else:
|
||||
|
@ -560,35 +559,18 @@ class SatelliteBase(BaseSoC):
|
|||
|
||||
rtio_clk_period = 1e9/rtio_clk_freq
|
||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
||||
if with_wrpll:
|
||||
self.submodules.wrpll_sampler = DDMTDSamplerGTP(
|
||||
self.drtio_transceiver,
|
||||
platform.request("cdr_clk_clean_fabric"))
|
||||
helper_clk_pads = platform.request("ddmtd_helper_clk")
|
||||
self.submodules.wrpll = WRPLL(
|
||||
helper_clk_pads=helper_clk_pads,
|
||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
||||
ddmtd_inputs=self.wrpll_sampler)
|
||||
self.csr_devices.append("wrpll")
|
||||
# note: do not use self.wrpll.cd_helper.clk; otherwise, vivado craps out with:
|
||||
# critical warning: create_clock attempting to set clock on an unknown port/pin
|
||||
# command: "create_clock -period 7.920000 -waveform {0.000000 3.960000} -name
|
||||
# helper_clk [get_xlnx_outside_genome_inst_pin 20 0]
|
||||
platform.add_period_constraint(helper_clk_pads.p, rtio_clk_period*0.99)
|
||||
platform.add_false_path_constraints(self.crg.cd_sys.clk, helper_clk_pads.p)
|
||||
else:
|
||||
self.submodules.siphaser = SiPhaser7Series(
|
||||
si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0"
|
||||
else platform.request("si5324_clkin"),
|
||||
rx_synchronizer=self.rx_synchronizer,
|
||||
ref_clk=self.crg.clk125_div2, ref_div2=True,
|
||||
rtio_clk_freq=rtio_clk_freq)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||
self.csr_devices.append("siphaser")
|
||||
self.config["HAS_SI5324"] = None
|
||||
self.config["SI5324_SOFT_RESET"] = None
|
||||
|
||||
self.submodules.siphaser = SiPhaser7Series(
|
||||
si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0"
|
||||
else platform.request("si5324_clkin"),
|
||||
rx_synchronizer=self.rx_synchronizer,
|
||||
ref_clk=self.crg.clk125_div2, ref_div2=True,
|
||||
rtio_clk_freq=rtio_clk_freq)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||
self.csr_devices.append("siphaser")
|
||||
self.config["HAS_SI5324"] = None
|
||||
self.config["SI5324_SOFT_RESET"] = None
|
||||
|
||||
gtp = self.drtio_transceiver.gtps[0]
|
||||
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
|
||||
|
@ -596,9 +578,6 @@ class SatelliteBase(BaseSoC):
|
|||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk,
|
||||
gtp.txoutclk, gtp.rxoutclk)
|
||||
if with_wrpll:
|
||||
platform.add_false_path_constraints(
|
||||
helper_clk_pads.p, gtp.rxoutclk)
|
||||
for gtp in self.drtio_transceiver.gtps[1:]:
|
||||
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
|
||||
platform.add_false_path_constraints(
|
||||
|
@ -677,14 +656,11 @@ def main():
|
|||
parser.add_argument("-V", "--variant", default="tester",
|
||||
help="variant: {} (default: %(default)s)".format(
|
||||
"/".join(sorted(VARIANTS.keys()))))
|
||||
parser.add_argument("--with-wrpll", default=False, action="store_true")
|
||||
parser.add_argument("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
args = parser.parse_args()
|
||||
|
||||
argdict = dict()
|
||||
if args.with_wrpll:
|
||||
argdict["with_wrpll"] = True
|
||||
argdict["gateware_identifier_str"] = args.gateware_identifier_str
|
||||
|
||||
variant = args.variant.lower()
|
||||
|
|
|
@ -21,7 +21,6 @@ from artiq.gateware import fmcdio_vhdci_eem
|
|||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_ultrascale, sawg
|
||||
from artiq.gateware.drtio.transceiver import gth_ultrascale
|
||||
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
||||
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerExtFF
|
||||
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
|
||||
from artiq.gateware.drtio import *
|
||||
from artiq.build_soc import *
|
||||
|
@ -54,7 +53,7 @@ class SatelliteBase(MiniSoC):
|
|||
}
|
||||
mem_map.update(MiniSoC.mem_map)
|
||||
|
||||
def __init__(self, rtio_clk_freq=125e6, identifier_suffix="", gateware_identifier_str=None, with_sfp=False, *, with_wrpll, **kwargs):
|
||||
def __init__(self, rtio_clk_freq=125e6, identifier_suffix="", gateware_identifier_str=None, with_sfp=False, *, **kwargs):
|
||||
MiniSoC.__init__(self,
|
||||
cpu_type="vexriscv",
|
||||
cpu_bus_width=64,
|
||||
|
@ -69,10 +68,6 @@ class SatelliteBase(MiniSoC):
|
|||
|
||||
platform = self.platform
|
||||
|
||||
if with_wrpll:
|
||||
clock_recout_pads = platform.request("ddmtd_rec_clk")
|
||||
else:
|
||||
clock_recout_pads = None
|
||||
if with_sfp:
|
||||
# Use SFP0 to connect to master (Kasli)
|
||||
self.comb += platform.request("sfp_tx_disable", 0).eq(0)
|
||||
|
@ -84,7 +79,7 @@ class SatelliteBase(MiniSoC):
|
|||
data_pads=[drtio_uplink, platform.request("rtm_amc_link")],
|
||||
sys_clk_freq=self.clk_freq,
|
||||
rtio_clk_freq=rtio_clk_freq,
|
||||
clock_recout_pads=clock_recout_pads)
|
||||
clock_recout_pads=None)
|
||||
self.csr_devices.append("drtio_transceiver")
|
||||
|
||||
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
|
||||
|
@ -134,39 +129,22 @@ class SatelliteBase(MiniSoC):
|
|||
|
||||
rtio_clk_period = 1e9/rtio_clk_freq
|
||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
||||
if with_wrpll:
|
||||
self.comb += [
|
||||
platform.request("filtered_clk_sel").eq(0),
|
||||
platform.request("ddmtd_main_dcxo_oe").eq(1),
|
||||
platform.request("ddmtd_helper_dcxo_oe").eq(1)
|
||||
]
|
||||
self.submodules.wrpll_sampler = DDMTDSamplerExtFF(
|
||||
platform.request("ddmtd_inputs"))
|
||||
self.submodules.wrpll = WRPLL(
|
||||
helper_clk_pads=platform.request("ddmtd_helper_clk"),
|
||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
||||
ddmtd_inputs=self.wrpll_sampler)
|
||||
self.csr_devices.append("wrpll")
|
||||
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
|
||||
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
|
||||
else:
|
||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
||||
self.submodules.siphaser = SiPhaser7Series(
|
||||
si5324_clkin=platform.request("si5324_clkin"),
|
||||
rx_synchronizer=self.rx_synchronizer,
|
||||
ultrascale=True,
|
||||
rtio_clk_freq=rtio_clk_freq)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||
self.csr_devices.append("siphaser")
|
||||
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
||||
self.csr_devices.append("si5324_rst_n")
|
||||
i2c = self.platform.request("i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
self.csr_devices.append("i2c")
|
||||
self.config["I2C_BUS_COUNT"] = 1
|
||||
self.config["HAS_SI5324"] = None
|
||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
||||
self.submodules.siphaser = SiPhaser7Series(
|
||||
si5324_clkin=platform.request("si5324_clkin"),
|
||||
rx_synchronizer=self.rx_synchronizer,
|
||||
ultrascale=True,
|
||||
rtio_clk_freq=rtio_clk_freq)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||
self.csr_devices.append("siphaser")
|
||||
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
||||
self.csr_devices.append("si5324_rst_n")
|
||||
i2c = self.platform.request("i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
self.csr_devices.append("i2c")
|
||||
self.config["I2C_BUS_COUNT"] = 1
|
||||
self.config["HAS_SI5324"] = None
|
||||
|
||||
gth = self.drtio_transceiver.gths[0]
|
||||
platform.add_period_constraint(gth.txoutclk, rtio_clk_period/2)
|
||||
|
@ -433,7 +411,6 @@ def main():
|
|||
default="sawg",
|
||||
help="Change type of signal generator. This is used exclusively for "
|
||||
"development and debugging.")
|
||||
parser.add_argument("--with-wrpll", default=False, action="store_true")
|
||||
parser.add_argument("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
args = parser.parse_args()
|
||||
|
@ -443,13 +420,11 @@ def main():
|
|||
soc = Satellite(
|
||||
with_sfp=args.sfp,
|
||||
jdcg_type=args.jdcg_type,
|
||||
with_wrpll=args.with_wrpll,
|
||||
gateware_identifier_str=args.gateware_identifier_str,
|
||||
**soc_sayma_amc_argdict(args))
|
||||
elif variant == "simplesatellite":
|
||||
soc = SimpleSatellite(
|
||||
with_sfp=args.sfp,
|
||||
with_wrpll=args.with_wrpll,
|
||||
gateware_identifier_str=args.gateware_identifier_str,
|
||||
**soc_sayma_amc_argdict(args))
|
||||
else:
|
||||
|
|
|
@ -21,7 +21,6 @@ from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series
|
|||
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path
|
||||
from artiq.gateware.drtio.transceiver import gtp_7series
|
||||
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
||||
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerGTP
|
||||
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
|
||||
from artiq.gateware.drtio import *
|
||||
from artiq.build_soc import add_identifier
|
||||
|
@ -34,7 +33,7 @@ class _SatelliteBase(BaseSoC):
|
|||
}
|
||||
mem_map.update(BaseSoC.mem_map)
|
||||
|
||||
def __init__(self, rtio_clk_freq, *, with_wrpll, gateware_identifier_str, **kwargs):
|
||||
def __init__(self, rtio_clk_freq, *, gateware_identifier_str, **kwargs):
|
||||
BaseSoC.__init__(self,
|
||||
cpu_type="vexriscv",
|
||||
cpu_bus_width=64,
|
||||
|
@ -96,41 +95,23 @@ class _SatelliteBase(BaseSoC):
|
|||
gtp = self.drtio_transceiver.gtps[0]
|
||||
rtio_clk_period = 1e9/rtio_clk_freq
|
||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
||||
if with_wrpll:
|
||||
self.comb += [
|
||||
platform.request("filtered_clk_sel").eq(0),
|
||||
platform.request("ddmtd_main_dcxo_oe").eq(1),
|
||||
platform.request("ddmtd_helper_dcxo_oe").eq(1)
|
||||
]
|
||||
self.submodules.wrpll_sampler = DDMTDSamplerGTP(
|
||||
self.drtio_transceiver,
|
||||
platform.request("cdr_clk_clean_fabric"))
|
||||
self.submodules.wrpll = WRPLL(
|
||||
helper_clk_pads=platform.request("ddmtd_helper_clk"),
|
||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
||||
ddmtd_inputs=self.wrpll_sampler)
|
||||
self.csr_devices.append("wrpll")
|
||||
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
|
||||
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
|
||||
platform.add_false_path_constraints(self.wrpll.cd_helper.clk, gtp.rxoutclk)
|
||||
else:
|
||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
||||
self.submodules.siphaser = SiPhaser7Series(
|
||||
si5324_clkin=platform.request("si5324_clkin"),
|
||||
rx_synchronizer=self.rx_synchronizer,
|
||||
ref_clk=self.crg.cd_sys.clk, ref_div2=True,
|
||||
rtio_clk_freq=rtio_clk_freq)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||
self.csr_devices.append("siphaser")
|
||||
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
||||
self.csr_devices.append("si5324_rst_n")
|
||||
i2c = self.platform.request("i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
self.csr_devices.append("i2c")
|
||||
self.config["I2C_BUS_COUNT"] = 1
|
||||
self.config["HAS_SI5324"] = None
|
||||
|
||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
||||
self.submodules.siphaser = SiPhaser7Series(
|
||||
si5324_clkin=platform.request("si5324_clkin"),
|
||||
rx_synchronizer=self.rx_synchronizer,
|
||||
ref_clk=self.crg.cd_sys.clk, ref_div2=True,
|
||||
rtio_clk_freq=rtio_clk_freq)
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||
self.csr_devices.append("siphaser")
|
||||
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
||||
self.csr_devices.append("si5324_rst_n")
|
||||
i2c = self.platform.request("i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
self.csr_devices.append("i2c")
|
||||
self.config["I2C_BUS_COUNT"] = 1
|
||||
self.config["HAS_SI5324"] = None
|
||||
|
||||
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
|
||||
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
|
||||
|
@ -258,7 +239,6 @@ def main():
|
|||
soc_sayma_rtm_args(parser)
|
||||
parser.add_argument("--rtio-clk-freq",
|
||||
default=150, type=int, help="RTIO clock frequency in MHz")
|
||||
parser.add_argument("--with-wrpll", default=False, action="store_true")
|
||||
parser.add_argument("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
parser.set_defaults(output_dir=os.path.join("artiq_sayma", "rtm"))
|
||||
|
@ -266,7 +246,6 @@ def main():
|
|||
|
||||
soc = Satellite(
|
||||
rtio_clk_freq=1e6*args.rtio_clk_freq,
|
||||
with_wrpll=args.with_wrpll,
|
||||
gateware_identifier_str=args.gateware_identifier_str,
|
||||
**soc_sayma_rtm_argdict(args))
|
||||
builder = SatmanSoCBuilder(soc, **builder_argdict(args))
|
||||
|
|
|
@ -1,158 +0,0 @@
|
|||
import unittest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from migen import *
|
||||
|
||||
from artiq.gateware.drtio.wrpll.ddmtd import Collector
|
||||
from artiq.gateware.drtio.wrpll import thls, filters
|
||||
|
||||
|
||||
class HelperChainTB(Module):
|
||||
def __init__(self, N):
|
||||
self.tag_ref = Signal(N)
|
||||
self.input_stb = Signal()
|
||||
self.adpll = Signal((24, True))
|
||||
self.out_stb = Signal()
|
||||
|
||||
###
|
||||
|
||||
self.submodules.collector = Collector(N)
|
||||
self.submodules.loop_filter = thls.make(filters.helper, data_width=48)
|
||||
|
||||
self.comb += [
|
||||
self.collector.tag_ref.eq(self.tag_ref),
|
||||
self.collector.ref_stb.eq(self.input_stb),
|
||||
self.collector.main_stb.eq(self.input_stb),
|
||||
self.loop_filter.input.eq(self.collector.out_helper << 22),
|
||||
self.loop_filter.input_stb.eq(self.collector.out_stb),
|
||||
self.adpll.eq(self.loop_filter.output),
|
||||
self.out_stb.eq(self.loop_filter.output_stb),
|
||||
]
|
||||
|
||||
|
||||
class TestDSP(unittest.TestCase):
|
||||
def test_main_collector(self):
|
||||
N = 2
|
||||
collector = Collector(N=N)
|
||||
# check collector phase unwrapping
|
||||
tags = [(0, 0, 0),
|
||||
(0, 1, 1),
|
||||
(2, 1, -1),
|
||||
(3, 1, -2),
|
||||
(0, 1, -3),
|
||||
(1, 1, -4),
|
||||
(2, 1, -5),
|
||||
(3, 1, -6),
|
||||
(3, 3, -4),
|
||||
(0, 0, -4),
|
||||
(0, 1, -3),
|
||||
(0, 2, -2),
|
||||
(0, 3, -1),
|
||||
(0, 0, 0)]
|
||||
for i in range(10):
|
||||
tags.append((i % (2**N), (i+1) % (2**N), 1))
|
||||
|
||||
def generator():
|
||||
for tag_ref, tag_main, out in tags:
|
||||
yield collector.tag_ref.eq(tag_ref)
|
||||
yield collector.tag_main.eq(tag_main)
|
||||
yield collector.main_stb.eq(1)
|
||||
yield collector.ref_stb.eq(1)
|
||||
|
||||
yield
|
||||
|
||||
yield collector.main_stb.eq(0)
|
||||
yield collector.ref_stb.eq(0)
|
||||
|
||||
while not (yield collector.out_stb):
|
||||
yield
|
||||
|
||||
out_main = yield collector.out_main
|
||||
self.assertEqual(out_main, out)
|
||||
|
||||
run_simulation(collector, generator())
|
||||
|
||||
def test_helper_collector(self):
|
||||
N = 3
|
||||
collector = Collector(N=N)
|
||||
# check collector phase unwrapping
|
||||
tags = [((2**N - 1 - tag) % (2**N), -1) for tag in range(20)]
|
||||
tags += [((tags[-1][0] + 1 + tag) % (2**N), 1) for tag in range(20)]
|
||||
tags += [((tags[-1][0] - 2 - 2*tag) % (2**N), -2) for tag in range(20)]
|
||||
|
||||
def generator():
|
||||
for tag_ref, out in tags:
|
||||
yield collector.tag_ref.eq(tag_ref)
|
||||
yield collector.main_stb.eq(1)
|
||||
yield collector.ref_stb.eq(1)
|
||||
|
||||
yield
|
||||
|
||||
yield collector.main_stb.eq(0)
|
||||
yield collector.ref_stb.eq(0)
|
||||
|
||||
while not (yield collector.out_stb):
|
||||
yield
|
||||
|
||||
out_helper = yield collector.out_helper
|
||||
self.assertEqual(out_helper, out)
|
||||
|
||||
run_simulation(collector, generator())
|
||||
|
||||
# test helper collector + filter against output from MATLAB model
|
||||
def test_helper_chain(self):
|
||||
pll = HelperChainTB(15)
|
||||
|
||||
initial_helper_out = -8000
|
||||
ref_tags = np.array([
|
||||
24778, 16789, 8801, 814, 25596, 17612, 9628, 1646,
|
||||
26433, 18453, 10474, 2496, 27287, 19311, 11337, 3364, 28160,
|
||||
20190, 12221, 4253, 29054, 21088, 13124, 5161, 29966, 22005,
|
||||
14045, 6087, 30897, 22940, 14985, 7031, 31847, 23895, 15944,
|
||||
7995, 47, 24869, 16923, 8978, 1035, 25861, 17920, 9981,
|
||||
2042, 26873, 18937, 11002, 3069, 27904, 19973, 12042, 4113,
|
||||
28953, 21026, 13100, 5175, 30020, 22098, 14177, 6257, 31106,
|
||||
23189, 15273, 7358, 32212, 24300, 16388, 8478, 569, 25429,
|
||||
17522, 9617, 1712, 26577, 18675, 10774, 2875, 27745, 19848,
|
||||
11951, 4056, 28930, 21038, 13147, 5256, 30135, 22247, 14361,
|
||||
6475, 31359, 23476, 15595, 7714, 32603, 24725, 16847, 8971,
|
||||
1096
|
||||
])
|
||||
adpll_sim = np.array([
|
||||
8, 24, 41, 57, 74, 91, 107, 124, 140, 157, 173,
|
||||
190, 206, 223, 239, 256, 273, 289, 306, 322, 339, 355,
|
||||
372, 388, 405, 421, 438, 454, 471, 487, 504, 520, 537,
|
||||
553, 570, 586, 603, 619, 636, 652, 668, 685, 701, 718,
|
||||
734, 751, 767, 784, 800, 817, 833, 850, 866, 882, 899,
|
||||
915, 932, 948, 965, 981, 998, 1014, 1030, 1047, 1063, 1080,
|
||||
1096, 1112, 1129, 1145, 1162, 1178, 1194, 1211, 1227, 1244, 1260,
|
||||
1276, 1293, 1309, 1326, 1342, 1358, 1375, 1391, 1407, 1424, 1440,
|
||||
1457, 1473, 1489, 1506, 1522, 1538, 1555, 1571, 1587, 1604, 1620,
|
||||
1636])
|
||||
|
||||
def sim():
|
||||
yield pll.collector.out_helper.eq(initial_helper_out)
|
||||
for ref_tag, adpll_matlab in zip(ref_tags, adpll_sim):
|
||||
# feed collector
|
||||
yield pll.tag_ref.eq(int(ref_tag))
|
||||
yield pll.input_stb.eq(1)
|
||||
|
||||
yield
|
||||
|
||||
yield pll.input_stb.eq(0)
|
||||
|
||||
while not (yield pll.collector.out_stb):
|
||||
yield
|
||||
|
||||
tag_diff = yield pll.collector.out_helper
|
||||
|
||||
while not (yield pll.loop_filter.output_stb):
|
||||
yield
|
||||
|
||||
adpll_migen = yield pll.adpll
|
||||
self.assertEqual(adpll_migen, adpll_matlab)
|
||||
|
||||
yield
|
||||
|
||||
run_simulation(pll, [sim()])
|
|
@ -1,55 +0,0 @@
|
|||
import unittest
|
||||
|
||||
from migen import *
|
||||
|
||||
from artiq.gateware.drtio.wrpll import thls
|
||||
|
||||
|
||||
a = 0
|
||||
|
||||
def simple_test(x):
|
||||
global a
|
||||
a = a + (x*4 >> 1)
|
||||
return a
|
||||
|
||||
|
||||
class TestTHLS(unittest.TestCase):
|
||||
def test_thls(self):
|
||||
global a
|
||||
|
||||
proc = thls.Processor()
|
||||
a = 0
|
||||
cp = thls.compile(proc, simple_test)
|
||||
print("Program:")
|
||||
cp.pretty_print()
|
||||
cp.dimension_processor()
|
||||
print("Encoded program:", cp.encode())
|
||||
proc_impl = proc.implement(cp.encode(), cp.data)
|
||||
|
||||
def send_values(values):
|
||||
for value in values:
|
||||
yield proc_impl.input.eq(value)
|
||||
yield proc_impl.input_stb.eq(1)
|
||||
yield
|
||||
yield proc_impl.input.eq(0)
|
||||
yield proc_impl.input_stb.eq(0)
|
||||
yield
|
||||
while (yield proc_impl.busy):
|
||||
yield
|
||||
@passive
|
||||
def receive_values(callback):
|
||||
while True:
|
||||
while not (yield proc_impl.output_stb):
|
||||
yield
|
||||
callback((yield proc_impl.output))
|
||||
yield
|
||||
|
||||
send_list = [42, 40, 10, 10]
|
||||
receive_list = []
|
||||
|
||||
run_simulation(proc_impl, [send_values(send_list), receive_values(receive_list.append)])
|
||||
print("Execution:", send_list, "->", receive_list)
|
||||
|
||||
a = 0
|
||||
expected_list = [simple_test(x) for x in send_list]
|
||||
self.assertEqual(receive_list, expected_list)
|
|
@ -14,7 +14,7 @@ ARTIQ itself does not depend on Nix, and it is also possible to compile everythi
|
|||
* Install the `Nix package manager <http://nixos.org/nix/>`_, version 2.4 or later. Prefer a single-user installation for simplicity.
|
||||
* If you did not install Vivado in its default location ``/opt``, clone the ARTIQ Git repository and edit ``flake.nix`` accordingly.
|
||||
* Enable flakes in Nix by e.g. adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (for example ``~/.config/nix/nix.conf``).
|
||||
* Enter the development shell by running ``nix develop git+https://github.com/m-labs/artiq.git``, or alternatively by cloning the ARTIQ Git repository and running ``nix develop`` at the root (where ``flake.nix`` is).
|
||||
* Enter the development shell by running ``nix develop git+https://github.com/m-labs/artiq.git\?ref=release-7``, or alternatively by cloning the ARTIQ Git repository and running ``nix develop`` at the root (where ``flake.nix`` is).
|
||||
* You can then build the firmware and gateware with a command such as ``$ python -m artiq.gateware.targets.kasli``. If you are using a JSON system description file, use ``$ python -m artiq.gateware.targets.kasli_generic file.json``.
|
||||
* Flash the binaries into the FPGA board with a command such as ``$ artiq_flash --srcbuild -d artiq_kasli -V <your_variant>``. You need to configure OpenOCD as explained :ref:`in the user section <configuring-openocd>`. OpenOCD is already part of the flake's development environment.
|
||||
* Check that the board boots and examine the UART messages by running a serial terminal program, e.g. ``$ flterm /dev/ttyUSB1`` (``flterm`` is part of MiSoC and installed in the flake's development environment). Leave the terminal running while you are flashing the board, so that you see the startup messages when the board boots immediately after flashing. You can also restart the board (without reflashing it) with ``$ artiq_flash start``.
|
||||
|
|
|
@ -21,7 +21,7 @@ Once Nix is installed, enable Flakes: ::
|
|||
$ mkdir -p ~/.config/nix
|
||||
$ echo "experimental-features = nix-command flakes" > ~/.config/nix/nix.conf
|
||||
|
||||
The easiest way to obtain ARTIQ is then to install it into the user environment with ``$ nix profile install git+https://github.com/m-labs/artiq.git``. Answer "Yes" to the questions about setting Nix configuration options. This provides a minimal installation of ARTIQ where the usual commands (``artiq_master``, ``artiq_dashboard``, ``artiq_run``, etc.) are available.
|
||||
The easiest way to obtain ARTIQ is then to install it into the user environment with ``$ nix profile install git+https://github.com/m-labs/artiq.git\?ref=release-7``. Answer "Yes" to the questions about setting Nix configuration options. This provides a minimal installation of ARTIQ where the usual commands (``artiq_master``, ``artiq_dashboard``, ``artiq_run``, etc.) are available.
|
||||
|
||||
This installation is however quite limited, as Nix creates a dedicated Python environment for the ARTIQ commands alone. This means that other useful Python packages that you may want (pandas, matplotlib, ...) are not available to them.
|
||||
|
||||
|
@ -30,8 +30,8 @@ Installing multiple packages and making them visible to the ARTIQ commands requi
|
|||
::
|
||||
|
||||
{
|
||||
inputs.artiq.url = "git+https://github.com/m-labs/artiq.git";
|
||||
inputs.extrapkg.url = "git+https://git.m-labs.hk/M-Labs/artiq-extrapkg.git";
|
||||
inputs.artiq.url = "git+https://github.com/m-labs/artiq.git?ref=release-7";
|
||||
inputs.extrapkg.url = "git+https://git.m-labs.hk/M-Labs/artiq-extrapkg.git?ref=release-7";
|
||||
inputs.extrapkg.inputs.artiq.follows = "artiq";
|
||||
outputs = { self, artiq, extrapkg }:
|
||||
let
|
||||
|
@ -261,19 +261,13 @@ If you purchased a Kasli device from M-Labs, it usually comes with the IP addres
|
|||
|
||||
and then reboot the device (with ``artiq_flash start`` or a power cycle).
|
||||
|
||||
If the ip config field is not set, or set to "use_dhcp" then the device will attempt to obtain an IP address using
|
||||
DHCP. If a static IP address is wanted, install OpenOCD as before, and flash the IP (and, if necessary, MAC) addresses
|
||||
directly: ::
|
||||
In other cases, install OpenOCD as before, and flash the IP (and, if necessary, MAC) addresses directly: ::
|
||||
|
||||
$ artiq_mkfs flash_storage.img -s mac xx:xx:xx:xx:xx:xx -s ip xx.xx.xx.xx
|
||||
$ artiq_flash -t [board] -V [variant] -f flash_storage.img storage start
|
||||
|
||||
For Kasli devices, flashing a MAC address is not necessary as they can obtain it from their EEPROM.
|
||||
|
||||
If DHCP has been used the address can be found in the console output, which can be viewed using: ::
|
||||
|
||||
$ python -m misoc.tools.flterm /dev/ttyUSB2
|
||||
|
||||
Check that you can ping the device. If ping fails, check that the Ethernet link LED is ON - on Kasli, it is the LED next to the SFP0 connector. As a next step, look at the messages emitted on the UART during boot. Use a program such as flterm or PuTTY to connect to the device's serial port at 115200bps 8-N-1 and reboot the device. On Kasli, the serial port is on FTDI channel 2 with v1.1 hardware (with channel 0 being JTAG) and on FTDI channel 1 with v1.0 hardware.
|
||||
|
||||
If you want to use IPv6, the device also has a link-local address that corresponds to its EUI-64, and an additional arbitrary IPv6 address can be defined by using the ``ip6`` configuration key. All IPv4 and IPv6 addresses can be used at the same time.
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 57 KiB |
|
@ -21,7 +21,7 @@
|
|||
artiqVersionMajor = 7;
|
||||
artiqVersionMinor = self.sourceInfo.revCount or 0;
|
||||
artiqVersionId = self.sourceInfo.shortRev or "unknown";
|
||||
artiqVersion = (builtins.toString artiqVersionMajor) + "." + (builtins.toString artiqVersionMinor) + "." + artiqVersionId + ".beta";
|
||||
artiqVersion = (builtins.toString artiqVersionMajor) + "." + (builtins.toString artiqVersionMinor) + "." + artiqVersionId;
|
||||
artiqRev = self.sourceInfo.rev or "unknown";
|
||||
|
||||
rustManifest = pkgs.fetchurl {
|
||||
|
|
Loading…
Reference in New Issue