From d872feeafcb6c401d32423b85149d958540dbb4a Mon Sep 17 00:00:00 2001 From: morgan Date: Tue, 15 Oct 2024 10:31:35 +0800 Subject: [PATCH] cxp_phys: low speed serial & GTX setup downconn: add QPLL & GTX setup downconn: add DRP to change linerate up to 12.5Gbps downconn testing: add txuserclk config upconn: add low speed serital setup upconn & downconn: add linerate changer --- src/libboard_artiq/src/cxp_phys.rs | 217 +++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 src/libboard_artiq/src/cxp_phys.rs diff --git a/src/libboard_artiq/src/cxp_phys.rs b/src/libboard_artiq/src/cxp_phys.rs new file mode 100644 index 0000000..d23e43a --- /dev/null +++ b/src/libboard_artiq/src/cxp_phys.rs @@ -0,0 +1,217 @@ +use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs; +use libboard_zynq::{println, timer::GlobalTimer}; +use log::info; + +use crate::pl::{csr, csr::CXP}; + +pub const CXP_CHANNELS: usize = csr::CXP_LEN; + +#[derive(Clone, Copy, Debug)] +#[allow(non_camel_case_types)] +pub enum CXP_SPEED { + CXP_1, + CXP_2, + CXP_3, + CXP_5, + CXP_6, + CXP_10, + CXP_12, +} + +pub fn setup(timer: &mut GlobalTimer) { + down_conn::setup(timer); + up_conn::setup(); + change_linerate(CXP_SPEED::CXP_1); +} + +pub fn change_linerate(speed: CXP_SPEED) { + info!("Changing all channels datarate to {:?}", speed); + down_conn::change_linerate(speed); + up_conn::change_linerate(speed); +} + +mod up_conn { + use super::*; + + pub fn setup() { + unsafe { + csr::cxp_phys::upconn_tx_enable_write(1); + } + } + + pub fn change_linerate(speed: CXP_SPEED) { + unsafe { + match speed { + CXP_SPEED::CXP_1 | CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 | CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => { + csr::cxp_phys::upconn_bitrate2x_enable_write(0); + } + CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => { + csr::cxp_phys::upconn_bitrate2x_enable_write(1); + } + }; + csr::cxp_phys::upconn_clk_reset_write(1); + } + } +} + +mod down_conn { + use super::*; + + pub fn setup(timer: &mut GlobalTimer) { + unsafe { + csr::cxp_phys::downconn_qpll_reset_write(1); + info!("waiting for QPLL/CPLL to lock..."); + while csr::cxp_phys::downconn_qpll_locked_read() != 1 {} + info!("QPLL locked"); + + for channel in 0..CXP_CHANNELS { + (CXP[channel].downconn_rx_start_init_write)(1); + } + + // DEBUG: printout + info!("waiting for rx setup..."); + timer.delay_us(50_000); + for channel in 0..CXP_CHANNELS { + info!("rx_phaligndone = {}", (CXP[channel].downconn_rxinit_phaligndone_read)(),); + } + } + } + + pub fn change_linerate(speed: CXP_SPEED) { + change_qpll_fb_divider(speed); + change_gtx_divider(speed); + change_cdr_cfg(speed); + + unsafe { + csr::cxp_phys::downconn_qpll_reset_write(1); + info!("waiting for QPLL/CPLL to lock..."); + while csr::cxp_phys::downconn_qpll_locked_read() != 1 {} + info!("QPLL locked"); + + for channel in 0..CXP_CHANNELS { + (CXP[channel].downconn_rx_restart_write)(1); + } + } + } + + fn change_qpll_fb_divider(speed: CXP_SPEED) { + let qpll_div_reg = match speed { + CXP_SPEED::CXP_1 | CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 | CXP_SPEED::CXP_10 => 0x0120, // FB_Divider = 80 + CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 | CXP_SPEED::CXP_12 => 0x0170, // FB_Divider = 100 + }; + + // DEBUG: + println!("QPLL DRP:"); + println!("0x36 = {:#06x}", qpll_read(0x36)); + qpll_write(0x36, qpll_div_reg); + println!("0x36 = {:#06x}", qpll_read(0x36)); + } + + fn change_gtx_divider(speed: CXP_SPEED) { + let div_reg = match speed { + CXP_SPEED::CXP_1 => 0x33, // RXOUT_DIV = 8 + CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 => 0x22, // RXOUT_DIV = 4 + CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => 0x11, // RXOUT_DIV = 2 + CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => 0x00, // RXOUT_DIV = 1 + }; + + // DEBUG: + println!("RX GTX DRP:"); + for channel in 0..CXP_CHANNELS { + println!("channel {}, 0x88 = {:#06x}", channel, gtx_read(channel, 0x88)); + gtx_write(channel, 0x88, div_reg); + println!("channel {}, 0x88 = {:#06x}", channel, gtx_read(channel, 0x88)); + } + } + + fn change_cdr_cfg(speed: CXP_SPEED) { + struct CdrConfig { + pub cfg_reg0: u16, // addr = 0xA8 + pub cfg_reg1: u16, // addr = 0xA9 + pub cfg_reg2: u16, // addr = 0xAA + pub cfg_reg3: u16, // addr = 0xAB + pub cfg_reg4: u16, // addr = 0xAC + } + + let cdr_cfg = match speed { + // when RXOUT_DIV = 8 + CXP_SPEED::CXP_1 => CdrConfig { + cfg_reg0: 0x0020, + cfg_reg1: 0x1008, + cfg_reg2: 0x23FF, + cfg_reg3: 0x0000, + cfg_reg4: 0x0003, + }, + // when RXOUT_DIV = 4 + CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 => CdrConfig { + cfg_reg0: 0x0020, + cfg_reg1: 0x1010, + cfg_reg2: 0x23FF, + cfg_reg3: 0x0000, + cfg_reg4: 0x0003, + }, + // when RXOUT_DIV= 2 + CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 => CdrConfig { + cfg_reg0: 0x0020, + cfg_reg1: 0x1020, + cfg_reg2: 0x23FF, + cfg_reg3: 0x0000, + cfg_reg4: 0x0003, + }, + // when RXOUT_DIV= 1 + CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => CdrConfig { + cfg_reg0: 0x0020, + cfg_reg1: 0x1040, + cfg_reg2: 0x23FF, + cfg_reg3: 0x0000, + cfg_reg4: 0x000B, + }, + }; + + for channel in 0..CXP_CHANNELS { + gtx_write(channel, 0x0A8, cdr_cfg.cfg_reg0); + gtx_write(channel, 0x0A9, cdr_cfg.cfg_reg1); + gtx_write(channel, 0x0AA, cdr_cfg.cfg_reg2); + gtx_write(channel, 0x0AB, cdr_cfg.cfg_reg3); + gtx_write(channel, 0x0AC, cdr_cfg.cfg_reg4); + } + } + + #[allow(dead_code)] + fn gtx_read(channel: usize, address: u16) -> u16 { + unsafe { + (CXP[channel].downconn_gtx_daddr_write)(address); + (CXP[channel].downconn_gtx_dread_write)(1); + while (CXP[channel].downconn_gtx_dready_read)() != 1 {} + (CXP[channel].downconn_gtx_dout_read)() + } + } + + fn gtx_write(channel: usize, address: u16, value: u16) { + unsafe { + (CXP[channel].downconn_gtx_daddr_write)(address); + (CXP[channel].downconn_gtx_din_write)(value); + (CXP[channel].downconn_gtx_din_stb_write)(1); + while (CXP[channel].downconn_gtx_dready_read)() != 1 {} + } + } + + #[allow(dead_code)] + fn qpll_read(address: u8) -> u16 { + unsafe { + csr::cxp_phys::downconn_qpll_daddr_write(address); + csr::cxp_phys::downconn_qpll_dread_write(1); + while csr::cxp_phys::downconn_qpll_dready_read() != 1 {} + csr::cxp_phys::downconn_qpll_dout_read() + } + } + + fn qpll_write(address: u8, value: u16) { + unsafe { + csr::cxp_phys::downconn_qpll_daddr_write(address); + csr::cxp_phys::downconn_qpll_din_write(value); + csr::cxp_phys::downconn_qpll_din_stb_write(1); + while csr::cxp_phys::downconn_qpll_dready_read() != 1 {} + } + } +}