samples: drivers: clock control: add sample

This adds a sample application for testing
the LiteX clock control driver.

Signed-off-by: Pawel Czarnecki <pczarnecki@internships.antmicro.com>
Signed-off-by: Mateusz Holenko <mholenko@antmicro.com>
This commit is contained in:
Pawel Czarnecki 2020-05-11 13:11:09 +02:00 committed by Anas Nashif
parent 967d336dae
commit 748e7b6d75
6 changed files with 465 additions and 0 deletions

View file

@ -450,6 +450,7 @@
/samples/boards/intel_s1000_crb/ @sathishkuttan @dcpleung @nashif
/samples/display/ @vanwinkeljan
/samples/drivers/can/ @alexanderwachter
/samples/drivers/clock_control_litex/ @mateusz-holenko @kgugala @pgielda
/samples/drivers/display/ @vanwinkeljan
/samples/drivers/ht16k33/ @henrikbrixandersen
/samples/drivers/lora/ @Mani-Sadhasivam

View file

@ -0,0 +1,9 @@
# Copyright (c) 2020 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(clock_control_litex)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View file

@ -0,0 +1,131 @@
.. _clock_control_litex_sample:
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.
.. literalinclude:: ../../../dts/riscv/riscv32-litex-vexriscv.dtsi
:start-at: clk0: clock-controller@0 {
:end-at: };
.. literalinclude:: ../../../dts/riscv/riscv32-litex-vexriscv.dtsi
:start-at: clk1: clock-controller@1 {
:end-at: };
.. literalinclude:: ../../../dts/riscv/riscv32-litex-vexriscv.dtsi
:start-at: clock0: clock@82005000 {
:end-at: };
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 :ref:`Clock Control API <clock_control_api>` function ``clock_control_on()`` and a LiteX driver specific structure:
.. doxygenstruct:: litex_clk_setup
:project: Zephyr
| To change clock parameter it is needed to cast a pointer to structure ``litex_clk_setup`` onto ``clock_control_subsys_t`` and use it with ``clock_control_on()``.
| This code will try to set on ``clk0`` frequency 50MHz, 90 degrees of phase offset and 75% duty cycle.
.. code-block:: c
struct device *dev;
int ret;
struct litex_clk_setup setup = {
.clkout_nr = 0,
.rate = 50000000,
.duty = 75,
.phase = 90
};
dev = device_get_binding("clock0");
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
********
.. code-block:: none
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
*************
.. code-block:: none
[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
References
**********
- :ref:`litex-vexriscv`

View file

@ -0,0 +1,3 @@
CONFIG_CLOCK_CONTROL=y
CONFIG_CLOCK_CONTROL_LITEX=y
CONFIG_HEAP_MEM_POOL_SIZE=4096

View file

@ -0,0 +1,6 @@
sample:
name: Sample for the clock control driver
tests:
sample.driver.clock_control_litex:
platform_allow: litex_vexriscv
tags: clock litex vexriscv mmcm

View file

@ -0,0 +1,315 @@
/*
* Copyright (c) 2020 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <stdio.h>
#include <devicetree.h>
#include <drivers/clock_control.h>
#include <drivers/clock_control/clock_control_litex.h>
/* Test defines */
/* Select clock outputs for tests [0-6] */
#define LITEX_CLK_TEST_CLK1 0
#define LITEX_CLK_TEST_CLK2 1
/* Values for frequency test */
#define LITEX_TEST_FREQUENCY_DUTY_VAL 50 /* [%] */
#define LITEX_TEST_FREQUENCY_PHASE_VAL 0 /* [deg] */
#define LITEX_TEST_FREQUENCY_DELAY_MS 1000 /* [ms] */
#define LITEX_TEST_FREQUENCY_MIN 5000000 /* [Hz] */
#define LITEX_TEST_FREQUENCY_MAX 1200000000 /* [Hz] */
#define LITEX_TEST_FREQUENCY_STEP 1000000 /* [Hz] */
/* Values for duty test */
#define LITEX_TEST_DUTY_FREQ_VAL 20000000 /* [Hz] */
#define LITEX_TEST_DUTY_PHASE_VAL 0 /* [deg] */
#define LITEX_TEST_DUTY_DELAY_MS 250 /* [ms] */
#define LITEX_TEST_DUTY_MIN 0 /* [%] */
#define LITEX_TEST_DUTY_MAX 100 /* [%] */
#define LITEX_TEST_DUTY_STEP 1 /* [%] */
/* Values for phase test */
#define LITEX_TEST_PHASE_FREQ_VAL 25000000 /* [Hz] */
#define LITEX_TEST_PHASE_DUTY_VAL 50 /* [%] */
#define LITEX_TEST_PHASE_DELAY_MS 50 /* [ms] */
#define LITEX_TEST_PHASE_MIN 0 /* [deg] */
#define LITEX_TEST_PHASE_MAX 360 /* [deg] */
#define LITEX_TEST_PHASE_STEP 1 /* [deg] */
/* Values for single parameters test */
#define LITEX_TEST_SINGLE_FREQ_VAL 15000000 /* [Hz] */
#define LITEX_TEST_SINGLE_DUTY_VAL 25 /* [%] */
#define LITEX_TEST_SINGLE_PHASE_VAL 90 /* [deg] */
#define LITEX_TEST_SINGLE_FREQ_VAL2 15000000 /* [Hz] */
#define LITEX_TEST_SINGLE_DUTY_VAL2 75 /* [%] */
#define LITEX_TEST_SINGLE_PHASE_VAL2 0 /* [deg] */
/* loop tests infinitely if true, otherwise do one loop */
#define LITEX_TEST_LOOP 0
/* Choose test type */
#define LITEX_TEST 4
#define LITEX_TEST_FREQUENCY 1
#define LITEX_TEST_DUTY 2
#define LITEX_TEST_PHASE 3
#define LITEX_TEST_SINGLE 4
#define LITEX_TEST_DUTY_DEN 100
/* LiteX Common Clock Driver tests */
int litex_clk_test_getters(const struct device *dev)
{
struct litex_clk_setup setup;
uint32_t rate;
int i;
clock_control_subsys_t sub_system = (clock_control_subsys_t *)&setup;
printf("Getters test\n");
for (i = 0; i < NCLKOUT; i++) {
setup.clkout_nr = i;
clock_control_get_status(dev, sub_system);
printf("CLKOUT%d: get_status: rate:%d phase:%d duty:%d\n",
i, setup.rate, setup.phase, setup.duty);
clock_control_get_rate(dev, sub_system, &rate);
printf("CLKOUT%d: get_rate:%d\n", i, rate);
}
return 0;
}
int litex_clk_test_single(const struct device *dev)
{
struct litex_clk_setup setup1 = {
.clkout_nr = LITEX_CLK_TEST_CLK1,
.rate = LITEX_TEST_SINGLE_FREQ_VAL,
.duty = LITEX_TEST_SINGLE_DUTY_VAL,
.phase = LITEX_TEST_SINGLE_PHASE_VAL
};
struct litex_clk_setup setup2 = {
.clkout_nr = LITEX_CLK_TEST_CLK2,
.rate = LITEX_TEST_SINGLE_FREQ_VAL2,
.duty = LITEX_TEST_SINGLE_DUTY_VAL2,
.phase = LITEX_TEST_SINGLE_PHASE_VAL2,
};
uint32_t ret = 0;
clock_control_subsys_t sub_system1 = (clock_control_subsys_t *)&setup1;
clock_control_subsys_t sub_system2 = (clock_control_subsys_t *)&setup2;
printf("Single test\n");
ret = clock_control_on(dev, sub_system1);
if (ret != 0)
return ret;
ret = clock_control_on(dev, sub_system2);
if (ret != 0)
return ret;
litex_clk_test_getters(dev);
return 0;
}
int litex_clk_test_freq(const struct device *dev)
{
struct litex_clk_setup setup = {
.clkout_nr = LITEX_CLK_TEST_CLK1,
.duty = LITEX_TEST_FREQUENCY_DUTY_VAL,
.phase = LITEX_TEST_FREQUENCY_PHASE_VAL
};
clock_control_subsys_t sub_system = (clock_control_subsys_t *)&setup;
uint32_t i, ret = 0;
printf("Frequency test\n");
do {
for (i = LITEX_TEST_FREQUENCY_MIN; i < LITEX_TEST_FREQUENCY_MAX;
i += LITEX_TEST_FREQUENCY_STEP) {
setup.clkout_nr = LITEX_CLK_TEST_CLK1;
setup.rate = i;
sub_system = (clock_control_subsys_t *)&setup;
/*
* Don't check for ENOTSUP here because it is expected.
* The reason is that set of possible frequencies for
* specific clock output depends on devicetree config
* (including margin) and also on other active clock
* outputs configuration. Some configurations may cause
* errors when the driver is trying to update one of
* the clkouts.
* Ignoring these errors ensure that
* test will be finished
*
*/
ret = clock_control_on(dev, sub_system);
if (ret != 0 && ret != -ENOTSUP)
return ret;
setup.clkout_nr = LITEX_CLK_TEST_CLK2;
ret = clock_control_on(dev, sub_system);
if (ret != 0)
return ret;
k_sleep(K_MSEC(LITEX_TEST_FREQUENCY_DELAY_MS));
}
for (i = LITEX_TEST_FREQUENCY_MAX; i > LITEX_TEST_FREQUENCY_MIN;
i -= LITEX_TEST_FREQUENCY_STEP) {
setup.clkout_nr = LITEX_CLK_TEST_CLK1;
setup.rate = i;
sub_system = (clock_control_subsys_t *)&setup;
ret = clock_control_on(dev, sub_system);
if (ret != 0 && ret != -ENOTSUP)
return ret;
setup.clkout_nr = LITEX_CLK_TEST_CLK2;
ret = clock_control_on(dev, sub_system);
if (ret != 0)
return ret;
k_sleep(K_MSEC(LITEX_TEST_FREQUENCY_DELAY_MS));
}
} while (LITEX_TEST_LOOP);
return 0;
}
int litex_clk_test_phase(const struct device *dev)
{
struct litex_clk_setup setup1 = {
.clkout_nr = LITEX_CLK_TEST_CLK1,
.rate = LITEX_TEST_PHASE_FREQ_VAL,
.duty = LITEX_TEST_PHASE_DUTY_VAL,
.phase = 0
};
struct litex_clk_setup setup2 = {
.clkout_nr = LITEX_CLK_TEST_CLK2,
.rate = LITEX_TEST_PHASE_FREQ_VAL,
.duty = LITEX_TEST_PHASE_DUTY_VAL
};
clock_control_subsys_t sub_system1 = (clock_control_subsys_t *)&setup1;
clock_control_subsys_t sub_system2 = (clock_control_subsys_t *)&setup2;
uint32_t ret = 0;
int i;
printf("Phase test\n");
ret = clock_control_on(dev, sub_system1);
if (ret != 0 && ret != -ENOTSUP)
return ret;
do {
for (i = LITEX_TEST_PHASE_MIN; i <= LITEX_TEST_PHASE_MAX;
i += LITEX_TEST_PHASE_STEP) {
setup2.phase = i;
sub_system2 = (clock_control_subsys_t *)&setup2;
ret = clock_control_on(dev, sub_system2);
if (ret != 0)
return ret;
k_sleep(K_MSEC(LITEX_TEST_PHASE_DELAY_MS));
}
} while (LITEX_TEST_LOOP);
return 0;
}
int litex_clk_test_duty(const struct device *dev)
{
struct litex_clk_setup setup1 = {
.clkout_nr = LITEX_CLK_TEST_CLK1,
.rate = LITEX_TEST_DUTY_FREQ_VAL,
.phase = LITEX_TEST_DUTY_PHASE_VAL,
.duty = 0
};
struct litex_clk_setup setup2 = {
.clkout_nr = LITEX_CLK_TEST_CLK2,
.rate = LITEX_TEST_DUTY_FREQ_VAL,
.phase = LITEX_TEST_DUTY_PHASE_VAL,
.duty = 0
};
uint32_t ret = 0, i;
clock_control_subsys_t sub_system1 = (clock_control_subsys_t *)&setup1;
clock_control_subsys_t sub_system2 = (clock_control_subsys_t *)&setup2;
ret = clock_control_on(dev, sub_system1);
if (ret != 0 && ret != -ENOTSUP)
return ret;
ret = clock_control_on(dev, sub_system2);
if (ret != 0 && ret != -ENOTSUP)
return ret;
printf("Duty test\n");
do {
for (i = LITEX_TEST_DUTY_MIN; i <= LITEX_TEST_DUTY_MAX;
i += LITEX_TEST_DUTY_STEP) {
setup1.duty = i;
sub_system1 = (clock_control_subsys_t *)&setup1;
ret = clock_control_on(dev, sub_system1);
if (ret != 0)
return ret;
setup2.duty = 100 - i;
sub_system2 = (clock_control_subsys_t *)&setup2;
ret = clock_control_on(dev, sub_system2);
if (ret != 0)
return ret;
k_sleep(K_MSEC(LITEX_TEST_DUTY_DELAY_MS));
}
for (i = LITEX_TEST_DUTY_MAX; i > LITEX_TEST_DUTY_MIN;
i -= LITEX_TEST_DUTY_STEP) {
setup1.duty = i;
sub_system1 = (clock_control_subsys_t *)&setup1;
ret = clock_control_on(dev, sub_system1);
if (ret != 0)
return ret;
setup2.duty = 100 - i;
sub_system2 = (clock_control_subsys_t *)&setup2;
ret = clock_control_on(dev, sub_system2);
if (ret != 0)
return ret;
k_sleep(K_MSEC(LITEX_TEST_DUTY_DELAY_MS));
}
} while (LITEX_TEST_LOOP);
return 0;
}
int litex_clk_test(const struct device *dev)
{
int ret;
printf("Clock test\n");
switch (LITEX_TEST) {
case LITEX_TEST_DUTY:
ret = litex_clk_test_duty(dev);
break;
case LITEX_TEST_PHASE:
ret = litex_clk_test_phase(dev);
break;
case LITEX_TEST_FREQUENCY:
ret = litex_clk_test_freq(dev);
break;
case LITEX_TEST_SINGLE:
default:
ret = litex_clk_test_single(dev);
}
printf("Clock test done returning: %d\n", ret);
return ret;
}
void main(void)
{
const struct device *dev;
printf("Clock Control Example! %s\n", CONFIG_ARCH);
printf("device name: %s\n", MMCM_NAME);
dev = device_get_binding(MMCM_NAME);
if (!dev) {
printf("error: no %s device\n", MMCM_NAME);
return;
}
printf("clock control device is %p, name is %s\n",
dev, dev->name);
litex_clk_test(dev);
}