diff mbox

[arm-ccnt,v1,1/1] ARM-CCNT: Implements the ARM PMCCNTR register

Message ID b392c38bcf744556a340efaedd30cd8c6ce726bc.1389846449.git.alistair.francis@xilinx.com
State New
Headers show

Commit Message

Alistair Francis Jan. 16, 2014, 4:31 a.m. UTC
This patch implements the ARM PMCCNTR register including
the disable and reset components of the PMCR register.

Signed-off-by: Alistair Francis <alistair.francis@xilinx.com>
---
This patch assumes that non-invasive debugging is not permitted
when determining if the counter is disabled

 target-arm/cpu.c    |    2 ++
 target-arm/cpu.h    |    3 +++
 target-arm/helper.c |   43 ++++++++++++++++++++++++++++++++++++++++---
 target-arm/helper.h |    9 +++++++++
 4 files changed, 54 insertions(+), 3 deletions(-)

Comments

Peter Maydell Jan. 18, 2014, 10:01 p.m. UTC | #1
On 16 January 2014 04:31, Alistair Francis <alistair.francis@xilinx.com> wrote:
> This patch implements the ARM PMCCNTR register including
> the disable and reset components of the PMCR register.
>
> Signed-off-by: Alistair Francis <alistair.francis@xilinx.com>
> ---
> This patch assumes that non-invasive debugging is not permitted
> when determining if the counter is disabled
>
>  target-arm/cpu.c    |    2 ++
>  target-arm/cpu.h    |    3 +++
>  target-arm/helper.c |   43 ++++++++++++++++++++++++++++++++++++++++---
>  target-arm/helper.h |    9 +++++++++
>  4 files changed, 54 insertions(+), 3 deletions(-)
>
> diff --git a/target-arm/cpu.c b/target-arm/cpu.c
> index 52efd5d..a185959 100644
> --- a/target-arm/cpu.c
> +++ b/target-arm/cpu.c
> @@ -76,6 +76,8 @@ static void arm_cpu_reset(CPUState *s)
>
>      acc->parent_reset(s);
>
> +    env->emm_time = 0;
> +
>      memset(env, 0, offsetof(CPUARMState, breakpoints));
>      g_hash_table_foreach(cpu->cp_regs, cp_reg_reset, cpu);
>      env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid;
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 198b6b8..8c73a56 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -215,8 +215,11 @@ typedef struct CPUARMState {
>          uint32_t c15_diagnostic; /* diagnostic register */
>          uint32_t c15_power_diagnostic;
>          uint32_t c15_power_control; /* power control */
> +        uint32_t c15_ccnt; /* Performance Monitors Cycle Count */
>      } cp15;
>
> +    int64_t emm_time; /* Used to hold the total emmulation time - in ns */

This looks bogus. You don't need to save the total emulation
time, because you can always get it by calling
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL).
(Also it won't get migrated, but the correct fix for that is to get
rid of it, see below.)

> +
>      /* System registers (AArch64) */
>      struct {
>          uint64_t tpidr_el0;
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index c708f15..72404cf 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -505,9 +505,16 @@ static int pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>      if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
>          return EXCP_UDEF;
>      }
> -    /* only the DP, X, D and E bits are writable */
> +    /* only the DP, X, D and E bits are writeable */

"writable" is a valid spelling and a quick grep shows it's
actually used much more in qemu than "writeable".

>      env->cp15.c9_pmcr &= ~0x39;
>      env->cp15.c9_pmcr |= (value & 0x39);
> +
> +    if (value & PMCRC) {
> +        /* Reset the counter */
> +        env->emm_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +        env->cp15.c15_ccnt = 0;

This is the wrong way to do this kind of thing. Don't store
the actual value of a resettable count register; store the
difference between the CLOCK_VIRTUAL count and
the guest-visible value. Then you don't have to separately
keep both emm_time and c15_ccnt as state.

> +    }
> +
>      return 0;
>  }
>
> @@ -584,6 +591,35 @@ static int vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
>      return 0;
>  }
>
> +static int pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri,
> +                       uint64_t *value)
> +{
> +    int64_t clock_value;
> +
> +    /* This assumes that non-invasive debugging is not permitted */
> +    if (env->cp15.c9_pmcr & PMCRDP ||
> +       !(env->cp15.c9_pmcr & PMCRE)) {
> +        /* Counter is disabled, do not change value */
> +        *value = env->cp15.c15_ccnt;
> +        return 0;
> +    }
> +
> +    clock_value = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +
> +    if (env->cp15.c9_pmcr & PMCRDP) {
> +        /* Increment once every 64 processor clock cycles */
> +        env->cp15.c15_ccnt += (uint32_t) CLOCKNSTOTICKS((clock_value - \
> +                                                       env->emm_time))/64;

Why the line continuation character?

> +    } else {
> +        env->cp15.c15_ccnt += (uint32_t) CLOCKNSTOTICKS((clock_value - \
> +                                                       env->emm_time));
> +    }
> +
> +    env->emm_time = clock_value;
> +    *value = env->cp15.c15_ccnt;
> +    return 0;
> +}
> +
>  static int ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri,
>                         uint64_t *value)
>  {
> @@ -644,9 +680,10 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
>       */
>      { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
>        .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
> -    /* Unimplemented, RAZ/WI. XXX PMUSERENR */
>      { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0,
> -      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
> +      .access = PL1_RW, .readfn = pmccntr_read,
> +      .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt),
> +      .resetvalue = 0, .type = ARM_CP_IO },
>      { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
>        .access = PL0_RW,
>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper),
> diff --git a/target-arm/helper.h b/target-arm/helper.h
> index 70872df..5b5dc0a 100644
> --- a/target-arm/helper.h
> +++ b/target-arm/helper.h
> @@ -492,6 +492,15 @@ DEF_HELPER_3(neon_qzip32, void, env, i32, i32)
>  DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32)
>  DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32)
>
> +/* Definitions for the PMCCNTR and PMCR registers */
> +#define PMCRDP  0x20
> +#define PMCRD   0x8
> +#define PMCRC   0x4
> +#define PMCRE   0x1
> +
> +#define CLOCKNSTOTICKS(input) ((((input * get_ticks_per_sec())/1000000000) >> 8) & \
> +                              0xFFFFFFFF)
> +

This is absolutely the wrong header for this kind of thing. Also that
macro is rather misleadingly named, looks pretty ugly and is not
really necessary given it's basically used in just one place.

thanks
-- PMM
Peter Crosthwaite Jan. 19, 2014, 12:48 a.m. UTC | #2
On Sun, Jan 19, 2014 at 8:01 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 16 January 2014 04:31, Alistair Francis <alistair.francis@xilinx.com> wrote:
>> This patch implements the ARM PMCCNTR register including
>> the disable and reset components of the PMCR register.
>>
>> Signed-off-by: Alistair Francis <alistair.francis@xilinx.com>
>> ---
>> This patch assumes that non-invasive debugging is not permitted
>> when determining if the counter is disabled
>>
>>  target-arm/cpu.c    |    2 ++
>>  target-arm/cpu.h    |    3 +++
>>  target-arm/helper.c |   43 ++++++++++++++++++++++++++++++++++++++++---
>>  target-arm/helper.h |    9 +++++++++
>>  4 files changed, 54 insertions(+), 3 deletions(-)
>>
>> diff --git a/target-arm/cpu.c b/target-arm/cpu.c
>> index 52efd5d..a185959 100644
>> --- a/target-arm/cpu.c
>> +++ b/target-arm/cpu.c
>> @@ -76,6 +76,8 @@ static void arm_cpu_reset(CPUState *s)
>>
>>      acc->parent_reset(s);
>>
>> +    env->emm_time = 0;
>> +
>>      memset(env, 0, offsetof(CPUARMState, breakpoints));
>>      g_hash_table_foreach(cpu->cp_regs, cp_reg_reset, cpu);
>>      env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid;
>> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
>> index 198b6b8..8c73a56 100644
>> --- a/target-arm/cpu.h
>> +++ b/target-arm/cpu.h
>> @@ -215,8 +215,11 @@ typedef struct CPUARMState {
>>          uint32_t c15_diagnostic; /* diagnostic register */
>>          uint32_t c15_power_diagnostic;
>>          uint32_t c15_power_control; /* power control */
>> +        uint32_t c15_ccnt; /* Performance Monitors Cycle Count */
>>      } cp15;
>>
>> +    int64_t emm_time; /* Used to hold the total emmulation time - in ns */
>
> This looks bogus. You don't need to save the total emulation
> time, because you can always get it by calling
> qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL).
> (Also it won't get migrated, but the correct fix for that is to get
> rid of it, see below.)
>

It means something slightly different doesnt it? It's the emulation
time last time the ccnt was synced. Looking below its always updated
syncronously with ccnt itself and never on its own. Perhaps a rename
to make that clearer.

>> +
>>      /* System registers (AArch64) */
>>      struct {
>>          uint64_t tpidr_el0;
>> diff --git a/target-arm/helper.c b/target-arm/helper.c
>> index c708f15..72404cf 100644
>> --- a/target-arm/helper.c
>> +++ b/target-arm/helper.c
>> @@ -505,9 +505,16 @@ static int pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>      if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
>>          return EXCP_UDEF;
>>      }
>> -    /* only the DP, X, D and E bits are writable */
>> +    /* only the DP, X, D and E bits are writeable */
>
> "writable" is a valid spelling and a quick grep shows it's
> actually used much more in qemu than "writeable".
>

Yes out of scope change, please revert.

>>      env->cp15.c9_pmcr &= ~0x39;
>>      env->cp15.c9_pmcr |= (value & 0x39);
>> +
>> +    if (value & PMCRC) {
>> +        /* Reset the counter */
>> +        env->emm_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>> +        env->cp15.c15_ccnt = 0;
>
> This is the wrong way to do this kind of thing. Don't store
> the actual value of a resettable count register; store the
> difference between the CLOCK_VIRTUAL count and
> the guest-visible value. Then you don't have to separately
> keep both emm_time and c15_ccnt as state.
>

Is the state minimisation really worth that pain? If only reset was
being implemented, it could be done as a simple case of snapping
clock-virtual as "clock_when_ccnt_reset" then removing the CCNT state
entirely but the problem is it does make the implementation of the
disable feature somewhat difficult. If the timer is disabled this
"diff" needs to start changing as emulation time progresses. If I
disable, wait for 1s CLOCK_VIRTUAL time then renable, i need to
recalulate this diff -= 1. The only way that calculation can be done
is with a second variable, which snapshots CLOCK_VIRTUAL when disabled
happened so this diff can be recalculated. Its also awkward as if you
disable for long enough (or immediately on emulation start),
"clock_when_ccnt_reset" can go negative.

What's missing from this patch is there is no sense of syncing the two
variables when you disable, re-enable or change the freqency scaler
(which needs to be fixed). All of these features can cause the timer
to change frequency while it is at a non-zero value, which means this
diff variable would need continual warping every time one of these
frequency affecting settings changed.

Regards,
Peter

>> +    }
>> +
>>      return 0;
>>  }
>>
>> @@ -584,6 +591,35 @@ static int vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>      return 0;
>>  }
>>
>> +static int pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri,
>> +                       uint64_t *value)
>> +{
>> +    int64_t clock_value;
>> +
>> +    /* This assumes that non-invasive debugging is not permitted */
>> +    if (env->cp15.c9_pmcr & PMCRDP ||
>> +       !(env->cp15.c9_pmcr & PMCRE)) {
>> +        /* Counter is disabled, do not change value */
>> +        *value = env->cp15.c15_ccnt;
>> +        return 0;
>> +    }
>> +
>> +    clock_value = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>> +
>> +    if (env->cp15.c9_pmcr & PMCRDP) {
>> +        /* Increment once every 64 processor clock cycles */
>> +        env->cp15.c15_ccnt += (uint32_t) CLOCKNSTOTICKS((clock_value - \
>> +                                                       env->emm_time))/64;
>
> Why the line continuation character?
>
>> +    } else {
>> +        env->cp15.c15_ccnt += (uint32_t) CLOCKNSTOTICKS((clock_value - \
>> +                                                       env->emm_time));
>> +    }
>> +
>> +    env->emm_time = clock_value;
>> +    *value = env->cp15.c15_ccnt;
>> +    return 0;
>> +}
>> +
>>  static int ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri,
>>                         uint64_t *value)
>>  {
>> @@ -644,9 +680,10 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
>>       */
>>      { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
>>        .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
>> -    /* Unimplemented, RAZ/WI. XXX PMUSERENR */
>>      { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0,
>> -      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
>> +      .access = PL1_RW, .readfn = pmccntr_read,
>> +      .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt),
>> +      .resetvalue = 0, .type = ARM_CP_IO },
>>      { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
>>        .access = PL0_RW,
>>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper),
>> diff --git a/target-arm/helper.h b/target-arm/helper.h
>> index 70872df..5b5dc0a 100644
>> --- a/target-arm/helper.h
>> +++ b/target-arm/helper.h
>> @@ -492,6 +492,15 @@ DEF_HELPER_3(neon_qzip32, void, env, i32, i32)
>>  DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32)
>>  DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32)
>>
>> +/* Definitions for the PMCCNTR and PMCR registers */
>> +#define PMCRDP  0x20
>> +#define PMCRD   0x8
>> +#define PMCRC   0x4
>> +#define PMCRE   0x1
>> +
>> +#define CLOCKNSTOTICKS(input) ((((input * get_ticks_per_sec())/1000000000) >> 8) & \
>> +                              0xFFFFFFFF)
>> +
>
> This is absolutely the wrong header for this kind of thing. Also that
> macro is rather misleadingly named, looks pretty ugly and is not
> really necessary given it's basically used in just one place.
>
> thanks
> -- PMM
>
Peter Maydell Jan. 19, 2014, 1:06 a.m. UTC | #3
On 19 January 2014 00:48, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Sun, Jan 19, 2014 at 8:01 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> On 16 January 2014 04:31, Alistair Francis <alistair.francis@xilinx.com> wrote:
>>>      env->cp15.c9_pmcr &= ~0x39;
>>>      env->cp15.c9_pmcr |= (value & 0x39);
>>> +
>>> +    if (value & PMCRC) {
>>> +        /* Reset the counter */
>>> +        env->emm_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>> +        env->cp15.c15_ccnt = 0;
>>
>> This is the wrong way to do this kind of thing. Don't store
>> the actual value of a resettable count register; store the
>> difference between the CLOCK_VIRTUAL count and
>> the guest-visible value. Then you don't have to separately
>> keep both emm_time and c15_ccnt as state.
>>
>
> Is the state minimisation really worth that pain?

You have to do calculation on read and write anyway, because
obviously we don't keep the state field continuously updated
to the value the guest needs to see.  (Actually the lack of
a writefn hook for this register looks like a bug to me.) So it doesn't
IMHO make much difference in terms of code complexity, and it
keeps the CPUState tidy (in particular, you don't end up with a
random field which is "this pertains to a single cp15 register, and
it holds state, but it's lurking randomly at the top level"). I think
the migration is really the key issue though: if you design the
state such that simply writing the incoming-migration value
to the register via the raw-write hook (and reading it via
raw-read on an outbound migration) is sufficient, then it all
just works. If you have this extra bit of state lurking about then
it is all going to need nasty special casing, and I went to quite
a lot of effort to make sure that cp15 registers didn't need
individual special casing.

> If only reset was
> being implemented, it could be done as a simple case of snapping
> clock-virtual as "clock_when_ccnt_reset" then removing the CCNT state
> entirely but the problem is it does make the implementation of the
> disable feature somewhat difficult. If the timer is disabled this
> "diff" needs to start changing as emulation time progresses. If I
> disable, wait for 1s CLOCK_VIRTUAL time then renable, i need to
> recalulate this diff -= 1. The only way that calculation can be done
> is with a second variable, which snapshots CLOCK_VIRTUAL when disabled
> happened so this diff can be recalculated. Its also awkward as if you
> disable for long enough (or immediately on emulation start),
> "clock_when_ccnt_reset" can go negative.

Typically for a disablable counter, when it's disabled you keep
"current disabled value" in the state field, and then when it's
reenabled you switch back to "diff from clock".

thanks
-- PMM
Peter Crosthwaite Jan. 19, 2014, 1:39 a.m. UTC | #4
On Sun, Jan 19, 2014 at 11:06 AM, Peter Maydell
<peter.maydell@linaro.org> wrote:
> On 19 January 2014 00:48, Peter Crosthwaite
> <peter.crosthwaite@xilinx.com> wrote:
>> On Sun, Jan 19, 2014 at 8:01 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>>> On 16 January 2014 04:31, Alistair Francis <alistair.francis@xilinx.com> wrote:
>>>>      env->cp15.c9_pmcr &= ~0x39;
>>>>      env->cp15.c9_pmcr |= (value & 0x39);
>>>> +
>>>> +    if (value & PMCRC) {
>>>> +        /* Reset the counter */
>>>> +        env->emm_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>>> +        env->cp15.c15_ccnt = 0;
>>>
>>> This is the wrong way to do this kind of thing. Don't store
>>> the actual value of a resettable count register; store the
>>> difference between the CLOCK_VIRTUAL count and
>>> the guest-visible value. Then you don't have to separately
>>> keep both emm_time and c15_ccnt as state.
>>>
>>
>> Is the state minimisation really worth that pain?
>
> You have to do calculation on read and write anyway, because
> obviously we don't keep the state field continuously updated
> to the value the guest needs to see.  (Actually the lack of
> a writefn hook for this register looks like a bug to me.)

Yes it should be easy enough.

 So it doesn't
> IMHO make much difference in terms of code complexity, and it
> keeps the CPUState tidy (in particular, you don't end up with a
> random field which is "this pertains to a single cp15 register, and
> it holds state, but it's lurking randomly at the top level"). I think
> the migration is really the key issue though: if you design the
> state such that simply writing the incoming-migration value
> to the register via the raw-write hook (and reading it via
> raw-read on an outbound migration) is sufficient, then it all
> just works. If you have this extra bit of state lurking about then
> it is all going to need nasty special casing, and I went to quite
> a lot of effort to make sure that cp15 registers didn't need
> individual special casing.
>

FWIW, is there anything wrong with just migrating the second variable
on the side? Is there no code path for migration of random ARM state
thats not a CP?

>> If only reset was
>> being implemented, it could be done as a simple case of snapping
>> clock-virtual as "clock_when_ccnt_reset" then removing the CCNT state
>> entirely but the problem is it does make the implementation of the
>> disable feature somewhat difficult. If the timer is disabled this
>> "diff" needs to start changing as emulation time progresses. If I
>> disable, wait for 1s CLOCK_VIRTUAL time then renable, i need to
>> recalulate this diff -= 1. The only way that calculation can be done
>> is with a second variable, which snapshots CLOCK_VIRTUAL when disabled
>> happened so this diff can be recalculated. Its also awkward as if you
>> disable for long enough (or immediately on emulation start),
>> "clock_when_ccnt_reset" can go negative.
>
> Typically for a disablable counter, when it's disabled you keep
> "current disabled value" in the state field, and then when it's
> reenabled you switch back to "diff from clock".
>

Ouch. Thats a variable with mixed meaning. You still have the
amkwardness however of this diff needing warping every time you change
the frequency. And it can go negative when you change from high
frequency to low frequency w/o a reset. Although none of this is a
functional showstopper I guess.

So to run with your idea, the register can be defined as a dual
meaning uint64_t to simplify migration but a comment should be added
to the effect that this value does represent the actual register
state:

/*
 * NOT always the value of the counter. If the counter
 * is enabled, it is the virtual clock time corresponding
 * to when the counter was reset. If the counter is disabled,
 * it is value of the counter.
 */
uint64_t c15_ccnt;

Regards,
Peter

> thanks
> -- PMM
>
Peter Maydell Jan. 19, 2014, 11:20 a.m. UTC | #5
On 19 January 2014 01:39, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Sun, Jan 19, 2014 at 11:06 AM, Peter Maydell
> <peter.maydell@linaro.org> wrote:
>  So it doesn't
>> IMHO make much difference in terms of code complexity, and it
>> keeps the CPUState tidy (in particular, you don't end up with a
>> random field which is "this pertains to a single cp15 register, and
>> it holds state, but it's lurking randomly at the top level"). I think
>> the migration is really the key issue though: if you design the
>> state such that simply writing the incoming-migration value
>> to the register via the raw-write hook (and reading it via
>> raw-read on an outbound migration) is sufficient, then it all
>> just works. If you have this extra bit of state lurking about then
>> it is all going to need nasty special casing, and I went to quite
>> a lot of effort to make sure that cp15 registers didn't need
>> individual special casing.
>>
>
> FWIW, is there anything wrong with just migrating the second variable
> on the side? Is there no code path for migration of random ARM state
> thats not a CP?

It would break migration between TCG and KVM -- the assumption
is that cp registers only need to be read and written via the
raw_read/write interface. There is obviously a mechanism for
migrating ARM state that isn't a coprocessor (you put an entry
for it in the vmstate structs in machine.c), but you need to
figure out what that state looks like for KVM as well and special
case it in the KVM state syncing functions.

>>> If only reset was
>>> being implemented, it could be done as a simple case of snapping
>>> clock-virtual as "clock_when_ccnt_reset" then removing the CCNT state
>>> entirely but the problem is it does make the implementation of the
>>> disable feature somewhat difficult. If the timer is disabled this
>>> "diff" needs to start changing as emulation time progresses. If I
>>> disable, wait for 1s CLOCK_VIRTUAL time then renable, i need to
>>> recalulate this diff -= 1. The only way that calculation can be done
>>> is with a second variable, which snapshots CLOCK_VIRTUAL when disabled
>>> happened so this diff can be recalculated. Its also awkward as if you
>>> disable for long enough (or immediately on emulation start),
>>> "clock_when_ccnt_reset" can go negative.
>>
>> Typically for a disablable counter, when it's disabled you keep
>> "current disabled value" in the state field, and then when it's
>> reenabled you switch back to "diff from clock".
>>
>
> Ouch. Thats a variable with mixed meaning. You still have the
> amkwardness however of this diff needing warping every time you change
> the frequency. And it can go negative when you change from high
> frequency to low frequency w/o a reset. Although none of this is a
> functional showstopper I guess.

I would assume that frequency doesn't change much, so to the
extent we care about efficiency we should make the read
path simple and do recalculation on frequency changes.

> So to run with your idea, the register can be defined as a dual
> meaning uint64_t to simplify migration but a comment should be added
> to the effect that this value does represent the actual register
> state:

Yeah, if you like. (Actually I think it's ok to have more state in the
CPU struct, as long as it's always valid to save and restore just
by saving and restoring the value through the raw accessors.
If you really want to have two fields then you should put them both
in the cp15 bit of the struct though; compare the handling of the
generic timer cp14_timer fields.) Your comment seems a bit
longwinded to me, though, since from my POV this is just the
obvious way to implement a counter.

thanks
-- PMM
Alistair Francis Jan. 20, 2014, 1:11 a.m. UTC | #6
I have made those changes you both mentioned above and submitted v2 of
my series. There is now only one extra variable in the CPUARMState
struct.

On Sun, Jan 19, 2014 at 9:20 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 19 January 2014 01:39, Peter Crosthwaite
> <peter.crosthwaite@xilinx.com> wrote:
>> On Sun, Jan 19, 2014 at 11:06 AM, Peter Maydell
>> <peter.maydell@linaro.org> wrote:
>>  So it doesn't
>>> IMHO make much difference in terms of code complexity, and it
>>> keeps the CPUState tidy (in particular, you don't end up with a
>>> random field which is "this pertains to a single cp15 register, and
>>> it holds state, but it's lurking randomly at the top level"). I think
>>> the migration is really the key issue though: if you design the
>>> state such that simply writing the incoming-migration value
>>> to the register via the raw-write hook (and reading it via
>>> raw-read on an outbound migration) is sufficient, then it all
>>> just works. If you have this extra bit of state lurking about then
>>> it is all going to need nasty special casing, and I went to quite
>>> a lot of effort to make sure that cp15 registers didn't need
>>> individual special casing.
>>>
>>
>> FWIW, is there anything wrong with just migrating the second variable
>> on the side? Is there no code path for migration of random ARM state
>> thats not a CP?
>
> It would break migration between TCG and KVM -- the assumption
> is that cp registers only need to be read and written via the
> raw_read/write interface. There is obviously a mechanism for
> migrating ARM state that isn't a coprocessor (you put an entry
> for it in the vmstate structs in machine.c), but you need to
> figure out what that state looks like for KVM as well and special
> case it in the KVM state syncing functions.
>
>>>> If only reset was
>>>> being implemented, it could be done as a simple case of snapping
>>>> clock-virtual as "clock_when_ccnt_reset" then removing the CCNT state
>>>> entirely but the problem is it does make the implementation of the
>>>> disable feature somewhat difficult. If the timer is disabled this
>>>> "diff" needs to start changing as emulation time progresses. If I
>>>> disable, wait for 1s CLOCK_VIRTUAL time then renable, i need to
>>>> recalulate this diff -= 1. The only way that calculation can be done
>>>> is with a second variable, which snapshots CLOCK_VIRTUAL when disabled
>>>> happened so this diff can be recalculated. Its also awkward as if you
>>>> disable for long enough (or immediately on emulation start),
>>>> "clock_when_ccnt_reset" can go negative.
>>>
>>> Typically for a disablable counter, when it's disabled you keep
>>> "current disabled value" in the state field, and then when it's
>>> reenabled you switch back to "diff from clock".
>>>
>>
>> Ouch. Thats a variable with mixed meaning. You still have the
>> amkwardness however of this diff needing warping every time you change
>> the frequency. And it can go negative when you change from high
>> frequency to low frequency w/o a reset. Although none of this is a
>> functional showstopper I guess.
>
> I would assume that frequency doesn't change much, so to the
> extent we care about efficiency we should make the read
> path simple and do recalculation on frequency changes.
>
>> So to run with your idea, the register can be defined as a dual
>> meaning uint64_t to simplify migration but a comment should be added
>> to the effect that this value does represent the actual register
>> state:
>
> Yeah, if you like. (Actually I think it's ok to have more state in the
> CPU struct, as long as it's always valid to save and restore just
> by saving and restoring the value through the raw accessors.
> If you really want to have two fields then you should put them both
> in the cp15 bit of the struct though; compare the handling of the
> generic timer cp14_timer fields.) Your comment seems a bit
> longwinded to me, though, since from my POV this is just the
> obvious way to implement a counter.
>
> thanks
> -- PMM
>
diff mbox

Patch

diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 52efd5d..a185959 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -76,6 +76,8 @@  static void arm_cpu_reset(CPUState *s)
 
     acc->parent_reset(s);
 
+    env->emm_time = 0;
+
     memset(env, 0, offsetof(CPUARMState, breakpoints));
     g_hash_table_foreach(cpu->cp_regs, cp_reg_reset, cpu);
     env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid;
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 198b6b8..8c73a56 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -215,8 +215,11 @@  typedef struct CPUARMState {
         uint32_t c15_diagnostic; /* diagnostic register */
         uint32_t c15_power_diagnostic;
         uint32_t c15_power_control; /* power control */
+        uint32_t c15_ccnt; /* Performance Monitors Cycle Count */
     } cp15;
 
+    int64_t emm_time; /* Used to hold the total emmulation time - in ns */
+
     /* System registers (AArch64) */
     struct {
         uint64_t tpidr_el0;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index c708f15..72404cf 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -505,9 +505,16 @@  static int pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
     if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
         return EXCP_UDEF;
     }
-    /* only the DP, X, D and E bits are writable */
+    /* only the DP, X, D and E bits are writeable */
     env->cp15.c9_pmcr &= ~0x39;
     env->cp15.c9_pmcr |= (value & 0x39);
+
+    if (value & PMCRC) {
+        /* Reset the counter */
+        env->emm_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        env->cp15.c15_ccnt = 0;
+    }
+
     return 0;
 }
 
@@ -584,6 +591,35 @@  static int vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
     return 0;
 }
 
+static int pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri,
+                       uint64_t *value)
+{
+    int64_t clock_value;
+
+    /* This assumes that non-invasive debugging is not permitted */
+    if (env->cp15.c9_pmcr & PMCRDP ||
+       !(env->cp15.c9_pmcr & PMCRE)) {
+        /* Counter is disabled, do not change value */
+        *value = env->cp15.c15_ccnt;
+        return 0;
+    }
+
+    clock_value = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+    if (env->cp15.c9_pmcr & PMCRDP) {
+        /* Increment once every 64 processor clock cycles */
+        env->cp15.c15_ccnt += (uint32_t) CLOCKNSTOTICKS((clock_value - \
+                                                       env->emm_time))/64;
+    } else {
+        env->cp15.c15_ccnt += (uint32_t) CLOCKNSTOTICKS((clock_value - \
+                                                       env->emm_time));
+    }
+
+    env->emm_time = clock_value;
+    *value = env->cp15.c15_ccnt;
+    return 0;
+}
+
 static int ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri,
                        uint64_t *value)
 {
@@ -644,9 +680,10 @@  static const ARMCPRegInfo v7_cp_reginfo[] = {
      */
     { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
       .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
-    /* Unimplemented, RAZ/WI. XXX PMUSERENR */
     { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0,
-      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+      .access = PL1_RW, .readfn = pmccntr_read,
+      .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt),
+      .resetvalue = 0, .type = ARM_CP_IO },
     { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
       .access = PL0_RW,
       .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper),
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 70872df..5b5dc0a 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -492,6 +492,15 @@  DEF_HELPER_3(neon_qzip32, void, env, i32, i32)
 DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32)
 DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32)
 
+/* Definitions for the PMCCNTR and PMCR registers */
+#define PMCRDP  0x20
+#define PMCRD   0x8
+#define PMCRC   0x4
+#define PMCRE   0x1
+
+#define CLOCKNSTOTICKS(input) ((((input * get_ticks_per_sec())/1000000000) >> 8) & \
+                              0xFFFFFFFF)
+
 #ifdef TARGET_AARCH64
 #include "helper-a64.h"
 #endif