forked from M-Labs/artiq
1
0
Fork 0

remove serwb

DRTIO is a better solution
This commit is contained in:
Sebastien Bourdeauducq 2019-10-06 18:09:42 +08:00
parent 7cd02d30b7
commit e9b81f6e33
19 changed files with 11 additions and 2815 deletions

View File

@ -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)]

View File

@ -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]));
}

View File

@ -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 */

View File

@ -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");

View File

@ -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

View File

@ -1 +0,0 @@
from artiq.gateware.serwb import s7serdes, kuserdes, genphy, phy, core, packet, etherbone

View File

@ -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)
]

View File

@ -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)))
)

View File

@ -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)
]

View File

@ -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))

View File

@ -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)

View File

@ -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)
)

View File

@ -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))

View File

@ -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)

View File

@ -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)
)
)
)

View File

@ -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":

View File

@ -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)

View File

@ -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))