From patchwork Tue Feb 25 13:14:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Damien Hedde X-Patchwork-Id: 11403915 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7E47B13A4 for ; Tue, 25 Feb 2020 14:03:15 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id DAA1A222C2 for ; Tue, 25 Feb 2020 14:03:14 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=greensocs.com header.i=@greensocs.com header.b="f1EsQ/Sw" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org DAA1A222C2 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=greensocs.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:56948 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1j6anp-0005jc-Pl for patchwork-qemu-devel@patchwork.kernel.org; Tue, 25 Feb 2020 09:03:13 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:37559) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1j6a2w-0008I1-Qf for qemu-devel@nongnu.org; Tue, 25 Feb 2020 08:14:50 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1j6a2s-0002BT-SZ for qemu-devel@nongnu.org; Tue, 25 Feb 2020 08:14:46 -0500 Received: from beetle.greensocs.com ([5.135.226.135]:57256) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1j6a2m-00022u-MM; Tue, 25 Feb 2020 08:14:37 -0500 Received: from crumble.bar.greensocs.com (crumble.bar.greensocs.com [172.16.11.102]) by beetle.greensocs.com (Postfix) with ESMTPS id 611BC96F53; Tue, 25 Feb 2020 13:14:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=greensocs.com; s=mail; t=1582636468; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HBdezKQlElS6Poj9kARTllYO42WnV/QmAowvM6zuyh0=; b=f1EsQ/Sw1Q5k5Vz8r+0biIdVRmitHVZIN13xSaAluYwbGe0GmaQEaQRw74U6cGOdK3okIe vnWlSicumkwnZ7voqYWlcht24N7a8Om6YX487UEtcRShrT8Wikop4LSS6oJPspwKJm0gDM ltLYIIGeF+oORJlBIJRazZJTzJX2Als= From: Damien Hedde To: qemu-devel@nongnu.org Subject: [PATCH v8 5/9] docs/clocks: add device's clock documentation Date: Tue, 25 Feb 2020 14:14:18 +0100 Message-Id: <20200225131422.53368-6-damien.hedde@greensocs.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200225131422.53368-1-damien.hedde@greensocs.com> References: <20200225131422.53368-1-damien.hedde@greensocs.com> MIME-Version: 1.0 ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=greensocs.com; s=mail; t=1582636468; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HBdezKQlElS6Poj9kARTllYO42WnV/QmAowvM6zuyh0=; b=xi2I88jl09uSfCl8QyHnxIDOqdR+bQos6d3zlpZLwX4w2um+VNJsdTHrxOVZmCZrqGqCnf oJrTQesf9zgp9U1XaIq93mbBQomNsK8HXxVpaQ2D7ZHQFXEr4bWAbt9zSWes/yLKywwtU9 OfSFv/KDd/y8Okey3IbLQ2TeAi4MUnI= ARC-Seal: i=1; s=mail; d=greensocs.com; t=1582636468; a=rsa-sha256; cv=none; b=TiJXcmxHuiayB9MB5BiiKo9+xLWeHstZWGAm2Yj2mLC78AjHxlUYGhOBZeAe8vMFxQkhk+ N+4Keokc6UcEn6/yN34eLDEHbqGWgG3SAJrV8NDnbd1ZzZ8Delbz7Tb7I1alsBDty6PE23 rE1KrfwPgRqmW6MBHK1BJsLejmaI+EU= ARC-Authentication-Results: i=1; beetle.greensocs.com; none X-Spam: Yes X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 5.135.226.135 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Damien Hedde , peter.maydell@linaro.org, berrange@redhat.com, ehabkost@redhat.com, pbonzini@redhat.com, alistair@alistair23.me, mark.burton@greensocs.com, qemu-arm@nongnu.org, marcandre.lureau@redhat.com, edgar.iglesias@gmail.com, philmd@redhat.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" Add the documentation about the clock inputs and outputs in devices. This is based on the original work of Frederic Konrad. Signed-off-by: Damien Hedde Reviewed-by: Alistair Francis --- v8: + fix list indentation + reduce title size v7: + update ClockIn/Out types + switch to rst format --- docs/devel/clocks.rst | 360 ++++++++++++++++++++++++++++++++++++++++++ docs/devel/index.rst | 1 + 2 files changed, 361 insertions(+) create mode 100644 docs/devel/clocks.rst diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst new file mode 100644 index 0000000000..d3c0146bd2 --- /dev/null +++ b/docs/devel/clocks.rst @@ -0,0 +1,360 @@ +Modeling a clock tree in QEMU +============================= + +What are clocks +--------------- + +Clocks are QOM objects developed for the purpose of modeling the +distribution of clocks in QEMU. + +They allow us to model the clock distribution of a platform and detect +configuration errors in the clock tree such as badly configured PLL, clock +source selection or disabled clock. + +The object is *Clock* and its QOM name is ``CLOCK``. + +Clocks are typically used with devices where they are used to model inputs +and outputs. They are created in a similar way as gpios. Inputs and outputs +of different devices can be connect together. + +In these cases a Clock object is a child of a Device object but this is not +a requirement. Clocks can be independent of devices. For example it is possible +to create a clock outside of any device to model the main clock source of a +machine. + +Here is an example of clocks:: + + +---------+ +----------------------+ +--------------+ + | Clock 1 | | Device B | | Device C | + | | | +-------+ +-------+ | | +-------+ | + | |>>-+-->>|Clock 2| |Clock 3|>>--->>|Clock 6| | + +---------+ | | | (in) | | (out) | | | | (in) | | + | | +-------+ +-------+ | | +-------+ | + | | +-------+ | +--------------+ + | | |Clock 4|>> + | | | (out) | | +--------------+ + | | +-------+ | | Device D | + | | +-------+ | | +-------+ | + | | |Clock 5|>>--->>|Clock 7| | + | | | (out) | | | | (in) | | + | | +-------+ | | +-------+ | + | +----------------------+ | | + | | +-------+ | + +----------------------------->>|Clock 8| | + | | (in) | | + | +-------+ | + +--------------+ + +Clocks are defined in include/hw/clock.h header and device related functions +are defined in include/hw/qdev-clock.h header. + +The clock state +--------------- + +The state of a clock is its period; it is stored as an integer representing +it in 2^-32ns unit. The special value of 0 is used to represent the clock being +inactive or gated. The clocks do not model the signal itself (pin toggling) +or other properties such as the duty cycle. + +All clocks contain this state: outputs as well as inputs. It allows to fetch +the current period of a clock at any time. When a clock is updated, the +value is immediately propagated to all connected clocks in the tree. + +To ease interaction with clocks. Helpers with a unit suffix are defined for +every clock state setter or getter. They are: + +- ``_ns`` for handling periods in nanosecond, +- ``_hz`` for handling frequencies in hertz. + +The 0 period value is converted to 0 in hertz and vice versa. 0 always means +that the clock is disabled. + +Adding a new a clock +-------------------- + +Adding clocks to a device must be done during the init method of the Device +instance. + +To add an input clock to a device, the function qdev_init_clock_in must be used. +It takes the name, a callback and an opaque parameter for the callback (this will +be explained in a following section below). +Output is more simple, only the name is required. Typically:: + + qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev); + qdev_init_clock_out(DEVICE(dev), "clk_out"); + +Both functions return the created Clock pointer, which should be saved in the +device's state structure for further use. + +These objects will be automatically deleted by the QOM reference mechanism. + +Note that it is possible to create a static array describing clock inputs and +outputs. The function ``qdev_init_clocks()`` must be called with the array as +parameter to initialize the clocks: it has the same behaviour as calling the +``qdev_init_clock_in/out()`` for each clock in the array. To ease the array +construction, some macros are defined in include/hw/qdev-clock.h. +As an example, the following creates 2 clocks to a device: one input and one +output. + +:: + + /* device structure containing pointer to the clock objects */ + typedef struct MyDeviceState { + DeviceState parent_obj; + Clock *clk_in; + Clock *clk_out; + } MyDeviceState; + + /* + * callback for the input clock (see "Callback on input clock + * change" section below for more information). + */ + static void clk_in_callback(void *opaque); + + /* + * static array describing clocks: + * + a clock input named "clk_in", whose pointer is stored in + * clk_in field of a MyDeviceState structure with callback + * clk_in_callback. + * + a clock output named "clk_out" whose pointer is stored in + * clk_out field of a MyDeviceState structure. + */ + static const ClockPortInitArray mydev_clocks = { + QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback), + QDEV_CLOCK_OUT(MyDeviceState, clk_out), + QDEV_CLOCK_END + }; + + /* device initialization function */ + static void mydev_init(Object *obj) + { + /* cast to MyDeviceState */ + MyDeviceState *mydev = MYDEVICE(obj); + /* create and fill the pointer fields in the MyDeviceState */ + qdev_init_clocks(mydev, mydev_clocks); + [...] + } + +An alternative way to create a clock is to simply call +``object_new(TYPE_CLOCK)``. In that case the clock will neither be an input nor +an output of a device. After the whole QOM hieracrhy of the clock has been set +``clock_setup_canonical_path()`` should be called. + +At creation, the period of the clock is 0: the clock is disabled. You can +change it using ``clock_set[_ns|_hz]()``. + +Note that if you are creating a clock with a fixed period which will never +change (for example the main clock source of a board), then you'll have +nothing else to do. This value will be propagated to other clocks when +connecting the clocks together and devices will fetch the right value during +the first reset. + +Retrieving clocks from a device +------------------------------- + +``qdev_get_clock_in()`` and ``dev_get_clock_out()`` are available to get the clock inputs or outputs of a device. For example:: + + Clock *clk = qdev_get_clock_in(DEVICE(mydev), "clk_in"); + +or:: + + Clock *clk = qdev_get_clock_out(DEVICE(mydev), "clk_out"); + +Connecting two clocks together +------------------------------ + +To connect two clocks together, use the ``clock_set_source()`` function. +Given two clocks ``clk1``, and ``clk2``, ``clock_set_source(clk2, clk1);`` +configure ``clk2`` to follow the ``clk1`` period changes. Every time ``clk1`` +is updated, ``clk2`` will be updated too. + +When connecting clock between devices, prefer using the +``qdev_connect_clock_in()`` function set the source of an input device clock. +For example, to connect the input clock ``clk2`` of ``devB`` to the output +clock ``clk1`` of ``devA``, do:: + + qdev_connect_clock_in(devB, "clk2", qdev_get_clock_out(devA, "clk1")) + +We used ``qdev_get_clock_out()`` above, but any clock can drive an input clock, +even another input clock. The following diagram shows some +examples of connections. Note also that a clock can drive several other clocks. + +:: + + +------------+ +--------------------------------------------------+ + | Device A | | Device B | + | | | +---------------------+ | + | | | | Device C | | + | +-------+ | | +-------+ | +-------+ +-------+ | +-------+ | + | |Clock 1|>>-->>|Clock 2|>>+-->>|Clock 3| |Clock 5|>>>>|Clock 6|>> + | | (out) | | | | (in) | | | | (in) | | (out) | | | (out) | | + | +-------+ | | +-------+ | | +-------+ +-------+ | +-------+ | + +------------+ | | +---------------------+ | + | | | + | | +--------------+ | + | | | Device D | | + | | | +-------+ | | + | +-->>|Clock 4| | | + | | | (in) | | | + | | +-------+ | | + | +--------------+ | + +--------------------------------------------------+ + +In the above example, when *Clock 1* is updated by *Device A*, three clocks gets the new clock period value: *Clock 2*, Clock 3* and *Clock 4*. + +It is not possible to disconnect a clock or to change the clock connection +after it is done. + +Unconnected input clocks +------------------------ + +A newly created input clock is disabled (period of 0). It means the clock will +be considered as disabled until the period is updated. If the clock remains +unconnected it will always keep its initial value of 0. If this is not the +wanted behaviour, ``clock_set()``, ``clock_set_ns()`` or ``clock_set_hz()`` +should be called on the Clock object during device instance init. For example:: + + clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback, + dev); + /* set initial value to 10ns / 100MHz */ + clock_set_ns(clk, 10); + +Fetching clock frequency/period +------------------------------- + +To get the current state of a clock, the function ``clock_get()``, +``clock_get_ns()`` or ``clock_get_hz()`` must be used. + +It is also possible to register a callback on clock frequency changes. +Here is an example:: + + void clock_callback(void *opaque) { + MyDeviceState *s = (MyDeviceState *) opaque; + /* + * opaque may not be the device state pointer, but most + * probably it is. (It depends on what is given to the + * qdev_init_clock_in function) + */ + + /* do something with the new period */ + fprintf(stdout, "device new period is %" PRIu64 "ns\n", + clock_get_ns(dev->my_clk_input)); + } + +Changing a clock period +----------------------- + +A device can change its outputs using the ``clock_update()``, +``clock_update_ns()`` or ``clock_update_hz()`` function. It will trigger +updates on every connected input. + +For example, let's say that we have an output clock *clkout* and we have a +pointer to it in the device state because we did the following in init phase:: + + dev->clkout = qdev_init_clock_out(DEVICE(dev), "clkout"); + +Then at any time (apart from the cases listed below), it is possible to +change the clock value by doing:: + + clock_update_hz(dev->clkout, 1000 * 1000 * 1000); /* 1Ghz */ + +Because updating a clock may trigger any side effects through connected clocks +and their callbacks, this operation must be done while holding the qemu io lock. + +For the same reason, one can updates clocks only when it is allowed to have +side effects on other objects. In consequence, it is forbidden: ++ during migration, ++ and in the enter phase of reset. + +Note that calling ``clock_update[_ns|_hz]()`` is equivalent to call +``clock_set[_ns|_hz]()`` (with the same arguments) then ``clock_propagate()`` on +the clock. Thus, setting the clock value can separated from triggering the +side-effects. This is often required to factorize code to handle reset and +migration in devices. + +Aliasing clocks +--------------- + +Sometimes, one needs to forward, or inherit, a clock from another device. +Typically, when doing device composition, a device might expose a sub-device's +clock without interfering with it. +The function ``qdev_alias_clock()`` can be used to achieve this behaviour. Note +that it is possible to expose the clock under a different name. This works for +both inputs and outputs. + +For example, if device B is a child of device A, ``device_a_instance_init()`` +may do something like this:: + + void device_a_instance_init(Object *obj) + { + AState *A = DEVICE_A(obj); + BState *B; + /* create B object as child of A */ + [...] + qdev_alias_clock(B, "clk", A, "b_clk"); + /* + * Now A has a clock "b_clk" which is an alias to + * the clock "clk" of its child B. + */ + } + +This function does not return any clock object. The new clock has the same +direction (input or output) as the original one. This function only adds a link +to the existing clock. In the above example, B object remains the only object +allowed to use the clock and device A must not try to change the clock period +or set a callback to the clock. Here follows a diagram describing the example +with an input clock:: + + +--------------------------+ + | Device A | + | +--------------+ | + | | Device B | | + | | +-------+ | | + >>"b_clk">>>| "clk" | | | + | (in) | | (in) | | | + | | +-------+ | | + | +--------------+ | + +--------------------------+ + +Migration +--------- + +Clock state are not migrated automatically. Every device must handle its +clock migration. Alias clocks must not be migrated. + +To ensure clock states are restored correctly during migration, there is two +solutions. + +Clocks states can be migrated by adding an entry into the device +vmstate description. To this purpose, the ``VMSTATE_CLOCK`` macro defines +such an entry and should be used. This is typically used to migrate an input +clock state. The following example describes it:: + + MyDeviceState { + DeviceState parent_obj; + [...] /* some fields */ + Clock *clk; + }; + + VMStateDescription my_device_vmstate = { + .name = "my_device", + .fields = (VMStateField[]) { + [...], /* other migrated fields */ + VMSTATE_CLOCK(clk, MyDeviceState), + VMSTATE_END_OF_LIST() + } + }; + +The second solution is to restore the clock state using information already +at our disposal. This can be used to restore output clocks states using the +device state. The functions ``clock_set[_ns|_hz]()`` can be used during +``post_load()`` migration callback. + +When adding a clock support to an existing device, if you care about migration +compatibility. To this end, you can use ``clock_set()`` in a ``pre_load()`` +function to setup a default value in case the source virtual machine does not +send the clock state. You may also need to use a vmstate subsection. + +Care should be taken not to use ``clock_update[_ns|_hz]()`` or +``clock_propagate()`` during the whole migration procedure because it will +trigger side effects to other devices in an unknown state. diff --git a/docs/devel/index.rst b/docs/devel/index.rst index 4dc2ca8d71..f9c8c668ee 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -25,3 +25,4 @@ Contents: tcg-plugins bitops reset + clocks