From 748e7b6d7515035ce901b259d08e607bcda61517 Mon Sep 17 00:00:00 2001 From: Pawel Czarnecki Date: Mon, 11 May 2020 13:11:09 +0200 Subject: [PATCH] samples: drivers: clock control: add sample This adds a sample application for testing the LiteX clock control driver. Signed-off-by: Pawel Czarnecki Signed-off-by: Mateusz Holenko --- CODEOWNERS | 1 + .../clock_control_litex/CMakeLists.txt | 9 + .../drivers/clock_control_litex/README.rst | 131 ++++++++ samples/drivers/clock_control_litex/prj.conf | 3 + .../drivers/clock_control_litex/sample.yaml | 6 + .../drivers/clock_control_litex/src/main.c | 315 ++++++++++++++++++ 6 files changed, 465 insertions(+) create mode 100644 samples/drivers/clock_control_litex/CMakeLists.txt create mode 100644 samples/drivers/clock_control_litex/README.rst create mode 100644 samples/drivers/clock_control_litex/prj.conf create mode 100644 samples/drivers/clock_control_litex/sample.yaml create mode 100644 samples/drivers/clock_control_litex/src/main.c diff --git a/CODEOWNERS b/CODEOWNERS index b79c90c7fd1..f464a729e52 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/samples/drivers/clock_control_litex/CMakeLists.txt b/samples/drivers/clock_control_litex/CMakeLists.txt new file mode 100644 index 00000000000..0fadbaa15f8 --- /dev/null +++ b/samples/drivers/clock_control_litex/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2020 Antmicro +# 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}) diff --git a/samples/drivers/clock_control_litex/README.rst b/samples/drivers/clock_control_litex/README.rst new file mode 100644 index 00000000000..1b186ff1756 --- /dev/null +++ b/samples/drivers/clock_control_litex/README.rst @@ -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 ` 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] CLK_CTRL_LITEX: CLKOUT0: set rate: 100000000 HZ + [00:00:00.240,000] CLK_CTRL_LITEX: CLKOUT1: updated rate: 100000000 to 100000000 HZ + [00:00:00.280,000] CLK_CTRL_LITEX: CLKOUT0: set duty: 50% + [00:00:00.320,000] CLK_CTRL_LITEX: CLKOUT0: set phase: 0 deg + [00:00:00.360,000] CLK_CTRL_LITEX: CLKOUT1: set rate: 100000000 HZ + [00:00:00.400,000] CLK_CTRL_LITEX: CLKOUT1: set duty: 50% + [00:00:00.440,000] CLK_CTRL_LITEX: CLKOUT1: set phase: 0 deg + [00:00:00.440,000] 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] CLK_CTRL_LITEX: CLKOUT0: set rate: 15000000 HZ + [00:00:00.550,000] CLK_CTRL_LITEX: CLKOUT0: set phase: 90 deg + [00:00:00.590,000] CLK_CTRL_LITEX: CLKOUT0: set duty: 25% + [00:00:00.630,000] CLK_CTRL_LITEX: CLKOUT1: set rate: 15000000 HZ + [00:00:00.670,000] 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` diff --git a/samples/drivers/clock_control_litex/prj.conf b/samples/drivers/clock_control_litex/prj.conf new file mode 100644 index 00000000000..f6f7e52c517 --- /dev/null +++ b/samples/drivers/clock_control_litex/prj.conf @@ -0,0 +1,3 @@ +CONFIG_CLOCK_CONTROL=y +CONFIG_CLOCK_CONTROL_LITEX=y +CONFIG_HEAP_MEM_POOL_SIZE=4096 diff --git a/samples/drivers/clock_control_litex/sample.yaml b/samples/drivers/clock_control_litex/sample.yaml new file mode 100644 index 00000000000..ad8f8db7933 --- /dev/null +++ b/samples/drivers/clock_control_litex/sample.yaml @@ -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 diff --git a/samples/drivers/clock_control_litex/src/main.c b/samples/drivers/clock_control_litex/src/main.c new file mode 100644 index 00000000000..a10b3ab686a --- /dev/null +++ b/samples/drivers/clock_control_litex/src/main.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2020 Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +/* 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); +}