LiteX Clock Control Driver Sample
Introduction
This sample is providing an overview of LiteX clock control driver capabilities. The driver uses Mixed Mode Clock Manager (MMCM) module to generate up to 7 clocks with defined phase, frequency and duty cycle.
Requirements
LiteX-capable FPGA platform with MMCM modules (for example Digilent Arty A7 development board)
SoC configuration with VexRiscv soft CPU and Xilinx 7-series MMCM interface in LiteX (S7MMCM module)
Optional: clock output signals redirected to output pins for testing
Configuration
Basic configuration of the driver, including default settings for clock outputs, is held in Device Tree clock control nodes.
clk0: clock-controller@0 {
#clock-cells = <1>;
reg = <0>;
compatible = "litex,clkout";
clock-output-names = "CLK_0";
litex,clock-frequency = <11289600>;
litex,clock-phase = <0>;
litex,clock-duty-num = <1>;
litex,clock-duty-den = <2>;
litex,clock-margin = <1>;
litex,clock-margin-exp = <2>;
status = "disabled";
};
clk1: clock-controller@1 {
#clock-cells = <1>;
reg = <1>;
compatible = "litex,clkout";
clock-output-names = "CLK_1";
litex,clock-frequency = <22579200>;
litex,clock-phase = <0>;
litex,clock-duty-num = <1>;
litex,clock-duty-den = <2>;
litex,clock-margin = <1>;
litex,clock-margin-exp = <2>;
status = "disabled";
};
clock0: clock@e0004800 {
compatible = "litex,clk";
reg = <0xe0004800 0x4
0xe0004804 0x4
0xe0004808 0x4
0xe000480c 0x4
0xe0004810 0x4
0xe0004814 0x4
0xe0004818 0x4
0xe000481c 0x4>;
reg-names = "drp_reset",
"drp_locked",
"drp_read",
"drp_write",
"drp_drdy",
"drp_adr",
"drp_dat_w",
"drp_dat_r";
#clock-cells = <1>;
clocks = <&clk0 0>, <&clk1 1>;
clock-output-names = "CLK_0", "CLK_1";
litex,lock-timeout = <10>;
litex,drdy-timeout = <10>;
litex,sys-clock-frequency = <100000000>;
litex,divclk-divide-min = <1>;
litex,divclk-divide-max = <107>;
litex,clkfbout-mult-min = <2>;
litex,clkfbout-mult-max = <65>;
litex,vco-freq-min = <600000000>;
litex,vco-freq-max = <1200000000>;
litex,clkout-divide-min = <1>;
litex,clkout-divide-max = <126>;
litex,vco-margin = <0>;
status = "disabled";
};
This configuration defines 2 clock outputs: clk0
and clk1
with default frequency set to 100MHz, 0 degrees phase offset and 50% duty cycle. Special care should be taken when defining values for FPGA-specific configuration (parameters from litex,divclk-divide-min
to litex,vco-margin
).
Important note: reg
properties in clk0
and clk1
nodes reference the clock output number (clkout_nr
)
Driver Usage
The driver is interfaced with the Clock Control API function clock_control_on()
and a LiteX driver specific structure:
-
struct litex_clk_setup
Structure for interfacing with clock control API.
- Param clkout_nr:
Number of clock output to be changed
- Param rate:
Frequency to set given in Hz
- Param phase:
Phase offset in degrees
- Param duty:
Duty cycle of clock signal in percent
litex_clk_setup
onto clock_control_subsys_t
and use it with clock_control_on()
.clk0
frequency 50MHz, 90 degrees of phase offset and 75% duty cycle.struct device *dev;
int ret;
struct litex_clk_setup setup = {
.clkout_nr = 0,
.rate = 50000000,
.duty = 75,
.phase = 90
};
dev = DEVICE_DT_GET(MMCM);
clock_control_subsys_t sub_system = (clock_control_subsys_t)&setup;
if ((ret = clock_control_on(dev, sub_system)) != 0) {
LOG_ERR("Set CLKOUT%d param error!", setup.clkout_nr);
return ret;
}
Clock output status (frequency, duty and phase offset) can be acquired with function clock_control_get_status()
and clock output frequency only can be queried with clock_control_get_rate()
In both getter functions, basic usage is similar to clock_control_on()
. Structure litex_clk_setup
is used to set clkout_nr
of clock output from which data is to be acquired.
Sample usage
This example provides a simple way of checking various clock output settings. User can pick one of 4 possible scenarios:
Frequency range,
Duty cycle range,
Phase range,
Setting frequency, duty and phase at once, then check clock status and rate,
Scenarios are selected by defining LITEX_CLK_TEST
as one of:
LITEX_TEST_FREQUENCY
LITEX_TEST_DUTY
LITEX_TEST_PHASE
LITEX_TEST_SINGLE
Code is performed on 2 clock outputs with clkout_nr
defined in LITEX_CLK_TEST_CLK1
and LITEX_CLK_TEST_CLK2
. Tests are controlled by separate defines for each scenario.
Building
west build -b litex_vexriscv zephyr/samples/drivers/clock_control
Drivers prints a lot of useful debugging information to the log. It is highly recommended to enable logging and synchronous processing of log messages and set log level to Info
.
Sample output
[00:00:00.200,000] <inf> CLK_CTRL_LITEX: CLKOUT0: set rate: 100000000 HZ
[00:00:00.240,000] <inf> CLK_CTRL_LITEX: CLKOUT1: updated rate: 100000000 to 100000000 HZ
[00:00:00.280,000] <inf> CLK_CTRL_LITEX: CLKOUT0: set duty: 50%
[00:00:00.320,000] <inf> CLK_CTRL_LITEX: CLKOUT0: set phase: 0 deg
[00:00:00.360,000] <inf> CLK_CTRL_LITEX: CLKOUT1: set rate: 100000000 HZ
[00:00:00.400,000] <inf> CLK_CTRL_LITEX: CLKOUT1: set duty: 50%
[00:00:00.440,000] <inf> CLK_CTRL_LITEX: CLKOUT1: set phase: 0 deg
[00:00:00.440,000] <inf> CLK_CTRL_LITEX: LiteX Clock Control driver initialized
*** Booting Zephyr OS build zephyr-v2.2.0-2810-g1ca5dda196c3 ***
Clock Control Example! riscv32
device name: clock0
clock control device is 0x40013460, name is clock0
Clock test
Single test
[00:00:00.510,000] <inf> CLK_CTRL_LITEX: CLKOUT0: set rate: 15000000 HZ
[00:00:00.550,000] <inf> CLK_CTRL_LITEX: CLKOUT0: set phase: 90 deg
[00:00:00.590,000] <inf> CLK_CTRL_LITEX: CLKOUT0: set duty: 25%
[00:00:00.630,000] <inf> CLK_CTRL_LITEX: CLKOUT1: set rate: 15000000 HZ
[00:00:00.670,000] <inf> CLK_CTRL_LITEX: CLKOUT1: set duty: 75%
Getters test
CLKOUT0: get_status: rate:15000000 phase:90 duty:25
CLKOUT0: get_rate:15000000
CLKOUT1: get_status: rate:15000000 phase:0 duty:75
CLKOUT1: get_rate:15000000
Clock test done returning: 0