Files
3d_audio/lib_sw_pll/python/sw_pll/pfd_model.py

61 lines
2.6 KiB
Python
Raw Normal View History

2025-12-11 09:43:42 +08:00
# Copyright 2023 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
class port_timer_pfd():
def __init__(self, nominal_output_hz, nominal_control_rate_hz, ppm_range=1000):
self.output_count_last = 0.0 # Integer value of last output_clock_count
self.first_loop = True
self.ppm_range = ppm_range
self.expected_output_clock_count_inc = nominal_output_hz / nominal_control_rate_hz
def get_error(self, output_clock_count_float, period_fraction=1.0):
"""
Calculate frequency error from the port output_count taken at the ref clock time.
Note it uses a floating point input clock count to make simulation easier. This
handles fractional counts and carries them properly.
If the time of sampling the output_count is not precisely 1.0 x the ref clock time,
you may pass a fraction to allow for a proportional value using period_fraction. This is optional.
"""
output_count_int = int(output_clock_count_float) # round down to nearest int to match hardware
output_count_inc = output_count_int - self.output_count_last
output_count_inc = output_count_inc / period_fraction
expected_output_clock_count = self.output_count_last + self.expected_output_clock_count_inc
error = output_count_inc - int(self.expected_output_clock_count_inc)
# Apply out of range detection so that the controller ignores startup or missed control loops (as per C)
if abs(error) > (self.ppm_range / 1e6) * self.expected_output_clock_count_inc:
# print("PFD FIRST LOOP", abs(error), (self.ppm_range / 10e6) * self.expected_output_clock_count_inc)
self.first_loop = True
else:
self.first_loop = False
self.output_count_last = output_count_int
return error, self.first_loop
if __name__ == '__main__':
"""
This module is not intended to be run directly. This is here for internal testing only.
"""
nominal_output_hz = 12288000
nominal_control_rate_hz = 93.75
expected_output_clock_inc = nominal_output_hz / nominal_control_rate_hz
pfd = port_timer_pfd(nominal_output_hz, nominal_control_rate_hz)
output_clock_count_float = 0.0
for output_hz in range(nominal_output_hz - 1000, nominal_output_hz + 1000, 10):
output_clock_count_float += output_hz / nominal_output_hz * expected_output_clock_inc
error = pfd.get_error(output_clock_count_float)
print(f"actual output Hz: {output_hz} output_clock_count: {output_clock_count_float} error: {error}")