Compare commits

..

6 Commits

Author SHA1 Message Date
morgan 14fa038118 Firmware: Runtime WRPLL
runtime: drive CLK_SEL to true when si549 is used
runtime & libboard_artiq: allow standalone to use io_expander
si549: add bit bang mmcm dynamic configuration
si549: add frequency counter for refclk
rtio_clocking & si549: add 125Mhz wrpll refclk setup
2024-04-12 16:38:46 +08:00
morgan b81323af30 Firmware: Satman skew calibration & tester
cargo template: add calibrate_wrpll_skew feature
tag collector: add TAG_OFFSET for Satman WRPLL
tag collector: add TAG_OFFSET getter & setter for calibration
wrpll: add skew tester and calibration
wrpll: gate calibration behind calibrate_wrpll_skew feature
2024-04-12 16:38:46 +08:00
morgan 291777f764 Firmware: Satman WRPLL
satman: drive CLK_SEL to true when si549 is used
satman : add main & helper si549 setup
satman : add WRPLL select_recovered_clock
si549: add tag collector to process gtx & main tags
si549: add frequency counter to set BASE_ADPLL
si549: add set_adpll for main & helper PLL
si549: add main & helper PLL
FIQ & si549: replace dummy with a custom handler for gtx & main tags ISR
2024-04-12 16:38:39 +08:00
morgan a1d80fb93b Firmware: Si549 and io_expander
io_expander: set CLK_SEL pin to output when si549 is used
io_expander: gate virtual leds for standalone
si549: add bit bang i2c
si549: add si549 programming
si549: add main & helper setup
2024-04-11 15:18:10 +08:00
morgan 7827c7b803 Gateware: kasli_soc WRPLL setup
kasli_soc: use enable_wrpll from json to switch from si5324 to si549
kasli_soc: add wrpll for all variants
kasli_soc: add gtx & main tag nFIQ for all variants
kasli_soc: add clk_synth_se for master & satellite
kasli_soc: add wrpll_refclk for runtime
kasli_soc: add skewtester for satman
kasli_soc: add WRPLL_REF_CLK config for firmware
2024-04-11 15:18:10 +08:00
morgan e4d8d44c7c Gateware: WRPLL
ddmtd: add DDMTD and deglitcher
wrpll: add helper clockdomain
wrpll: add frequency counter
wrpll: add skewtester
wrpll: add gtx & main tag collection
wrpll: add gtx & main tag eventmanager for shared peripheral interrupt
wrpll: add SMA frequency multiplier to generate 125Mhz refclk
si549: add i2c and adpll programmer
2024-04-11 15:18:04 +08:00
3 changed files with 46 additions and 45 deletions

View File

@ -26,7 +26,7 @@ class DDMTDSampler(Module):
class DDMTDDeglitcherFirstEdge(Module):
def __init__(self, input_signal, blind_period=300):
def __init__(self, input_signal, blind_period=400):
self.detect = Signal()
rising = Signal()
input_signal_r = Signal()

View File

@ -7,7 +7,7 @@ from ddmtd import DDMTDSampler, DDMTD
from si549 import Si549
class FrequencyCounter(Module, AutoCSR):
def __init__(self, domains, counter_width=24, freq_div=2):
def __init__(self, domains, counter_width=24):
self.update = CSR()
self.busy = CSRStatus()
@ -21,8 +21,8 @@ class FrequencyCounter(Module, AutoCSR):
self.submodules += fsm
fsm.act("IDLE",
counter_reset.eq(1),
If(self.update.re,
counter_reset.eq(1),
NextValue(timer, 2**counter_width - 1),
NextState("COUNTING")
)
@ -42,37 +42,29 @@ class FrequencyCounter(Module, AutoCSR):
counter_csr = CSRStatus(counter_width, name=name)
setattr(self, name, counter_csr)
counter = Signal(max=freq_div)
result = Signal(counter_width)
divider = Signal(2)
divided = Signal()
divided_sys = Signal()
divided_sys_r = Signal()
divided_tick = Signal()
counter = Signal(counter_width)
# # #
reset_ps = PulseSynchronizer("sys", domain)
stb_ps = PulseSynchronizer("sys", domain)
self.submodules +=[
reset_ps,
stb_ps
]
self.sync +=[
reset_ps.i.eq(counter_reset),
stb_ps.i.eq(counter_stb)
]
sync_domain = getattr(self.sync, domain)
sync_domain += [
If(counter != 0,
counter.eq(counter - 1)
).Else(
result.eq(result + 1),
counter.eq(freq_div - 1)
),
If(reset_ps.o,
counter.eq(0),
result.eq(0)
),
If(stb_ps.o, counter_csr.status.eq(result))
sync_domain +=[
divider.eq(divider + 1),
divided.eq(divider[-1])
]
self.specials += MultiReg(divided, divided_sys)
self.sync += divided_sys_r.eq(divided_sys)
self.comb += divided_tick.eq(divided_sys & ~divided_sys_r)
self.sync += [
If(counter_stb, counter_csr.status.eq(counter)),
If(divided_tick, counter.eq(counter + 1)),
If(counter_reset, counter.eq(0))
]
class SkewTester(Module, AutoCSR):
def __init__(self, rx_synchronizer):

View File

@ -333,14 +333,18 @@ pub mod wrpll {
const BEATING_PERIOD: i32 = 0x8000;
const BEATING_HALFPERIOD: i32 = 0x4000;
const COUNTER_WIDTH: u32 = 24;
const FREQ_DIV: u32 = 2;
const DIV_WIDTH: u32 = 2;
const KP: i32 = 6;
const KI: i32 = 2;
// 4 ppm capture range
const ADPLL_LIM: i32 = (4.0 / 0.0001164) as i32;
static mut BASE_ADPLL: i32 = 0;
static mut H_INTEGRATOR: i32 = 0;
static mut M_INTEGRATOR: i32 = 0;
static mut H_LAST_ADPLL: i32 = 0;
static mut LAST_PERIOD_ERR: i32 = 0;
static mut M_LAST_ADPLL: i32 = 0;
static mut LAST_PHASE_ERR: i32 = 0;
#[derive(Clone, Copy)]
pub enum ISR {
@ -439,10 +443,9 @@ pub mod wrpll {
}
}
/// To get within capture range
fn set_base_adpll() -> Result<(), &'static str> {
let count2adpll =
|error: i32| ((error as f64 * FREQ_DIV as f64 * 1e6) / (0.0001164 * (1 << COUNTER_WIDTH) as f64)) as i32;
|error: i32| ((error as f64 * 1e6) / (0.0001164 * (1 << (COUNTER_WIDTH - DIV_WIDTH)) as f64)) as i32;
let (ref_count, main_count) = get_freq_counts();
unsafe {
@ -469,8 +472,10 @@ pub mod wrpll {
fn reset_plls(timer: &mut GlobalTimer) -> Result<(), &'static str> {
unsafe {
H_INTEGRATOR = 0;
M_INTEGRATOR = 0;
H_LAST_ADPLL = 0;
LAST_PERIOD_ERR = 0;
M_LAST_ADPLL = 0;
LAST_PHASE_ERR = 0;
}
set_adpll(i2c::DCXO::Main, 0)?;
set_adpll(i2c::DCXO::Helper, 0)?;
@ -497,7 +502,6 @@ pub mod wrpll {
if is_pending(ISR::RefTag) {
tag_collector::collect_tags(ISR::RefTag);
clear_pending(ISR::RefTag);
helper_pll().expect("failed to run helper DCXO PLL");
}
@ -514,22 +518,25 @@ pub mod wrpll {
fn helper_pll() -> Result<(), &'static str> {
let period_err = tag_collector::get_period_error();
let h_adpll = unsafe {
H_INTEGRATOR += period_err * KI;
(BASE_ADPLL + period_err * KP + H_INTEGRATOR).clamp(-ADPLL_MAX, ADPLL_MAX)
unsafe {
// Based on https://hackmd.io/IACbwcOTSt6Adj3_F9bKuw?view#Integral-wind-up-and-output-limiting
let adpll = (H_LAST_ADPLL + (KP + KI) * period_err - KP * LAST_PERIOD_ERR).clamp(-ADPLL_LIM, ADPLL_LIM);
set_adpll(i2c::DCXO::Helper, BASE_ADPLL + adpll)?;
H_LAST_ADPLL = adpll;
LAST_PERIOD_ERR = period_err;
};
set_adpll(i2c::DCXO::Helper, h_adpll)?;
Ok(())
}
fn main_pll() -> Result<(), &'static str> {
let phase_err = tag_collector::get_phase_error();
let m_adpll = unsafe {
M_INTEGRATOR += phase_err * KI;
(BASE_ADPLL + phase_err * KP + M_INTEGRATOR).clamp(-ADPLL_MAX, ADPLL_MAX)
unsafe {
// Based on https://hackmd.io/IACbwcOTSt6Adj3_F9bKuw?view#Integral-wind-up-and-output-limiting
let adpll = (M_LAST_ADPLL + (KP + KI) * phase_err - KP * LAST_PHASE_ERR).clamp(-ADPLL_LIM, ADPLL_LIM);
set_adpll(i2c::DCXO::Main, BASE_ADPLL + adpll)?;
M_LAST_ADPLL = adpll;
LAST_PHASE_ERR = phase_err;
};
set_adpll(i2c::DCXO::Main, m_adpll)?;
Ok(())
}
@ -646,6 +653,8 @@ pub mod wrpll {
if rc {
tag_collector::reset();
reset_plls(timer).expect("failed to reset main and helper PLL");
// get within capture range
set_base_adpll().expect("failed to set base adpll");
// clear gateware pending flag