1
0
Fork 0

cxp downconn fw: add DRP for linerate

This commit is contained in:
morgan 2024-07-31 16:10:25 +08:00
parent 1e87428c68
commit e0369d2eb2
1 changed files with 415 additions and 90 deletions

View File

@ -4,115 +4,440 @@ use log::info;
use crate::pl::csr;
pub fn main(timer: &mut GlobalTimer) {
pub struct CXP_DownConn_Settings {
pub rxdiv: u8,
pub qpll_fbdiv: u8,
}
#[derive(Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum CXP_SPEED {
CXP_1,
CXP_2,
CXP_3,
CXP_5,
CXP_6,
CXP_10,
CXP_12,
}
fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
unsafe {
info!("turning on pmc loopback mode...");
csr::cxp::downconn_loopback_mode_write(0b010); // Near-End PMA Loopback
// send K28_5 for CDR to align
const K28_5: u8 = 0xBC;
const K28_1: u8 = 0x3C;
const D21_5: u8 = 21 | 5 << 5;
// brute force aligner only align K28_5 on rxdata[:10]
// don't send too much K28_5 in case phase is locked on the wrong stuff
// seems like I need at least 2 K28_5 to work
loopback_testing(timer, 0x00, 0);
}
// NOTE: testing with IDLE word + buildin comma alignment
// 62.5MHz OK
// 125MHz OK
// 156.25MHz OK
// 250MHz
// 312.5MHz
const LEN: usize = 4;
const DATA: [[u8; LEN]; 2] = [
// [K28_5, K28_5, K28_5, K28_1, K28_5, K28_5, K28_5, K28_5],
// [1, 1, 1, 1, 1, 1, 1, 1],
[K28_5, K28_1, K28_1, D21_5],
[1, 1, 1, 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;
const K28_1: u8 = 0x3C;
const D21_5: u8 = 21 | 5 << 5;
// brute force aligner only align K28_5 on rxdata[:10]
// don't send too much K28_5 in case phase is locked on the wrong stuff
// seems like I need at least 2 K28_5 to work
// // STEP 1: reset QPLL
// csr::cxp::downconn_qpll_reset_write(1);
// info!("waiting for QPLL/CPLL to lock...");
// while csr::cxp::downconn_qpll_locked_read() != 1 {}
// info!("QPLL locked");
// NOTE: testing with IDLE word + buildin comma alignment
// 62.5MHz OK
// 125MHz OK
// 156.25MHz OK
// 250MHz
// 312.5MHz
const LEN: usize = 4;
const DATA: [[u8; LEN]; 2] = [
// [K28_5, K28_5, K28_5, K28_1, K28_5, K28_5, K28_5, K28_5],
// [1, 1, 1, 1, 1, 1, 1, 1],
[K28_5, K28_1, K28_1, D21_5],
[1, 1, 1, 0],
];
// STEP 2: setup tx/rx gtx
csr::cxp::downconn_data_0_write(DATA[0][0]);
csr::cxp::downconn_data_1_write(DATA[0][1]);
csr::cxp::downconn_data_2_write(DATA[0][2]);
csr::cxp::downconn_data_3_write(DATA[0][3]);
// STEP 1: reset QPLL
csr::cxp::downconn_qpll_reset_write(1);
info!("waiting for QPLL/CPLL to lock...");
while csr::cxp::downconn_qpll_locked_read() != 1 {}
info!("QPLL locked");
csr::cxp::downconn_control_bit_0_write(DATA[1][0]);
csr::cxp::downconn_control_bit_1_write(DATA[1][1]);
csr::cxp::downconn_control_bit_2_write(DATA[1][2]);
csr::cxp::downconn_control_bit_3_write(DATA[1][3]);
// STEP 2: setup tx/rx gtx
csr::cxp::downconn_data_0_write(DATA[0][0]);
csr::cxp::downconn_data_1_write(DATA[0][1]);
csr::cxp::downconn_data_2_write(DATA[0][2]);
csr::cxp::downconn_data_3_write(DATA[0][3]);
// TEST: change rx linerate
// works great
csr::cxp::downconn_control_bit_0_write(DATA[1][0]);
csr::cxp::downconn_control_bit_1_write(DATA[1][1]);
csr::cxp::downconn_control_bit_2_write(DATA[1][2]);
csr::cxp::downconn_control_bit_3_write(DATA[1][3]);
// enable cxp gtx clock domains
// enable cxp gtx clock domains
csr::cxp::downconn_tx_start_init_write(1);
csr::cxp::downconn_rx_start_init_write(1);
info!("waiting for tx setup...");
timer.delay_us(50_000);
info!(
"tx_phaligndone = {} | rx_phaligndone = {}",
csr::cxp::downconn_txinit_phaligndone_read(),
csr::cxp::downconn_rxinit_phaligndone_read(),
);
info!("waiting for tx setup...");
timer.delay_us(50_000);
info!(
"tx_phaligndone = {} | rx_phaligndone = {}",
csr::cxp::downconn_txinit_phaligndone_read(),
csr::cxp::downconn_rxinit_phaligndone_read(),
);
// enable txdata tranmission thought MGTXTXP, required by PMA loopback
csr::cxp::downconn_txenable_write(1);
// enable txdata tranmission thought MGTXTXP, required by PMA loopback
csr::cxp::downconn_txenable_write(1);
info!("waiting for rx to align...");
while csr::cxp::downconn_rx_ready_read() != 1 {}
timer.delay_us(50_000);
info!("rx ready!");
info!("waiting for rx to align...");
while csr::cxp::downconn_rx_ready_read() != 1 {}
timer.delay_us(50_000);
info!("rx ready!");
// csr::cxp::data_3_write(data);
// csr::cxp::control_bit_3_write(control_bit);
println!(
"data[0] = {:#04x} control bit = {:#b} encoded = {:#012b}",
csr::cxp::downconn_data_0_read(),
csr::cxp::downconn_control_bit_0_read(),
csr::cxp::downconn_encoded_0_read(),
);
println!(
"data[1] = {:#04x} control bit = {:#b} encoded = {:#012b}",
csr::cxp::downconn_data_1_read(),
csr::cxp::downconn_control_bit_1_read(),
csr::cxp::downconn_encoded_1_read(),
);
// csr::cxp::data_3_write(data);
// csr::cxp::control_bit_3_write(control_bit);
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!(
"data[0] = {:#04x} control bit = {:#b} encoded = {:#012b}",
csr::cxp::downconn_data_0_read(),
csr::cxp::downconn_control_bit_0_read(),
csr::cxp::downconn_encoded_0_read(),
"decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}",
csr::cxp::downconn_decoded_data_0_read(),
csr::cxp::downconn_decoded_k_0_read(),
csr::cxp::downconn_decoded_data_1_read(),
csr::cxp::downconn_decoded_k_1_read(),
);
println!(
"data[1] = {:#04x} control bit = {:#b} encoded = {:#012b}",
csr::cxp::downconn_data_1_read(),
csr::cxp::downconn_control_bit_1_read(),
csr::cxp::downconn_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::downconn_decoded_data_0_read(),
csr::cxp::downconn_decoded_k_0_read(),
csr::cxp::downconn_decoded_data_1_read(),
csr::cxp::downconn_decoded_k_1_read(),
);
}
}
}
}
pub fn change_linerate() {
pub fn setup(timer: &mut GlobalTimer, speed: CXP_SPEED) {
unsafe {
info!("turning on pmc loopback mode...");
csr::cxp::downconn_loopback_mode_write(0b010); // Near-End PMA Loopback
// QPLL setup
csr::cxp::downconn_qpll_reset_write(1);
info!("waiting for QPLL/CPLL to lock...");
while csr::cxp::downconn_qpll_locked_read() != 1 {}
info!("QPLL locked");
// tx/rx setup
csr::cxp::downconn_tx_start_init_write(1);
csr::cxp::downconn_rx_start_init_write(1);
info!("waiting for tx setup...");
timer.delay_us(50_000);
info!(
"tx_phaligndone = {} | rx_phaligndone = {}",
csr::cxp::downconn_txinit_phaligndone_read(),
csr::cxp::downconn_rxinit_phaligndone_read(),
);
}
change_linerate(timer, speed);
loopback_testing(timer, 0x00, 0);
}
pub fn change_linerate(timer: &mut GlobalTimer, speed: CXP_SPEED) {
// TODO: switch QPLL divider for RXUSRCLK
// TODO: switch TX/RXDIV via TX/RXRATE
// no need for DRP for this
// TODO: set TX/RXDIV via TX/RXRATE
change_qpll_settings(speed);
// TODO: switch pll for TXUSRCLK = freq(linerate)/20
// TODO: reset tx&rx for phase alignment
unsafe {
csr::cxp::downconn_qpll_reset_write(1);
info!("waiting for QPLL/CPLL to lock...");
while csr::cxp::downconn_qpll_locked_read() != 1 {}
info!("QPLL locked");
}
// DEBUG: DRP pll for TXUSRCLK = freq(linerate)/20
let settings = txusrclk::get_txusrclk_config(speed);
txusrclk::setup(timer, settings);
// reset tx&rx for phase alignment
unsafe {
// csr::cxp::downconn_tx_restart_write(1); // <--- NOTE: changing TXRATE will do reset automatically, no need to manually reset
csr::cxp::downconn_rx_restart_write(1); // <--- NOTE: this doesn't do anything atm
}
}
fn change_qpll_settings(speed: CXP_SPEED) {
let divider = match speed {
CXP_SPEED::CXP_1 => 0b100, // Divided by 8
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 => 0b011, // Divided by 4
CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => 0b010, // Divided by 2
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => 0b010, // Divided by 1
};
unsafe {
csr::cxp::downconn_rx_div_write(divider);
csr::cxp::downconn_tx_div_write(divider);
}
}
pub mod txusrclk {
use super::*;
pub struct PLLSetting {
pub clkout0_reg1: u16, //0x08
pub clkout0_reg2: u16, //0x09
pub clkfbout_reg1: u16, //0x14
pub clkfbout_reg2: u16, //0x15
pub div_reg: u16, //0x16
pub lock_reg1: u16, //0x18
pub lock_reg2: u16, //0x19
pub lock_reg3: u16, //0x1A
pub power_reg: u16, //0x28
pub filt_reg1: u16, //0x4E
pub filt_reg2: u16, //0x4F
}
fn one_clock_cycle() {
unsafe {
csr::cxp::downconn_pll_dclk_write(1);
csr::cxp::downconn_pll_dclk_write(0);
}
}
fn set_addr(address: u8) {
unsafe {
csr::cxp::downconn_pll_daddr_write(address);
}
}
fn set_data(value: u16) {
unsafe {
csr::cxp::downconn_pll_din_write(value);
}
}
fn set_enable(en: bool) {
unsafe {
let val = if en { 1 } else { 0 };
csr::cxp::downconn_pll_den_write(val);
}
}
fn set_write_enable(en: bool) {
unsafe {
let val = if en { 1 } else { 0 };
csr::cxp::downconn_pll_dwen_write(val);
}
}
fn get_data() -> u16 {
unsafe { csr::cxp::downconn_pll_dout_read() }
}
fn drp_ready() -> bool {
unsafe { csr::cxp::downconn_pll_dready_read() == 1 }
}
#[allow(dead_code)]
fn read(address: u8) -> u16 {
set_addr(address);
set_enable(true);
// Set DADDR on the mmcm and assert DEN for one clock cycle
one_clock_cycle();
set_enable(false);
while !drp_ready() {
// keep the clock signal until data is ready
one_clock_cycle();
}
get_data()
}
fn write(address: u8, value: u16) {
set_addr(address);
set_data(value);
set_write_enable(true);
set_enable(true);
// Set DADDR, DI on the mmcm and assert DWE, DEN for one clock cycle
one_clock_cycle();
set_write_enable(false);
set_enable(false);
while !drp_ready() {
// keep the clock signal until write is finished
one_clock_cycle();
}
}
fn reset(rst: bool) {
unsafe {
let val = if rst { 1 } else { 0 };
csr::cxp::downconn_txpll_reset_write(val)
}
}
pub fn setup(timer: &mut GlobalTimer, settings: PLLSetting) {
if false {
info!("0x08 = {:#06x}", read(0x08));
info!("0x09 = {:#06x}", read(0x09));
info!("0x14 = {:#06x}", read(0x14));
info!("0x15 = {:#06x}", read(0x15));
info!("0x16 = {:#06x}", read(0x16));
info!("0x18 = {:#06x}", read(0x18));
info!("0x19 = {:#06x}", read(0x19));
info!("0x1A = {:#06x}", read(0x1A));
info!("0x28 = {:#06x}", read(0x28));
info!("0x4E = {:#06x}", read(0x4E));
info!("0x4F = {:#06x}", read(0x4F));
} else {
// Based on "DRP State Machine" from XAPP888
// hold reset HIGH during pll config
reset(true);
write(0x08, settings.clkout0_reg1);
write(0x09, settings.clkout0_reg2);
write(0x14, settings.clkfbout_reg1);
write(0x15, settings.clkfbout_reg2);
write(0x16, settings.div_reg);
write(0x18, settings.lock_reg1);
write(0x19, settings.lock_reg2);
write(0x1A, settings.lock_reg3);
write(0x28, settings.power_reg);
write(0x4E, settings.filt_reg1);
write(0x4F, settings.filt_reg2);
reset(false);
// wait for the pll to lock
timer.delay_us(100);
let locked = unsafe { csr::cxp::downconn_txpll_locked_read() == 1 };
info!("txusrclk locked = {}", locked);
}
}
pub fn get_txusrclk_config(speed: CXP_SPEED) -> PLLSetting {
match speed {
CXP_SPEED::CXP_1 => {
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 16
// TXUSRCLK=62.5MHz
PLLSetting {
clkout0_reg1: 0x1208, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1104, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x5801, //0x19
lock_reg3: 0xdbe9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9808, //0x4E
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_2 => {
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
// TXUSRCLK=125MHz
PLLSetting {
clkout0_reg1: 0x1104, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1104, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x5801, //0x19
lock_reg3: 0xdbe9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9808, //0x4E
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_3 => {
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
// TXUSRCLK=125MHz
PLLSetting {
clkout0_reg1: 0x1104, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1145, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x7001, //0x19
lock_reg3: 0xf3e9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9908, //0x4E
filt_reg2: 0x1900, //0x4F
}
}
CXP_SPEED::CXP_5 => {
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
// TXUSRCLK=250MHz
PLLSetting {
clkout0_reg1: 0x1082, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1104, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x5801, //0x19
lock_reg3: 0xdbe9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9808, //0x4E
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_6 => {
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
// TXUSRCLK=312.5MHz
PLLSetting {
clkout0_reg1: 0x1082, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1145, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x7001, //0x19
lock_reg3: 0xf3e9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9908, //0x4E
filt_reg2: 0x1900, //0x4F
}
}
CXP_SPEED::CXP_10 => {
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 2
// TXUSRCLK=500MHz
PLLSetting {
clkout0_reg1: 0x1041, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1104, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x5801, //0x19
lock_reg3: 0xdbe9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9808, //0x4E
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_12 => {
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 2
// TXUSRCLK=625MHz
PLLSetting {
clkout0_reg1: 0x1041, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1145, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x7001, //0x19
lock_reg3: 0xf3e9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9908, //0x4E
filt_reg2: 0x1900, //0x4F
}
}
}
}
}