forked from M-Labs/artiq
parent
7cd02d30b7
commit
e9b81f6e33
|
@ -34,8 +34,6 @@ pub mod i2c_eeprom;
|
||||||
|
|
||||||
#[cfg(has_slave_fpga_cfg)]
|
#[cfg(has_slave_fpga_cfg)]
|
||||||
pub mod slave_fpga;
|
pub mod slave_fpga;
|
||||||
#[cfg(has_serwb_phy_amc)]
|
|
||||||
pub mod serwb;
|
|
||||||
#[cfg(has_hmc830_7043)]
|
#[cfg(has_hmc830_7043)]
|
||||||
pub mod hmc830_7043;
|
pub mod hmc830_7043;
|
||||||
#[cfg(has_ad9154)]
|
#[cfg(has_ad9154)]
|
||||||
|
|
|
@ -1,138 +0,0 @@
|
||||||
use core::{cmp, str};
|
|
||||||
use board_misoc::{csr, clock};
|
|
||||||
|
|
||||||
fn debug_print(rtm: bool) {
|
|
||||||
debug!("AMC serwb settings:");
|
|
||||||
debug!(" bitslip: {}", unsafe { csr::serwb_phy_amc::control_bitslip_read() });
|
|
||||||
debug!(" ready: {}", unsafe { csr::serwb_phy_amc::control_ready_read() });
|
|
||||||
debug!(" error: {}", unsafe { csr::serwb_phy_amc::control_error_read() });
|
|
||||||
|
|
||||||
if rtm {
|
|
||||||
debug!("RTM serwb settings:");
|
|
||||||
debug!(" bitslip: {}", unsafe { csr::serwb_phy_rtm::control_bitslip_read() });
|
|
||||||
debug!(" ready: {}", unsafe { csr::serwb_phy_rtm::control_ready_read() });
|
|
||||||
debug!(" error: {}", unsafe { csr::serwb_phy_rtm::control_error_read() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prbs_test() {
|
|
||||||
let prbs_test_cycles : u32 = 1<<22;
|
|
||||||
// 40 bits @125MHz linerate
|
|
||||||
let prbs_test_us : u64 = ((prbs_test_cycles as u64)*40)/125;
|
|
||||||
|
|
||||||
info!("RTM to AMC link test...");
|
|
||||||
unsafe {
|
|
||||||
csr::serwb_phy_amc::control_prbs_cycles_write(prbs_test_cycles);
|
|
||||||
csr::serwb_phy_amc::control_prbs_start_write(1);
|
|
||||||
}
|
|
||||||
clock::spin_us(prbs_test_us*110/100); // PRBS test time + 10%
|
|
||||||
let errors = unsafe {
|
|
||||||
csr::serwb_phy_amc::control_prbs_errors_read()
|
|
||||||
};
|
|
||||||
if errors == 0 {
|
|
||||||
info!(" ...passed")
|
|
||||||
} else {
|
|
||||||
error!(" {} errors found", errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("AMC to RTM link test...");
|
|
||||||
unsafe {
|
|
||||||
csr::serwb_phy_rtm::control_prbs_cycles_write(prbs_test_cycles);
|
|
||||||
csr::serwb_phy_rtm::control_prbs_start_write(1);
|
|
||||||
}
|
|
||||||
clock::spin_us(prbs_test_us*110/100); // PRBS test time + 10%
|
|
||||||
let errors = unsafe {
|
|
||||||
csr::serwb_phy_rtm::control_prbs_errors_read()
|
|
||||||
};
|
|
||||||
if errors == 0 {
|
|
||||||
info!(" ...passed");
|
|
||||||
} else {
|
|
||||||
error!(" {} errors found", errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn magic_test() {
|
|
||||||
// Try reading the magic number register on the other side of the bridge.
|
|
||||||
let rtm_magic = unsafe {
|
|
||||||
csr::rtm_magic::magic_read()
|
|
||||||
};
|
|
||||||
if rtm_magic != 0x5352544d {
|
|
||||||
error!("incorrect RTM magic number: 0x{:08x}", rtm_magic);
|
|
||||||
// proceed anyway
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prng32(seed: &mut u32) -> u32 {
|
|
||||||
*seed = 1664525 * *seed + 1013904223;
|
|
||||||
*seed
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wishbone_test() {
|
|
||||||
let test_length: u32 = 512;
|
|
||||||
let mut test_errors : u32 = 0;
|
|
||||||
|
|
||||||
let mut seed : u32;
|
|
||||||
|
|
||||||
info!("Wishbone test...");
|
|
||||||
unsafe {
|
|
||||||
// Alternate pseudo random write/read bursts of
|
|
||||||
// increasing size.
|
|
||||||
for length in 0..test_length {
|
|
||||||
// Pseudo random writes
|
|
||||||
seed = length;
|
|
||||||
for _ in 0..length {
|
|
||||||
csr::rtm_scratch::write_data_write(prng32(&mut seed));
|
|
||||||
csr::rtm_scratch::write_stb_write(1);
|
|
||||||
}
|
|
||||||
// Pseudo random reads
|
|
||||||
seed = length;
|
|
||||||
for _ in 0..length {
|
|
||||||
if csr::rtm_scratch::read_data_read() != prng32(&mut seed) {
|
|
||||||
test_errors += 1;
|
|
||||||
}
|
|
||||||
csr::rtm_scratch::read_ack_write(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if test_errors == 0 {
|
|
||||||
info!(" ...passed");
|
|
||||||
} else {
|
|
||||||
error!(" {} errors found", test_errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_rtm_ident(buf: &mut [u8]) -> &str {
|
|
||||||
unsafe {
|
|
||||||
csr::rtm_identifier::address_write(0);
|
|
||||||
let len = csr::rtm_identifier::data_read();
|
|
||||||
let len = cmp::min(len, buf.len() as u8);
|
|
||||||
for i in 0..len {
|
|
||||||
csr::rtm_identifier::address_write(1 + i);
|
|
||||||
buf[i as usize] = csr::rtm_identifier::data_read();
|
|
||||||
}
|
|
||||||
str::from_utf8_unchecked(&buf[..len as usize])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wait_init() {
|
|
||||||
info!("waiting for AMC/RTM serwb bridge to be ready...");
|
|
||||||
unsafe {
|
|
||||||
csr::serwb_phy_amc::control_reset_write(1);
|
|
||||||
while csr::serwb_phy_amc::control_ready_read() == 0 {
|
|
||||||
if csr::serwb_phy_amc::control_error_read() == 1 {
|
|
||||||
debug_print(false);
|
|
||||||
warn!("AMC/RTM serwb bridge initialization failed, retrying.");
|
|
||||||
csr::serwb_phy_amc::control_reset_write(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info!(" ...done.");
|
|
||||||
|
|
||||||
prbs_test();
|
|
||||||
magic_test();
|
|
||||||
wishbone_test();
|
|
||||||
|
|
||||||
debug_print(true);
|
|
||||||
|
|
||||||
info!("RTM gateware version {}", read_rtm_ident(&mut [0; 64]));
|
|
||||||
}
|
|
|
@ -91,8 +91,6 @@ fn setup_log_levels() {
|
||||||
fn sayma_hw_init() {
|
fn sayma_hw_init() {
|
||||||
#[cfg(has_slave_fpga_cfg)]
|
#[cfg(has_slave_fpga_cfg)]
|
||||||
board_artiq::slave_fpga::load().expect("cannot load RTM FPGA gateware");
|
board_artiq::slave_fpga::load().expect("cannot load RTM FPGA gateware");
|
||||||
#[cfg(has_serwb_phy_amc)]
|
|
||||||
board_artiq::serwb::wait_init();
|
|
||||||
|
|
||||||
#[cfg(has_hmc830_7043)]
|
#[cfg(has_hmc830_7043)]
|
||||||
/* must be the first SPI init because of HMC830 SPI mode selection */
|
/* must be the first SPI init because of HMC830 SPI mode selection */
|
||||||
|
|
|
@ -10,8 +10,6 @@ extern crate board_artiq;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use board_misoc::{csr, irq, ident, clock, uart_logger};
|
use board_misoc::{csr, irq, ident, clock, uart_logger};
|
||||||
use board_artiq::{i2c, spi, si5324, drtioaux};
|
use board_artiq::{i2c, spi, si5324, drtioaux};
|
||||||
#[cfg(has_serwb_phy_amc)]
|
|
||||||
use board_artiq::serwb;
|
|
||||||
use board_artiq::drtio_routing;
|
use board_artiq::drtio_routing;
|
||||||
#[cfg(has_hmc830_7043)]
|
#[cfg(has_hmc830_7043)]
|
||||||
use board_artiq::hmc830_7043;
|
use board_artiq::hmc830_7043;
|
||||||
|
@ -413,8 +411,6 @@ pub extern fn main() -> i32 {
|
||||||
|
|
||||||
#[cfg(has_slave_fpga_cfg)]
|
#[cfg(has_slave_fpga_cfg)]
|
||||||
board_artiq::slave_fpga::load().expect("cannot load RTM FPGA gateware");
|
board_artiq::slave_fpga::load().expect("cannot load RTM FPGA gateware");
|
||||||
#[cfg(has_serwb_phy_amc)]
|
|
||||||
serwb::wait_init();
|
|
||||||
|
|
||||||
i2c::init();
|
i2c::init();
|
||||||
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
|
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
from collections import OrderedDict
|
|
||||||
from operator import itemgetter
|
|
||||||
import csv
|
|
||||||
|
|
||||||
from misoc.interconnect.csr import CSRStatus, CSRStorage
|
|
||||||
|
|
||||||
|
|
||||||
def _get_csr_data(csv_file):
|
|
||||||
csr_data = OrderedDict()
|
|
||||||
with open(csv_file) as csv_file_f:
|
|
||||||
csv_reader = csv.reader(csv_file_f)
|
|
||||||
for name, address, length, ro in csv_reader:
|
|
||||||
region_name, csr_name = name.split(".")
|
|
||||||
address = int(address, 0)
|
|
||||||
length = int(length, 0)
|
|
||||||
if ro == "ro":
|
|
||||||
ro = True
|
|
||||||
elif ro == "rw":
|
|
||||||
ro = False
|
|
||||||
else:
|
|
||||||
raise ValueError
|
|
||||||
if region_name not in csr_data:
|
|
||||||
csr_data[region_name] = []
|
|
||||||
csr_data[region_name].append((csr_name, address, length, ro))
|
|
||||||
return csr_data
|
|
||||||
|
|
||||||
|
|
||||||
def get_remote_csr_regions(offset, csv_file):
|
|
||||||
busword = 32
|
|
||||||
regions = []
|
|
||||||
for region_name, csrs_info in _get_csr_data(csv_file).items():
|
|
||||||
csrs_info = sorted(csrs_info, key=itemgetter(1))
|
|
||||||
origin = csrs_info[0][1]
|
|
||||||
next_address = origin
|
|
||||||
csrs = []
|
|
||||||
for csr_name, address, length, ro in csrs_info:
|
|
||||||
if address != next_address:
|
|
||||||
raise ValueError("CSRs are not contiguous")
|
|
||||||
nr = (length + busword - 1)//busword
|
|
||||||
next_address += nr*busword//8
|
|
||||||
if ro:
|
|
||||||
csr = CSRStatus(length, name=csr_name)
|
|
||||||
else:
|
|
||||||
csr = CSRStorage(length, name=csr_name)
|
|
||||||
csrs.append(csr)
|
|
||||||
regions.append((region_name, offset + origin, busword, csrs))
|
|
||||||
return regions
|
|
|
@ -1 +0,0 @@
|
||||||
from artiq.gateware.serwb import s7serdes, kuserdes, genphy, phy, core, packet, etherbone
|
|
|
@ -1,37 +0,0 @@
|
||||||
from migen import *
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
|
|
||||||
from artiq.gateware.serwb.packet import Packetizer, Depacketizer
|
|
||||||
from artiq.gateware.serwb.etherbone import Etherbone
|
|
||||||
|
|
||||||
|
|
||||||
class SERWBCore(Module):
|
|
||||||
def __init__(self, phy, clk_freq, mode):
|
|
||||||
# etherbone
|
|
||||||
self.submodules.etherbone = etherbone = Etherbone(mode)
|
|
||||||
|
|
||||||
# packetizer / depacketizer
|
|
||||||
depacketizer = Depacketizer(clk_freq)
|
|
||||||
packetizer = Packetizer()
|
|
||||||
self.submodules += depacketizer, packetizer
|
|
||||||
|
|
||||||
# fifos
|
|
||||||
tx_fifo = stream.SyncFIFO([("data", 32)], 8, buffered=True)
|
|
||||||
rx_fifo = stream.SyncFIFO([("data", 32)], 8, buffered=True)
|
|
||||||
self.submodules += tx_fifo, rx_fifo
|
|
||||||
|
|
||||||
# modules connection
|
|
||||||
self.comb += [
|
|
||||||
# core --> phy
|
|
||||||
packetizer.source.connect(tx_fifo.sink),
|
|
||||||
tx_fifo.source.connect(phy.sink),
|
|
||||||
|
|
||||||
# phy --> core
|
|
||||||
phy.source.connect(rx_fifo.sink),
|
|
||||||
rx_fifo.source.connect(depacketizer.sink),
|
|
||||||
|
|
||||||
# etherbone <--> core
|
|
||||||
depacketizer.source.connect(etherbone.sink),
|
|
||||||
etherbone.source.connect(packetizer.sink)
|
|
||||||
]
|
|
|
@ -1,200 +0,0 @@
|
||||||
from migen import *
|
|
||||||
from migen.genlib.io import *
|
|
||||||
from migen.genlib.misc import BitSlip, WaitTimer
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
|
||||||
|
|
||||||
from artiq.gateware.serwb.scrambler import Scrambler, Descrambler
|
|
||||||
|
|
||||||
|
|
||||||
def K(x, y):
|
|
||||||
return (y << 5) | x
|
|
||||||
|
|
||||||
|
|
||||||
class _8b10bEncoder(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.sink = sink = stream.Endpoint([("d", 32), ("k", 4)])
|
|
||||||
self.source = source = stream.Endpoint([("data", 40)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
encoder = CEInserter()(Encoder(4, True))
|
|
||||||
self.submodules += encoder
|
|
||||||
|
|
||||||
# control
|
|
||||||
self.comb += [
|
|
||||||
source.stb.eq(sink.stb),
|
|
||||||
sink.ack.eq(source.ack),
|
|
||||||
encoder.ce.eq(source.stb & source.ack)
|
|
||||||
]
|
|
||||||
|
|
||||||
# datapath
|
|
||||||
for i in range(4):
|
|
||||||
self.comb += [
|
|
||||||
encoder.k[i].eq(sink.k[i]),
|
|
||||||
encoder.d[i].eq(sink.d[8*i:8*(i+1)]),
|
|
||||||
source.data[10*i:10*(i+1)].eq(encoder.output[i])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _8b10bDecoder(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.sink = sink = stream.Endpoint([("data", 40)])
|
|
||||||
self.source = source = stream.Endpoint([("d", 32), ("k", 4)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
decoders = [CEInserter()(Decoder(True)) for _ in range(4)]
|
|
||||||
self.submodules += decoders
|
|
||||||
|
|
||||||
# control
|
|
||||||
self.comb += [
|
|
||||||
source.stb.eq(sink.stb),
|
|
||||||
sink.ack.eq(source.ack)
|
|
||||||
]
|
|
||||||
self.comb += [decoders[i].ce.eq(source.stb & source.ack) for i in range(4)]
|
|
||||||
|
|
||||||
# datapath
|
|
||||||
for i in range(4):
|
|
||||||
self.comb += [
|
|
||||||
decoders[i].input.eq(sink.data[10*i:10*(i+1)]),
|
|
||||||
source.k[i].eq(decoders[i].k),
|
|
||||||
source.d[8*i:8*(i+1)].eq(decoders[i].d)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _Bitslip(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.value = value = Signal(6)
|
|
||||||
self.sink = sink = stream.Endpoint([("data", 40)])
|
|
||||||
self.source = source = stream.Endpoint([("data", 40)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
bitslip = CEInserter()(BitSlip(40))
|
|
||||||
self.submodules += bitslip
|
|
||||||
|
|
||||||
# control
|
|
||||||
self.comb += [
|
|
||||||
source.stb.eq(sink.stb),
|
|
||||||
sink.ack.eq(source.ack),
|
|
||||||
bitslip.value.eq(value),
|
|
||||||
bitslip.ce.eq(source.stb & source.ack)
|
|
||||||
]
|
|
||||||
|
|
||||||
# datapath
|
|
||||||
self.comb += [
|
|
||||||
bitslip.i.eq(sink.data),
|
|
||||||
source.data.eq(bitslip.o)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class TXDatapath(Module):
|
|
||||||
def __init__(self, phy_dw, with_scrambling=True):
|
|
||||||
self.idle = idle = Signal()
|
|
||||||
self.comma = comma = Signal()
|
|
||||||
self.sink = sink = stream.Endpoint([("data", 32)])
|
|
||||||
self.source = source = stream.Endpoint([("data", phy_dw)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# scrambler
|
|
||||||
if with_scrambling:
|
|
||||||
self.submodules.scrambler = scrambler = Scrambler()
|
|
||||||
|
|
||||||
# line coding
|
|
||||||
self.submodules.encoder = encoder = _8b10bEncoder()
|
|
||||||
|
|
||||||
# converter
|
|
||||||
self.submodules.converter = converter = stream.Converter(40, phy_dw)
|
|
||||||
|
|
||||||
# dataflow
|
|
||||||
if with_scrambling:
|
|
||||||
self.comb += [
|
|
||||||
sink.connect(scrambler.sink),
|
|
||||||
If(comma,
|
|
||||||
encoder.sink.stb.eq(1),
|
|
||||||
encoder.sink.k.eq(1),
|
|
||||||
encoder.sink.d.eq(K(28,5))
|
|
||||||
).Else(
|
|
||||||
scrambler.source.connect(encoder.sink)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.comb += [
|
|
||||||
If(comma,
|
|
||||||
encoder.sink.stb.eq(1),
|
|
||||||
encoder.sink.k.eq(1),
|
|
||||||
encoder.sink.d.eq(K(28,5))
|
|
||||||
).Else(
|
|
||||||
sink.connect(encoder.sink, omit={"data"}),
|
|
||||||
encoder.sink.d.eq(sink.data)
|
|
||||||
),
|
|
||||||
]
|
|
||||||
self.comb += [
|
|
||||||
If(idle,
|
|
||||||
converter.sink.stb.eq(1),
|
|
||||||
converter.sink.data.eq(0)
|
|
||||||
).Else(
|
|
||||||
encoder.source.connect(converter.sink),
|
|
||||||
),
|
|
||||||
converter.source.connect(source)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class RXDatapath(Module):
|
|
||||||
def __init__(self, phy_dw, with_scrambling=True):
|
|
||||||
self.bitslip_value = bitslip_value = Signal(6)
|
|
||||||
self.sink = sink = stream.Endpoint([("data", phy_dw)])
|
|
||||||
self.source = source = stream.Endpoint([("data", 32)])
|
|
||||||
self.idle = idle = Signal()
|
|
||||||
self.comma = comma = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# converter
|
|
||||||
self.submodules.converter = converter = stream.Converter(phy_dw, 40)
|
|
||||||
|
|
||||||
# bitslip
|
|
||||||
self.submodules.bitslip = bitslip = _Bitslip()
|
|
||||||
self.comb += bitslip.value.eq(bitslip_value)
|
|
||||||
|
|
||||||
# line coding
|
|
||||||
self.submodules.decoder = decoder = _8b10bDecoder()
|
|
||||||
|
|
||||||
# descrambler
|
|
||||||
if with_scrambling:
|
|
||||||
self.submodules.descrambler = descrambler = Descrambler()
|
|
||||||
|
|
||||||
# dataflow
|
|
||||||
self.comb += [
|
|
||||||
sink.connect(converter.sink),
|
|
||||||
converter.source.connect(bitslip.sink),
|
|
||||||
bitslip.source.connect(decoder.sink)
|
|
||||||
]
|
|
||||||
if with_scrambling:
|
|
||||||
self.comb += [
|
|
||||||
decoder.source.connect(descrambler.sink),
|
|
||||||
descrambler.source.connect(source)
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.comb += [
|
|
||||||
decoder.source.connect(source, omit={"d", "k"}),
|
|
||||||
source.data.eq(decoder.source.d)
|
|
||||||
]
|
|
||||||
|
|
||||||
# idle decoding
|
|
||||||
idle_timer = WaitTimer(32)
|
|
||||||
self.submodules += idle_timer
|
|
||||||
self.sync += [
|
|
||||||
If(converter.source.stb,
|
|
||||||
idle_timer.wait.eq((converter.source.data == 0) | (converter.source.data == (2**40-1)))
|
|
||||||
),
|
|
||||||
idle.eq(idle_timer.done)
|
|
||||||
]
|
|
||||||
# comma decoding
|
|
||||||
self.sync += \
|
|
||||||
If(decoder.source.stb,
|
|
||||||
comma.eq((decoder.source.k == 1) & (decoder.source.d == K(28, 5)))
|
|
||||||
)
|
|
|
@ -1,741 +0,0 @@
|
||||||
"""
|
|
||||||
Etherbone
|
|
||||||
|
|
||||||
CERN's Etherbone protocol is initially used to run a Wishbone bus over an
|
|
||||||
ethernet network. This re-implementation is meant to be run over serdes
|
|
||||||
and introduces some limitations:
|
|
||||||
- no probing (pf/pr)
|
|
||||||
- no address spaces (rca/bca/wca/wff)
|
|
||||||
- 32bits data and address
|
|
||||||
- 1 record per frame
|
|
||||||
"""
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
from misoc.interconnect import wishbone
|
|
||||||
|
|
||||||
from artiq.gateware.serwb.packet import *
|
|
||||||
|
|
||||||
|
|
||||||
class _Packetizer(Module):
|
|
||||||
def __init__(self, sink_description, source_description, header):
|
|
||||||
self.sink = sink = stream.Endpoint(sink_description)
|
|
||||||
self.source = source = stream.Endpoint(source_description)
|
|
||||||
self.header = Signal(header.length*8)
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
dw = len(self.sink.data)
|
|
||||||
|
|
||||||
header_reg = Signal(header.length*8, reset_less=True)
|
|
||||||
header_words = (header.length*8)//dw
|
|
||||||
load = Signal()
|
|
||||||
shift = Signal()
|
|
||||||
counter = Signal(max=max(header_words, 2))
|
|
||||||
counter_reset = Signal()
|
|
||||||
counter_ce = Signal()
|
|
||||||
self.sync += \
|
|
||||||
If(counter_reset,
|
|
||||||
counter.eq(0)
|
|
||||||
).Elif(counter_ce,
|
|
||||||
counter.eq(counter + 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.comb += header.encode(sink, self.header)
|
|
||||||
if header_words == 1:
|
|
||||||
self.sync += [
|
|
||||||
If(load,
|
|
||||||
header_reg.eq(self.header)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.sync += [
|
|
||||||
If(load,
|
|
||||||
header_reg.eq(self.header)
|
|
||||||
).Elif(shift,
|
|
||||||
header_reg.eq(Cat(header_reg[dw:], Signal(dw)))
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
fsm = FSM(reset_state="IDLE")
|
|
||||||
self.submodules += fsm
|
|
||||||
|
|
||||||
if header_words == 1:
|
|
||||||
idle_next_state = "COPY"
|
|
||||||
else:
|
|
||||||
idle_next_state = "SEND_HEADER"
|
|
||||||
|
|
||||||
fsm.act("IDLE",
|
|
||||||
sink.ack.eq(1),
|
|
||||||
counter_reset.eq(1),
|
|
||||||
If(sink.stb,
|
|
||||||
sink.ack.eq(0),
|
|
||||||
source.stb.eq(1),
|
|
||||||
source.eop.eq(0),
|
|
||||||
source.data.eq(self.header[:dw]),
|
|
||||||
If(source.stb & source.ack,
|
|
||||||
load.eq(1),
|
|
||||||
NextState(idle_next_state)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if header_words != 1:
|
|
||||||
fsm.act("SEND_HEADER",
|
|
||||||
source.stb.eq(1),
|
|
||||||
source.eop.eq(0),
|
|
||||||
source.data.eq(header_reg[dw:2*dw]),
|
|
||||||
If(source.stb & source.ack,
|
|
||||||
shift.eq(1),
|
|
||||||
counter_ce.eq(1),
|
|
||||||
If(counter == header_words-2,
|
|
||||||
NextState("COPY")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if hasattr(sink, "error"):
|
|
||||||
self.comb += source.error.eq(sink.error)
|
|
||||||
fsm.act("COPY",
|
|
||||||
source.stb.eq(sink.stb),
|
|
||||||
source.eop.eq(sink.eop),
|
|
||||||
source.data.eq(sink.data),
|
|
||||||
If(source.stb & source.ack,
|
|
||||||
sink.ack.eq(1),
|
|
||||||
If(source.eop,
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _Depacketizer(Module):
|
|
||||||
def __init__(self, sink_description, source_description, header):
|
|
||||||
self.sink = sink = stream.Endpoint(sink_description)
|
|
||||||
self.source = source = stream.Endpoint(source_description)
|
|
||||||
self.header = Signal(header.length*8)
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
dw = len(sink.data)
|
|
||||||
|
|
||||||
header_reg = Signal(header.length*8, reset_less=True)
|
|
||||||
header_words = (header.length*8)//dw
|
|
||||||
|
|
||||||
shift = Signal()
|
|
||||||
counter = Signal(max=max(header_words, 2))
|
|
||||||
counter_reset = Signal()
|
|
||||||
counter_ce = Signal()
|
|
||||||
self.sync += \
|
|
||||||
If(counter_reset,
|
|
||||||
counter.eq(0)
|
|
||||||
).Elif(counter_ce,
|
|
||||||
counter.eq(counter + 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
if header_words == 1:
|
|
||||||
self.sync += \
|
|
||||||
If(shift,
|
|
||||||
header_reg.eq(sink.data)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.sync += \
|
|
||||||
If(shift,
|
|
||||||
header_reg.eq(Cat(header_reg[dw:], sink.data))
|
|
||||||
)
|
|
||||||
self.comb += self.header.eq(header_reg)
|
|
||||||
|
|
||||||
fsm = FSM(reset_state="IDLE")
|
|
||||||
self.submodules += fsm
|
|
||||||
|
|
||||||
if header_words == 1:
|
|
||||||
idle_next_state = "COPY"
|
|
||||||
else:
|
|
||||||
idle_next_state = "RECEIVE_HEADER"
|
|
||||||
|
|
||||||
fsm.act("IDLE",
|
|
||||||
sink.ack.eq(1),
|
|
||||||
counter_reset.eq(1),
|
|
||||||
If(sink.stb,
|
|
||||||
shift.eq(1),
|
|
||||||
NextState(idle_next_state)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if header_words != 1:
|
|
||||||
fsm.act("RECEIVE_HEADER",
|
|
||||||
sink.ack.eq(1),
|
|
||||||
If(sink.stb,
|
|
||||||
counter_ce.eq(1),
|
|
||||||
shift.eq(1),
|
|
||||||
If(counter == header_words-2,
|
|
||||||
NextState("COPY")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
no_payload = Signal()
|
|
||||||
self.sync += \
|
|
||||||
If(fsm.before_entering("COPY"),
|
|
||||||
no_payload.eq(sink.eop)
|
|
||||||
)
|
|
||||||
|
|
||||||
if hasattr(sink, "error"):
|
|
||||||
self.comb += source.error.eq(sink.error)
|
|
||||||
self.comb += [
|
|
||||||
source.eop.eq(sink.eop | no_payload),
|
|
||||||
source.data.eq(sink.data),
|
|
||||||
header.decode(self.header, source)
|
|
||||||
]
|
|
||||||
fsm.act("COPY",
|
|
||||||
sink.ack.eq(source.ack),
|
|
||||||
source.stb.eq(sink.stb | no_payload),
|
|
||||||
If(source.stb & source.ack & source.eop,
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
etherbone_magic = 0x4e6f
|
|
||||||
etherbone_version = 1
|
|
||||||
etherbone_packet_header_length = 8
|
|
||||||
etherbone_packet_header_fields = {
|
|
||||||
"magic": HeaderField(0, 0, 16),
|
|
||||||
|
|
||||||
"version": HeaderField(2, 4, 4),
|
|
||||||
"nr": HeaderField(2, 2, 1),
|
|
||||||
"pr": HeaderField(2, 1, 1), # unused
|
|
||||||
"pf": HeaderField(2, 0, 1), # unused
|
|
||||||
|
|
||||||
"addr_size": HeaderField(3, 4, 4), # static
|
|
||||||
"port_size": HeaderField(3, 0, 4) # static
|
|
||||||
}
|
|
||||||
etherbone_packet_header = Header(etherbone_packet_header_fields,
|
|
||||||
etherbone_packet_header_length,
|
|
||||||
swap_field_bytes=True)
|
|
||||||
|
|
||||||
etherbone_record_header_length = 4
|
|
||||||
etherbone_record_header_fields = {
|
|
||||||
"bca": HeaderField(0, 0, 1), # unused
|
|
||||||
"rca": HeaderField(0, 1, 1), # unused
|
|
||||||
"rff": HeaderField(0, 2, 1), # unused
|
|
||||||
"cyc": HeaderField(0, 4, 1), # unused
|
|
||||||
"wca": HeaderField(0, 5, 1), # unused
|
|
||||||
"wff": HeaderField(0, 6, 1), # unused
|
|
||||||
|
|
||||||
"byte_enable": HeaderField(1, 0, 8),
|
|
||||||
|
|
||||||
"wcount": HeaderField(2, 0, 8),
|
|
||||||
|
|
||||||
"rcount": HeaderField(3, 0, 8)
|
|
||||||
}
|
|
||||||
etherbone_record_header = Header(etherbone_record_header_fields,
|
|
||||||
etherbone_record_header_length,
|
|
||||||
swap_field_bytes=True)
|
|
||||||
|
|
||||||
def _remove_from_layout(layout, *args):
|
|
||||||
r = []
|
|
||||||
for f in layout:
|
|
||||||
remove = False
|
|
||||||
for arg in args:
|
|
||||||
if f[0] == arg:
|
|
||||||
remove = True
|
|
||||||
if not remove:
|
|
||||||
r.append(f)
|
|
||||||
return r
|
|
||||||
|
|
||||||
def etherbone_packet_description(dw):
|
|
||||||
layout = etherbone_packet_header.get_layout()
|
|
||||||
layout += [("data", dw)]
|
|
||||||
return stream.EndpointDescription(layout)
|
|
||||||
|
|
||||||
def etherbone_packet_user_description(dw):
|
|
||||||
layout = etherbone_packet_header.get_layout()
|
|
||||||
layout = _remove_from_layout(layout,
|
|
||||||
"magic",
|
|
||||||
"portsize",
|
|
||||||
"addrsize",
|
|
||||||
"version")
|
|
||||||
layout += user_description(dw).payload_layout
|
|
||||||
return stream.EndpointDescription(layout)
|
|
||||||
|
|
||||||
def etherbone_record_description(dw):
|
|
||||||
layout = etherbone_record_header.get_layout()
|
|
||||||
layout += [("data", dw)]
|
|
||||||
return stream.EndpointDescription(layout)
|
|
||||||
|
|
||||||
def etherbone_mmap_description(dw):
|
|
||||||
layout = [
|
|
||||||
("we", 1),
|
|
||||||
("count", 8),
|
|
||||||
("base_addr", 32),
|
|
||||||
("be", dw//8),
|
|
||||||
("addr", 32),
|
|
||||||
("data", dw)
|
|
||||||
]
|
|
||||||
return stream.EndpointDescription(layout)
|
|
||||||
|
|
||||||
|
|
||||||
# etherbone packet
|
|
||||||
|
|
||||||
class _EtherbonePacketPacketizer(_Packetizer):
|
|
||||||
def __init__(self):
|
|
||||||
_Packetizer.__init__(self,
|
|
||||||
etherbone_packet_description(32),
|
|
||||||
user_description(32),
|
|
||||||
etherbone_packet_header)
|
|
||||||
|
|
||||||
|
|
||||||
class _EtherbonePacketTX(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.sink = sink = stream.Endpoint(etherbone_packet_user_description(32))
|
|
||||||
self.source = source = stream.Endpoint(user_description(32))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.submodules.packetizer = packetizer = _EtherbonePacketPacketizer()
|
|
||||||
self.comb += [
|
|
||||||
packetizer.sink.stb.eq(sink.stb),
|
|
||||||
packetizer.sink.eop.eq(sink.eop),
|
|
||||||
sink.ack.eq(packetizer.sink.ack),
|
|
||||||
|
|
||||||
packetizer.sink.magic.eq(etherbone_magic),
|
|
||||||
packetizer.sink.port_size.eq(32//8),
|
|
||||||
packetizer.sink.addr_size.eq(32//8),
|
|
||||||
packetizer.sink.nr.eq(sink.nr),
|
|
||||||
packetizer.sink.version.eq(etherbone_version),
|
|
||||||
|
|
||||||
packetizer.sink.data.eq(sink.data)
|
|
||||||
]
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
fsm.act("IDLE",
|
|
||||||
packetizer.source.ack.eq(1),
|
|
||||||
If(packetizer.source.stb,
|
|
||||||
packetizer.source.ack.eq(0),
|
|
||||||
NextState("SEND")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("SEND",
|
|
||||||
packetizer.source.connect(source),
|
|
||||||
source.length.eq(sink.length + etherbone_packet_header.length),
|
|
||||||
If(source.stb & source.eop & source.ack,
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _EtherbonePacketDepacketizer(_Depacketizer):
|
|
||||||
def __init__(self):
|
|
||||||
_Depacketizer.__init__(self,
|
|
||||||
user_description(32),
|
|
||||||
etherbone_packet_description(32),
|
|
||||||
etherbone_packet_header)
|
|
||||||
|
|
||||||
|
|
||||||
class _EtherbonePacketRX(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.sink = sink = stream.Endpoint(user_description(32))
|
|
||||||
self.source = source = stream.Endpoint(etherbone_packet_user_description(32))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.submodules.depacketizer = depacketizer = _EtherbonePacketDepacketizer()
|
|
||||||
self.comb += sink.connect(depacketizer.sink)
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
fsm.act("IDLE",
|
|
||||||
depacketizer.source.ack.eq(1),
|
|
||||||
If(depacketizer.source.stb,
|
|
||||||
depacketizer.source.ack.eq(0),
|
|
||||||
NextState("CHECK")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
stb = Signal()
|
|
||||||
self.sync += stb.eq(
|
|
||||||
depacketizer.source.stb &
|
|
||||||
(depacketizer.source.magic == etherbone_magic)
|
|
||||||
)
|
|
||||||
fsm.act("CHECK",
|
|
||||||
If(stb,
|
|
||||||
NextState("PRESENT")
|
|
||||||
).Else(
|
|
||||||
NextState("DROP")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.comb += [
|
|
||||||
source.eop.eq(depacketizer.source.eop),
|
|
||||||
|
|
||||||
source.nr.eq(depacketizer.source.nr),
|
|
||||||
|
|
||||||
source.data.eq(depacketizer.source.data),
|
|
||||||
|
|
||||||
source.length.eq(sink.length - etherbone_packet_header.length)
|
|
||||||
]
|
|
||||||
fsm.act("PRESENT",
|
|
||||||
source.stb.eq(depacketizer.source.stb),
|
|
||||||
depacketizer.source.ack.eq(source.ack),
|
|
||||||
If(source.stb & source.eop & source.ack,
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("DROP",
|
|
||||||
depacketizer.source.ack.eq(1),
|
|
||||||
If(depacketizer.source.stb &
|
|
||||||
depacketizer.source.eop &
|
|
||||||
depacketizer.source.ack,
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _EtherbonePacket(Module):
|
|
||||||
def __init__(self, port_sink, port_source):
|
|
||||||
self.submodules.tx = tx = _EtherbonePacketTX()
|
|
||||||
self.submodules.rx = rx = _EtherbonePacketRX()
|
|
||||||
self.comb += [
|
|
||||||
tx.source.connect(port_sink),
|
|
||||||
port_source.connect(rx.sink)
|
|
||||||
]
|
|
||||||
self.sink, self.source = self.tx.sink, self.rx.source
|
|
||||||
|
|
||||||
# etherbone record
|
|
||||||
|
|
||||||
class _EtherboneRecordPacketizer(_Packetizer):
|
|
||||||
def __init__(self):
|
|
||||||
_Packetizer.__init__(self,
|
|
||||||
etherbone_record_description(32),
|
|
||||||
etherbone_packet_user_description(32),
|
|
||||||
etherbone_record_header)
|
|
||||||
|
|
||||||
|
|
||||||
class _EtherboneRecordDepacketizer(_Depacketizer):
|
|
||||||
def __init__(self):
|
|
||||||
_Depacketizer.__init__(self,
|
|
||||||
etherbone_packet_user_description(32),
|
|
||||||
etherbone_record_description(32),
|
|
||||||
etherbone_record_header)
|
|
||||||
|
|
||||||
|
|
||||||
class _EtherboneRecordReceiver(Module):
|
|
||||||
def __init__(self, buffer_depth=4):
|
|
||||||
self.sink = sink = stream.Endpoint(etherbone_record_description(32))
|
|
||||||
self.source = source = stream.Endpoint(etherbone_mmap_description(32))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
fifo = stream.SyncFIFO(etherbone_record_description(32), buffer_depth,
|
|
||||||
buffered=True)
|
|
||||||
self.submodules += fifo
|
|
||||||
self.comb += sink.connect(fifo.sink)
|
|
||||||
|
|
||||||
base_addr = Signal(32)
|
|
||||||
base_addr_update = Signal()
|
|
||||||
self.sync += If(base_addr_update, base_addr.eq(fifo.source.data))
|
|
||||||
|
|
||||||
counter = Signal(max=512)
|
|
||||||
counter_reset = Signal()
|
|
||||||
counter_ce = Signal()
|
|
||||||
self.sync += \
|
|
||||||
If(counter_reset,
|
|
||||||
counter.eq(0)
|
|
||||||
).Elif(counter_ce,
|
|
||||||
counter.eq(counter + 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
fsm.act("IDLE",
|
|
||||||
fifo.source.ack.eq(1),
|
|
||||||
counter_reset.eq(1),
|
|
||||||
If(fifo.source.stb,
|
|
||||||
base_addr_update.eq(1),
|
|
||||||
If(fifo.source.wcount,
|
|
||||||
NextState("RECEIVE_WRITES")
|
|
||||||
).Elif(fifo.source.rcount,
|
|
||||||
NextState("RECEIVE_READS")
|
|
||||||
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("RECEIVE_WRITES",
|
|
||||||
source.stb.eq(fifo.source.stb),
|
|
||||||
source.eop.eq(counter == fifo.source.wcount-1),
|
|
||||||
source.count.eq(fifo.source.wcount),
|
|
||||||
source.be.eq(fifo.source.byte_enable),
|
|
||||||
source.addr.eq(base_addr[2:] + counter),
|
|
||||||
source.we.eq(1),
|
|
||||||
source.data.eq(fifo.source.data),
|
|
||||||
fifo.source.ack.eq(source.ack),
|
|
||||||
If(source.stb & source.ack,
|
|
||||||
counter_ce.eq(1),
|
|
||||||
If(source.eop,
|
|
||||||
If(fifo.source.rcount,
|
|
||||||
NextState("RECEIVE_BASE_RET_ADDR")
|
|
||||||
).Else(
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("RECEIVE_BASE_RET_ADDR",
|
|
||||||
counter_reset.eq(1),
|
|
||||||
If(fifo.source.stb,
|
|
||||||
base_addr_update.eq(1),
|
|
||||||
NextState("RECEIVE_READS")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("RECEIVE_READS",
|
|
||||||
source.stb.eq(fifo.source.stb),
|
|
||||||
source.eop.eq(counter == fifo.source.rcount-1),
|
|
||||||
source.count.eq(fifo.source.rcount),
|
|
||||||
source.base_addr.eq(base_addr),
|
|
||||||
source.addr.eq(fifo.source.data[2:]),
|
|
||||||
fifo.source.ack.eq(source.ack),
|
|
||||||
If(source.stb & source.ack,
|
|
||||||
counter_ce.eq(1),
|
|
||||||
If(source.eop,
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _EtherboneRecordSender(Module):
|
|
||||||
def __init__(self, buffer_depth=4):
|
|
||||||
self.sink = sink = stream.Endpoint(etherbone_mmap_description(32))
|
|
||||||
self.source = source = stream.Endpoint(etherbone_record_description(32))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
pbuffer = stream.SyncFIFO(etherbone_mmap_description(32), buffer_depth,
|
|
||||||
buffered=True)
|
|
||||||
self.submodules += pbuffer
|
|
||||||
self.comb += sink.connect(pbuffer.sink)
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
fsm.act("IDLE",
|
|
||||||
pbuffer.source.ack.eq(1),
|
|
||||||
If(pbuffer.source.stb,
|
|
||||||
pbuffer.source.ack.eq(0),
|
|
||||||
NextState("SEND_BASE_ADDRESS")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.comb += [
|
|
||||||
source.byte_enable.eq(pbuffer.source.be),
|
|
||||||
If(pbuffer.source.we,
|
|
||||||
source.wcount.eq(pbuffer.source.count)
|
|
||||||
).Else(
|
|
||||||
source.rcount.eq(pbuffer.source.count)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
fsm.act("SEND_BASE_ADDRESS",
|
|
||||||
source.stb.eq(pbuffer.source.stb),
|
|
||||||
source.eop.eq(0),
|
|
||||||
source.data.eq(pbuffer.source.base_addr),
|
|
||||||
If(source.ack,
|
|
||||||
NextState("SEND_DATA")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("SEND_DATA",
|
|
||||||
source.stb.eq(pbuffer.source.stb),
|
|
||||||
source.eop.eq(pbuffer.source.eop),
|
|
||||||
source.data.eq(pbuffer.source.data),
|
|
||||||
If(source.stb & source.ack,
|
|
||||||
pbuffer.source.ack.eq(1),
|
|
||||||
If(source.eop,
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _EtherboneRecord(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.sink = sink = stream.Endpoint(etherbone_packet_user_description(32))
|
|
||||||
self.source = source = stream.Endpoint(etherbone_packet_user_description(32))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# receive record, decode it and generate mmap stream
|
|
||||||
self.submodules.depacketizer = depacketizer = _EtherboneRecordDepacketizer()
|
|
||||||
self.submodules.receiver = receiver = _EtherboneRecordReceiver()
|
|
||||||
self.comb += [
|
|
||||||
sink.connect(depacketizer.sink),
|
|
||||||
depacketizer.source.connect(receiver.sink)
|
|
||||||
]
|
|
||||||
|
|
||||||
# receive mmap stream, encode it and send records
|
|
||||||
self.submodules.sender = sender = _EtherboneRecordSender()
|
|
||||||
self.submodules.packetizer = packetizer = _EtherboneRecordPacketizer()
|
|
||||||
self.comb += [
|
|
||||||
sender.source.connect(packetizer.sink),
|
|
||||||
packetizer.source.connect(source),
|
|
||||||
source.length.eq(etherbone_record_header.length +
|
|
||||||
(sender.source.wcount != 0)*4 + sender.source.wcount*4 +
|
|
||||||
(sender.source.rcount != 0)*4 + sender.source.rcount*4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# etherbone wishbone
|
|
||||||
|
|
||||||
class _EtherboneWishboneMaster(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.sink = sink = stream.Endpoint(etherbone_mmap_description(32))
|
|
||||||
self.source = source = stream.Endpoint(etherbone_mmap_description(32))
|
|
||||||
self.bus = bus = wishbone.Interface()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
data = Signal(32)
|
|
||||||
data_update = Signal()
|
|
||||||
self.sync += If(data_update, data.eq(bus.dat_r))
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
fsm.act("IDLE",
|
|
||||||
sink.ack.eq(1),
|
|
||||||
If(sink.stb,
|
|
||||||
sink.ack.eq(0),
|
|
||||||
If(sink.we,
|
|
||||||
NextState("WRITE_DATA")
|
|
||||||
).Else(
|
|
||||||
NextState("READ_DATA")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("WRITE_DATA",
|
|
||||||
bus.adr.eq(sink.addr),
|
|
||||||
bus.dat_w.eq(sink.data),
|
|
||||||
bus.sel.eq(sink.be),
|
|
||||||
bus.stb.eq(sink.stb),
|
|
||||||
bus.we.eq(1),
|
|
||||||
bus.cyc.eq(1),
|
|
||||||
If(bus.stb & bus.ack,
|
|
||||||
sink.ack.eq(1),
|
|
||||||
If(sink.eop,
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("READ_DATA",
|
|
||||||
bus.adr.eq(sink.addr),
|
|
||||||
bus.sel.eq(sink.be),
|
|
||||||
bus.stb.eq(sink.stb),
|
|
||||||
bus.cyc.eq(1),
|
|
||||||
If(bus.stb & bus.ack,
|
|
||||||
data_update.eq(1),
|
|
||||||
NextState("SEND_DATA")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("SEND_DATA",
|
|
||||||
source.stb.eq(sink.stb),
|
|
||||||
source.eop.eq(sink.eop),
|
|
||||||
source.base_addr.eq(sink.base_addr),
|
|
||||||
source.addr.eq(sink.addr),
|
|
||||||
source.count.eq(sink.count),
|
|
||||||
source.be.eq(sink.be),
|
|
||||||
source.we.eq(1),
|
|
||||||
source.data.eq(data),
|
|
||||||
If(source.stb & source.ack,
|
|
||||||
sink.ack.eq(1),
|
|
||||||
If(source.eop,
|
|
||||||
NextState("IDLE")
|
|
||||||
).Else(
|
|
||||||
NextState("READ_DATA")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _EtherboneWishboneSlave(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.bus = bus = wishbone.Interface()
|
|
||||||
self.ready = Signal(reset=1)
|
|
||||||
self.sink = sink = stream.Endpoint(etherbone_mmap_description(32))
|
|
||||||
self.source = source = stream.Endpoint(etherbone_mmap_description(32))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
fsm.act("IDLE",
|
|
||||||
sink.ack.eq(1),
|
|
||||||
If(bus.stb & bus.cyc,
|
|
||||||
If(self.ready,
|
|
||||||
If(bus.we,
|
|
||||||
NextState("SEND_WRITE")
|
|
||||||
).Else(
|
|
||||||
NextState("SEND_READ")
|
|
||||||
)
|
|
||||||
).Else(
|
|
||||||
NextState("SEND_ERROR")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("SEND_WRITE",
|
|
||||||
If(~self.ready,
|
|
||||||
NextState("SEND_ERROR")
|
|
||||||
).Else(
|
|
||||||
source.stb.eq(1),
|
|
||||||
source.eop.eq(1),
|
|
||||||
source.base_addr[2:].eq(bus.adr),
|
|
||||||
source.count.eq(1),
|
|
||||||
source.be.eq(bus.sel),
|
|
||||||
source.we.eq(1),
|
|
||||||
source.data.eq(bus.dat_w),
|
|
||||||
If(source.stb & source.ack,
|
|
||||||
bus.ack.eq(1),
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("SEND_READ",
|
|
||||||
If(~self.ready,
|
|
||||||
NextState("SEND_ERROR")
|
|
||||||
).Else(
|
|
||||||
source.stb.eq(1),
|
|
||||||
source.eop.eq(1),
|
|
||||||
source.base_addr.eq(0),
|
|
||||||
source.count.eq(1),
|
|
||||||
source.be.eq(bus.sel),
|
|
||||||
source.we.eq(0),
|
|
||||||
source.data[2:].eq(bus.adr),
|
|
||||||
If(source.stb & source.ack,
|
|
||||||
NextState("WAIT_READ")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("WAIT_READ",
|
|
||||||
sink.ack.eq(1),
|
|
||||||
If(~self.ready,
|
|
||||||
NextState("SEND_ERROR")
|
|
||||||
).Elif(sink.stb & sink.we,
|
|
||||||
bus.ack.eq(1),
|
|
||||||
bus.dat_r.eq(sink.data),
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("SEND_ERROR",
|
|
||||||
bus.ack.eq(1),
|
|
||||||
bus.err.eq(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
# etherbone
|
|
||||||
|
|
||||||
class Etherbone(Module):
|
|
||||||
def __init__(self, mode="master"):
|
|
||||||
self.sink = sink = stream.Endpoint(user_description(32))
|
|
||||||
self.source = source = stream.Endpoint(user_description(32))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.submodules.packet = _EtherbonePacket(source, sink)
|
|
||||||
self.submodules.record = _EtherboneRecord()
|
|
||||||
if mode == "master":
|
|
||||||
self.submodules.wishbone = _EtherboneWishboneMaster()
|
|
||||||
elif mode == "slave":
|
|
||||||
self.submodules.wishbone = _EtherboneWishboneSlave()
|
|
||||||
else:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
self.packet.source.connect(self.record.sink),
|
|
||||||
self.record.source.connect(self.packet.sink),
|
|
||||||
self.record.receiver.source.connect(self.wishbone.sink),
|
|
||||||
self.wishbone.source.connect(self.record.sender.sink)
|
|
||||||
]
|
|
|
@ -1,346 +0,0 @@
|
||||||
from migen import *
|
|
||||||
from migen.genlib.io import *
|
|
||||||
from migen.genlib.misc import BitSlip, WaitTimer
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
from misoc.interconnect.csr import *
|
|
||||||
|
|
||||||
from artiq.gateware.serwb.datapath import TXDatapath, RXDatapath
|
|
||||||
|
|
||||||
|
|
||||||
class _SerdesClocking(Module):
|
|
||||||
def __init__(self, pads, mode="master"):
|
|
||||||
self.refclk = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# In Master mode, generate the clock with 180° phase shift so that Slave
|
|
||||||
# can use this clock to sample data
|
|
||||||
if mode == "master":
|
|
||||||
self.specials += DDROutput(0, 1, self.refclk)
|
|
||||||
if hasattr(pads, "clk_p"):
|
|
||||||
self.specials += DifferentialOutput(self.refclk, pads.clk_p, pads.clk_n)
|
|
||||||
else:
|
|
||||||
self.comb += pads.clk.eq(self.refclk)
|
|
||||||
# In Slave mode, use the clock provided by Master
|
|
||||||
elif mode == "slave":
|
|
||||||
if hasattr(pads, "clk_p"):
|
|
||||||
self.specials += DifferentialInput(pads.clk_p, pads.clk_n, self.refclk)
|
|
||||||
else:
|
|
||||||
self.comb += self.refclk.eq(pads.clk)
|
|
||||||
else:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
|
|
||||||
class _SerdesTX(Module):
|
|
||||||
def __init__(self, pads):
|
|
||||||
# Control
|
|
||||||
self.idle = idle = Signal()
|
|
||||||
self.comma = comma = Signal()
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.sink = sink = stream.Endpoint([("data", 32)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.submodules.datapath = datapath = TXDatapath(1)
|
|
||||||
self.comb += [
|
|
||||||
sink.connect(datapath.sink),
|
|
||||||
datapath.source.ack.eq(1),
|
|
||||||
datapath.idle.eq(idle),
|
|
||||||
datapath.comma.eq(comma)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Output data (on rising edge of sys_clk)
|
|
||||||
data = Signal()
|
|
||||||
self.sync += data.eq(datapath.source.data)
|
|
||||||
if hasattr(pads, "tx_p"):
|
|
||||||
self.specials += DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
|
||||||
else:
|
|
||||||
self.comb += pads.tx.eq(data)
|
|
||||||
|
|
||||||
|
|
||||||
class _SerdesRX(Module):
|
|
||||||
def __init__(self, pads):
|
|
||||||
# Control
|
|
||||||
self.bitslip_value = bitslip_value = Signal(6)
|
|
||||||
|
|
||||||
# Status
|
|
||||||
self.idle = idle = Signal()
|
|
||||||
self.comma = comma = Signal()
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.source = source = stream.Endpoint([("data", 32)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# Input data (on rising edge of sys_clk)
|
|
||||||
data = Signal()
|
|
||||||
data_d = Signal()
|
|
||||||
if hasattr(pads, "rx_p"):
|
|
||||||
self.specials += DifferentialInput(pads.rx_p, pads.rx_n, data)
|
|
||||||
else:
|
|
||||||
self.comb += data.eq(pads.rx)
|
|
||||||
self.sync += data_d.eq(data)
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.submodules.datapath = datapath = RXDatapath(1)
|
|
||||||
self.comb += [
|
|
||||||
datapath.sink.stb.eq(1),
|
|
||||||
datapath.sink.data.eq(data_d),
|
|
||||||
datapath.bitslip_value.eq(bitslip_value),
|
|
||||||
datapath.source.connect(source),
|
|
||||||
idle.eq(datapath.idle),
|
|
||||||
comma.eq(datapath.comma)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@ResetInserter()
|
|
||||||
class _Serdes(Module):
|
|
||||||
def __init__(self, pads, mode="master"):
|
|
||||||
self.submodules.clocking = _SerdesClocking(pads, mode)
|
|
||||||
self.submodules.tx = _SerdesTX(pads)
|
|
||||||
self.submodules.rx = _SerdesRX(pads)
|
|
||||||
|
|
||||||
|
|
||||||
# SERWB Master <--> Slave physical synchronization process:
|
|
||||||
# 1) Master sends idle patterns (zeroes) to Slave to reset it.
|
|
||||||
# 2) Master sends K28.5 commas to allow Slave to calibrate, Slave sends idle patterns.
|
|
||||||
# 3) Slave sends K28.5 commas to allow Master to calibrate, Master sends K28.5 commas.
|
|
||||||
# 4) Master stops sending K28.5 commas.
|
|
||||||
# 5) Slave stops sending K28.5 commas.
|
|
||||||
# 6) Physical link is ready.
|
|
||||||
|
|
||||||
|
|
||||||
@ResetInserter()
|
|
||||||
class _SerdesMasterInit(Module):
|
|
||||||
def __init__(self, serdes, timeout):
|
|
||||||
self.ready = Signal()
|
|
||||||
self.error = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.bitslip = bitslip = Signal(max=40)
|
|
||||||
|
|
||||||
self.submodules.timer = timer = WaitTimer(timeout)
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
fsm.act("IDLE",
|
|
||||||
NextValue(bitslip, 0),
|
|
||||||
NextState("RESET_SLAVE"),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("RESET_SLAVE",
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
timer.wait.eq(0),
|
|
||||||
NextState("SEND_PATTERN")
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("SEND_PATTERN",
|
|
||||||
If(~serdes.rx.idle,
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
NextState("CHECK_PATTERN")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("WAIT_STABLE",
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
timer.wait.eq(0),
|
|
||||||
NextState("CHECK_PATTERN")
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("CHECK_PATTERN",
|
|
||||||
If(serdes.rx.comma,
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
NextState("READY")
|
|
||||||
)
|
|
||||||
).Else(
|
|
||||||
NextState("INC_BITSLIP")
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
self.comb += serdes.rx.bitslip_value.eq(bitslip)
|
|
||||||
fsm.act("INC_BITSLIP",
|
|
||||||
NextState("WAIT_STABLE"),
|
|
||||||
If(bitslip == (40 - 1),
|
|
||||||
NextState("ERROR")
|
|
||||||
).Else(
|
|
||||||
NextValue(bitslip, bitslip + 1)
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("READY",
|
|
||||||
self.ready.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("ERROR",
|
|
||||||
self.error.eq(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@ResetInserter()
|
|
||||||
class _SerdesSlaveInit(Module, AutoCSR):
|
|
||||||
def __init__(self, serdes, timeout):
|
|
||||||
self.ready = Signal()
|
|
||||||
self.error = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.bitslip = bitslip = Signal(max=40)
|
|
||||||
|
|
||||||
self.submodules.timer = timer = WaitTimer(timeout)
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
# reset
|
|
||||||
fsm.act("IDLE",
|
|
||||||
NextValue(bitslip, 0),
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
timer.wait.eq(0),
|
|
||||||
NextState("WAIT_STABLE"),
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("WAIT_STABLE",
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
timer.wait.eq(0),
|
|
||||||
NextState("CHECK_PATTERN")
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("CHECK_PATTERN",
|
|
||||||
If(serdes.rx.comma,
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
NextState("SEND_PATTERN")
|
|
||||||
)
|
|
||||||
).Else(
|
|
||||||
NextState("INC_BITSLIP")
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
self.comb += serdes.rx.bitslip_value.eq(bitslip)
|
|
||||||
fsm.act("INC_BITSLIP",
|
|
||||||
NextState("WAIT_STABLE"),
|
|
||||||
If(bitslip == (40 - 1),
|
|
||||||
NextState("ERROR")
|
|
||||||
).Else(
|
|
||||||
NextValue(bitslip, bitslip + 1)
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("SEND_PATTERN",
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
If(~serdes.rx.comma,
|
|
||||||
NextState("READY")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("READY",
|
|
||||||
self.ready.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("ERROR",
|
|
||||||
self.error.eq(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _SerdesControl(Module, AutoCSR):
|
|
||||||
def __init__(self, serdes, init, mode="master"):
|
|
||||||
if mode == "master":
|
|
||||||
self.reset = CSR()
|
|
||||||
self.ready = CSRStatus()
|
|
||||||
self.error = CSRStatus()
|
|
||||||
|
|
||||||
self.bitslip = CSRStatus(6)
|
|
||||||
|
|
||||||
self.prbs_error = Signal()
|
|
||||||
self.prbs_start = CSR()
|
|
||||||
self.prbs_cycles = CSRStorage(32)
|
|
||||||
self.prbs_errors = CSRStatus(32)
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
if mode == "master":
|
|
||||||
# In Master mode, reset is coming from CSR,
|
|
||||||
# it resets the Master that will also reset
|
|
||||||
# the Slave by putting the link in idle.
|
|
||||||
self.sync += init.reset.eq(self.reset.re)
|
|
||||||
else:
|
|
||||||
# In Slave mode, reset is coming from link,
|
|
||||||
# Master reset the Slave by putting the link
|
|
||||||
# in idle.
|
|
||||||
self.sync += [
|
|
||||||
init.reset.eq(serdes.rx.idle),
|
|
||||||
serdes.reset.eq(serdes.rx.idle)
|
|
||||||
]
|
|
||||||
self.comb += [
|
|
||||||
self.ready.status.eq(init.ready),
|
|
||||||
self.error.status.eq(init.error),
|
|
||||||
self.bitslip.status.eq(init.bitslip)
|
|
||||||
]
|
|
||||||
|
|
||||||
# prbs
|
|
||||||
prbs_cycles = Signal(32)
|
|
||||||
prbs_errors = self.prbs_errors.status
|
|
||||||
prbs_fsm = FSM(reset_state="IDLE")
|
|
||||||
self.submodules += prbs_fsm
|
|
||||||
prbs_fsm.act("IDLE",
|
|
||||||
NextValue(prbs_cycles, 0),
|
|
||||||
If(self.prbs_start.re,
|
|
||||||
NextValue(prbs_errors, 0),
|
|
||||||
NextState("CHECK")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
prbs_fsm.act("CHECK",
|
|
||||||
NextValue(prbs_cycles, prbs_cycles + 1),
|
|
||||||
If(self.prbs_error,
|
|
||||||
NextValue(prbs_errors, prbs_errors + 1),
|
|
||||||
),
|
|
||||||
If(prbs_cycles == self.prbs_cycles.storage,
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SERWBPHY(Module, AutoCSR):
|
|
||||||
def __init__(self, device, pads, mode="master", init_timeout=2**16):
|
|
||||||
self.sink = sink = stream.Endpoint([("data", 32)])
|
|
||||||
self.source = source = stream.Endpoint([("data", 32)])
|
|
||||||
assert mode in ["master", "slave"]
|
|
||||||
self.submodules.serdes = _Serdes(pads, mode)
|
|
||||||
if mode == "master":
|
|
||||||
self.submodules.init = _SerdesMasterInit(self.serdes, init_timeout)
|
|
||||||
else:
|
|
||||||
self.submodules.init = _SerdesSlaveInit(self.serdes, init_timeout)
|
|
||||||
self.submodules.control = _SerdesControl(self.serdes, self.init, mode)
|
|
||||||
|
|
||||||
# tx/rx dataflow
|
|
||||||
self.comb += [
|
|
||||||
If(self.init.ready,
|
|
||||||
If(sink.stb,
|
|
||||||
sink.connect(self.serdes.tx.sink),
|
|
||||||
),
|
|
||||||
self.serdes.rx.source.connect(source)
|
|
||||||
).Else(
|
|
||||||
self.serdes.rx.source.ack.eq(1)
|
|
||||||
),
|
|
||||||
self.serdes.tx.sink.stb.eq(1) # always transmitting
|
|
||||||
]
|
|
||||||
|
|
||||||
# For PRBS test we are using the scrambler/descrambler as PRBS,
|
|
||||||
# sending 0 to the scrambler and checking that descrambler
|
|
||||||
# output is always 0.
|
|
||||||
self.comb += self.control.prbs_error.eq(
|
|
||||||
source.stb &
|
|
||||||
source.ack &
|
|
||||||
(source.data != 0))
|
|
|
@ -1,149 +0,0 @@
|
||||||
from migen import *
|
|
||||||
from migen.genlib.io import *
|
|
||||||
from migen.genlib.misc import BitSlip, WaitTimer
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
|
||||||
|
|
||||||
from artiq.gateware.serwb.datapath import TXDatapath, RXDatapath
|
|
||||||
|
|
||||||
|
|
||||||
class _KUSerdesClocking(Module):
|
|
||||||
def __init__(self, pads, mode="master"):
|
|
||||||
self.refclk = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# In Master mode, generate the linerate/10 clock. Slave will re-multiply it.
|
|
||||||
if mode == "master":
|
|
||||||
converter = stream.Converter(40, 8)
|
|
||||||
self.submodules += converter
|
|
||||||
self.comb += [
|
|
||||||
converter.sink.stb.eq(1),
|
|
||||||
converter.source.ack.eq(1),
|
|
||||||
converter.sink.data.eq(Replicate(Signal(10, reset=0b1111100000), 4)),
|
|
||||||
]
|
|
||||||
self.specials += [
|
|
||||||
Instance("OSERDESE3",
|
|
||||||
p_DATA_WIDTH=8, p_INIT=0,
|
|
||||||
p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0,
|
|
||||||
p_IS_RST_INVERTED=0,
|
|
||||||
|
|
||||||
o_OQ=self.refclk,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
|
||||||
i_D=converter.source.data
|
|
||||||
),
|
|
||||||
DifferentialOutput(self.refclk, pads.clk_p, pads.clk_n)
|
|
||||||
]
|
|
||||||
|
|
||||||
# In Slave mode, multiply the clock provided by Master with a PLL/MMCM
|
|
||||||
elif mode == "slave":
|
|
||||||
self.specials += DifferentialInput(pads.clk_p, pads.clk_n, self.refclk)
|
|
||||||
|
|
||||||
|
|
||||||
class _KUSerdesTX(Module):
|
|
||||||
def __init__(self, pads):
|
|
||||||
# Control
|
|
||||||
self.idle = idle = Signal()
|
|
||||||
self.comma = comma = Signal()
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.sink = sink = stream.Endpoint([("data", 32)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.submodules.datapath = datapath = TXDatapath(8)
|
|
||||||
self.comb += [
|
|
||||||
sink.connect(datapath.sink),
|
|
||||||
datapath.source.ack.eq(1),
|
|
||||||
datapath.idle.eq(idle),
|
|
||||||
datapath.comma.eq(comma)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Output Data(DDR with sys4x)
|
|
||||||
data = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("OSERDESE3",
|
|
||||||
p_DATA_WIDTH=8, p_INIT=0,
|
|
||||||
p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0,
|
|
||||||
|
|
||||||
o_OQ=data,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
|
||||||
i_D=datapath.source.data
|
|
||||||
),
|
|
||||||
DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _KUSerdesRX(Module):
|
|
||||||
def __init__(self, pads):
|
|
||||||
# Control
|
|
||||||
self.delay_rst = Signal()
|
|
||||||
self.delay_inc = Signal()
|
|
||||||
self.bitslip_value = bitslip_value = Signal(6)
|
|
||||||
|
|
||||||
# Status
|
|
||||||
self.idle = idle = Signal()
|
|
||||||
self.comma = comma = Signal()
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.source = source = stream.Endpoint([("data", 32)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# Data input (DDR with sys4x)
|
|
||||||
data_nodelay = Signal()
|
|
||||||
data_delayed = Signal()
|
|
||||||
data_deserialized = Signal(8)
|
|
||||||
self.specials += [
|
|
||||||
DifferentialInput(pads.rx_p, pads.rx_n, data_nodelay),
|
|
||||||
Instance("IDELAYE3",
|
|
||||||
p_CASCADE="NONE", p_UPDATE_MODE="ASYNC", p_REFCLK_FREQUENCY=200.0,
|
|
||||||
p_IS_CLK_INVERTED=0, p_IS_RST_INVERTED=0,
|
|
||||||
p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN",
|
|
||||||
p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=0,
|
|
||||||
|
|
||||||
i_CLK=ClockSignal("sys"),
|
|
||||||
i_RST=self.delay_rst, i_LOAD=0,
|
|
||||||
i_INC=1, i_EN_VTC=0,
|
|
||||||
i_CE=self.delay_inc,
|
|
||||||
|
|
||||||
i_IDATAIN=data_nodelay, o_DATAOUT=data_delayed
|
|
||||||
),
|
|
||||||
Instance("ISERDESE3",
|
|
||||||
p_IS_CLK_INVERTED=0,
|
|
||||||
p_IS_CLK_B_INVERTED=1,
|
|
||||||
p_DATA_WIDTH=8,
|
|
||||||
|
|
||||||
i_D=data_delayed,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0,
|
|
||||||
i_CLK=ClockSignal("sys4x"),
|
|
||||||
i_CLK_B=ClockSignal("sys4x"), # locally inverted
|
|
||||||
i_CLKDIV=ClockSignal("sys"),
|
|
||||||
o_Q=data_deserialized
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.submodules.datapath = datapath = RXDatapath(8)
|
|
||||||
self.comb += [
|
|
||||||
datapath.sink.stb.eq(1),
|
|
||||||
datapath.sink.data.eq(data_deserialized),
|
|
||||||
datapath.bitslip_value.eq(bitslip_value),
|
|
||||||
datapath.source.connect(source),
|
|
||||||
idle.eq(datapath.idle),
|
|
||||||
comma.eq(datapath.comma)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@ResetInserter()
|
|
||||||
class KUSerdes(Module):
|
|
||||||
def __init__(self, pads, mode="master"):
|
|
||||||
self.submodules.clocking = _KUSerdesClocking(pads, mode)
|
|
||||||
self.submodules.tx = _KUSerdesTX(pads)
|
|
||||||
self.submodules.rx = _KUSerdesRX(pads)
|
|
|
@ -1,173 +0,0 @@
|
||||||
from math import ceil
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
from migen.genlib.misc import WaitTimer
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
|
|
||||||
|
|
||||||
def reverse_bytes(signal):
|
|
||||||
n = ceil(len(signal)/8)
|
|
||||||
return Cat(iter([signal[i*8:(i+1)*8] for i in reversed(range(n))]))
|
|
||||||
|
|
||||||
|
|
||||||
class HeaderField:
|
|
||||||
def __init__(self, byte, offset, width):
|
|
||||||
self.byte = byte
|
|
||||||
self.offset = offset
|
|
||||||
self.width = width
|
|
||||||
|
|
||||||
|
|
||||||
class Header:
|
|
||||||
def __init__(self, fields, length, swap_field_bytes=True):
|
|
||||||
self.fields = fields
|
|
||||||
self.length = length
|
|
||||||
self.swap_field_bytes = swap_field_bytes
|
|
||||||
|
|
||||||
def get_layout(self):
|
|
||||||
layout = []
|
|
||||||
for k, v in sorted(self.fields.items()):
|
|
||||||
layout.append((k, v.width))
|
|
||||||
return layout
|
|
||||||
|
|
||||||
def get_field(self, obj, name, width):
|
|
||||||
if "_lsb" in name:
|
|
||||||
field = getattr(obj, name.replace("_lsb", ""))[:width]
|
|
||||||
elif "_msb" in name:
|
|
||||||
field = getattr(obj, name.replace("_msb", ""))[width:2*width]
|
|
||||||
else:
|
|
||||||
field = getattr(obj, name)
|
|
||||||
if len(field) != width:
|
|
||||||
raise ValueError("Width mismatch on " + name + " field")
|
|
||||||
return field
|
|
||||||
|
|
||||||
def encode(self, obj, signal):
|
|
||||||
r = []
|
|
||||||
for k, v in sorted(self.fields.items()):
|
|
||||||
start = v.byte*8 + v.offset
|
|
||||||
end = start + v.width
|
|
||||||
field = self.get_field(obj, k, v.width)
|
|
||||||
if self.swap_field_bytes:
|
|
||||||
field = reverse_bytes(field)
|
|
||||||
r.append(signal[start:end].eq(field))
|
|
||||||
return r
|
|
||||||
|
|
||||||
def decode(self, signal, obj):
|
|
||||||
r = []
|
|
||||||
for k, v in sorted(self.fields.items()):
|
|
||||||
start = v.byte*8 + v.offset
|
|
||||||
end = start + v.width
|
|
||||||
field = self.get_field(obj, k, v.width)
|
|
||||||
if self.swap_field_bytes:
|
|
||||||
r.append(field.eq(reverse_bytes(signal[start:end])))
|
|
||||||
else:
|
|
||||||
r.append(field.eq(signal[start:end]))
|
|
||||||
return r
|
|
||||||
|
|
||||||
def phy_description(dw):
|
|
||||||
layout = [("data", dw)]
|
|
||||||
return stream.EndpointDescription(layout)
|
|
||||||
|
|
||||||
|
|
||||||
def user_description(dw):
|
|
||||||
layout = [
|
|
||||||
("data", 32),
|
|
||||||
("length", 32)
|
|
||||||
]
|
|
||||||
return stream.EndpointDescription(layout)
|
|
||||||
|
|
||||||
|
|
||||||
class Packetizer(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.sink = sink = stream.Endpoint(user_description(32))
|
|
||||||
self.source = source = stream.Endpoint(phy_description(32))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# Packet description
|
|
||||||
# - preamble : 4 bytes
|
|
||||||
# - length : 4 bytes
|
|
||||||
# - payload
|
|
||||||
|
|
||||||
fsm = FSM(reset_state="PREAMBLE")
|
|
||||||
self.submodules += fsm
|
|
||||||
|
|
||||||
fsm.act("PREAMBLE",
|
|
||||||
If(sink.stb,
|
|
||||||
source.stb.eq(1),
|
|
||||||
source.data.eq(0x5aa55aa5),
|
|
||||||
If(source.ack,
|
|
||||||
NextState("LENGTH")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("LENGTH",
|
|
||||||
source.stb.eq(1),
|
|
||||||
source.data.eq(sink.length),
|
|
||||||
If(source.ack,
|
|
||||||
NextState("DATA")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("DATA",
|
|
||||||
source.stb.eq(sink.stb),
|
|
||||||
source.data.eq(sink.data),
|
|
||||||
sink.ack.eq(source.ack),
|
|
||||||
If(source.ack & sink.eop,
|
|
||||||
NextState("PREAMBLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Depacketizer(Module):
|
|
||||||
def __init__(self, clk_freq, timeout=10):
|
|
||||||
self.sink = sink = stream.Endpoint(phy_description(32))
|
|
||||||
self.source = source = stream.Endpoint(user_description(32))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
count = Signal(len(source.length))
|
|
||||||
length = Signal(len(source.length))
|
|
||||||
|
|
||||||
# Packet description
|
|
||||||
# - preamble : 4 bytes
|
|
||||||
# - length : 4 bytes
|
|
||||||
# - payload
|
|
||||||
|
|
||||||
fsm = FSM(reset_state="PREAMBLE")
|
|
||||||
self.submodules += fsm
|
|
||||||
|
|
||||||
timer = WaitTimer(clk_freq*timeout)
|
|
||||||
self.submodules += timer
|
|
||||||
|
|
||||||
fsm.act("PREAMBLE",
|
|
||||||
sink.ack.eq(1),
|
|
||||||
If(sink.stb &
|
|
||||||
(sink.data == 0x5aa55aa5),
|
|
||||||
NextState("LENGTH")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("LENGTH",
|
|
||||||
sink.ack.eq(1),
|
|
||||||
If(sink.stb,
|
|
||||||
NextValue(count, 0),
|
|
||||||
NextValue(length, sink.data),
|
|
||||||
NextState("DATA")
|
|
||||||
),
|
|
||||||
timer.wait.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("DATA",
|
|
||||||
source.stb.eq(sink.stb),
|
|
||||||
source.eop.eq(count == (length[2:] - 1)),
|
|
||||||
source.length.eq(length),
|
|
||||||
source.data.eq(sink.data),
|
|
||||||
sink.ack.eq(source.ack),
|
|
||||||
If(timer.done,
|
|
||||||
NextState("PREAMBLE")
|
|
||||||
).Elif(source.stb & source.ack,
|
|
||||||
NextValue(count, count + 1),
|
|
||||||
If(source.eop,
|
|
||||||
NextState("PREAMBLE")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
timer.wait.eq(1)
|
|
||||||
)
|
|
|
@ -1,381 +0,0 @@
|
||||||
from migen import *
|
|
||||||
from migen.genlib.misc import WaitTimer
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
from misoc.interconnect.csr import *
|
|
||||||
|
|
||||||
from artiq.gateware.serwb.kuserdes import KUSerdes
|
|
||||||
from artiq.gateware.serwb.s7serdes import S7Serdes
|
|
||||||
|
|
||||||
|
|
||||||
# SERWB Master <--> Slave physical synchronization process:
|
|
||||||
# 1) Master sends idle patterns (zeroes) to Slave to reset it.
|
|
||||||
# 2) Master sends K28.5 commas to allow Slave to calibrate, Slave sends idle patterns.
|
|
||||||
# 3) Slave sends K28.5 commas to allow Master to calibrate, Master sends K28.5 commas.
|
|
||||||
# 4) Master stops sending K28.5 commas.
|
|
||||||
# 5) Slave stops sending K28.5 commas.
|
|
||||||
# 6) Physical link is ready.
|
|
||||||
|
|
||||||
|
|
||||||
@ResetInserter()
|
|
||||||
class _SerdesMasterInit(Module):
|
|
||||||
def __init__(self, serdes, taps, timeout):
|
|
||||||
self.ready = Signal()
|
|
||||||
self.error = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.delay = delay = Signal(max=taps)
|
|
||||||
self.delay_min = delay_min = Signal(max=taps)
|
|
||||||
self.delay_min_found = delay_min_found = Signal()
|
|
||||||
self.delay_max = delay_max = Signal(max=taps)
|
|
||||||
self.delay_max_found = delay_max_found = Signal()
|
|
||||||
self.bitslip = bitslip = Signal(max=40)
|
|
||||||
|
|
||||||
self.submodules.timer = timer = WaitTimer(timeout)
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
fsm.act("IDLE",
|
|
||||||
NextValue(delay, 0),
|
|
||||||
NextValue(delay_min, 0),
|
|
||||||
NextValue(delay_min_found, 0),
|
|
||||||
NextValue(delay_max, 0),
|
|
||||||
NextValue(delay_max_found, 0),
|
|
||||||
serdes.rx.delay_rst.eq(1),
|
|
||||||
NextValue(bitslip, 0),
|
|
||||||
NextState("RESET_SLAVE"),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("RESET_SLAVE",
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
timer.wait.eq(0),
|
|
||||||
NextState("SEND_PATTERN")
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("SEND_PATTERN",
|
|
||||||
If(~serdes.rx.idle,
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
NextState("CHECK_PATTERN")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("WAIT_STABLE",
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
timer.wait.eq(0),
|
|
||||||
NextState("CHECK_PATTERN")
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("CHECK_PATTERN",
|
|
||||||
If(~delay_min_found,
|
|
||||||
If(serdes.rx.comma,
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
timer.wait.eq(0),
|
|
||||||
NextValue(delay_min, delay),
|
|
||||||
NextValue(delay_min_found, 1)
|
|
||||||
)
|
|
||||||
).Else(
|
|
||||||
NextState("INC_DELAY_BITSLIP")
|
|
||||||
),
|
|
||||||
).Else(
|
|
||||||
If(~serdes.rx.comma,
|
|
||||||
NextValue(delay_max, delay),
|
|
||||||
NextValue(delay_max_found, 1),
|
|
||||||
NextState("CHECK_SAMPLING_WINDOW")
|
|
||||||
).Else(
|
|
||||||
NextState("INC_DELAY_BITSLIP")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
self.comb += serdes.rx.bitslip_value.eq(bitslip)
|
|
||||||
fsm.act("INC_DELAY_BITSLIP",
|
|
||||||
NextState("WAIT_STABLE"),
|
|
||||||
If(delay == (taps - 1),
|
|
||||||
If(bitslip == (40 - 1),
|
|
||||||
NextState("ERROR")
|
|
||||||
).Else(
|
|
||||||
NextValue(delay_min_found, 0),
|
|
||||||
NextValue(delay_min, 0),
|
|
||||||
NextValue(delay_max_found, 0),
|
|
||||||
NextValue(delay_max, 0),
|
|
||||||
NextValue(bitslip, bitslip + 1)
|
|
||||||
),
|
|
||||||
NextValue(delay, 0),
|
|
||||||
serdes.rx.delay_rst.eq(1)
|
|
||||||
).Else(
|
|
||||||
NextValue(delay, delay + 1),
|
|
||||||
serdes.rx.delay_inc.eq(1)
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("CHECK_SAMPLING_WINDOW",
|
|
||||||
If((delay_min == 0) |
|
|
||||||
(delay_max == (taps - 1)) |
|
|
||||||
((delay_max - delay_min) < taps//16),
|
|
||||||
NextValue(delay_min_found, 0),
|
|
||||||
NextValue(delay_max_found, 0),
|
|
||||||
NextState("WAIT_STABLE")
|
|
||||||
).Else(
|
|
||||||
NextValue(delay, 0),
|
|
||||||
serdes.rx.delay_rst.eq(1),
|
|
||||||
NextState("CONFIGURE_SAMPLING_WINDOW")
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("CONFIGURE_SAMPLING_WINDOW",
|
|
||||||
If(delay == (delay_min + (delay_max - delay_min)[1:]),
|
|
||||||
NextState("READY")
|
|
||||||
).Else(
|
|
||||||
NextValue(delay, delay + 1),
|
|
||||||
serdes.rx.delay_inc.eq(1)
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("READY",
|
|
||||||
self.ready.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("ERROR",
|
|
||||||
self.error.eq(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@ResetInserter()
|
|
||||||
class _SerdesSlaveInit(Module, AutoCSR):
|
|
||||||
def __init__(self, serdes, taps, timeout):
|
|
||||||
self.ready = Signal()
|
|
||||||
self.error = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.delay = delay = Signal(max=taps)
|
|
||||||
self.delay_min = delay_min = Signal(max=taps)
|
|
||||||
self.delay_min_found = delay_min_found = Signal()
|
|
||||||
self.delay_max = delay_max = Signal(max=taps)
|
|
||||||
self.delay_max_found = delay_max_found = Signal()
|
|
||||||
self.bitslip = bitslip = Signal(max=40)
|
|
||||||
|
|
||||||
self.submodules.timer = timer = WaitTimer(timeout)
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
# reset
|
|
||||||
fsm.act("IDLE",
|
|
||||||
NextValue(delay, 0),
|
|
||||||
NextValue(delay_min, 0),
|
|
||||||
NextValue(delay_min_found, 0),
|
|
||||||
NextValue(delay_max, 0),
|
|
||||||
NextValue(delay_max_found, 0),
|
|
||||||
serdes.rx.delay_rst.eq(1),
|
|
||||||
NextValue(bitslip, 0),
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
timer.wait.eq(0),
|
|
||||||
NextState("WAIT_STABLE"),
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("WAIT_STABLE",
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
timer.wait.eq(0),
|
|
||||||
NextState("CHECK_PATTERN")
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("CHECK_PATTERN",
|
|
||||||
If(~delay_min_found,
|
|
||||||
If(serdes.rx.comma,
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
timer.wait.eq(0),
|
|
||||||
NextValue(delay_min, delay),
|
|
||||||
NextValue(delay_min_found, 1)
|
|
||||||
)
|
|
||||||
).Else(
|
|
||||||
NextState("INC_DELAY_BITSLIP")
|
|
||||||
),
|
|
||||||
).Else(
|
|
||||||
If(~serdes.rx.comma,
|
|
||||||
NextValue(delay_max, delay),
|
|
||||||
NextValue(delay_max_found, 1),
|
|
||||||
NextState("CHECK_SAMPLING_WINDOW")
|
|
||||||
).Else(
|
|
||||||
NextState("INC_DELAY_BITSLIP")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
self.comb += serdes.rx.bitslip_value.eq(bitslip)
|
|
||||||
fsm.act("INC_DELAY_BITSLIP",
|
|
||||||
NextState("WAIT_STABLE"),
|
|
||||||
If(delay == (taps - 1),
|
|
||||||
If(bitslip == (40 - 1),
|
|
||||||
NextState("ERROR")
|
|
||||||
).Else(
|
|
||||||
NextValue(delay_min_found, 0),
|
|
||||||
NextValue(delay_min, 0),
|
|
||||||
NextValue(delay_max_found, 0),
|
|
||||||
NextValue(delay_max, 0),
|
|
||||||
NextValue(bitslip, bitslip + 1)
|
|
||||||
),
|
|
||||||
NextValue(delay, 0),
|
|
||||||
serdes.rx.delay_rst.eq(1)
|
|
||||||
).Else(
|
|
||||||
NextValue(delay, delay + 1),
|
|
||||||
serdes.rx.delay_inc.eq(1)
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("CHECK_SAMPLING_WINDOW",
|
|
||||||
If((delay_min == 0) |
|
|
||||||
(delay_max == (taps - 1)) |
|
|
||||||
((delay_max - delay_min) < taps//16),
|
|
||||||
NextValue(delay_min_found, 0),
|
|
||||||
NextValue(delay_max_found, 0),
|
|
||||||
NextState("WAIT_STABLE")
|
|
||||||
).Else(
|
|
||||||
NextValue(delay, 0),
|
|
||||||
serdes.rx.delay_rst.eq(1),
|
|
||||||
NextState("CONFIGURE_SAMPLING_WINDOW")
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("CONFIGURE_SAMPLING_WINDOW",
|
|
||||||
If(delay == (delay_min + (delay_max - delay_min)[1:]),
|
|
||||||
NextState("SEND_PATTERN")
|
|
||||||
).Else(
|
|
||||||
NextValue(delay, delay + 1),
|
|
||||||
serdes.rx.delay_inc.eq(1),
|
|
||||||
),
|
|
||||||
serdes.tx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("SEND_PATTERN",
|
|
||||||
timer.wait.eq(1),
|
|
||||||
If(timer.done,
|
|
||||||
If(~serdes.rx.comma,
|
|
||||||
NextState("READY")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
serdes.tx.comma.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("READY",
|
|
||||||
self.ready.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("ERROR",
|
|
||||||
self.error.eq(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _SerdesControl(Module, AutoCSR):
|
|
||||||
def __init__(self, serdes, init, mode="master"):
|
|
||||||
if mode == "master":
|
|
||||||
self.reset = CSR()
|
|
||||||
self.ready = CSRStatus()
|
|
||||||
self.error = CSRStatus()
|
|
||||||
|
|
||||||
self.delay = CSRStatus(9)
|
|
||||||
self.delay_min_found = CSRStatus()
|
|
||||||
self.delay_min = CSRStatus(9)
|
|
||||||
self.delay_max_found = CSRStatus()
|
|
||||||
self.delay_max = CSRStatus(9)
|
|
||||||
self.bitslip = CSRStatus(6)
|
|
||||||
|
|
||||||
self.prbs_error = Signal()
|
|
||||||
self.prbs_start = CSR()
|
|
||||||
self.prbs_cycles = CSRStorage(32)
|
|
||||||
self.prbs_errors = CSRStatus(32)
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
if mode == "master":
|
|
||||||
# In Master mode, reset is coming from CSR,
|
|
||||||
# it resets the Master that will also reset
|
|
||||||
# the Slave by putting the link in idle.
|
|
||||||
self.sync += init.reset.eq(self.reset.re)
|
|
||||||
else:
|
|
||||||
# In Slave mode, reset is coming from link,
|
|
||||||
# Master reset the Slave by putting the link
|
|
||||||
# in idle.
|
|
||||||
self.sync += [
|
|
||||||
init.reset.eq(serdes.rx.idle),
|
|
||||||
serdes.reset.eq(serdes.rx.idle)
|
|
||||||
]
|
|
||||||
self.comb += [
|
|
||||||
self.ready.status.eq(init.ready),
|
|
||||||
self.error.status.eq(init.error),
|
|
||||||
self.delay.status.eq(init.delay),
|
|
||||||
self.delay_min_found.status.eq(init.delay_min_found),
|
|
||||||
self.delay_min.status.eq(init.delay_min),
|
|
||||||
self.delay_max_found.status.eq(init.delay_max_found),
|
|
||||||
self.delay_max.status.eq(init.delay_max),
|
|
||||||
self.bitslip.status.eq(init.bitslip)
|
|
||||||
]
|
|
||||||
|
|
||||||
# prbs
|
|
||||||
prbs_cycles = Signal(32)
|
|
||||||
prbs_errors = self.prbs_errors.status
|
|
||||||
prbs_fsm = FSM(reset_state="IDLE")
|
|
||||||
self.submodules += prbs_fsm
|
|
||||||
prbs_fsm.act("IDLE",
|
|
||||||
NextValue(prbs_cycles, 0),
|
|
||||||
If(self.prbs_start.re,
|
|
||||||
NextValue(prbs_errors, 0),
|
|
||||||
NextState("CHECK")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
prbs_fsm.act("CHECK",
|
|
||||||
NextValue(prbs_cycles, prbs_cycles + 1),
|
|
||||||
If(self.prbs_error,
|
|
||||||
NextValue(prbs_errors, prbs_errors + 1),
|
|
||||||
),
|
|
||||||
If(prbs_cycles == self.prbs_cycles.storage,
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SERWBPHY(Module, AutoCSR):
|
|
||||||
def __init__(self, device, pads, mode="master", init_timeout=2**15):
|
|
||||||
self.sink = sink = stream.Endpoint([("data", 32)])
|
|
||||||
self.source = source = stream.Endpoint([("data", 32)])
|
|
||||||
assert mode in ["master", "slave"]
|
|
||||||
if device[:4] == "xcku":
|
|
||||||
taps = 512
|
|
||||||
self.submodules.serdes = KUSerdes(pads, mode)
|
|
||||||
elif device[:4] == "xc7a":
|
|
||||||
taps = 32
|
|
||||||
self.submodules.serdes = S7Serdes(pads, mode)
|
|
||||||
else:
|
|
||||||
raise NotImplementedError
|
|
||||||
if mode == "master":
|
|
||||||
self.submodules.init = _SerdesMasterInit(self.serdes, taps, init_timeout)
|
|
||||||
else:
|
|
||||||
self.submodules.init = _SerdesSlaveInit(self.serdes, taps, init_timeout)
|
|
||||||
self.submodules.control = _SerdesControl(self.serdes, self.init, mode)
|
|
||||||
|
|
||||||
# tx/rx dataflow
|
|
||||||
self.comb += [
|
|
||||||
If(self.init.ready,
|
|
||||||
If(sink.stb,
|
|
||||||
sink.connect(self.serdes.tx.sink),
|
|
||||||
),
|
|
||||||
self.serdes.rx.source.connect(source)
|
|
||||||
).Else(
|
|
||||||
self.serdes.rx.source.ack.eq(1)
|
|
||||||
),
|
|
||||||
self.serdes.tx.sink.stb.eq(1) # always transmitting
|
|
||||||
]
|
|
||||||
|
|
||||||
# For PRBS test we are using the scrambler/descrambler as PRBS,
|
|
||||||
# sending 0 to the scrambler and checking that descrambler
|
|
||||||
# output is always 0.
|
|
||||||
self.comb += self.control.prbs_error.eq(
|
|
||||||
source.stb &
|
|
||||||
source.ack &
|
|
||||||
(source.data != 0))
|
|
|
@ -1,160 +0,0 @@
|
||||||
from migen import *
|
|
||||||
from migen.genlib.io import *
|
|
||||||
from migen.genlib.misc import BitSlip, WaitTimer
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
|
||||||
|
|
||||||
from artiq.gateware.serwb.datapath import TXDatapath, RXDatapath
|
|
||||||
|
|
||||||
|
|
||||||
class _S7SerdesClocking(Module):
|
|
||||||
def __init__(self, pads, mode="master"):
|
|
||||||
self.refclk = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# In Master mode, generate the linerate/10 clock. Slave will re-multiply it.
|
|
||||||
if mode == "master":
|
|
||||||
converter = stream.Converter(40, 8)
|
|
||||||
self.submodules += converter
|
|
||||||
self.comb += [
|
|
||||||
converter.sink.stb.eq(1),
|
|
||||||
converter.source.ack.eq(1),
|
|
||||||
converter.sink.data.eq(Replicate(Signal(10, reset=0b1111100000), 4)),
|
|
||||||
]
|
|
||||||
self.specials += [
|
|
||||||
Instance("OSERDESE2",
|
|
||||||
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
|
|
||||||
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
|
|
||||||
p_SERDES_MODE="MASTER",
|
|
||||||
|
|
||||||
o_OQ=self.refclk,
|
|
||||||
i_OCE=1,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
|
||||||
i_D1=converter.source.data[0], i_D2=converter.source.data[1],
|
|
||||||
i_D3=converter.source.data[2], i_D4=converter.source.data[3],
|
|
||||||
i_D5=converter.source.data[4], i_D6=converter.source.data[5],
|
|
||||||
i_D7=converter.source.data[6], i_D8=converter.source.data[7]
|
|
||||||
),
|
|
||||||
DifferentialOutput(self.refclk, pads.clk_p, pads.clk_n)
|
|
||||||
]
|
|
||||||
|
|
||||||
# In Slave mode, multiply the clock provided by Master with a PLL/MMCM
|
|
||||||
elif mode == "slave":
|
|
||||||
self.specials += DifferentialInput(pads.clk_p, pads.clk_n, self.refclk)
|
|
||||||
|
|
||||||
|
|
||||||
class _S7SerdesTX(Module):
|
|
||||||
def __init__(self, pads, mode="master"):
|
|
||||||
# Control
|
|
||||||
self.idle = idle = Signal()
|
|
||||||
self.comma = comma = Signal()
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.sink = sink = stream.Endpoint([("data", 32)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.submodules.datapath = datapath = TXDatapath(8)
|
|
||||||
self.comb += [
|
|
||||||
sink.connect(datapath.sink),
|
|
||||||
datapath.source.ack.eq(1),
|
|
||||||
datapath.idle.eq(idle),
|
|
||||||
datapath.comma.eq(comma)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Output Data(DDR with sys4x)
|
|
||||||
data = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("OSERDESE2",
|
|
||||||
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
|
|
||||||
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
|
|
||||||
p_SERDES_MODE="MASTER",
|
|
||||||
|
|
||||||
o_OQ=data,
|
|
||||||
i_OCE=1,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
|
||||||
i_D1=datapath.source.data[0], i_D2=datapath.source.data[1],
|
|
||||||
i_D3=datapath.source.data[2], i_D4=datapath.source.data[3],
|
|
||||||
i_D5=datapath.source.data[4], i_D6=datapath.source.data[5],
|
|
||||||
i_D7=datapath.source.data[6], i_D8=datapath.source.data[7]
|
|
||||||
),
|
|
||||||
DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _S7SerdesRX(Module):
|
|
||||||
def __init__(self, pads, mode="master"):
|
|
||||||
# Control
|
|
||||||
self.delay_rst = Signal()
|
|
||||||
self.delay_inc = Signal()
|
|
||||||
self.bitslip_value = bitslip_value = Signal(6)
|
|
||||||
|
|
||||||
# Status
|
|
||||||
self.idle = idle = Signal()
|
|
||||||
self.comma = comma = Signal()
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.source = source = stream.Endpoint([("data", 32)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# Data input (DDR with sys4x)
|
|
||||||
data_nodelay = Signal()
|
|
||||||
data_delayed = Signal()
|
|
||||||
data_deserialized = Signal(8)
|
|
||||||
self.specials += [
|
|
||||||
DifferentialInput(pads.rx_p, pads.rx_n, data_nodelay),
|
|
||||||
Instance("IDELAYE2",
|
|
||||||
p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA",
|
|
||||||
p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE",
|
|
||||||
p_REFCLK_FREQUENCY=200.0, p_PIPE_SEL="FALSE",
|
|
||||||
p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=0,
|
|
||||||
|
|
||||||
i_C=ClockSignal(),
|
|
||||||
i_LD=self.delay_rst,
|
|
||||||
i_CE=self.delay_inc,
|
|
||||||
i_LDPIPEEN=0, i_INC=1,
|
|
||||||
|
|
||||||
i_IDATAIN=data_nodelay, o_DATAOUT=data_delayed
|
|
||||||
),
|
|
||||||
Instance("ISERDESE2",
|
|
||||||
p_DATA_WIDTH=8, p_DATA_RATE="DDR",
|
|
||||||
p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING",
|
|
||||||
p_NUM_CE=1, p_IOBDELAY="IFD",
|
|
||||||
|
|
||||||
i_DDLY=data_delayed,
|
|
||||||
i_CE1=1,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKB=~ClockSignal("sys4x"),
|
|
||||||
i_CLKDIV=ClockSignal("sys"),
|
|
||||||
i_BITSLIP=0,
|
|
||||||
o_Q8=data_deserialized[0], o_Q7=data_deserialized[1],
|
|
||||||
o_Q6=data_deserialized[2], o_Q5=data_deserialized[3],
|
|
||||||
o_Q4=data_deserialized[4], o_Q3=data_deserialized[5],
|
|
||||||
o_Q2=data_deserialized[6], o_Q1=data_deserialized[7]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Datapath
|
|
||||||
self.submodules.datapath = datapath = RXDatapath(8)
|
|
||||||
self.comb += [
|
|
||||||
datapath.sink.stb.eq(1),
|
|
||||||
datapath.sink.data.eq(data_deserialized),
|
|
||||||
datapath.bitslip_value.eq(bitslip_value),
|
|
||||||
datapath.source.connect(source),
|
|
||||||
idle.eq(datapath.idle),
|
|
||||||
comma.eq(datapath.comma)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@ResetInserter()
|
|
||||||
class S7Serdes(Module):
|
|
||||||
def __init__(self, pads, mode="master"):
|
|
||||||
self.submodules.clocking = _S7SerdesClocking(pads, mode)
|
|
||||||
self.submodules.tx = _S7SerdesTX(pads, mode)
|
|
||||||
self.submodules.rx = _S7SerdesRX(pads, mode)
|
|
|
@ -1,89 +0,0 @@
|
||||||
from functools import reduce
|
|
||||||
from operator import xor
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
|
|
||||||
|
|
||||||
def K(x, y):
|
|
||||||
return (y << 5) | x
|
|
||||||
|
|
||||||
|
|
||||||
@ResetInserter()
|
|
||||||
@CEInserter()
|
|
||||||
class _Scrambler(Module):
|
|
||||||
def __init__(self, n_io, n_state=23, taps=[17, 22]):
|
|
||||||
self.i = Signal(n_io)
|
|
||||||
self.o = Signal(n_io)
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
state = Signal(n_state, reset=1)
|
|
||||||
curval = [state[i] for i in range(n_state)]
|
|
||||||
for i in reversed(range(n_io)):
|
|
||||||
flip = reduce(xor, [curval[tap] for tap in taps])
|
|
||||||
self.comb += self.o[i].eq(flip ^ self.i[i])
|
|
||||||
curval.insert(0, flip)
|
|
||||||
curval.pop()
|
|
||||||
|
|
||||||
self.sync += state.eq(Cat(*curval[:n_state]))
|
|
||||||
|
|
||||||
|
|
||||||
class Scrambler(Module):
|
|
||||||
def __init__(self, sync_interval=2**10):
|
|
||||||
self.sink = sink = stream.Endpoint([("data", 32)])
|
|
||||||
self.source = source = stream.Endpoint([("d", 32), ("k", 4)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# Scrambler
|
|
||||||
self.submodules.scrambler = scrambler = _Scrambler(32)
|
|
||||||
|
|
||||||
# Insert K29.7 SYNC character every "sync_interval" cycles
|
|
||||||
count = Signal(max=sync_interval)
|
|
||||||
self.sync += If(source.ack, count.eq(count + 1))
|
|
||||||
self.comb += [
|
|
||||||
source.stb.eq(1),
|
|
||||||
If(count == 0,
|
|
||||||
scrambler.reset.eq(1),
|
|
||||||
source.k.eq(0b1),
|
|
||||||
source.d.eq(K(29, 7)),
|
|
||||||
).Else(
|
|
||||||
If(sink.stb, scrambler.i.eq(sink.data)),
|
|
||||||
source.k.eq(0),
|
|
||||||
source.d.eq(scrambler.o),
|
|
||||||
If(source.ack,
|
|
||||||
sink.ack.eq(1),
|
|
||||||
scrambler.ce.eq(1)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Descrambler(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.sink = sink = stream.Endpoint([("d", 32), ("k", 4)])
|
|
||||||
self.source = source = stream.Endpoint([("data", 32)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# Descrambler
|
|
||||||
self.submodules.descrambler = descrambler = _Scrambler(32)
|
|
||||||
self.comb += descrambler.i.eq(sink.d)
|
|
||||||
|
|
||||||
# Detect K29.7 SYNC character and synchronize Descrambler
|
|
||||||
self.comb += \
|
|
||||||
If(sink.stb,
|
|
||||||
If((sink.k == 0b1) & (sink.d == K(29,7)),
|
|
||||||
sink.ack.eq(1),
|
|
||||||
descrambler.reset.eq(1)
|
|
||||||
).Else(
|
|
||||||
source.stb.eq(1),
|
|
||||||
source.data.eq(descrambler.o),
|
|
||||||
If(source.ack,
|
|
||||||
sink.ack.eq(1),
|
|
||||||
descrambler.ce.eq(1)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -14,7 +14,6 @@ from misoc.targets.sayma_amc import *
|
||||||
from artiq.gateware.amp import AMPSoC
|
from artiq.gateware.amp import AMPSoC
|
||||||
from artiq.gateware import eem
|
from artiq.gateware import eem
|
||||||
from artiq.gateware import fmcdio_vhdci_eem
|
from artiq.gateware import fmcdio_vhdci_eem
|
||||||
from artiq.gateware import serwb, remote_csr
|
|
||||||
from artiq.gateware import rtio
|
from artiq.gateware import rtio
|
||||||
from artiq.gateware import jesd204_tools
|
from artiq.gateware import jesd204_tools
|
||||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_ultrascale, sawg
|
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_ultrascale, sawg
|
||||||
|
@ -216,10 +215,19 @@ class AD9154NoSAWG(Module, AutoCSR):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class RTMSerWb:
|
class Satellite(SatelliteBase):
|
||||||
def __init__(self):
|
"""
|
||||||
|
DRTIO satellite with local DAC/SAWG channels.
|
||||||
|
"""
|
||||||
|
def __init__(self, with_sawg, **kwargs):
|
||||||
|
SatelliteBase.__init__(self, 150e6,
|
||||||
|
identifier_suffix=".without-sawg" if not with_sawg else "",
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
platform = self.platform
|
platform = self.platform
|
||||||
|
|
||||||
|
self.submodules += RTMUARTForward(platform)
|
||||||
|
|
||||||
# RTM bitstream upload
|
# RTM bitstream upload
|
||||||
slave_fpga_cfg = self.platform.request("rtm_fpga_cfg")
|
slave_fpga_cfg = self.platform.request("rtm_fpga_cfg")
|
||||||
self.submodules.slave_fpga_cfg = gpio.GPIOTristate([
|
self.submodules.slave_fpga_cfg = gpio.GPIOTristate([
|
||||||
|
@ -232,36 +240,6 @@ class RTMSerWb:
|
||||||
self.csr_devices.append("slave_fpga_cfg")
|
self.csr_devices.append("slave_fpga_cfg")
|
||||||
self.config["SLAVE_FPGA_GATEWARE"] = 0x200000
|
self.config["SLAVE_FPGA_GATEWARE"] = 0x200000
|
||||||
|
|
||||||
# AMC/RTM serwb
|
|
||||||
serwb_pads = platform.request("amc_rtm_serwb")
|
|
||||||
serwb_phy_amc = serwb.genphy.SERWBPHY(platform.device, serwb_pads, mode="master")
|
|
||||||
self.submodules.serwb_phy_amc = serwb_phy_amc
|
|
||||||
self.csr_devices.append("serwb_phy_amc")
|
|
||||||
|
|
||||||
serwb_core = serwb.core.SERWBCore(serwb_phy_amc, int(self.clk_freq), mode="slave")
|
|
||||||
self.submodules += serwb_core
|
|
||||||
self.add_wb_slave(self.mem_map["serwb"], 8192, serwb_core.etherbone.wishbone.bus)
|
|
||||||
|
|
||||||
|
|
||||||
class Satellite(SatelliteBase, RTMSerWb):
|
|
||||||
"""
|
|
||||||
DRTIO satellite with local DAC/SAWG channels.
|
|
||||||
"""
|
|
||||||
mem_map = {
|
|
||||||
"serwb": 0x13000000,
|
|
||||||
}
|
|
||||||
mem_map.update(SatelliteBase.mem_map)
|
|
||||||
|
|
||||||
def __init__(self, with_sawg, **kwargs):
|
|
||||||
SatelliteBase.__init__(self, 150e6,
|
|
||||||
identifier_suffix=".without-sawg" if not with_sawg else "",
|
|
||||||
**kwargs)
|
|
||||||
RTMSerWb.__init__(self)
|
|
||||||
|
|
||||||
platform = self.platform
|
|
||||||
|
|
||||||
self.submodules += RTMUARTForward(platform)
|
|
||||||
|
|
||||||
rtio_channels = []
|
rtio_channels = []
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
phy = ttl_simple.Output(platform.request("user_led", i))
|
phy = ttl_simple.Output(platform.request("user_led", i))
|
||||||
|
@ -509,16 +487,6 @@ def main():
|
||||||
variant = args.variant.lower()
|
variant = args.variant.lower()
|
||||||
if variant == "satellite":
|
if variant == "satellite":
|
||||||
soc = Satellite(with_sawg=not args.without_sawg, **soc_sayma_amc_argdict(args))
|
soc = Satellite(with_sawg=not args.without_sawg, **soc_sayma_amc_argdict(args))
|
||||||
remote_csr_regions = remote_csr.get_remote_csr_regions(
|
|
||||||
soc.mem_map["serwb"] | soc.shadow_base,
|
|
||||||
args.rtm_csr_csv)
|
|
||||||
for name, origin, busword, csrs in remote_csr_regions:
|
|
||||||
soc.add_csr_region(name, origin, busword, csrs)
|
|
||||||
# Configuration for RTM peripherals. Keep in sync with sayma_rtm.py!
|
|
||||||
soc.config["HAS_HMC830_7043"] = None
|
|
||||||
soc.config["CONVERTER_SPI_HMC830_CS"] = 0
|
|
||||||
soc.config["CONVERTER_SPI_HMC7043_CS"] = 1
|
|
||||||
soc.config["CONVERTER_SPI_FIRST_AD9154_CS"] = 2
|
|
||||||
elif variant == "simplesatellite":
|
elif variant == "simplesatellite":
|
||||||
soc = SimpleSatellite(**soc_sayma_amc_argdict(args))
|
soc = SimpleSatellite(**soc_sayma_amc_argdict(args))
|
||||||
elif variant == "master":
|
elif variant == "master":
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
import unittest
|
|
||||||
import random
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
|
|
||||||
from artiq.gateware.serwb import scrambler
|
|
||||||
from artiq.gateware.serwb import core
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
from misoc.interconnect.wishbone import SRAM
|
|
||||||
|
|
||||||
|
|
||||||
class FakeInit(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.ready = Signal(reset=1)
|
|
||||||
|
|
||||||
|
|
||||||
class FakeSerdes(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.tx_ce = Signal()
|
|
||||||
self.tx_k = Signal(4)
|
|
||||||
self.tx_d = Signal(32)
|
|
||||||
self.rx_ce = Signal()
|
|
||||||
self.rx_k = Signal(4)
|
|
||||||
self.rx_d = Signal(32)
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
data_ce = Signal(5, reset=0b00001)
|
|
||||||
self.sync += data_ce.eq(Cat(data_ce[1:], data_ce[0]))
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
self.tx_ce.eq(data_ce[0]),
|
|
||||||
self.rx_ce.eq(data_ce[0])
|
|
||||||
]
|
|
||||||
|
|
||||||
class FakePHY(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.sink = sink = stream.Endpoint([("data", 32)])
|
|
||||||
self.source = source = stream.Endpoint([("data", 32)])
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.submodules.init = FakeInit()
|
|
||||||
self.submodules.serdes = FakeSerdes()
|
|
||||||
|
|
||||||
# tx dataflow
|
|
||||||
self.comb += \
|
|
||||||
If(self.init.ready,
|
|
||||||
sink.ack.eq(self.serdes.tx_ce),
|
|
||||||
If(sink.stb,
|
|
||||||
self.serdes.tx_d.eq(sink.data)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# rx dataflow
|
|
||||||
self.comb += \
|
|
||||||
If(self.init.ready,
|
|
||||||
source.stb.eq(self.serdes.rx_ce),
|
|
||||||
source.data.eq(self.serdes.rx_d)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DUTScrambler(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.submodules.scrambler = scrambler.Scrambler(sync_interval=16)
|
|
||||||
self.submodules.descrambler = scrambler.Descrambler()
|
|
||||||
self.comb += self.scrambler.source.connect(self.descrambler.sink)
|
|
||||||
|
|
||||||
|
|
||||||
class DUTCore(Module):
|
|
||||||
def __init__(self):
|
|
||||||
# wishbone slave
|
|
||||||
phy_slave = FakePHY()
|
|
||||||
serwb_slave = core.SERWBCore(phy_slave, int(1e6), "slave")
|
|
||||||
self.submodules += phy_slave, serwb_slave
|
|
||||||
|
|
||||||
# wishbone master
|
|
||||||
phy_master = FakePHY()
|
|
||||||
serwb_master = core.SERWBCore(phy_master, int(1e6), "master")
|
|
||||||
self.submodules += phy_master, serwb_master
|
|
||||||
|
|
||||||
# connect phy
|
|
||||||
self.comb += [
|
|
||||||
phy_master.serdes.rx_ce.eq(phy_slave.serdes.tx_ce),
|
|
||||||
phy_master.serdes.rx_k.eq(phy_slave.serdes.tx_k),
|
|
||||||
phy_master.serdes.rx_d.eq(phy_slave.serdes.tx_d),
|
|
||||||
|
|
||||||
phy_slave.serdes.rx_ce.eq(phy_master.serdes.tx_ce),
|
|
||||||
phy_slave.serdes.rx_k.eq(phy_master.serdes.tx_k),
|
|
||||||
phy_slave.serdes.rx_d.eq(phy_master.serdes.tx_d)
|
|
||||||
]
|
|
||||||
|
|
||||||
# add wishbone sram to wishbone master
|
|
||||||
sram = SRAM(1024, bus=serwb_master.etherbone.wishbone.bus)
|
|
||||||
self.submodules += sram
|
|
||||||
|
|
||||||
# expose wishbone slave
|
|
||||||
self.wishbone = serwb_slave.etherbone.wishbone.bus
|
|
||||||
|
|
||||||
|
|
||||||
class TestSERWBCore(unittest.TestCase):
|
|
||||||
def test_scrambler(self):
|
|
||||||
def generator(dut, rand_level=50):
|
|
||||||
# prepare test
|
|
||||||
prng = random.Random(42)
|
|
||||||
i = 0
|
|
||||||
last_data = -1
|
|
||||||
# test loop
|
|
||||||
while i != 256:
|
|
||||||
# stim
|
|
||||||
yield dut.scrambler.sink.stb.eq(1)
|
|
||||||
if (yield dut.scrambler.sink.stb) & (yield dut.scrambler.sink.ack):
|
|
||||||
i += 1
|
|
||||||
yield dut.scrambler.sink.data.eq(i)
|
|
||||||
|
|
||||||
# check
|
|
||||||
yield dut.descrambler.source.ack.eq(prng.randrange(100) > rand_level)
|
|
||||||
if (yield dut.descrambler.source.stb) & (yield dut.descrambler.source.ack):
|
|
||||||
current_data = (yield dut.descrambler.source.data)
|
|
||||||
if (current_data != (last_data + 1)):
|
|
||||||
dut.errors += 1
|
|
||||||
last_data = current_data
|
|
||||||
|
|
||||||
# cycle
|
|
||||||
yield
|
|
||||||
|
|
||||||
dut = DUTScrambler()
|
|
||||||
dut.errors = 0
|
|
||||||
run_simulation(dut, generator(dut))
|
|
||||||
self.assertEqual(dut.errors, 0)
|
|
||||||
|
|
||||||
def test_serwb(self):
|
|
||||||
def generator(dut):
|
|
||||||
# prepare test
|
|
||||||
prng = random.Random(42)
|
|
||||||
data_base = 0x100
|
|
||||||
data_length = 4
|
|
||||||
datas_w = [prng.randrange(2**32) for i in range(data_length)]
|
|
||||||
datas_r = []
|
|
||||||
|
|
||||||
# write
|
|
||||||
for i in range(data_length):
|
|
||||||
yield from dut.wishbone.write(data_base + i, datas_w[i])
|
|
||||||
|
|
||||||
# read
|
|
||||||
for i in range(data_length):
|
|
||||||
datas_r.append((yield from dut.wishbone.read(data_base + i)))
|
|
||||||
|
|
||||||
# check
|
|
||||||
for i in range(data_length):
|
|
||||||
if datas_r[i] != datas_w[i]:
|
|
||||||
dut.errors += 1
|
|
||||||
|
|
||||||
dut = DUTCore()
|
|
||||||
dut.errors = 0
|
|
||||||
run_simulation(dut, generator(dut))
|
|
||||||
self.assertEqual(dut.errors, 0)
|
|
|
@ -1,144 +0,0 @@
|
||||||
import unittest
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
|
|
||||||
from artiq.gateware.serwb import packet
|
|
||||||
from artiq.gateware.serwb.phy import _SerdesMasterInit, _SerdesSlaveInit
|
|
||||||
|
|
||||||
|
|
||||||
class SerdesModel(Module):
|
|
||||||
def __init__(self, taps, mode="slave"):
|
|
||||||
self.tx = Module()
|
|
||||||
self.rx = Module()
|
|
||||||
|
|
||||||
self.tx.idle = Signal()
|
|
||||||
self.tx.comma = Signal()
|
|
||||||
self.rx.idle = Signal()
|
|
||||||
self.rx.comma = Signal()
|
|
||||||
|
|
||||||
self.rx.bitslip_value = Signal(6)
|
|
||||||
self.rx.delay_rst = Signal()
|
|
||||||
self.rx.delay_inc = Signal()
|
|
||||||
|
|
||||||
self.valid_bitslip = Signal(6)
|
|
||||||
self.valid_delays = Signal(taps)
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
delay = Signal(max=taps)
|
|
||||||
bitslip = Signal(6)
|
|
||||||
|
|
||||||
valid_delays = Array(Signal() for i in range(taps))
|
|
||||||
for i in range(taps):
|
|
||||||
self.comb += valid_delays[taps-1-i].eq(self.valid_delays[i])
|
|
||||||
|
|
||||||
self.sync += [
|
|
||||||
bitslip.eq(self.rx.bitslip_value),
|
|
||||||
If(self.rx.delay_rst,
|
|
||||||
delay.eq(0)
|
|
||||||
).Elif(self.rx.delay_inc,
|
|
||||||
delay.eq(delay + 1)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
if mode == "master":
|
|
||||||
self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE"))
|
|
||||||
self.comb += self.fsm.reset.eq(self.tx.idle)
|
|
||||||
fsm.act("IDLE",
|
|
||||||
If(self.tx.comma,
|
|
||||||
NextState("SEND_COMMA")
|
|
||||||
),
|
|
||||||
self.rx.idle.eq(1)
|
|
||||||
)
|
|
||||||
fsm.act("SEND_COMMA",
|
|
||||||
If(valid_delays[delay] &
|
|
||||||
(bitslip == self.valid_bitslip),
|
|
||||||
self.rx.comma.eq(1)
|
|
||||||
),
|
|
||||||
If(~self.tx.comma,
|
|
||||||
NextState("READY")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("READY")
|
|
||||||
elif mode == "slave":
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
fsm.act("IDLE",
|
|
||||||
self.rx.idle.eq(1),
|
|
||||||
NextState("SEND_COMMA")
|
|
||||||
)
|
|
||||||
fsm.act("SEND_COMMA",
|
|
||||||
If(valid_delays[delay] &
|
|
||||||
(bitslip == self.valid_bitslip),
|
|
||||||
self.rx.comma.eq(1)
|
|
||||||
),
|
|
||||||
If(~self.tx.idle,
|
|
||||||
NextState("READY")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fsm.act("READY")
|
|
||||||
|
|
||||||
|
|
||||||
class DUTMaster(Module):
|
|
||||||
def __init__(self, taps=32):
|
|
||||||
self.submodules.serdes = SerdesModel(taps, mode="master")
|
|
||||||
self.submodules.init = _SerdesMasterInit(self.serdes, taps, timeout=1)
|
|
||||||
|
|
||||||
|
|
||||||
class DUTSlave(Module):
|
|
||||||
def __init__(self, taps=32):
|
|
||||||
self.submodules.serdes = SerdesModel(taps, mode="slave")
|
|
||||||
self.submodules.init = _SerdesSlaveInit(self.serdes, taps, timeout=1)
|
|
||||||
|
|
||||||
|
|
||||||
def generator(test, dut, valid_bitslip, valid_delays, check_success):
|
|
||||||
yield dut.serdes.valid_bitslip.eq(valid_bitslip)
|
|
||||||
yield dut.serdes.valid_delays.eq(valid_delays)
|
|
||||||
while not ((yield dut.init.ready) or
|
|
||||||
(yield dut.init.error)):
|
|
||||||
yield
|
|
||||||
if check_success:
|
|
||||||
ready = (yield dut.init.ready)
|
|
||||||
error = (yield dut.init.error)
|
|
||||||
delay_min = (yield dut.init.delay_min)
|
|
||||||
delay_max = (yield dut.init.delay_max)
|
|
||||||
delay = (yield dut.init.delay)
|
|
||||||
bitslip = (yield dut.init.bitslip)
|
|
||||||
test.assertEqual(ready, 1)
|
|
||||||
test.assertEqual(error, 0)
|
|
||||||
test.assertEqual(delay_min, 4)
|
|
||||||
test.assertEqual(delay_max, 9)
|
|
||||||
test.assertEqual(delay, 6)
|
|
||||||
test.assertEqual(bitslip, valid_bitslip)
|
|
||||||
else:
|
|
||||||
ready = (yield dut.init.ready)
|
|
||||||
error = (yield dut.init.error)
|
|
||||||
test.assertEqual(ready, 0)
|
|
||||||
test.assertEqual(error, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class TestSERWBInit(unittest.TestCase):
|
|
||||||
def test_master_init_success(self):
|
|
||||||
dut = DUTMaster()
|
|
||||||
valid_bitslip = 2
|
|
||||||
valid_delays = 0b10001111100000111110000011111000
|
|
||||||
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, True))
|
|
||||||
|
|
||||||
def test_master_init_failure(self):
|
|
||||||
# too small window
|
|
||||||
dut = DUTMaster()
|
|
||||||
valid_bitslip = 2
|
|
||||||
valid_delays = 0b00000000000000010000000000000000
|
|
||||||
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False))
|
|
||||||
|
|
||||||
def test_slave_init_success(self):
|
|
||||||
dut = DUTSlave()
|
|
||||||
valid_bitslip = 2
|
|
||||||
valid_delays = 0b10001111100000111110000011111000
|
|
||||||
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, True))
|
|
||||||
|
|
||||||
def test_slave_init_failure(self):
|
|
||||||
# too small window
|
|
||||||
dut = DUTSlave()
|
|
||||||
valid_bitslip = 2
|
|
||||||
valid_delays = 0b00000000000000010000000000000000
|
|
||||||
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False))
|
|
Loading…
Reference in New Issue