
What is a QEMU_CLOCK
====================

A QEMU_CLOCK is a QOM Object developed for the purpose of modeling a clock tree
with QEMU.

It only simulates the clock by keeping a copy of the current frequency and
doesn't model the signal itself such as pin toggle or duty cycle.

It allows to model the impact of badly configured PLL, clock source selection
or disabled clock on the models.

Binding the clock together to create a tree
===========================================

In order to create a clock tree with QEMU_CLOCK two or more clock must be bound
together. Let's say there are two clocks clk_a and clk_b:
Using qemu_clk_bind(clk_a, clk_b) will bind clk_a and clk_b.

Binding two qemu-clk together creates a unidirectional link which means that
changing the rate of clk_a will propagate to clk_b and not the opposite.
The binding process automatically refreshes clk_b rate.

Clock can be bound and unbound during execution for modeling eg: a clock
selector.

A clock can drive more than one other clock. eg with this code:
qemu_clk_bind(clk_a, clk_b);
qemu_clk_bind(clk_a, clk_c);

A clock rate change one clk_a will propagate to clk_b and clk_c.

Implementing a callback on a rate change
========================================

The function prototype is the following:
typedef uint64_t QEMUClkRateUpdateCallback(void *opaque, uint64_t rate);

It's main goal is to modify the rate before it's passed to the next clocks in
the tree.

eg: for a 4x PLL the function will be:
uint64_t qemu_clk_rate_change_cb(void *opaque, uint64_t rate)
{
    return 4 * rate;
}

To set the callback for the clock:
void qemu_clk_set_callback(qemu_clk clk, QEMUClkRateUpdateCallback *cb,
                           void *opaque);
can be called.

The rate update process
=======================

The rate update happen in this way:
When a model wants to update a clock frequency (eg: based on a register change
or something similar) it will call qemu_clk_update_rate(..) on the clock:
  * The callback associated to the clock is called with the new rate.
  * qemu_clk_update_rate(..) is then called on all bound clocks with the value
    returned by the callback.

NOTE: When no callback is attached, the clock qemu_clk_update_rate(..) is called
on the next clock in the tree with the rate unmodified.

Adding a QEMU_CLOCK to a DeviceState
====================================

Adding a qemu-clk to a DeviceState is required to be able to get the clock
outside the model through qemu_clk_device_get_clock(..).

It is also required to be able to print the clock and its rate with info qtree.
For example:

  type System
  dev: xlnx.zynqmp_crf, id ""
    gpio-out "sysbus-irq" 1
    gpio-out "RST_A9" 4
    qemu-clk "dbg_trace" 0
    qemu-clk "vpll_to_lpd" 625000000
    qemu-clk "dp_stc_ref" 0
    qemu-clk "dpll_to_lpd" 12500000
    qemu-clk "acpu_clk" 0
    qemu-clk "pcie_ref" 0
    qemu-clk "topsw_main" 0
    qemu-clk "topsw_lsbus" 0
    qemu-clk "dp_audio_ref" 0
    qemu-clk "sata_ref" 0
    qemu-clk "dp_video_ref" 71428568
    qemu-clk "vpll_clk" 2500000000
    qemu-clk "apll_to_lpd" 12500000
    qemu-clk "dpll_clk" 50000000
    qemu-clk "gpu_ref" 0
    qemu-clk "aux_refclk" 0
    qemu-clk "video_clk" 27000000
    qemu-clk "gdma_ref" 0
    qemu-clk "gt_crx_ref_clk" 0
    qemu-clk "dbg_fdp" 0
    qemu-clk "apll_clk" 50000000
    qemu-clk "pss_alt_ref_clk" 0
    qemu-clk "ddr" 0
    qemu-clk "pss_ref_clk" 50000000
    qemu-clk "dpdma_ref" 0
    qemu-clk "dbg_tstmp" 0
    mmio 00000000fd1a0000/000000000000010c

This way a DeviceState can have multiple clock input or output.

Examples
========

Those are the different way of using the QEMUClock object.

Modelling a fixed clock generator
=================================

Here is a brief example of a device acting as a clock source:

typedef struct {
    DeviceState parent_obj;

    uint32_t rate;
    QEMUClock out;
} FixedClock;

During the initialization the device must initialize its clock object:

static void fixed_clock_instance_init(Object *obj)
{
    FixedClock *s = FIXED_CLOCK(obj);

    object_initialize(&s->out, sizeof(s->out), TYPE_CLOCK);
    qemu_clk_device_add_clock(DEVICE(obj), &s->out, "clk_out");
}

As the device acts as a clock source it must refresh the clock tree during the
realize phase:

static void fixed_clock_realizefn(DeviceState *dev, Error **errp)
{
    FixedClock *s = FIXED_CLOCK(dev);

    qemu_clk_update_rate(&s->out, s->rate);
}

This means that the clock tree must be finished before realize is called on the
fixed clock.

Modelling a clock user device
=============================

Here is a brief example of a clock user:

typedef struct {
    DeviceState parent_obj;

    QEMUClock in;
} ClockUser;

As before the clock must be initialized through the device initialize function:

static void clock_user_instance_init(Object *obj)
{
    ClockUser *s = CLOCK_USER(obj);

    object_initialize(&s->in, sizeof(s->in), TYPE_CLOCK);
    qemu_clk_device_add_clock(DEVICE(obj), &s->in, "clk_in");
    /*
     * Call on_rate_change_cb when something change on clk_in.
     */
    qemu_clk_set_callback(s->in, on_rate_change_cb, obj);
}

The callback is in this case used as a notifier when the clock tree which
sources the device change:

static uint64_t on_rate_change_cb(void *opaque, uint64_t input_rate)
{
    printf("the new rate is %ld\n", input_rate);

    /* The return is ignored if nothing is bound to clk_in. */
    return input_rate;
}

Modelling a clock multiplier
============================

Here is a brief example of a device acting as a clock modifier:

typedef struct {
    DeviceState parent_obj;

    uint32_t rate;
    QEMUClock out;
    QEMUClock in;
} ClockMultiplier;

As before the clocks must be initialized through the device initialize function
but they must be bound together so a change on the input will propagate on the
output:

static void clock_multiplier_instance_init(Object *obj)
{
    ClockMultiplier *s = CLOCK_MULTIPLIER(obj);

    object_initialize(&s->out, sizeof(s->out), TYPE_CLOCK);
    object_initialize(&s->in, sizeof(s->in), TYPE_CLOCK);

    qemu_clk_device_add_clock(DEVICE(obj), &s->in, "clk_in");
    qemu_clk_device_add_clock(DEVICE(obj), &s->out, "clk_out");
    /*
     * Propagate the change from in to out, this can be done dynamically during
     * the simulation but we need to do the initial binding here to get the
     * initial refresh happening when the realize function is called on the
     * fixed clock.
     */
    qemu_clk_bind(s->in, s->out);
    /*
     * But before propagating the rate modify it with multiplier_cb.
     */
    qemu_clk_set_callback(s->out, multiplier_cb, obj);
}

In this example when clk_in changes it will trigger a change on clk_out and this
callback can modify the rate of clk_out (the return value of this callback)
accordingly to clk_in rate (input_rate).

static uint64_t multiplier_cb(void *opaque, uint64_t input_rate)
{
    return input_rate * 4;
}

This device doesn't refresh the clock tree as it will be done by the clock tree
source.

Modelling a clock selector
==========================

Here is a brief example of a device acting as a device which alter the clock
topology such as a clock selector:

typedef struct {
    DeviceState parent_obj;

    QEMUClock out;
    QEMUClock in_a;
    QEMUClock in_b;
} ClockSelector;

The clocks must be initialized through the device initialize function but they
must be bound together like they will be when the device is reset so a change on
the input during the realize of the fixed clock will propagate to the output:

static void clock_selector_instance_init(Object *obj)
{
    ClockSelector *s = CLOCK_SELECTOR(obj);

    object_initialize(&s->out, sizeof(s->out), TYPE_CLOCK);
    object_initialize(&s->in_a, sizeof(s->in_a), TYPE_CLOCK);
    object_initialize(&s->in_b, sizeof(s->in_b), TYPE_CLOCK);

    qemu_clk_device_add_clock(DEVICE(obj), &s->in_a, "clk_in_a");
    qemu_clk_device_add_clock(DEVICE(obj), &s->in_b, "clk_in_b");
    qemu_clk_device_add_clock(DEVICE(obj), &s->out, "clk_out");

    /* Assuming at the reset that the input_a is connected to output. */
    qemu_clk_bind(s->in_a, s->out);
}

/* This is called for example by a register change or something like that */
void update_topology(ClockSelector *s)
{
    /* Unbind the old clock */
    qemu_clk_unbind(s->in_a, s->out);
    /* Bind the new one, the rate is automatically refreshed. */
    qemu_clk_bind(s->in_b, s->out);
}
