{"id":23178457,"url":"https://github.com/intel-retail/software-vsync-modulation-sample","last_synced_at":"2025-08-18T12:30:47.569Z","repository":{"id":160522409,"uuid":"599769304","full_name":"intel-retail/software-vsync-modulation-sample","owner":"intel-retail","description":"A SW Library for Digital Signage that provides references on how to modify MMIO PLL registers to slow/speed up VSYNC timings on a system to achieve synchronization. For documentation click on the link below.","archived":false,"fork":false,"pushed_at":"2025-07-18T07:25:06.000Z","size":5579,"stargazers_count":7,"open_issues_count":2,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-07-18T11:19:55.496Z","etag":null,"topics":["display","genlock","sync","synchronization","timecode"],"latest_commit_sha":null,"homepage":"https://intel-retail.github.io/software-vsync-modulation-sample/","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/intel-retail.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"security.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-02-09T20:57:59.000Z","updated_at":"2025-07-18T07:25:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"b9bb48d0-0a54-498d-8993-fd5b5e865349","html_url":"https://github.com/intel-retail/software-vsync-modulation-sample","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/intel-retail/software-vsync-modulation-sample","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intel-retail%2Fsoftware-vsync-modulation-sample","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intel-retail%2Fsoftware-vsync-modulation-sample/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intel-retail%2Fsoftware-vsync-modulation-sample/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intel-retail%2Fsoftware-vsync-modulation-sample/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/intel-retail","download_url":"https://codeload.github.com/intel-retail/software-vsync-modulation-sample/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intel-retail%2Fsoftware-vsync-modulation-sample/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270994984,"owners_count":24681852,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-18T02:00:08.743Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["display","genlock","sync","synchronization","timecode"],"created_at":"2024-12-18T07:11:12.157Z","updated_at":"2025-08-18T12:30:47.547Z","avatar_url":"https://github.com/intel-retail.png","language":"C++","readme":" Copyright (C) 2025 Intel Corporation\r\n SPDX-License-Identifier: MIT\r\n\r\n# Introduction to SW Genlock\r\nSW Genlock is a software-based solution designed to **synchronize the display refresh (vblank) signals** across multiple computer systems with high-precision microsecond-level accuracy. It ensures that the vertical blanking intervals (vblanks) of all connected displays occur in tight alignment, enabling seamless, tear-free visual output across multiple screens. Such synchronization is critical in environments like video walls, virtual reality setups, digital signage, and other multi-display configurations where visual continuity is essential.\r\n\r\nIn use cases such as digital signage, there are typically **two levels of synchronization** required:\r\n\r\n* **Low-level synchronization** of the display hardware to align the vblanks across systems, and\r\n\r\n* **High-level synchronization** by the application to ensure that decoded frames are delivered in sync with those vblanks.\r\n\r\nSW Genlock addresses the **first, low-level synchronization** layer, making sure that the vblank signals are precisely aligned across all participating systems. This provides a reliable foundation for applications to build on and manage frame-level synchronization effectively.\r\n\r\nIn addition to precise timing, SW Genlock features an `adaptive clock alignment mechanism` that allows secondary systems to gradually adjust their internal clocks to match the primary system. Through permanent, micro-scale adjustments controlled by a tunable learning rate, the system \"learns\" and adapts over time, reducing synchronization overhead and increasing long-term stability. This makes SW Genlock ideal for continuous, low-maintenance operation across varied hardware environments.\r\n\r\n## Key Features of SW Genlock\r\n\r\n- **High Precision Synchronization**: Ensures that vertical sync (vblank) intervals occur simultaneously across all connected systems, maintaining the integrity of visual displays.\r\n- **Flexible Integration**: Offers both dynamic (.so) and static (.a) linking options to accommodate various application needs.\r\n- **Comprehensive Tools**: Includes a suite of tools and libraries that facilitate the synchronization process, such as obtaining vblank timestamps and adjusting the phase-locked loop (PLL) frequencies.\r\n- **Extensive Compatibility**: SW Genlock supports a wide range of system configurations, including mixed platform generations (e.g., Tiger Lake, Panther Lake) and various Linux distributions such as Ubuntu 22.04 and 24.04. It offers flexibility in real-time clock synchronization setups by supporting both Precision Time Protocol (PTP) for high-precision environments and Chrony for simpler or less time-sensitive deployments. This makes it suitable for diverse hardware and OS combinations across development and production environments.\r\n- **Adaptive Clock Alignment:** Implements optional, micro-scale permanent clock adjustments using a tunable learning rate. This adaptive mechanism helps secondary systems gradually align with the primary clock, reducing sync frequency and improving long-term stability with minimal overhead.\r\n- **User-Centric Utilities**: Provides reference applications that demonstrate effective usage of the library, assisting users in integrating Genlock capabilities into their own projects.\r\n\r\nThe SW Genlock system operates by aligning the internal clocks of all systems involved, ensuring that every display refresh is triggered simultaneously. This alignment is achieved through the utilization of industry-standard protocols and our advanced algorithms, which manage the synchronization over ethernet or direct network connections.\r\n\r\n\u003cdiv align=\"center\"\u003e\r\n  \u003cfigure\u003e\r\n    \u003cimg src=\"./docs/images/swgenlock.png\" alt=\"Synchronization\"/\u003e\r\n    \u003cfigcaption\u003eFigure: SW GenLock\u003c/figcaption\u003e\r\n  \u003c/figure\u003e\r\n\u003c/div\u003e\r\n\r\n# Supported Platforms\r\n\r\nPlatforms based on the following architectures should be supported:\r\n\r\n- Tiger Lake\r\n- Alder Lake\r\n- Raptor Lake\r\n- Meteor Lake\r\n- Arrow Lake\r\n- Panther Lake\r\n\r\n## Important Notes\r\n* This tool requires root privileges (e.g., via sudo) by default, as it accesses the PCI address space directly. Alternatively, users may configure appropriate permissions or use cgroup-based access control to avoid running as root. Please note that the accompanying reference applications do not handle this, and it is the user's responsibility to ensure proper permissions are set up.\r\n\r\n* The vsync_test reference application provided is intended for demonstration purposes only. It uses unencrypted socket communication to send and receive VBlank timestamps. Users are strongly encouraged to implement appropriate encryption and security measures for any production or real-world deployment.\r\n\r\n# Installation\r\n\r\nInstalling SW Genlock involves a few straightforward steps. This section guides through the process of setting up SW Genlock on target systems, ensuring that user have everything needed to start synchronizing displays.\r\n\r\n## System Requirements\r\n\r\nBefore the user begin the installation, ensure the systems meet the following criteria:\r\n\r\nThe build system assumes the following, prior to executing the build steps:\r\n\r\n1) The system has [Ubuntu 22.04 or 24.04](https://ubuntu.com/tutorials/install-ubuntu-desktop#1-overview).  Other Linux flavors should work too.\r\n1) Libraries installed: `sudo apt install -y git build-essential make`\r\n1) [Disable secure boot](https://wiki.ubuntu.com/UEFI/SecureBoot/DKMS)\r\n1) MUST apply the [PLL kernel patch](./resources/shared_dpll-kernel-6.xx.patch) located in resources folder.\r\n\r\n\tApply shared_dpll-kernel-x.x.patch according to kernel version.  e.g shared_dpll-kernel-6.10+.patch applicable from 6.10 till 6.13.  6.14 has its own patch in resource directory.\r\n\r\n\t\te.g patch -p1 \u003c \u003cSWGenLockdir\u003e/resources/shared_dpll-kernel-6.10+.patch\r\n\r\n   Compile the kernel and update either the entire kernel or just the i915.ko/xe.ko module, depending on the Linux configuration. Note: In some setups, the i915.ko/xe.ko module is integrated into the\r\n   initrd.img, and updating it in /lib/module/... might not reflect the changes.\r\n   - Following the application of the patch, edit the grub settings:\r\n ```console\r\n $ sudo vi /etc/grub/default\r\n # add i915.share_dplls=0 to the GRUB_CMDLINE_LINUX options:\r\n # GRUB_CMDLINE_LINUX=\"i915.share_dplls=0\"\r\n # Save and exit, then update grub\r\n update-grub\r\n ```\r\n\r\n1) Apply **[the monotonic timstamp patch](./resources/0001-Revert-drm-vblank-remove-drm_timestamp_monotonic-par.patch)**\r\n to Linux kernel drm module which allows it to provide vsync timestamps in real time\r\n instead of the default monotonic time.\u003cbr\u003e\r\n Note that the monotonic timestamp patch is generated based on Linux v6.4 and has been tested upto v6.15.\u003cbr\u003e\r\n Please follow the steps to disable monotonic timestamp after installing the local built Linux image on a target. Compile the kernel and update either the entire kernel or just the drm.ko module based on the installed Linux configuration. \u003cbr\u003e\r\n  1. Add ```drm.timestamp_monotonic=0``` option to **GRUB_CMDLINE_LINUX** in /etc/default/grub\r\n  2. Update **GRUB_DEFAULT** in /etc/default/grub to address the local built Linux image\r\n  3. Run ```update-grub``` to apply the change\r\n  4. Reboot a target\r\n1) Turn off NTP time synchronization service by using this command:\r\n ```timedatectl set-ntp no```\r\n1) All the involved systems should support PTP time synchronization via ethernet (e.g ptp4l, phc2sys)\r\n   To verify if an ethernet interface supports PTP, the `ethtool` linux tool can be used. Run the following command, replacing `eth0` with the relevant interface name:\r\n\r\n   ```console\r\n   $ ethtool -T eth0\r\n   ```\r\n\r\n   If the interface supports PTP, the output will display specific PTP hardware clock information.\r\n   To ensure proper synchronization, the Ethernet interfaces on the involved systems should be directly connected either via a crossover cable or through a PTP-supported router or switch. If the systems only have a single network interface, it may be necessary to add a separate network adapter, such as a USB-to-Ethernet or USB-to-WiFi dongle, to maintain SSH access and connectivity to external networks.\r\n\r\n\tAlternatively, `Chrony` can be used for real-time clock synchronization. While it offers easier setup and broader compatibility, its precision is generally lower, and time drift may not be as stable compared to hardware-assisted PTP.\r\n\r\n1) Displays must be at same refresh rate (e.g 60Hz)\r\n\r\n## Build steps\r\n\r\n1) Git clone this repository\r\n1) `sudo apt install libdrm-dev libpciaccess-dev`\r\n    1) If the directory /usr/include/drm does not exist, it may be necessary to run the command`sudo apt install -y linux-libc-dev`\r\n2) Type `make` from the main directory. It compiles everything and creates library and executable binaries in respective folders along with code which can then be copied to the target systems.\r\n\r\n# Software Components\r\n\r\n## Vsync Library\r\n\r\nThe Vsync library is distributed in both dynamic (.so) and static (.a) binary formats, allowing users to choose between dynamic and static linking depending on their application requirements. This library includes essential functions such as retrieving vblank timestamps and adjusting the vblank timestamp drift by specified microseconds. Additionally, all operations related to Phase-Locked Loop (PLL) programming are encapsulated within the library, providing a comprehensive suite of tools for managing display synchronization.\r\n\r\n### 🔧 Static Library (.a)\r\nWhen linking against the static library (libvsyncalter.a), the final binary must be linked using g++ rather than gcc. This is because the static library contains C++ object code but does not include the C++ standard library symbols (e.g., operator new, std::mutex, virtual tables).\r\n\r\n```bash\r\n$ g++ -o app main.c libvsyncalter.a -lpciaccess -ldrm -lrt -lm\r\n```\r\n\r\nLinking with gcc in this case will typically result in undefined reference errors to C++ runtime symbols.\r\n\r\n### 🔧 Shared Library (.so)\r\nWhen using the shared library version (libvsyncalter.so), the final binary can be linked using either gcc or g++. All necessary C++ symbols are already resolved and included in the shared object.\r\n\r\nThe system dynamic linker will load the required C++ runtime (libstdc++) at runtime.\r\n\r\n```bash\r\n$ gcc -o app main.c -L. -lvsyncalter -lpciaccess -ldrm -lrt -lm\r\n```\r\n\r\nThis allows the shared library to be used safely from C code, since the C++ functions in vsync lib are exposed with C-compatible linkage (i.e., using extern \"C\" in headers). The reference applications are linked using g++, while the unit tests are compiled with gcc to ensure compatibility with the pure C interface.\r\n\r\n## Reference Applications\r\n\r\nAccompanying the library, three reference applications are provided to demonstrate how to utilize the library effectively. These include:\r\n\r\n- A `vsync test` app that runs in either primary mode or secondary mode.  primary mode is run on a single PC whereas all other PCs are run as secondary mode with parameters pointing to primary mode system ethernet address.  The communication is done either in ethernet mode or IP address mode.\r\n\r\n```console\r\nUsage: ./vsync_test [-m mode] [-i interface] [-c mac_address] [-d delta] [-p pipe] [-s shift] [-v loglevel] [-h]\r\nOptions:\r\n  -m mode           Mode of operation: pri, sec (default: pri)\r\n  -i interface      Network interface to listen on (primary) or connect to (secondary) (default: 127.0.0.1)\r\n  -c mac_address    MAC address of the network interface to connect to. Applicable to ethernet interface mode only.\r\n  -d delta          Drift time in microseconds to allow before pll reprogramming (default: 100 us)\r\n  -p pipe           Pipe to get stamps for.  4 (all) or 0,1,2 ... (default: 0)\r\n  -s shift          PLL frequency change fraction as percentage (default: 0.01 = 0.01%)\r\n  -x shift2         PLL frequency change fraction for large drift (default: 0.0; Disabled)\r\n  -f frequency      PLL clock value to set at start (default -\u003e Do Not Set : 0.0)\r\n  -e device         Device string (default: /dev/dri/card0)\r\n  -v loglevel       Log level: error, warning, info, debug or trace (default: info)\r\n  -k time_period    Time period in seconds during which learning rate will be applied.  (default: 240 sec)\r\n  -l learning_rate  Learning rate for convergence. Secondary mode only. e.g 0.00001 (default: 0.0  Disabled)\r\n  -o overshoot_ratio Allow the clock to go beyond zero alignment by a ratio of the delta (value between 0 and 1).\r\n                        For example, with -o 0.5 and delta=500, the target offset becomes -250 us in the apposite direction (default: 0.0)\r\n  -t step_threshold Delta threshold in microseconds to trigger stepping mode (default: 1000 us)\r\n  -w step_wait      Wait in milliseconds between steps (default: 50 ms)\r\n  -n                Use DP M \u0026 N Path. (default: no)\r\n  -h                Display this help message\r\n```\r\n\r\n* A `vbltest` app to print average vblank period between sync.  This is useful in verification and validation.\r\n\r\n```console\r\n[INFO] Vbltest Version: 2.0.0\r\n Usage: ./vbltest [-p pipe] [-c vsync_count] [-v loglevel] [-h]\r\n Options:\r\n  -p pipe        Pipe to get stamps for.  0,1,2 ... (default: 0)\r\n  -c vsync_count Number of vsyncs to get timestamp for (default: 300)\r\n  -e device      Device string (default: /dev/dri/card0)\r\n  -l loop        Loop mode: 0 = no loop, 1 = loop (default: 0)\r\n  -v loglevel    Log level: error, warning, info, debug or trace (default: info)\r\n  -h             Display this help message\r\n```\r\n\r\n* A `synctest` app to drift vblank clock on a single display by certain period such as 1000 microseconds (or 1.0 ms).\r\n\r\n```console\r\n[INFO] Synctest Version: 2.0.0\r\nUsage: ./synctest [-p pipe] [-d delta] [-s shift] [-v loglevel] [-h]\r\nOptions:\r\n  -p pipe            Pipe to get stamps for.  0,1,2 ... (default: 0)\r\n  -d delta           Drift time in us to achieve (default: 1000 us) e.g 1000 us = 1.0 ms\r\n  -s shift           PLL frequency change fraction (default: 0.01)\r\n  -x shift2          PLL frequency change fraction for large drift (default: 0.0; Disabled)\r\n  -e device          Device string (default: /dev/dri/card0)\r\n  -f frequency       Clock value to directly set (default -\u003e Do not set : 0.0)\r\n  -v loglevel        Log level: error, warning, info, debug or trace (default: info)\r\n  -t step_threshold  Delta threshold in microseconds to trigger stepping mode (default: 1000 us)\r\n  -w step_wait       Wait in milliseconds between steps (default: 50 ms)\r\n  --no-reset         Do no reset to original values. Keep modified PLL frequency and exit (default: reset)\r\n  --no-commit        Do no commit changes.  Just print (default: commit)\r\n  -m                 Use DP M \u0026 N Path. (default: no)\r\n  -h                 Display this help message\r\n```\r\n\r\n**synctest sample output:**\r\n\r\n```console\r\n$ ./synctest\r\n[INFO] Synctest Version: 2.0.0\r\n[INFO] DRM Info:\r\n[INFO]   CRTCs found: 4\r\n[INFO]    Pipe:  0, CRTC ID:   80, Mode Valid: Yes, Mode Name: , Position: (   0,    0), Resolution: 1920x1080, Refresh Rate: 60.00 Hz\r\n[INFO]    Pipe:  1, CRTC ID:  131, Mode Valid:  No, Mode Name: , Position: (   0,    0), Resolution:    0x0   , Refresh Rate: 0.00 Hz\r\n[INFO]    Pipe:  2, CRTC ID:  182, Mode Valid:  No, Mode Name: , Position: (   0,    0), Resolution:    0x0   , Refresh Rate: 0.00 Hz\r\n[INFO]    Pipe:  3, CRTC ID:  233, Mode Valid:  No, Mode Name: , Position: (   0,    0), Resolution:    0x0   , Refresh Rate: 0.00 Hz\r\n[INFO]   Connectors found: 6\r\n[INFO]    Connector: 0    (ID: 236 ), Type: 11   (HDMI-A      ), Type ID: 1   , Connection: Disconnected\r\n[INFO]    Connector: 1    (ID: 246 ), Type: 11   (HDMI-A      ), Type ID: 2   , Connection: Connected\r\n[INFO]    Encoder ID: 245, CRTC ID: 80\r\n[INFO]    Connector: 2    (ID: 250 ), Type: 10   (DisplayPort ), Type ID: 1   , Connection: Disconnected\r\n[INFO]    Connector: 3    (ID: 259 ), Type: 11   (HDMI-A      ), Type ID: 3   , Connection: Disconnected\r\n[INFO]    Connector: 4    (ID: 263 ), Type: 10   (DisplayPort ), Type ID: 2   , Connection: Disconnected\r\n[INFO]    Connector: 5    (ID: 272 ), Type: 10   (DisplayPort ), Type ID: 3   , Connection: Disconnected\r\n[INFO] VBlank interval before starting synchronization: 16.667000 ms\r\n[INFO] VBlank interval during synchronization -\u003e\r\n[INFO]   VBlank interval on pipe 0 is 16.6650 ms\r\n[INFO]   VBlank interval on pipe 0 is 16.6650 ms\r\n[INFO]   VBlank interval on pipe 0 is 16.6650 ms\r\n[INFO]   VBlank interval on pipe 0 is 16.6650 ms\r\n[INFO]   VBlank interval on pipe 0 is 16.6640 ms\r\n[INFO]   VBlank interval on pipe 0 is 16.6650 ms\r\n[INFO]   VBlank interval on pipe 0 is 16.6650 ms\r\n[INFO]   VBlank interval on pipe 0 is 16.6660 ms\r\n[INFO] VBlank interval after synchronization ends: 16.667000 ms\r\n```\r\n\r\n# Generating Doxygen documents\r\n\r\nPlease install doxygen and graphviz packages before generating Doxygen documents:\r\n ```sudo apt install doxygen graphviz```\r\n\r\n1. Type ```make doxygen VERSION=\"1.2.3\"``` from the main directory. It will generate SW Genlock doxygen documents\r\n to output/doxygen folder. Change the version to be that of the release number for the project.\r\n2. Open output/doxygen/html/index.html with a web-browser\r\n\r\n# Running on single system\r\n\r\nThe reference applications vbltest and synctest are designed to operate on a single system. The vbltest app displays the average vblank period for a specific pipe, while synctest allows user to introduce a specific drift in the vblank timing for a pipe. Synctest serves as a practical tool to verify the effectiveness of the synchronization methodology.\r\n\r\n1) On the system, go to the directory where these files exist and set environment variable:\r\n2) For dynamic linking with the .so library, it's necessary to set the LD_LIBRARY_PATH environment variable so that applications can locate the vsync dynamic library. This step is not required when using static linking.\r\n\r\n```console\r\n$ export LD_LIBRARY_PATH=`pwd`/lib\r\n``````\r\n\r\n3) On the primary system, run accompanied reference apps as follows:\r\n4) e.g synctest\r\n ```console\r\n   $ ./synctest\r\n   ```\r\n\r\nThe synctest application offers two specialized modes as command line parameters:\r\n\r\n`--no-reset`: Applies new PLL clock values to the registers without reverting them after the timer expires. Use this mode for persistent changes, particularly during debugging and troubleshooting.\r\n\r\n`--no-commit`: Calculates and displays the new PLL clock values without writing them to the registers. This allows for safe previewing of changes before applying them.\r\n\r\nBuilding of this program has been successfully tested on both Ubuntu 2x and Fedora 30.\u003cbr\u003e\r\n\r\n## Direct PLL Clock Setting with Stepped Adjustment\r\n\r\nUser can also update pll clock directly via vsync lib function call.  The newly added set_pll_clock API, allowing for precise and direct control over PLL clock frequencies. To handle large frequency changes gracefully, the implementation incorporates an stepped adjustment mechanism. When a significant difference exists between the current and desired PLL clock frequencies, the function automatically calculates the optimal number of steps and adjusts the frequency incrementally based on given shift factor. This ensures stability and reliable locking without blank screens, even during substantial frequency transitions. The synctest application has been updated to demonstrate the usage of this new set_pll_clock API, providing a practical reference implementation for developers. For more details refer to the set_pll_clock api documentation.  Current PLL clock value can be inferred by running synctest in regular mode which will print the value in console. User then can run synctest with -f option and desired PLL clock as an absolute floating point value parameter.\r\n\r\n```shell\r\n  $./synctest -f 8100.532\r\n```\r\n\r\n# Synchronization Between Two Systems\r\n\r\nSynchronizing displays across two systems involves a two-step process. Firstly, the Real Time Clocks of both systems need to be kept in synchronized state using the ptp4l Linux tool. Subsequently, the vsync test app should be run in primary mode on one system and in secondary mode on the other, ensuring that the vblank of the secondary system remains synchronized with that of the primary system.\r\n\r\n## Synchronizing Real-time Clocks Between Two Systems\r\n\r\nFor SW Genlock to function effectively, all participating systems must have their system clocks synchronized with **microsecond-level accuracy**. This is critical because SW Genlock shares vblank timestamps from the primary system with secondary systems, and these timestamps are based on the system clock, not the kernel’s monotonic clock (which starts counting from kernel boot time).\r\n\r\nSW Genlock works with real time clocks synchronized using either PTP (Precision Time Protocol) or Chrony, depending on the system capabilities and hardware environment:\r\n\r\n* **PTP** offers high-precision synchronization (sub-microsecond to low microsecond) and is the recommended option for best results. It typically requires hardware timestamping and a direct Ethernet connection between systems.\r\n\r\n* **Chrony**, while generally offering lower precision (typically within tens of microseconds), has been successfully tested with SW Genlock and provides sufficiently accurate synchronization in many practical scenarios.\r\n\r\nThe setup instructions for both synchronization methods are available in the docs folder of this repository:\r\n\r\n- [PTP Setup Guide](docs/ptp.md)\r\n- [Chrony Setup Guide](docs/chrony.md)\r\n\r\nFor best results, especially in display-critical environments, it’s recommended to use a direct network cable between the Ethernet ports of the systems involved.\r\n\r\n\r\n## Synchronizing Display VBlanks\r\n\r\nOnce the system clocks are synchronized, user can proceed to run the vsync test tool on both the primary and secondary systems.\r\nInstalling and running the programs:\u003cbr\u003e\r\n\r\n1. Copy the release folder contents to both primary and secondary systems.\u003cbr\u003e\r\n2. On both systems, go to the directory where these files exist and set environment variable:\r\nexport LD_LIBRARY_PATH=.\u003cbr\u003e\r\n3. On the primary system, run it as follows:\r\n ```console\r\n $ ./vsync_test -m pri -i [PTP_ETH_Interface or Local IP Address]\r\n  ```\r\n\r\n5. On the secondary system, run it as follows:\r\n ```console\r\n $ ./vsync_test -m sec -i [PTP_ETH_Interface or Remote IP Address] -c [Primary_ETH_MAC_Addr] -d [sync after # us of drift]\r\n  ```\r\n\r\nThis program runs in server mode on the primary system and in client mode on the\r\nsecondary system.\r\n\r\n\u003cdiv align=\"center\"\u003e\r\n\u003cfigure\u003e\u003cimg src=\"./docs/images/vsync_test.png\" alt=\"VBlank synchronization setup\" /\u003e\r\n\u003cfigcaption\u003eFigure: VBlank synchronization setup.\u003c/figcaption\u003e\u003c/figure\u003e\r\n\u003c/div\u003e\r\n\r\nIf using PTP protocol to communicate between primary and secondary, there are some extra\r\nparameters required. The primary system must identify the PTP Interface (ex: enp176s0). The\r\nsecondary system also requires its PTP interface as well as this port's ethernet MAC address.\r\nAn example of PTP communication between primary and secondary looks like this:\r\nOn the primary system, run it as follows:\r\n\r\n```console\r\n   $ ./vsync_test -m pri -i enp176s0\r\n```\r\nOn the secondary system, run it as follows:\r\n```console\r\n  $ ./vsync_test -m sec -i enp176s0 -c 84:47:09:04:eb:0e\r\n```\r\n\r\nIn the above examples, we were just sync'ing the secondary system once with the primary.\r\nHowever, due to reference clock differences, we see that there is a drift pretty much as\r\nsoon as we have sync'd them. We also have another capability which allows us to resync\r\nas soon as it goes above a threshold. For example:\r\nOn the primary system, run it as follows:\r\n ```./vsync_test -m pri -i enp176s0```\u003cbr\u003e\r\nOn the secondary system, run it as follows:\r\n ```./vsync_test -m sec -i enp176s0 -c 84:47:09:04:eb:0e 100```\r\n\r\nThe secondary system would sync with the primary once but after that\r\nit would constantly check to see if the drift is going above 100 us. As soon as it does,\r\nthe secondary system would automatically resync with the primary. It is recommended to\r\nselect a large number like 60 or 100 since if we use a smaller number, then we will be\r\nresyncing all the time. On the other hand, if we chose a really big number like 1000 us,\r\nthen we are allowing the systems to get out of sync from each other for 1 ms. On a\r\nTiger Lake system, 100 us difference is usually happening in around 60-70 seconds or\r\nabout a minute. So this program would automatically resync with the primary every minute\r\nor so.\r\n\r\nThe app features a learning mode as a secondary mode option, where it gradually adjusts the PLL clock to align closely with the primary clock. This adjustment occurs at a specified learning rate, and the app continuously modifies the secondary clock over several iterations and a set duration until it closely mirrors the primary clock, significantly delaying any drift. You can set the learning rate through a command-line parameter, with the default setting being 0, which disables this feature.  The time_period parameter defines the duration within which the app responds to any drift by adjusting the clock. If the drift happens after this period has expired, the app does not make permanent adjustments to the PLL clock, assuming it has already closely synchronized with the primary display.\r\n\r\nSimilar to ptp4l, For user convenience, an automation Bash script is available in the scripts folder to facilitate vsync synchronization on both primary and secondary systems. Users simply need to update the `config.sh` file with relevant details such as the secondary system's address, username, and Ethernet address, among others. After these updates, run the scripts/run_vsync.sh script from the primary system. This script will automatically copy vblank_sync to secondary under ~/.vblanksync directory and execute the vblank_sync tool on both the primary and secondary systems in their respective modes. Note that the script requires the secondary system to enable passwordless SSH to execute vsyncs commands through the SSH interface.\r\n\r\n```console\r\n$ ./scripts/run_vsync.sh\r\n```\r\n\r\n# SW GenLock Key Features\r\n\r\n## Drift Correction Logic for larger drift\r\nWhen the time delta between displays exceeds a certain threshold (see `-t` command line parameter), the system supports a two-phase adjustment mechanism to quickly reduce the drift.\r\n\r\nThe shift value is the desired % difference from current frequency.  0.01 means 0.01% difference (either increase or decrease depending on which side the drift is). If a shift2 value is configured (typically larger than shift), it will be used to accelerate the convergence. However, applying a large shift directly may cause display blanking. To avoid this, the implementation internally breaks shift2 into smaller increments (based on shift) and applies them step-by-step to reach the desired frequency smoothly.  There is a minor delay added to settle down frequency before next step (see `-w` command line parameter).\r\n\r\nAfter a configured timer expires, the same logic is applied in reverse to gradually return the system to its original frequency.\r\n\r\nIf shift2 is not set (i.e., shift2 = 0), then only shift is used, and no internal stepping is performed. This results in slower convergence but avoids any risk of display instability.\r\n\r\nUsing shift2 is particularly beneficial during the initial run, when VBlank timestamps between displays may be significantly misaligned. It helps reduce the initial drift quickly, and subsequent synchronization steps use the regular shift value to fine-tune the timing.\r\n\r\nWhen a very small shift is used together with a defined step size and a slightly longer wait time between steps, a minor overshoot is expected. In typical use cases with continuous drift monitoring, this overshoot is automatically corrected over time. However, when testing with synctest, which runs only a single adjustment cycle, this correction does not take place. Users are advised to experiment and tune the shift, step threshold and wait values according to their specific hardware setup.\r\n\r\n## Adaptive Clock Adjustment via Learning Rate (Secondary Mode)\r\n\r\nOne of the key features of the Software Genlock system is the ability of a secondary system to gradually align its clock with the primary system through **incremental, permanent adjustments**. This mechanism helps reduce the frequency of synchronization events over time.\r\n\r\nWhen the **learning rate** parameter is provided via command line (e.g., `0.00001`), the secondary system does not only apply temporary drift corrections — it also updates its base clock gradually in the direction of the drift. This behavior helps the local clock \"learn\" and adapt to the long-term behavior of the primary system.\r\n\r\n### How It Works\r\n\r\n- After each detected clock drift and adjustment, the secondary clock is **permanently nudged** toward the primary clock using the learning rate.\r\n- The adjustment is calculated as:\r\n\t\tnew_clock_offset = current_offset + (drift * learning_rate)\r\n- Over time, this reduces the overall drift and **increases the interval between needed sync events**, resulting in a more stable and low-overhead synchronization model.\r\n\r\nThis tiny correction is applied to the secondary clock base value. With repeated learning cycles, the secondary system adapts and remains closer to the primary clock with minimal intervention.\r\n\r\nThis learning mechanism is optional and tunable via the command line parameter -l, allowing fine-grained control over synchronization behavior based on system stability and timing accuracy needs.\r\n\r\n## 📈 Step-Based Frequency Correction\r\nTo achieve smooth and compliant synchronization, SW GenLock applies clock frequency corrections in controlled steps, allowing fast convergence without violating PHY or PLL constraints.\r\n\r\n🔹 **Shift 1** – Fine-Grained Correction\r\nShift 1 represents a small, controlled adjustment to the secondary clock’s frequency, defined as a percentage of the base PLL frequency—typically between 0.01% to 0.1%. This step is used continuously to either slightly speed up or slow down the clock to stay in sync with the primary. It ensures the corrections stay within the compliance limits of the underlying PHY and PLL, enabling stable and gradual convergence.\r\n\r\n🔹 **Shift 2** – Rapid Drift Recovery\r\nShift 2 is triggered when the drift between primary and secondary clocks crosses a defined threshold, such as 1 millisecond. It calculates a larger correction to close the timing gap more quickly. However, instead of applying this large adjustment at once, the correction is split into multiple smaller Shift 1 steps to maintain safe operating limits and avoid abrupt changes that could disrupt system behavior.\r\n\r\nThis two-step correction model ensures the secondary clock reaches alignment with the primary efficiently and within hardware limitations, even under large initial drift conditions.\r\n\r\n\r\n## Offset Overshoot Control\r\nThis feature allows the secondary clock to intentionally overshoot the ideal alignment point (zero delta) within the permitted drift range. Controlled via the -o parameter (default: 0.0, range: 0.0 to 1.0), it defines how far in the opposite direction the clock is allowed to go before beginning convergence. For example, setting -o 0.5 with a delta of 500 µs shifts the sync target to -250 µs, helping reduce the frequency of corrections and avoiding abrupt PLL adjustments. This results in smoother synchronization and longer stable intervals.\r\n\r\n\r\n## Data collection and Graph generation\r\nThe tool logs key synchronization metrics in CSV format, such as time between sync events, delta values at the point of sync trigger, and the applied PLL frequency. A Python script is included to generate plots that help visualize the system’s behavior over long durations. It is recommended to use a virtual environment (especially on Ubuntu 24.04 or later) to avoid conflicts with system packages. You can create and activate a virtual environment as follows:\r\n\r\n```bash\r\npython3 -m venv venv\r\nsource venv/bin/activate\r\npip install matplotlib\r\n```\r\n\r\nNow run\r\n\r\n```bash\r\n(venv)$ python ./scripts/plot_sync_interval.py ./sync_pipe_0_20250610_110827.csv\r\n```\r\n\r\n\u003cdiv align=\"center\"\u003e\r\n\u003cfigure\u003e\u003cimg src=\"./docs/images/graph.png\" alt=\"Long hour synchronization with Chrony\" /\u003e\r\n\u003cfigcaption\u003eFigure: Long hour synchronization with Chrony\u003c/figcaption\u003e\u003c/figure\u003e\r\n\u003c/div\u003e\r\n\r\n**To read graph:​**  The data points show the duration between sync events. With a learning time window of 480 seconds (8 minutes), the secondary PLL clock continued adjusting for approximately the first 1.5 hours. Once the sync interval exceeded 8 minutes, learning stopped, and the system remained stable, consistently maintaining sync intervals longer than 8 minutes. A single dip observed around the 4-hour mark is likely due to a transient issue with Chrony.\r\n\r\nThis data proves valuable for debugging, performance tuning, and validating sync stability. Additionally, the logged PLL frequency can be reused as a command-line parameter to initialize the application with an optimal frequency, allowing it to skip the learning phase and achieve quicker synchronization.\r\n\r\n## Running vsync_test as a Regular User without sudo\r\n\r\nWhile the ptp synchronization section provides methods for configuring passwordless sudo, it may be preferable to operate without using sudo or root privileges. The `vsync_test` application, which requires access to protected system resources for memory-mapped I/O (MMIO) operations at `/sys/bus/pci/devices/0000:00:02.0/resource0`, can be configured to run under regular user permissions. This can be achieved by modifying system permissions either through direct file permission changes or by adjusting user group memberships. Note that the resource path might change in future versions of the kernel. The resource path can be verified by running the application with the `strace` tool in Linux as a regular user and checking for permission denied errors or by examining `/proc/processid/maps` for resource mappings.\r\n\r\n### Option 1: Modifying File Permissions\r\n\r\nAltering the file permissions to allow all users to read and write to the device resource removes the requirement for root access. This method should be used with caution as it can lower the security level of the system, especially in environments with multiple users or in production settings.\r\n\r\nTo change the permissions, the following command can be executed:\r\n\r\n```bash\r\nsudo chmod o+rw /sys/bus/pci/devices/0000:00:02.0/resource0\r\n```\r\n\r\nThis command adjusts the permissions to permit all users (o) read and write (rw) access to the specified resource.\r\n\r\n### Option 2: Configuring User Group Permissions and Creating a Persistent udev Rule\r\n\r\nA more secure method is to assign the resource to a specific user group, such as the `video` group, and add the user to that group. This method confines permissions to a controlled group of users. To ensure the changes persist after a reboot, a `udev` rule can be created.\r\n\r\n#### Step 1: Add the Resource to the Video Group\r\n\r\nChange the group ownership of the resource file to the `video` group using the following command:\r\n\r\n```bash\r\nsudo chgrp video /sys/bus/pci/devices/0000:00:02.0/resource0\r\n```\r\n\r\n#### Step 2: Modify Group Permissions\r\n\r\nSet the group permissions to allow read and write access with:\r\n\r\n```bash\r\nsudo chmod g+rw /sys/bus/pci/devices/0000:00:02.0/resource0\r\n```\r\n\r\n#### Step 3: Add the User to the Video Group\r\n\r\nAdd the user to the video group using this command (replace username with the actual user name):\r\n\r\n```bash\r\nsudo usermod -a -G video username\r\n```\r\n\r\nLog out and log back in, or start a new session, for the group changes to take effect.\r\n\r\n#### Step 4: Create a udev Rule for Persistent Permissions\r\n\r\nTo make the permissions persist after reboot, create a `udev` rule by adding a new rule file in `/etc/udev/rules.d/`:\r\n\r\n```bash\r\necho 'ACTION==\"add\", SUBSYSTEM==\"pci\", KERNEL==\"0000:00:02.0\", RUN+=\"/bin/chgrp video %S%p/resource0\", RUN+=\"/bin/chmod 0660 %S%p/resource0\"' | sudo tee /etc/udev/rules.d/99-vsync-access.rules\r\n```\r\n\r\nThis rule ensures that the permissions are correctly set when the system boots.\r\n\r\n#### Step 5: Reload udev Rules\r\n\r\nAfter creating the rule, reload the udev rules to apply them immediately without rebooting:\r\n\r\n```bash\r\nsudo udevadm control --reload-rules \u0026\u0026 sudo udevadm trigger -s pci --action=add\r\n```\r\n\r\n#### Verifying the Changes\r\n\r\nAfter applying one of the above methods, confirm that the user has the necessary permissions by running the vsync_test application without elevated privileges. If the setup is correct, the application should run without requiring sudo or root access.\r\n\r\n## Running ptp4l as a Regular User without sudo\r\n\r\nSimilarly, the ptp4l command can be executed by a regular user. This can be done by either altering the permissions or modifying the group for `/dev/ptp0`, or by creating a udev rule to ensure persistent permissions.\r\n\r\n```bash\r\necho 'SUBSYSTEM==\"ptp\", KERNEL==\"ptp0\", GROUP=\"video\", MODE=\"0660\"' | sudo tee /etc/udev/rules.d/99-ptp-access.rules\r\n```\r\n\r\nReload udev Rules\r\n\r\n```bash\r\nsudo udevadm control --reload-rules \u0026\u0026  sudo udevadm trigger\r\n```\r\n\r\nHowever, `phc2sys` will still need to be run with root or sudo privileges as it involves modifying system time. Running it under a normal user account could be possible if permissions to update the system clock are granted to the application.\r\n\r\n# Debug Tools\r\n\r\nWhen performing debugging, it is helpful to have the following libraries installed:\r\n\r\n```bash\r\napt install -y intel-gpu-tools edid-decode\r\n```\r\n\r\n## Debug Tool Usage\r\n\r\nWhen encountering unexpected behavior that requires additional debugging, the installed tools can assist in narrowing down the potential problem.\r\n\r\n**[intel-gpu-tools](https://cgit.freedesktop.org/xorg/app/intel-gpu-tools/)**\r\nProvides tools to read and write to registers, it must be run as the root user. Example:\r\n\r\n```console\r\n$ intel_reg dump --all \u003e /tmp/reg_dump.txt\r\n\r\n# output excerpt\r\n                    GEN6_RP_CONTROL (0x0000a024): 0x00000400\r\nGen6 disabled\r\n                      GEN6_RPNSWREQ (0x0000a008): 0x150a8000\r\n               GEN6_RP_DOWN_TIMEOUT (0x0000a010): 0x00000000\r\n           GEN6_RP_INTERRUPT_LIMITS (0x0000a014): 0x00000000\r\n               GEN6_RP_UP_THRESHOLD (0x0000a02c): 0x00000000\r\n                      GEN6_RP_UP_EI (0x0000a068): 0x00000000\r\n                    GEN6_RP_DOWN_EI (0x0000a06c): 0x00000000\r\n             GEN6_RP_IDLE_HYSTERSIS (0x0000a070): 0x00000000\r\n                      GEN6_RC_STATE (0x0000a094): 0x00040000\r\n                    GEN6_RC_CONTROL (0x0000a090): 0x00040000\r\n           GEN6_RC1_WAKE_RATE_LIMIT (0x0000a098): 0x00000000\r\n           GEN6_RC6_WAKE_RATE_LIMIT (0x0000a09c): 0x00000000\r\n        GEN6_RC_EVALUATION_INTERVAL (0x0000a0a8): 0x00000000\r\n             GEN6_RC_IDLE_HYSTERSIS (0x0000a0ac): 0x00000019\r\n                      GEN6_RC_SLEEP (0x0000a0b0): 0x00000000\r\n                GEN6_RC1e_THRESHOLD (0x0000a0b4): 0x00000000\r\n                 GEN6_RC6_THRESHOLD (0x0000a0b8): 0x00000000\r\n...\r\n\r\nintel_reg read 0x00062400\r\n                PIPE_DDI_FUNC_CTL_C (0x00062400): 0x00000000 (disabled, no port, HDMI, 8 bpc, -VSync, -HSync, EDP A ON, x1)\r\n```\r\n\r\n**[edid-decode](https://manpages.debian.org/unstable/edid-decode/edid-decode.1.en.html)**\r\nis a tool used to parse the Extended Display Identification Data (EDID) from monitors and display devices. EDID contains metadata about the display's capabilities, such as supported resolutions, manufacturer, serial number, and other attributes.\r\n\r\nTo operate edid-decode, it is typically necessary to supply a binary EDID file or data. Here's an example of how to use it:\r\n\r\n```console\r\n$ cat /sys/class/drm/card0-HDMI-A-1/edid \u003e edid.bin\r\n\r\n# run edid-decode to parse and display the information\r\nedid-decode edid.bin\r\n\r\n# sample output\r\nExtracted contents:\r\nheader:          00 ff ff ff ff ff ff 00\r\nserial number:   10 ac 64 a4 4c 30 30 32\r\nversion:         01 03\r\nbasic params:    80 34 20 78 2e\r\nchroma info:     c5 c4 a3 57 4a 9c 25 12 50 54\r\nestablished:     bf ef 80\r\nstandard:        71 4f 81 00 81 40 81 80 95 00 a9 40 b3 00 01 01\r\ndescriptor 1:    4d d0 00 a0 f8 70 3e 80 30 20 35 00 55 50 21 00 00 1a\r\ndescriptor 2:    02 3a 80 18 71 38 2d 40 58 2c 45 00 55 50 21 00 00 1e\r\ndescriptor 3:    00 00 00 ff 00 43 32 30 30 38 30 4e 50 30 0a 20 20 20\r\ndescriptor 4:    00 00 00 fd 00 32 4b 1e 53 11 00 0a 20 20 20 20 20 20\r\nextensions:      01\r\nchecksum:        1c\r\n\r\nManufacturer: DEL Model a464 Serial Number 808909324\r\nMade week 12 of 2008\r\nEDID version: 1.3\r\nDigital display\r\nMaximum image size: 52 cm x 32 cm\r\nGamma: 2.20\r\nDPMS levels: Standby Suspend Off\r\nRGB color display\r\nFirst detailed timing is preferred timing\r\nDisplay x,y Chromaticity:\r\n  Red:   0.6396, 0.3300\r\n  Green: 0.2998, 0.5996\r\n  Blue:  0.1503, 0.0595\r\n  White: 0.3134, 0.3291\r\nEstablished timings supported:\r\n  720x400@70Hz\r\n  640x480@60Hz\r\n  640x480@67Hz\r\n  640x480@72Hz\r\n  640x480@75Hz\r\n  800x600@56Hz\r\n  800x600@60Hz\r\n  ...\r\nStandard timings supported:\r\n  1152x864@75Hz\r\n  1280x800@60Hz\r\n  1280x960@60Hz\r\n  ...\r\nDetailed mode: Clock 148.500 MHz, 509 mm x 286 mm\r\n               1920 2008 2052 2200 hborder 0\r\n               1080 1084 1089\r\n```\r\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintel-retail%2Fsoftware-vsync-modulation-sample","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fintel-retail%2Fsoftware-vsync-modulation-sample","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintel-retail%2Fsoftware-vsync-modulation-sample/lists"}