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 = <100000000>;
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 = <100000000>;
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@82005000 {
compatible = "litex,clk";
label = "clock0";
reg = <0x82005000 0x1
0x82005004 0x1
0x82005008 0x1
0x8200500c 0x1
0x82005010 0x1
0x82005014 0x1
0x82005018 0x2
0x82005020 0x2>;
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