1
0
Fork 0

Compare commits

..

8 Commits

Author SHA1 Message Date
morgan 6c53447808 cxp downconn firmware: init
cxp down fw: cleanup
2024-07-23 17:43:06 +08:00
morgan 14aa81c8a2 cxp upconn firmware: init 2024-07-23 17:09:33 +08:00
morgan 82af01350f cxp: add upconn, downconn & crc
cxp: add crc32 for cxp
cxp: add upconn & downconn
2024-07-23 17:09:22 +08:00
morgan 1f033d605c cxp upconn: add low speed serial
cxp upconn: add low speed serial
cxp upconn: add pll for bitrate2x mode
cxp upconn: add tx_fifos & idle with encoder
cxp upconn: add priority packet that send in word/char boundary
2024-07-23 17:08:33 +08:00
morgan 7d5e3c1ef9 cxp downconn: add high speed serial
cxp downconn: add bruteforcephase aligner
cxp downconn: add gtx with mmcm for TXUSRCLK freq requirement
cxp downconn: add loopback mode parameter for testing
2024-07-23 17:08:33 +08:00
morgan 95ec9b1253 zc706: add CXP_DEMO variant
cxp fmc: add USER LED to allow compilation
cxp fmc: add debug pmod pads
2024-07-23 17:01:29 +08:00
morgan abd9157065 fmc: add cxp_4r_fmc adepter io 2024-07-23 17:01:23 +08:00
morgan 1a05dd6ac4 flake: add CXP_DEMO variant build options 2024-07-23 17:01:23 +08:00
8 changed files with 288 additions and 56 deletions

View File

@ -341,7 +341,7 @@
{
inherit fastnumbers artiq-netboot ramda migen-axi binutils-arm;
} //
(board-package-set { target = "zc706"; variant = "cxp_fmc"; }) //
(board-package-set { target = "zc706"; variant = "cxp_demo"; }) //
(board-package-set { target = "zc706"; variant = "nist_clock"; }) //
(board-package-set { target = "zc706"; variant = "nist_clock_master"; }) //
(board-package-set { target = "zc706"; variant = "nist_clock_master_100mhz"; }) //

View File

@ -1,14 +1,15 @@
from migen import *
from misoc.interconnect.csr import *
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface
from artiq.gateware.drtio.core import ChannelInterface
from cxp_downconn import CXP_DownConn
from cxp_upconn import CXP_UpConn
class CXP(Module, AutoCSR):
def __init__(self, refclk, pads, sys_clk_freq, debug_sma):
self.nchannels = nchannels = len(pads)
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
nchannels = len(pads)
self.rx_start_init = CSRStorage()
self.rx_restart = CSRStatus()
self.rx_bypass_clk_alignment = CSRStorage()
@ -19,6 +20,7 @@ class CXP(Module, AutoCSR):
self.loopback_mode = CSRStorage(3)
self.txinit_phaligndone = CSRStatus()
self.rxinit_phaligndone = CSRStatus()
self.rx_ready = CSRStatus()
self.data_0 = CSRStorage(8)
@ -37,20 +39,31 @@ class CXP(Module, AutoCSR):
# # #
# single CXP
self.submodules.crc = CXP_CRC(8)
# FIFOs with transmission priority
# 0: Trigger packet
# 1: IO acknowledgment for trigger packet
# 2: All other packets
self.submodules.upconn = CXP_UpConn(debug_sma, sys_clk_freq, pmod_pads)
# single & master tx_mode can lock with rx in loopback
self.submodules.gtx = gtx = CXP_DownConn(refclk, pads, sys_clk_freq, tx_mode="single", rx_mode="single")
# ! loopback for debugging
# DEBUG:loopback
self.sync += gtx.loopback_mode.eq(self.loopback_mode.storage)
# ! debug sma
self.specials += [
Instance("OBUF", i_I=gtx.rxoutclk, o_O=debug_sma.p_tx),
Instance("OBUF", i_I=gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx)
]
# # ! debug sma
# self.specials += [
# Instance("OBUF", i_I=gtx.rxoutclk, o_O=debug_sma.p_tx),
# Instance("OBUF", i_I=gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx)
# ]
self.comb += [
self.txinit_phaligndone.status.eq(self.gtx.tx_init.Xxphaligndone),
self.rxinit_phaligndone.status.eq(self.gtx.rx_init.Xxphaligndone),
self.rx_ready.status.eq(self.gtx.rx_ready),
]
@ -115,4 +128,58 @@ class CXP(Module, AutoCSR):
getattr(self, "cd_cxp_gtx_rx" + str(0)).rst.eq(self.gtx.cd_cxp_gtx_rx.rst)
]
# TODO: add low speed SERDES
class CXP_CRC(Module, AutoCSR):
width = 32
polynom = 0x04C11DB7
seed = 2**width-1
def __init__(self, data_width):
self.d = Signal(data_width)
self.stb = Signal()
self.reset = Signal()
self.val = Signal(self.width, reset=self.seed)
self.data = CSR(data_width)
self.en = CSR()
self.value = CSRStatus(self.width)
self.processed = CSRStatus(self.width)
# # #
self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom)
self.sync += [
self.val.eq(self.engine.next),
If(self.stb,
self.engine.data.eq(self.d),
If(self.reset,
self.engine.last.eq(self.seed),
# clear reset bit
self.reset.eq(0),
).Else(
self.engine.last.eq(self.val),
)
),
]
# DEBUG: remove those csr
# TODO: do char bit reverse outside of this submodule
p0 = Signal(8)
p1 = Signal(8)
p2 = Signal(8)
p3 = Signal(8)
self.comb += [
p3.eq(self.engine.next[:8][::-1]),
p2.eq(self.engine.next[8:16][::-1]),
p1.eq(self.engine.next[16:24][::-1]),
p0.eq(self.engine.next[24:32][::-1]),
]
self.sync += [
self.d.eq(self.data.r),
self.stb.eq(self.data.re),
If(self.en.re, self.reset.eq(1)),
self.value.status.eq(self.engine.next),
self.processed.status.eq(Cat(p3, p2, p1, p0)),
]

View File

@ -5,7 +5,6 @@ from migen.genlib.cdc import MultiReg, PulseSynchronizer
from misoc.cores.code_8b10b import Encoder, Decoder
from misoc.interconnect.csr import *
from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface
from artiq.gateware.drtio.transceiver.gtx_7series_init import *
from operator import add
@ -29,13 +28,13 @@ from functools import reduce
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
# compared to the usual 8b10b binary representation.
class CXP_BruteforceClockAligner(Module):
def __init__(self, comma, tx_clk_freq, check_period=6e-3):
def __init__(self, comma, sys_clk_freq, check_period):
self.rxdata = Signal(20)
self.restart = Signal()
self.ready = Signal()
check_max_val = ceil(check_period*tx_clk_freq)
check_max_val = ceil(check_period*sys_clk_freq)
check_counter = Signal(max=check_max_val+1)
check = Signal()
reset_check_counter = Signal()
@ -135,7 +134,6 @@ class CXP_DownConn(Module):
pll_div = int(40/cpll_div)
self.rx_restart = Signal()
self.rx_bypass_clk_alignment = Signal()
self.tx_restart = Signal()
self.loopback_mode = Signal(3)
@ -417,7 +415,8 @@ class CXP_DownConn(Module):
self.decoders[1].input.eq(rxdata[10:])
]
clock_aligner = CXP_BruteforceClockAligner(0b0101111100, sys_clk_freq, check_period=1e-3)
# 6e-3 is too slow for 3.25Gbps line rate
clock_aligner = CXP_BruteforceClockAligner(0b0101111100, sys_clk_freq, check_period=1e-2)
self.submodules += clock_aligner
self.comb += [
clock_aligner.rxdata.eq(rxdata),

View File

@ -8,7 +8,8 @@ from misoc.interconnect.csr import *
class CXP_UpConn(Module, AutoCSR):
def __init__(self, pads, sys_clk_freq, pmod, nfifos=3, fifo_depth=32):
nfifos = 3
def __init__(self, pads, sys_clk_freq, pmod, fifo_depth=32):
self.clock_domains.cd_cxp_upconn = ClockDomain()
self.clk_reset = CSRStorage(reset=1)
self.bitrate2x_enable = CSRStorage()
@ -50,26 +51,23 @@ class CXP_UpConn(Module, AutoCSR):
]
self.submodules.fsm = ClockDomainsRenamer("cxp_upconn")(FSM(reset_state="WAIT_TX_ENABLE"))
self.submodules.tx_fifos = TxFIFOs(nfifos, fifo_depth)
self.submodules.tx_fifos = TxFIFOs(self.nfifos, fifo_depth)
self.submodules.tx_idle = TxIdle()
o = Signal()
tx_en = Signal()
tx_bitcount = Signal(max=10)
tx_wordcount = Signal()
tx_wordcount = Signal(max=4)
tx_reg = Signal(10)
disp = Signal()
tx_wordcount = Signal(max=4)
priority = Signal(max=nfifos)
priorities = Signal(max=self.nfifos)
idling = Signal()
# startup sequence
self.fsm.act("WAIT_TX_ENABLE",
NextValue(self.tx_idle.disp_in, 0),
NextValue(self.tx_idle.word_idx, 0),
If(self.tx_enable.storage,
NextValue(self.tx_idle.word_idx, 0),
NextValue(tx_wordcount, 0),
NextValue(tx_bitcount, 0),
NextState("LOAD_CHAR")
@ -91,11 +89,6 @@ class CXP_UpConn(Module, AutoCSR):
)
)
# CXP 2.1 section 9.2.4
# Higher priority packet can be inserted into a lower priority packet during transmission
# Priority lv 0 can be inserted in char boundary of the packet
# Priority lv 1-2 need to be inserted in word boundary of the packet
self.sync.cxp_upconn += [
self.tx_fifos.disp_in.eq(disp),
self.tx_idle.disp_in.eq(disp),
@ -110,39 +103,33 @@ class CXP_UpConn(Module, AutoCSR):
If(tx_bitcount == 9,
tx_bitcount.eq(0),
If((~self.tx_fifos.pe.n) & (self.tx_fifos.pe.o == 0),
# trigger packet can interrupt at char level
# trigger packets are inserted at char boundary and don't contribute to word count
tx_reg.eq(self.tx_fifos.source_data[0]),
self.tx_fifos.source_ack[0].eq(1),
disp.eq(self.tx_fifos.disp_out[0]),
).Else(
# priority zero doesn't contribute to word count
If(tx_wordcount != 3,
tx_wordcount.eq(tx_wordcount + 1),
).Else(
tx_wordcount.eq(0),
),
# new word boundary
# word boundary
If(tx_wordcount == 3,
tx_wordcount.eq(0),
If(~self.tx_fifos.pe.n,
# priority lv 1 & 2 packets are inserted at word boundary
idling.eq(0),
priority.eq(self.tx_fifos.pe.o),
priorities.eq(self.tx_fifos.pe.o),
self.tx_fifos.source_ack[self.tx_fifos.pe.o].eq(1),
tx_reg.eq(self.tx_fifos.source_data[self.tx_fifos.pe.o]),
disp.eq(self.tx_fifos.disp_out[self.tx_fifos.pe.o]),
).Else(
idling.eq(1),
self.tx_idle.source_ack.eq(1),
tx_reg.eq(self.tx_idle.source_data),
disp.eq(self.tx_idle.disp_out),
)
).Else(
tx_wordcount.eq(tx_wordcount + 1),
If(~idling,
self.tx_fifos.source_ack[priority].eq(1),
tx_reg.eq(self.tx_fifos.source_data[priority]),
disp.eq(self.tx_fifos.disp_out[priority]),
self.tx_fifos.source_ack[priorities].eq(1),
tx_reg.eq(self.tx_fifos.source_data[priorities]),
disp.eq(self.tx_fifos.disp_out[priorities]),
).Else(
self.tx_idle.source_ack.eq(1),
tx_reg.eq(self.tx_idle.source_data),
@ -244,6 +231,7 @@ class TxFIFOs(Module):
# reset ack after asserted
If(self.source_ack[i], self.source_ack[i].eq(0)),
]
# FIFOs transmission priority
self.submodules.pe = PriorityEncoder(nfifos)
self.comb += self.pe.i.eq(source_stb)
@ -261,7 +249,7 @@ class TxIdle(Module):
# CXP 2.1 section 9.2.5
IDLE_CHARS = Array([
#[data, k]
#[char, k]
[0b10111100, 1], #K28.5
[0b00111100, 1], #K28.1
[0b00111100, 1], #K28.1

View File

@ -649,7 +649,7 @@ class _NIST_QC2_RTIO:
self.add_rtio(rtio_channels)
class _CXP_FMC_RTIO():
class CXP_FMC():
"""
CoaXpress FMC with 4 CXP channel and 1 SMA trigger
"""
@ -664,17 +664,32 @@ class _CXP_FMC_RTIO():
Subsignal("n_rx", Pins("AD19"), IOStandard("LVCMOS33")),
),
]
pmod1_33 = [
("pmod1_33", 0, Pins("AJ21"), IOStandard("LVCMOS33")),
("pmod1_33", 1, Pins("AK21"), IOStandard("LVCMOS33")),
("pmod1_33", 2, Pins("AB21"), IOStandard("LVCMOS33")),
("pmod1_33", 3, Pins("AB16"), IOStandard("LVCMOS33")),
("pmod1_33", 4, Pins("Y20"), IOStandard("LVCMOS33")),
("pmod1_33", 5, Pins("AA20"), IOStandard("LVCMOS33")),
("pmod1_33", 6, Pins("AC18"), IOStandard("LVCMOS33")),
("pmod1_33", 7, Pins("AC19"), IOStandard("LVCMOS33")),
]
platform.add_extension(debug_sma)
platform.add_extension(pmod1_33)
pmod_pads = [platform.request("pmod1_33", i) for i in range(8)]
clk_freq = 125e6
self.submodules.cxp_gtx = cxp.CXP(
self.submodules.cxp = cxp.CXP(
refclk=self.cdr_clk,
pads=platform.request("CXP_HS", 0),
sys_clk_freq=clk_freq,
debug_sma=platform.request("user_sma_clock_33")
debug_sma=platform.request("user_sma_clock_33"),
pmod_pads = pmod_pads
)
self.csr_devices.append("cxp_gtx")
self.csr_devices.append("cxp")
rtio_channels = []
# FIXME remove this placeholder RTIO channel
@ -722,14 +737,13 @@ class NIST_QC2_Satellite(_SatelliteBase, _NIST_QC2_RTIO):
_SatelliteBase.__init__(self, acpki, drtio100mhz)
_NIST_QC2_RTIO.__init__(self)
class CXP_FMC(ZC706, _CXP_FMC_RTIO):
class CXP_Demo(ZC706, CXP_FMC):
def __init__(self, acpki, drtio100mhz):
ZC706.__init__(self, acpki)
# self.submodules += SMAClkinForward(self.platform)
_CXP_FMC_RTIO.__init__(self)
CXP_FMC.__init__(self)
VARIANTS = {cls.__name__.lower(): cls for cls in [NIST_CLOCK, NIST_CLOCK_Master, NIST_CLOCK_Satellite,
NIST_QC2, NIST_QC2_Master, NIST_QC2_Satellite, CXP_FMC]}
NIST_QC2, NIST_QC2_Master, NIST_QC2_Satellite, CXP_Demo]}
def main():
parser = argparse.ArgumentParser(

View File

@ -0,0 +1,71 @@
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
use libboard_zynq::{println, timer::GlobalTimer};
use log::info;
use crate::pl::csr;
pub fn main(timer: &mut GlobalTimer) {
unsafe {
info!("turning on pmc loopback mode...");
csr::cxp::loopback_mode_write(0b010); // Near-End PMA Loopback
// enable cxp gtx clock domains
csr::cxp::tx_start_init_write(1);
csr::cxp::rx_start_init_write(1);
info!("waiting for QPLL/CPLL to lock...");
timer.delay_us(50_000);
info!("tx_phaligndone = {} ", csr::cxp::txinit_phaligndone_read(),);
// enable txdata tranmission thought MGTXTXP, required by PMA loopback
csr::cxp::txenable_write(1);
loopback_testing(timer, 0x00, 0);
}
fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
unsafe {
// send K28_5 for CDR to align
const K28_5: u8 = 0xBC;
csr::cxp::data_0_write(K28_5);
csr::cxp::control_bit_0_write(1);
csr::cxp::data_1_write(K28_5);
csr::cxp::control_bit_1_write(1);
info!("waiting for rx to align...");
while csr::cxp::rx_ready_read() != 1 {}
info!("rx ready!");
csr::cxp::data_1_write(data);
csr::cxp::control_bit_1_write(control_bit);
println!(
"data[0] = {:#04x} control bit = {:#b} encoded = {:#012b}",
csr::cxp::data_0_read(),
csr::cxp::control_bit_0_read(),
csr::cxp::encoded_0_read(),
);
println!(
"data[1] = {:#04x} control bit = {:#b} encoded = {:#012b}",
csr::cxp::data_1_read(),
csr::cxp::control_bit_1_read(),
csr::cxp::encoded_1_read(),
);
for _ in 0..20 {
timer.delay_us(100);
// println!(
// "data[0] = {:#012b} data[1] = {:#012b}",
// csr::cxp::rxdata_0_read(),
// csr::cxp::rxdata_1_read(),
// );
println!(
"decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}",
csr::cxp::decoded_data_0_read(),
csr::cxp::decoded_k_0_read(),
csr::cxp::decoded_data_1_read(),
csr::cxp::decoded_k_1_read(),
);
}
}
}
}

View File

@ -0,0 +1,88 @@
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
use libboard_zynq::{println, timer::GlobalTimer};
use crate::pl::csr;
pub fn crc_test() {
let arr = [
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, // CXP CRC-32
0x56, 0x86, 0x5D, 0x6f,
];
let mut crc: u32; // seed = 0xFFFFFFFF
unsafe {
csr::cxp::crc_en_write(1);
for a in arr.iter() {
csr::cxp::crc_data_write(*a);
crc = csr::cxp::crc_value_read();
println!("input = {:#04x}", *a);
println!("CRC NOT(val.reverse) = {:#010x}", !crc.reverse_bits());
// since the input bit are reversed when entering the crc engine, the output char need to be reversed to cancel out on the receiver side
println!("CRC CXP = {:#010x}", crc);
}
}
}
pub fn tx_test(timer: &mut GlobalTimer) {
// the 8bit shift is k symbol
// const K28_1: u16 = 0x3C | (1 << 8);
// const K28_5: u16 = 0xBC | (1 << 8);
const D31_1: u16 = 0x3F;
const D01_1: u16 = 0x21;
const LEN: usize = 100;
let mut arr: [u16; LEN] = [0; LEN];
unsafe {
csr::cxp::upconn_clk_reset_write(1);
// csr::cxp::upconn_bitrate2x_enable_write(1);
csr::cxp::upconn_clk_reset_write(0);
loop {
// TODO: verify the char & word boundary thingy
for _ in 0..8 {
csr::cxp::upconn_symbol1_write(D01_1);
}
for _ in 0..4 {
csr::cxp::upconn_symbol2_write(D31_1);
}
timer.delay_us(1);
csr::cxp::upconn_tx_enable_write(1);
for i in 0..LEN {
arr[i] = get_encoded();
}
for i in 0..LEN {
match arr[i] {
0b1010111001 | 0b0101001001 => {
println!("encoded = {:#012b} D31.1", arr[i])
}
0b0111011001 | 0b1000101001 => {
println!("encoded = {:#012b} D01.1", arr[i])
}
0b0011111010 | 0b1100000101 => {
println!("encoded = {:#012b} K28.5 start idling....", arr[i])
}
0b0011111001 | 0b1100000110 => {
println!("encoded = {:#012b} K28.1 idling...", arr[i])
}
0b0011101010 => {
println!("encoded = {:#012b} D28.5 END idle", arr[i])
}
_ => {
println!("encoded = {:#012b}", arr[i])
}
}
}
println!("-------------------------------------");
csr::cxp::upconn_tx_enable_write(0);
timer.delay_us(2_000_000);
}
}
fn get_encoded() -> u16 {
unsafe { csr::cxp::upconn_encoded_data_read().reverse_bits() >> 6 }
}
}

View File

@ -42,6 +42,11 @@ pub mod si5324;
pub mod si549;
use core::{cmp, str};
#[cfg(has_cxp)]
pub mod cxp_downconn;
#[cfg(has_cxp)]
pub mod cxp_upconn;
pub fn identifier_read(buf: &mut [u8]) -> &str {
unsafe {
pl::csr::identifier::address_write(0);