Patchwork [v2,4/4] zynq_slcr: Implement CPU reset and halting

login
register
mail settings
Submitter Peter A. G. Crosthwaite
Date Oct. 4, 2012, 12:16 a.m.
Message ID <ca19ed9b547b5d5d9191e445e524ab44d1c11c7e.1349308835.git.peter.crosthwaite@xilinx.com>
Download mbox | patch
Permalink /patch/188969/
State New
Headers show

Comments

Peter A. G. Crosthwaite - Oct. 4, 2012, 12:16 a.m.
From: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>

Implement the CPU reset and halt functions of the A9_CPU_RST_CTRL register
(offset 0x244).

Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
---

 hw/zynq_slcr.c |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)
Peter Maydell - Oct. 9, 2012, 11:41 a.m.
On 4 October 2012 01:16, Peter Crosthwaite
<peter.crosthwaite@petalogix.com> wrote:
> @@ -400,6 +404,20 @@ static void zynq_slcr_write(void *opaque, target_phys_addr_t offset,
>                  goto bad_reg;
>              }
>              s->reset[(offset - 0x200) / 4] = val;
> +            if (offset - 0x200 == A9_CPU * 4) { /* CPU Reset */
> +                for (i = 0; i < NUM_CPUS && s->cpus[i]; ++i) {
> +                    bool is_rst = val & (1 << (A9_CPU_RST_CTRL_RST_SHIFT + i));
> +                    bool is_clkstop = val &
> +                                    (1 << (A9_CPU_RST_CTRL_CLKSTOP_SHIFT + i));
> +                    if (is_rst) {
> +                        CPU_GET_CLASS(CPU(s->cpus[i]))->reset(CPU(s->cpus[i]));
> +                        DB_PRINT("resetting cpu %d\n", i);
> +                    }
> +                    s->cpus[i]->env.halted = is_rst || is_clkstop;
> +                    DB_PRINT("%shalting cpu %d\n", s->cpus[i]->env.halted ?
> +                             "" : "un", i);
> +                }
> +            }

We don't support per-CPU reset right now, and I don't think this is the
right approach. This external device shouldn't be reaching into the
implementation of the CPU object like this.

My suggestion would be to expose an inbound GPIO line on the CPU object
(corresponding to the hardware's per-CPU reset lines) and then have an
appropriate implementation inside the CPU. (In general QEMU doesn't really
handle reset very cleanly IMHO.)

Not sure how much sense it makes to model the 'stop the CPU clock' stuff
within QEMU -- probably depends whether there are any programmer-visible
consequences.

-- PMM
Andreas Färber - Oct. 9, 2012, 12:42 p.m.
Am 04.10.2012 02:16, schrieb Peter Crosthwaite:
> From: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
> 
> Implement the CPU reset and halt functions of the A9_CPU_RST_CTRL register
> (offset 0x244).
> 
> Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
> ---
> 
>  hw/zynq_slcr.c |   18 ++++++++++++++++++
>  1 files changed, 18 insertions(+), 0 deletions(-)
> 
> diff --git a/hw/zynq_slcr.c b/hw/zynq_slcr.c
> index 6eafad5..c1922fc 100644
> --- a/hw/zynq_slcr.c
> +++ b/hw/zynq_slcr.c
> @@ -116,6 +116,9 @@ typedef enum {
>    RESET_MAX
>  } ResetValues;
>  
> +#define A9_CPU_RST_CTRL_RST_SHIFT 0
> +#define A9_CPU_RST_CTRL_CLKSTOP_SHIFT 4
> +
>  typedef struct {
>      SysBusDevice busdev;
>      MemoryRegion iomem;
> @@ -346,6 +349,7 @@ static void zynq_slcr_write(void *opaque, target_phys_addr_t offset,
>                            uint64_t val, unsigned size)
>  {
>      ZynqSLCRState *s = (ZynqSLCRState *)opaque;
> +    int i;
>  
>      DB_PRINT("offset: %08x data: %08x\n", offset, (unsigned)val);
>  
> @@ -400,6 +404,20 @@ static void zynq_slcr_write(void *opaque, target_phys_addr_t offset,
>                  goto bad_reg;
>              }
>              s->reset[(offset - 0x200) / 4] = val;
> +            if (offset - 0x200 == A9_CPU * 4) { /* CPU Reset */
> +                for (i = 0; i < NUM_CPUS && s->cpus[i]; ++i) {
> +                    bool is_rst = val & (1 << (A9_CPU_RST_CTRL_RST_SHIFT + i));
> +                    bool is_clkstop = val &
> +                                    (1 << (A9_CPU_RST_CTRL_CLKSTOP_SHIFT + i));
> +                    if (is_rst) {
> +                        CPU_GET_CLASS(CPU(s->cpus[i]))->reset(CPU(s->cpus[i]));

Isn't that just cpu_reset(CPU(s->cpus[i]));? Please prefer that over
open-coding.

Thanks,
Andreas

> +                        DB_PRINT("resetting cpu %d\n", i);
> +                    }
> +                    s->cpus[i]->env.halted = is_rst || is_clkstop;
> +                    DB_PRINT("%shalting cpu %d\n", s->cpus[i]->env.halted ?
> +                             "" : "un", i);
> +                }
> +            }
>              break;
>          case 0x300:
>              s->apu_ctrl = val;
>
Peter A. G. Crosthwaite - Oct. 9, 2012, 1:38 p.m.
On Tue, Oct 9, 2012 at 9:41 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 4 October 2012 01:16, Peter Crosthwaite
> <peter.crosthwaite@petalogix.com> wrote:
>> @@ -400,6 +404,20 @@ static void zynq_slcr_write(void *opaque, target_phys_addr_t offset,
>>                  goto bad_reg;
>>              }
>>              s->reset[(offset - 0x200) / 4] = val;
>> +            if (offset - 0x200 == A9_CPU * 4) { /* CPU Reset */
>> +                for (i = 0; i < NUM_CPUS && s->cpus[i]; ++i) {
>> +                    bool is_rst = val & (1 << (A9_CPU_RST_CTRL_RST_SHIFT + i));
>> +                    bool is_clkstop = val &
>> +                                    (1 << (A9_CPU_RST_CTRL_CLKSTOP_SHIFT + i));
>> +                    if (is_rst) {
>> +                        CPU_GET_CLASS(CPU(s->cpus[i]))->reset(CPU(s->cpus[i]));
>> +                        DB_PRINT("resetting cpu %d\n", i);
>> +                    }
>> +                    s->cpus[i]->env.halted = is_rst || is_clkstop;
>> +                    DB_PRINT("%shalting cpu %d\n", s->cpus[i]->env.halted ?
>> +                             "" : "un", i);
>> +                }
>> +            }
>
> We don't support per-CPU reset right now, and I don't think this is the
> right approach. This external device shouldn't be reaching into the
> implementation of the CPU object like this.
>

cpu reset is part of the QOM interface for TYPE_CPU. Theres no
privatisation of that so disagree that this is "reaching into the
implementation".

> My suggestion would be to expose an inbound GPIO line on the CPU object
> (corresponding to the hardware's per-CPU reset lines) and then have an
> appropriate implementation inside the CPU.

This would be awkward, considering the standard GPIO functionality
comes from TYPE_DEVICE, which is not a parent class of TYPE_ARM_CPU.
Would have to add GPIOs directly as properties. Does
object_property_set_gpio of something usable for that even exist?

Modelling internal signal as a GPIO is a slipperly slope as well. All
over QEMU there are assumptions made about what device connects to
what which abstract away the signalling layer, and I dont see how a
reset connection is different. We dont model an I2C bus as a wiggling
wire, so why do we have to model CPU resets like that?

(In general QEMU doesn't really
> handle reset very cleanly IMHO.)
>
> Not sure how much sense it makes to model the 'stop the CPU clock' stuff
> within QEMU -- probably depends whether there are any programmer-visible
> consequences.
>

Theres a race condition in my test vectors if the CPU immediately runs
after the reset. CPU0 holds CPU1 in reset, then rewrites the vector
table while holding reset. If CPU1 kicks immediately then it will use
out an out of date reset vector.

Regards,
Peter

> -- PMM
Peter Maydell - Oct. 9, 2012, 1:52 p.m.
On 9 October 2012 14:38, Peter Crosthwaite
<peter.crosthwaite@petalogix.com> wrote:
> On Tue, Oct 9, 2012 at 9:41 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> We don't support per-CPU reset right now, and I don't think this is the
>> right approach. This external device shouldn't be reaching into the
>> implementation of the CPU object like this.
>>
>
> cpu reset is part of the QOM interface for TYPE_CPU. Theres no
> privatisation of that so disagree that this is "reaching into the
> implementation".

You're modifying s->cpus[i]->env.halted ...

Also, the QOM cpu reset function just does an instantaneous reset
of the CPU. What you actually want is "while I assert this then
please hold the CPU in reset indefinitely", which is functionality
we don't expose at the moment.

>> My suggestion would be to expose an inbound GPIO line on the CPU object
>> (corresponding to the hardware's per-CPU reset lines) and then have an
>> appropriate implementation inside the CPU.
>
> This would be awkward, considering the standard GPIO functionality
> comes from TYPE_DEVICE, which is not a parent class of TYPE_ARM_CPU.
> Would have to add GPIOs directly as properties. Does
> object_property_set_gpio of something usable for that even exist?
>
> Modelling internal signal as a GPIO is a slipperly slope as well. All
> over QEMU there are assumptions made about what device connects to
> what which abstract away the signalling layer, and I dont see how a
> reset connection is different. We dont model an I2C bus as a wiggling
> wire, so why do we have to model CPU resets like that?

We do (or should) model an I2C bus as a connection between two
QEMU objects, where the connection is made by the board model
as part of its initialisation. Similarly, we should model CPU
reset as a connection between two QEMU objects (the power controller
and the CPU) which is made by the board model as part of its
initialisation.

I2C is more complicated and so you want to bundle things up
into a more abstracted connection rather than modelling things
at the single-wire level. CPU reset really is a single wire though.

>> (In general QEMU doesn't really
>> handle reset very cleanly IMHO.)
>>
>> Not sure how much sense it makes to model the 'stop the CPU clock' stuff
>> within QEMU -- probably depends whether there are any programmer-visible
>> consequences.
>>
>
> Theres a race condition in my test vectors if the CPU immediately runs
> after the reset. CPU0 holds CPU1 in reset, then rewrites the vector
> table while holding reset. If CPU1 kicks immediately then it will use
> out an out of date reset vector.

Hmm, shouldn't CPU1 only start executing from the reset vector
when we come out of reset? (There is an issue like this for M
profile cores I expect, since they read the PC from the vector
table rather than just jumping into the vector table like A
profile cores.)

But either way the implementation of the reset gpio input
should be that while it's asserted we hold the CPU in reset,
and it doesn't execute until the GPIO line goes low.

-- PMM
Peter A. G. Crosthwaite - Oct. 9, 2012, 2:31 p.m.
On Tue, Oct 9, 2012 at 11:52 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 9 October 2012 14:38, Peter Crosthwaite
> <peter.crosthwaite@petalogix.com> wrote:
>> On Tue, Oct 9, 2012 at 9:41 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
>>> We don't support per-CPU reset right now, and I don't think this is the
>>> right approach. This external device shouldn't be reaching into the
>>> implementation of the CPU object like this.
>>>
>>
>> cpu reset is part of the QOM interface for TYPE_CPU. Theres no
>> privatisation of that so disagree that this is "reaching into the
>> implementation".
>
> You're modifying s->cpus[i]->env.halted ...
>

My understanding is this is tangled with andreas's CPU refactorings:

http://lists.gnu.org/archive/html/qemu-devel/2012-06/msg02882.html

And once that issue is surmounted we should be able to add APIs to do
this on the TYPE_CPU level rather than the TYPE_ARM_CPU level.

> Also, the QOM cpu reset function just does an instantaneous reset
> of the CPU. What you actually want is "while I assert this then
> please hold the CPU in reset indefinitely", which is functionality
> we don't expose at the moment.
>

I implemented this my just holding the halt while the reset bit is
asserted. Is this inaccurate?

>>> My suggestion would be to expose an inbound GPIO line on the CPU object
>>> (corresponding to the hardware's per-CPU reset lines) and then have an
>>> appropriate implementation inside the CPU.
>>
>> This would be awkward, considering the standard GPIO functionality
>> comes from TYPE_DEVICE, which is not a parent class of TYPE_ARM_CPU.
>> Would have to add GPIOs directly as properties. Does
>> object_property_set_gpio of something usable for that even exist?
>>

This is still my bigger concern. Can we actually do the GPIO thing
without major infrastructural developments?

>> Modelling internal signal as a GPIO is a slipperly slope as well. All
>> over QEMU there are assumptions made about what device connects to
>> what which abstract away the signalling layer, and I dont see how a
>> reset connection is different. We dont model an I2C bus as a wiggling
>> wire, so why do we have to model CPU resets like that?
>
> We do (or should) model an I2C bus as a connection between two
> QEMU objects, where the connection is made by the board model
> as part of its initialisation. Similarly, we should model CPU
> reset as a connection between two QEMU objects (the power controller
> and the CPU) which is made by the board model as part of its
> initialisation.
>

But the machine model does make the connection in this case as well.
Just using QOM links rather than GPIOs. The Machine model explicitly
sets the link (using object_property_set_link()) from the Zynq SLCR to
the CPU. The SLCR can be connected to any CPU. This also means that
when we come to implement the halting functionality, the interconnect
is handled as one link an not 2 GPIOs (or N gpios depending on how
many CPU control/status signals you want to hookup).

Regards,
Peter

> I2C is more complicated and so you want to bundle things up
> into a more abstracted connection rather than modelling things
> at the single-wire level. CPU reset really is a single wire though.
>
>>> (In general QEMU doesn't really
>>> handle reset very cleanly IMHO.)
>>>
>>> Not sure how much sense it makes to model the 'stop the CPU clock' stuff
>>> within QEMU -- probably depends whether there are any programmer-visible
>>> consequences.
>>>
>>
>> Theres a race condition in my test vectors if the CPU immediately runs
>> after the reset. CPU0 holds CPU1 in reset, then rewrites the vector
>> table while holding reset. If CPU1 kicks immediately then it will use
>> out an out of date reset vector.
>
> Hmm, shouldn't CPU1 only start executing from the reset vector
> when we come out of reset? (There is an issue like this for M
> profile cores I expect, since they read the PC from the vector
> table rather than just jumping into the vector table like A
> profile cores.)
>
> But either way the implementation of the reset gpio input
> should be that while it's asserted we hold the CPU in reset,
> and it doesn't execute until the GPIO line goes low.
>
> -- PMM

Patch

diff --git a/hw/zynq_slcr.c b/hw/zynq_slcr.c
index 6eafad5..c1922fc 100644
--- a/hw/zynq_slcr.c
+++ b/hw/zynq_slcr.c
@@ -116,6 +116,9 @@  typedef enum {
   RESET_MAX
 } ResetValues;
 
+#define A9_CPU_RST_CTRL_RST_SHIFT 0
+#define A9_CPU_RST_CTRL_CLKSTOP_SHIFT 4
+
 typedef struct {
     SysBusDevice busdev;
     MemoryRegion iomem;
@@ -346,6 +349,7 @@  static void zynq_slcr_write(void *opaque, target_phys_addr_t offset,
                           uint64_t val, unsigned size)
 {
     ZynqSLCRState *s = (ZynqSLCRState *)opaque;
+    int i;
 
     DB_PRINT("offset: %08x data: %08x\n", offset, (unsigned)val);
 
@@ -400,6 +404,20 @@  static void zynq_slcr_write(void *opaque, target_phys_addr_t offset,
                 goto bad_reg;
             }
             s->reset[(offset - 0x200) / 4] = val;
+            if (offset - 0x200 == A9_CPU * 4) { /* CPU Reset */
+                for (i = 0; i < NUM_CPUS && s->cpus[i]; ++i) {
+                    bool is_rst = val & (1 << (A9_CPU_RST_CTRL_RST_SHIFT + i));
+                    bool is_clkstop = val &
+                                    (1 << (A9_CPU_RST_CTRL_CLKSTOP_SHIFT + i));
+                    if (is_rst) {
+                        CPU_GET_CLASS(CPU(s->cpus[i]))->reset(CPU(s->cpus[i]));
+                        DB_PRINT("resetting cpu %d\n", i);
+                    }
+                    s->cpus[i]->env.halted = is_rst || is_clkstop;
+                    DB_PRINT("%shalting cpu %d\n", s->cpus[i]->env.halted ?
+                             "" : "un", i);
+                }
+            }
             break;
         case 0x300:
             s->apu_ctrl = val;