diff mbox series

[rc3,02/30] target/avr: Introduce AVR CPU class object

Message ID 1580079311-20447-3-git-send-email-aleksandar.markovic@rt-rk.com
State New
Headers show
Series target/avr merger | expand

Commit Message

Aleksandar Markovic Jan. 26, 2020, 10:54 p.m. UTC
From: Michael Rolnik <mrolnik@gmail.com>

This introduces AVR CPU class object and its bacis elements
and functions.

Co-developed-by: Michael Rolnik <mrolnik@gmail.com>
Co-developed-by: Sarah Harris <S.E.Harris@kent.ac.uk>
Signed-off-by: Michael Rolnik <mrolnik@gmail.com>
Signed-off-by: Sarah Harris <S.E.Harris@kent.ac.uk>
Signed-off-by: Michael Rolnik <mrolnik@gmail.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Aleksandar Markovic <aleksandar.m.mail@gmail.com>
---
 target/avr/cpu-qom.h |  54 +++++++++++++
 target/avr/cpu.c     | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++
 target/avr/cpu.h     | 134 +++++++++++++++++++++++++++++++
 3 files changed, 408 insertions(+)
 create mode 100644 target/avr/cpu-qom.h
 create mode 100644 target/avr/cpu.c

Comments

Aleksandar Markovic Jan. 27, 2020, 2:39 a.m. UTC | #1
> +static void avr_cpu_initfn(Object *obj)
> +{
> +    AVRCPU *cpu = AVR_CPU(obj);
> +
> +    cpu_set_cpustate_pointers(cpu);
> +
> +#ifndef CONFIG_USER_ONLY
> +    /* Set the number of interrupts supported by the CPU. */
> +    qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int,
> +            sizeof(cpu->env.intsrc) * 8);
> +#endif
> +}

A question for Michael:

What is CONFIG_USER_ONLY doing here, if we know AVR cores from this
series doesn't have and will never have user mode, since they do not
work with the kernel?

Do you plan to support some AVR 32-bit CPUs in the same file, which
would be able to have linux-user mode? But, in this case, many files
must be completely redone. My advice is to separate them completely,
and that this file supports only 8-bit AVR cores, and in that case
checks for CONFIG_USER_ONLY do not make sense.

Regards,
Aleksandar

> +
> +static ObjectClass *avr_cpu_class_by_name(const char *cpu_model)
> +{
> +    ObjectClass *oc;
> +
> +    oc = object_class_by_name(cpu_model);
> +    if (object_class_dynamic_cast(oc, TYPE_AVR_CPU) == NULL ||
> +        object_class_is_abstract(oc)) {
> +        oc = NULL;
> +    }
> +    return oc;
> +}
> +
> +static void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags)
> +{
> +    AVRCPU *cpu = AVR_CPU(cs);
> +    CPUAVRState *env = &cpu->env;
> +    int i;
> +
> +    qemu_fprintf(f, "\n");
> +    qemu_fprintf(f, "PC:    %06x\n", env->pc_w);
> +    qemu_fprintf(f, "SP:      %04x\n", env->sp);
> +    qemu_fprintf(f, "rampD:     %02x\n", env->rampD >> 16);
> +    qemu_fprintf(f, "rampX:     %02x\n", env->rampX >> 16);
> +    qemu_fprintf(f, "rampY:     %02x\n", env->rampY >> 16);
> +    qemu_fprintf(f, "rampZ:     %02x\n", env->rampZ >> 16);
> +    qemu_fprintf(f, "EIND:      %02x\n", env->eind >> 16);
> +    qemu_fprintf(f, "X:       %02x%02x\n", env->r[27], env->r[26]);
> +    qemu_fprintf(f, "Y:       %02x%02x\n", env->r[29], env->r[28]);
> +    qemu_fprintf(f, "Z:       %02x%02x\n", env->r[31], env->r[30]);
> +    qemu_fprintf(f, "SREG:    [ %c %c %c %c %c %c %c %c ]\n",
> +                        env->sregI ? 'I' : '-',
> +                        env->sregT ? 'T' : '-',
> +                        env->sregH ? 'H' : '-',
> +                        env->sregS ? 'S' : '-',
> +                        env->sregV ? 'V' : '-',
> +                        env->sregN ? '-' : 'N', /* Zf has negative logic */
> +                        env->sregZ ? 'Z' : '-',
> +                        env->sregC ? 'I' : '-');
> +    qemu_fprintf(f, "SKIP:    %02x\n", env->skip);
> +
> +    qemu_fprintf(f, "\n");
> +    for (i = 0; i < ARRAY_SIZE(env->r); i++) {
> +        qemu_fprintf(f, "R[%02d]:  %02x   ", i, env->r[i]);
> +
> +        if ((i % 8) == 7) {
> +            qemu_fprintf(f, "\n");
> +        }
> +    }
> +    qemu_fprintf(f, "\n");
> +}
> +
> +static void avr_cpu_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +    CPUClass *cc = CPU_CLASS(oc);
> +    AVRCPUClass *mcc = AVR_CPU_CLASS(oc);
> +
> +    mcc->parent_realize = dc->realize;
> +    dc->realize = avr_cpu_realizefn;
> +
> +    mcc->parent_reset = cc->reset;
> +    cc->reset = avr_cpu_reset;
> +
> +    cc->class_by_name = avr_cpu_class_by_name;
> +
> +    cc->has_work = avr_cpu_has_work;
> +    cc->do_interrupt = avr_cpu_do_interrupt;
> +    cc->cpu_exec_interrupt = avr_cpu_exec_interrupt;
> +    cc->dump_state = avr_cpu_dump_state;
> +    cc->set_pc = avr_cpu_set_pc;
> +#if !defined(CONFIG_USER_ONLY)
> +    cc->memory_rw_debug = avr_cpu_memory_rw_debug;
> +#endif
> +#ifdef CONFIG_USER_ONLY
> +    cc->handle_mmu_fault = avr_cpu_handle_mmu_fault;
> +#else
> +    cc->get_phys_page_debug = avr_cpu_get_phys_page_debug;
> +#endif

The same question here.

> +    cc->disas_set_info = avr_cpu_disas_set_info;
> +    cc->tlb_fill = avr_cpu_tlb_fill;
> +    cc->tcg_initialize = avr_cpu_tcg_init;
> +    cc->synchronize_from_tb = avr_cpu_synchronize_from_tb;
> +}
> diff --git a/target/avr/cpu.h b/target/avr/cpu.h
> index d122611..f7a403a 100644
> --- a/target/avr/cpu.h
> +++ b/target/avr/cpu.h
> @@ -69,4 +69,138 @@
>
>  #define EF_AVR_MACH 0x7F
>
> +typedef struct CPUAVRState CPUAVRState;
> +
> +struct CPUAVRState {
> +    uint32_t pc_w; /* 0x003fffff up to 22 bits */
> +
> +    uint32_t sregC; /* 0x00000001 1 bit */
> +    uint32_t sregZ; /* 0x00000001 1 bit */
> +    uint32_t sregN; /* 0x00000001 1 bit */
> +    uint32_t sregV; /* 0x00000001 1 bit */
> +    uint32_t sregS; /* 0x00000001 1 bit */
> +    uint32_t sregH; /* 0x00000001 1 bit */
> +    uint32_t sregT; /* 0x00000001 1 bit */
> +    uint32_t sregI; /* 0x00000001 1 bit */
> +
> +    uint32_t rampD; /* 0x00ff0000 8 bits */
> +    uint32_t rampX; /* 0x00ff0000 8 bits */
> +    uint32_t rampY; /* 0x00ff0000 8 bits */
> +    uint32_t rampZ; /* 0x00ff0000 8 bits */
> +    uint32_t eind; /* 0x00ff0000 8 bits */
> +
> +    uint32_t r[NUMBER_OF_CPU_REGISTERS]; /* 8 bits each */
> +    uint32_t sp; /* 16 bits */
> +
> +    uint32_t skip; /* if set skip instruction */
> +
> +    uint64_t intsrc; /* interrupt sources */
> +    bool fullacc; /* CPU/MEM if true MEM only otherwise */
> +
> +    uint32_t features;
> +};
> +
> +/**
> + *  AVRCPU:
> + *  @env: #CPUAVRState
> + *
> + *  A AVR CPU.
> + */
> +typedef struct AVRCPU {
> +    /*< private >*/
> +    CPUState parent_obj;
> +    /*< public >*/
> +
> +    CPUNegativeOffsetState neg;
> +    CPUAVRState env;
> +} AVRCPU;
> +
> +void avr_cpu_do_interrupt(CPUState *cpu);
> +bool avr_cpu_exec_interrupt(CPUState *cpu, int int_req);
> +hwaddr avr_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
> +
> +#define cpu_list avr_cpu_list
> +#define cpu_signal_handler cpu_avr_signal_handler
> +#define cpu_mmu_index avr_cpu_mmu_index
> +
> +static inline int avr_cpu_mmu_index(CPUAVRState *env, bool ifetch)
> +{
> +    return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX;
> +}
> +
> +void avr_cpu_tcg_init(void);
> +
> +void avr_cpu_list(void);
> +int cpu_avr_exec(CPUState *cpu);
> +int cpu_avr_signal_handler(int host_signum, void *pinfo, void *puc);
> +int avr_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
> +                                int rw, int mmu_idx);
> +int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf,
> +                                int len, bool is_write);
> +
> +enum {
> +    TB_FLAGS_FULL_ACCESS = 1,
> +    TB_FLAGS_SKIP = 2,
> +};
> +
> +static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc,
> +                                target_ulong *cs_base, uint32_t *pflags)
> +{
> +    uint32_t flags = 0;
> +
> +    *pc = env->pc_w * 2;
> +    *cs_base = 0;
> +
> +    if (env->fullacc) {
> +        flags |= TB_FLAGS_FULL_ACCESS;
> +    }
> +    if (env->skip) {
> +        flags |= TB_FLAGS_SKIP;
> +    }
> +
> +    *pflags = flags;
> +}
> +
> +static inline int cpu_interrupts_enabled(CPUAVRState *env)
> +{
> +    return env->sregI != 0;
> +}
> +
> +static inline uint8_t cpu_get_sreg(CPUAVRState *env)
> +{
> +    uint8_t sreg;
> +    sreg = (env->sregC) << 0
> +         | (env->sregZ) << 1
> +         | (env->sregN) << 2
> +         | (env->sregV) << 3
> +         | (env->sregS) << 4
> +         | (env->sregH) << 5
> +         | (env->sregT) << 6
> +         | (env->sregI) << 7;
> +    return sreg;
> +}
> +
> +static inline void cpu_set_sreg(CPUAVRState *env, uint8_t sreg)
> +{
> +    env->sregC = (sreg >> 0) & 0x01;
> +    env->sregZ = (sreg >> 1) & 0x01;
> +    env->sregN = (sreg >> 2) & 0x01;
> +    env->sregV = (sreg >> 3) & 0x01;
> +    env->sregS = (sreg >> 4) & 0x01;
> +    env->sregH = (sreg >> 5) & 0x01;
> +    env->sregT = (sreg >> 6) & 0x01;
> +    env->sregI = (sreg >> 7) & 0x01;
> +}
> +
> +bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> +                        MMUAccessType access_type, int mmu_idx,
> +                        bool probe, uintptr_t retaddr);
> +
> +typedef CPUAVRState CPUArchState;
> +typedef AVRCPU ArchCPU;
> +
> +#include "exec/cpu-all.h"
> +
> +const char *avr_flags_to_cpu_type(uint32_t flags, const char *def_cpu_type);
> +
>  #endif /* !defined (QEMU_AVR_CPU_H) */
> --
> 2.7.4
>
Philippe Mathieu-Daudé Jan. 27, 2020, 8:03 a.m. UTC | #2
On 1/27/20 3:39 AM, Aleksandar Markovic wrote:
>> +static void avr_cpu_initfn(Object *obj)
>> +{
>> +    AVRCPU *cpu = AVR_CPU(obj);
>> +
>> +    cpu_set_cpustate_pointers(cpu);
>> +
>> +#ifndef CONFIG_USER_ONLY
>> +    /* Set the number of interrupts supported by the CPU. */
>> +    qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int,
>> +            sizeof(cpu->env.intsrc) * 8);
>> +#endif
>> +}
> 
> A question for Michael:
> 
> What is CONFIG_USER_ONLY doing here, if we know AVR cores from this
> series doesn't have and will never have user mode, since they do not
> work with the kernel?
> 
> Do you plan to support some AVR 32-bit CPUs in the same file, which
> would be able to have linux-user mode? But, in this case, many files
> must be completely redone. My advice is to separate them completely,
> and that this file supports only 8-bit AVR cores, and in that case
> checks for CONFIG_USER_ONLY do not make sense.

You are right, CONFIG_USER_ONLY doesn't make sense.

Suggestion: add in target/avr/cpu.h:

   #ifdef CONFIG_USER_ONLY
   #error "AVR 8-bit does not support user mode"
   #endif
Aleksandar Markovic Jan. 27, 2020, 8:43 a.m. UTC | #3
09:03 Pon, 27.01.2020. Philippe Mathieu-Daudé <philmd@redhat.com> је
написао/ла:
>
> On 1/27/20 3:39 AM, Aleksandar Markovic wrote:
> >> +static void avr_cpu_initfn(Object *obj)
> >> +{
> >> +    AVRCPU *cpu = AVR_CPU(obj);
> >> +
> >> +    cpu_set_cpustate_pointers(cpu);
> >> +
> >> +#ifndef CONFIG_USER_ONLY
> >> +    /* Set the number of interrupts supported by the CPU. */
> >> +    qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int,
> >> +            sizeof(cpu->env.intsrc) * 8);
> >> +#endif
> >> +}
> >
> > A question for Michael:
> >
> > What is CONFIG_USER_ONLY doing here, if we know AVR cores from this
> > series doesn't have and will never have user mode, since they do not
> > work with the kernel?
> >
> > Do you plan to support some AVR 32-bit CPUs in the same file, which
> > would be able to have linux-user mode? But, in this case, many files
> > must be completely redone. My advice is to separate them completely,
> > and that this file supports only 8-bit AVR cores, and in that case
> > checks for CONFIG_USER_ONLY do not make sense.
>
> You are right, CONFIG_USER_ONLY doesn't make sense.
>
> Suggestion: add in target/avr/cpu.h:
>
>    #ifdef CONFIG_USER_ONLY
>    #error "AVR 8-bit does not support user mode"
>    #endif
>

I agree with your suggestion, in fact, it is an excellent one.
Michael Rolnik Jan. 27, 2020, 8:51 a.m. UTC | #4
Hi all.

As for now I have no plans to add support for AVR 32 bit CPU. If there are
requests I might consider it.
When I started to implement AVR 8 bit CPU I added that `#ifndef
CONFIG_USER_ONLY` as other CPUs did, so I guess they could be removed.

Regards,
Michael Rolnik

On Mon, Jan 27, 2020 at 4:39 AM Aleksandar Markovic <
aleksandar.m.mail@gmail.com> wrote:

> > +static void avr_cpu_initfn(Object *obj)
> > +{
> > +    AVRCPU *cpu = AVR_CPU(obj);
> > +
> > +    cpu_set_cpustate_pointers(cpu);
> > +
> > +#ifndef CONFIG_USER_ONLY
> > +    /* Set the number of interrupts supported by the CPU. */
> > +    qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int,
> > +            sizeof(cpu->env.intsrc) * 8);
> > +#endif
> > +}
>
> A question for Michael:
>
> What is CONFIG_USER_ONLY doing here, if we know AVR cores from this
> series doesn't have and will never have user mode, since they do not
> work with the kernel?
>
> Do you plan to support some AVR 32-bit CPUs in the same file, which
> would be able to have linux-user mode? But, in this case, many files
> must be completely redone. My advice is to separate them completely,
> and that this file supports only 8-bit AVR cores, and in that case
> checks for CONFIG_USER_ONLY do not make sense.
>
> Regards,
> Aleksandar
>
> > +
> > +static ObjectClass *avr_cpu_class_by_name(const char *cpu_model)
> > +{
> > +    ObjectClass *oc;
> > +
> > +    oc = object_class_by_name(cpu_model);
> > +    if (object_class_dynamic_cast(oc, TYPE_AVR_CPU) == NULL ||
> > +        object_class_is_abstract(oc)) {
> > +        oc = NULL;
> > +    }
> > +    return oc;
> > +}
> > +
> > +static void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags)
> > +{
> > +    AVRCPU *cpu = AVR_CPU(cs);
> > +    CPUAVRState *env = &cpu->env;
> > +    int i;
> > +
> > +    qemu_fprintf(f, "\n");
> > +    qemu_fprintf(f, "PC:    %06x\n", env->pc_w);
> > +    qemu_fprintf(f, "SP:      %04x\n", env->sp);
> > +    qemu_fprintf(f, "rampD:     %02x\n", env->rampD >> 16);
> > +    qemu_fprintf(f, "rampX:     %02x\n", env->rampX >> 16);
> > +    qemu_fprintf(f, "rampY:     %02x\n", env->rampY >> 16);
> > +    qemu_fprintf(f, "rampZ:     %02x\n", env->rampZ >> 16);
> > +    qemu_fprintf(f, "EIND:      %02x\n", env->eind >> 16);
> > +    qemu_fprintf(f, "X:       %02x%02x\n", env->r[27], env->r[26]);
> > +    qemu_fprintf(f, "Y:       %02x%02x\n", env->r[29], env->r[28]);
> > +    qemu_fprintf(f, "Z:       %02x%02x\n", env->r[31], env->r[30]);
> > +    qemu_fprintf(f, "SREG:    [ %c %c %c %c %c %c %c %c ]\n",
> > +                        env->sregI ? 'I' : '-',
> > +                        env->sregT ? 'T' : '-',
> > +                        env->sregH ? 'H' : '-',
> > +                        env->sregS ? 'S' : '-',
> > +                        env->sregV ? 'V' : '-',
> > +                        env->sregN ? '-' : 'N', /* Zf has negative
> logic */
> > +                        env->sregZ ? 'Z' : '-',
> > +                        env->sregC ? 'I' : '-');
> > +    qemu_fprintf(f, "SKIP:    %02x\n", env->skip);
> > +
> > +    qemu_fprintf(f, "\n");
> > +    for (i = 0; i < ARRAY_SIZE(env->r); i++) {
> > +        qemu_fprintf(f, "R[%02d]:  %02x   ", i, env->r[i]);
> > +
> > +        if ((i % 8) == 7) {
> > +            qemu_fprintf(f, "\n");
> > +        }
> > +    }
> > +    qemu_fprintf(f, "\n");
> > +}
> > +
> > +static void avr_cpu_class_init(ObjectClass *oc, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(oc);
> > +    CPUClass *cc = CPU_CLASS(oc);
> > +    AVRCPUClass *mcc = AVR_CPU_CLASS(oc);
> > +
> > +    mcc->parent_realize = dc->realize;
> > +    dc->realize = avr_cpu_realizefn;
> > +
> > +    mcc->parent_reset = cc->reset;
> > +    cc->reset = avr_cpu_reset;
> > +
> > +    cc->class_by_name = avr_cpu_class_by_name;
> > +
> > +    cc->has_work = avr_cpu_has_work;
> > +    cc->do_interrupt = avr_cpu_do_interrupt;
> > +    cc->cpu_exec_interrupt = avr_cpu_exec_interrupt;
> > +    cc->dump_state = avr_cpu_dump_state;
> > +    cc->set_pc = avr_cpu_set_pc;
> > +#if !defined(CONFIG_USER_ONLY)
> > +    cc->memory_rw_debug = avr_cpu_memory_rw_debug;
> > +#endif
> > +#ifdef CONFIG_USER_ONLY
> > +    cc->handle_mmu_fault = avr_cpu_handle_mmu_fault;
> > +#else
> > +    cc->get_phys_page_debug = avr_cpu_get_phys_page_debug;
> > +#endif
>
> The same question here.
>
> > +    cc->disas_set_info = avr_cpu_disas_set_info;
> > +    cc->tlb_fill = avr_cpu_tlb_fill;
> > +    cc->tcg_initialize = avr_cpu_tcg_init;
> > +    cc->synchronize_from_tb = avr_cpu_synchronize_from_tb;
> > +}
> > diff --git a/target/avr/cpu.h b/target/avr/cpu.h
> > index d122611..f7a403a 100644
> > --- a/target/avr/cpu.h
> > +++ b/target/avr/cpu.h
> > @@ -69,4 +69,138 @@
> >
> >  #define EF_AVR_MACH 0x7F
> >
> > +typedef struct CPUAVRState CPUAVRState;
> > +
> > +struct CPUAVRState {
> > +    uint32_t pc_w; /* 0x003fffff up to 22 bits */
> > +
> > +    uint32_t sregC; /* 0x00000001 1 bit */
> > +    uint32_t sregZ; /* 0x00000001 1 bit */
> > +    uint32_t sregN; /* 0x00000001 1 bit */
> > +    uint32_t sregV; /* 0x00000001 1 bit */
> > +    uint32_t sregS; /* 0x00000001 1 bit */
> > +    uint32_t sregH; /* 0x00000001 1 bit */
> > +    uint32_t sregT; /* 0x00000001 1 bit */
> > +    uint32_t sregI; /* 0x00000001 1 bit */
> > +
> > +    uint32_t rampD; /* 0x00ff0000 8 bits */
> > +    uint32_t rampX; /* 0x00ff0000 8 bits */
> > +    uint32_t rampY; /* 0x00ff0000 8 bits */
> > +    uint32_t rampZ; /* 0x00ff0000 8 bits */
> > +    uint32_t eind; /* 0x00ff0000 8 bits */
> > +
> > +    uint32_t r[NUMBER_OF_CPU_REGISTERS]; /* 8 bits each */
> > +    uint32_t sp; /* 16 bits */
> > +
> > +    uint32_t skip; /* if set skip instruction */
> > +
> > +    uint64_t intsrc; /* interrupt sources */
> > +    bool fullacc; /* CPU/MEM if true MEM only otherwise */
> > +
> > +    uint32_t features;
> > +};
> > +
> > +/**
> > + *  AVRCPU:
> > + *  @env: #CPUAVRState
> > + *
> > + *  A AVR CPU.
> > + */
> > +typedef struct AVRCPU {
> > +    /*< private >*/
> > +    CPUState parent_obj;
> > +    /*< public >*/
> > +
> > +    CPUNegativeOffsetState neg;
> > +    CPUAVRState env;
> > +} AVRCPU;
> > +
> > +void avr_cpu_do_interrupt(CPUState *cpu);
> > +bool avr_cpu_exec_interrupt(CPUState *cpu, int int_req);
> > +hwaddr avr_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
> > +
> > +#define cpu_list avr_cpu_list
> > +#define cpu_signal_handler cpu_avr_signal_handler
> > +#define cpu_mmu_index avr_cpu_mmu_index
> > +
> > +static inline int avr_cpu_mmu_index(CPUAVRState *env, bool ifetch)
> > +{
> > +    return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX;
> > +}
> > +
> > +void avr_cpu_tcg_init(void);
> > +
> > +void avr_cpu_list(void);
> > +int cpu_avr_exec(CPUState *cpu);
> > +int cpu_avr_signal_handler(int host_signum, void *pinfo, void *puc);
> > +int avr_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
> > +                                int rw, int mmu_idx);
> > +int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf,
> > +                                int len, bool is_write);
> > +
> > +enum {
> > +    TB_FLAGS_FULL_ACCESS = 1,
> > +    TB_FLAGS_SKIP = 2,
> > +};
> > +
> > +static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong
> *pc,
> > +                                target_ulong *cs_base, uint32_t *pflags)
> > +{
> > +    uint32_t flags = 0;
> > +
> > +    *pc = env->pc_w * 2;
> > +    *cs_base = 0;
> > +
> > +    if (env->fullacc) {
> > +        flags |= TB_FLAGS_FULL_ACCESS;
> > +    }
> > +    if (env->skip) {
> > +        flags |= TB_FLAGS_SKIP;
> > +    }
> > +
> > +    *pflags = flags;
> > +}
> > +
> > +static inline int cpu_interrupts_enabled(CPUAVRState *env)
> > +{
> > +    return env->sregI != 0;
> > +}
> > +
> > +static inline uint8_t cpu_get_sreg(CPUAVRState *env)
> > +{
> > +    uint8_t sreg;
> > +    sreg = (env->sregC) << 0
> > +         | (env->sregZ) << 1
> > +         | (env->sregN) << 2
> > +         | (env->sregV) << 3
> > +         | (env->sregS) << 4
> > +         | (env->sregH) << 5
> > +         | (env->sregT) << 6
> > +         | (env->sregI) << 7;
> > +    return sreg;
> > +}
> > +
> > +static inline void cpu_set_sreg(CPUAVRState *env, uint8_t sreg)
> > +{
> > +    env->sregC = (sreg >> 0) & 0x01;
> > +    env->sregZ = (sreg >> 1) & 0x01;
> > +    env->sregN = (sreg >> 2) & 0x01;
> > +    env->sregV = (sreg >> 3) & 0x01;
> > +    env->sregS = (sreg >> 4) & 0x01;
> > +    env->sregH = (sreg >> 5) & 0x01;
> > +    env->sregT = (sreg >> 6) & 0x01;
> > +    env->sregI = (sreg >> 7) & 0x01;
> > +}
> > +
> > +bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> > +                        MMUAccessType access_type, int mmu_idx,
> > +                        bool probe, uintptr_t retaddr);
> > +
> > +typedef CPUAVRState CPUArchState;
> > +typedef AVRCPU ArchCPU;
> > +
> > +#include "exec/cpu-all.h"
> > +
> > +const char *avr_flags_to_cpu_type(uint32_t flags, const char
> *def_cpu_type);
> > +
> >  #endif /* !defined (QEMU_AVR_CPU_H) */
> > --
> > 2.7.4
> >
>
Sarah Harris Jan. 29, 2020, 12:20 p.m. UTC | #5
Hi,

I think I've found a minor bug: the stack pointer should be initialised to the size of SRAM in some or most cases.
Currently, SP is initialised to zero.

It seems modern AVRs set SP to the size of SRAM (RAMEND) at power-on, though a few older ones initialise to zero.
The ATmega328 (from 2009) [1], ATmega2560 (from 2005) [2], ATtiny2313 (from 2003) [6], and ATtiny85 (from 2005) [3] all use RAMEND.
The ATmega8 (from 2001) [4], ATmega8535 (from 2002) [5], and AT90S8535 (from 1998) [7] use zero.
I haven't found a list of which AVRs use which value (other than reading every datasheet).

Given that GCC performs this initialisation in software anyway (so what the hardware does doesn't matter), I think this is a minor issue.
It will only affect hand written assembly programs that don't do their own initialisation (which seems to be discouraged as not all resets are power-on events).
I'm not sure what, if anything, needs to be done about it but it might be worth fixing now we're emulating specific chips.

Kind regards,
Sarah Harris

[1] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf (section 6.5.1)
[2] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf (section 7.6)
[3] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf (section 4.6.1)
[4] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2486-8-bit-AVR-microcontroller-ATmega8_L_datasheet.pdf (page 13)
[5] http://ww1.microchip.com/downloads/en/DeviceDoc/doc2502.pdf (page 12)
[6] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2543-AVR-ATtiny2313_Datasheet.pdf (page 11)
[7] http://ww1.microchip.com/downloads/en/DeviceDoc/doc1041.pdf (page 20)

On Sun, 26 Jan 2020 23:54:43 +0100
Aleksandar Markovic <aleksandar.markovic@rt-rk.com> wrote:

> +static void avr_cpu_reset(CPUState *cs)
> +{
> +    AVRCPU *cpu = AVR_CPU(cs);
> +    AVRCPUClass *mcc = AVR_CPU_GET_CLASS(cpu);
> +    CPUAVRState *env = &cpu->env;
> +
> +    mcc->parent_reset(cs);
> +
> +    env->pc_w = 0;
> +    env->sregI = 1;
> +    env->sregC = 0;
> +    env->sregZ = 0;
> +    env->sregN = 0;
> +    env->sregV = 0;
> +    env->sregS = 0;
> +    env->sregH = 0;
> +    env->sregT = 0;
> +
> +    env->rampD = 0;
> +    env->rampX = 0;
> +    env->rampY = 0;
> +    env->rampZ = 0;
> +    env->eind = 0;
> +    env->sp = 0;
> +
> +    env->skip = 0;
> +
> +    memset(env->r, 0, sizeof(env->r));
> +
> +    tlb_flush(cs);
> +}
Aleksandar Markovic Jan. 29, 2020, 3:11 p.m. UTC | #6
On Wed, Jan 29, 2020 at 1:20 PM Sarah Harris <seh53@kent.ac.uk> wrote:
>
> Hi,
>
> I think I've found a minor bug: the stack pointer should be initialised to the size of SRAM in some or most cases.
> Currently, SP is initialised to zero.
>

Very good (that you spotted the bug)!

> It seems modern AVRs set SP to the size of SRAM (RAMEND) at power-on, though a few older ones initialise to zero.
> The ATmega328 (from 2009) [1], ATmega2560 (from 2005) [2], ATtiny2313 (from 2003) [6], and ATtiny85 (from 2005) [3] all use RAMEND.
> The ATmega8 (from 2001) [4], ATmega8535 (from 2002) [5], and AT90S8535 (from 1998) [7] use zero.

It looks we'll have tremendous "fun" dealing with huge variety of AVR SoCs.

> I haven't found a list of which AVRs use which value (other than reading every datasheet).

Yes, I find this to be a big practical problem, this glaring lack of
some summary documentation, and, unfortunately, this affects not only
this particular case (SP initialization). It appears to me that for
many features and details (especially regarding SoC devices) one will
have to carefully examine multiple documents, as you did in this
email.

>
> Given that GCC performs this initialisation in software anyway (so what the hardware does doesn't matter), I think this is a minor issue.
> It will only affect hand written assembly programs that don't do their own initialisation (which seems to be discouraged as not all resets are power-on events).
> I'm not sure what, if anything, needs to be done about it but it might be worth fixing now we're emulating specific chips.
>

Let's think a little bit about it. In my honest opinion, this may be
handled after the series merge, and if you guys agree, there is no
rush.

> Kind regards,
> Sarah Harris
>

Thanks!
Aleksandar

> [1] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf (section 6.5.1)
> [2] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf (section 7.6)
> [3] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf (section 4.6.1)
> [4] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2486-8-bit-AVR-microcontroller-ATmega8_L_datasheet.pdf (page 13)
> [5] http://ww1.microchip.com/downloads/en/DeviceDoc/doc2502.pdf (page 12)
> [6] http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2543-AVR-ATtiny2313_Datasheet.pdf (page 11)
> [7] http://ww1.microchip.com/downloads/en/DeviceDoc/doc1041.pdf (page 20)
>
> On Sun, 26 Jan 2020 23:54:43 +0100
> Aleksandar Markovic <aleksandar.markovic@rt-rk.com> wrote:
>
> > +static void avr_cpu_reset(CPUState *cs)
> > +{
> > +    AVRCPU *cpu = AVR_CPU(cs);
> > +    AVRCPUClass *mcc = AVR_CPU_GET_CLASS(cpu);
> > +    CPUAVRState *env = &cpu->env;
> > +
> > +    mcc->parent_reset(cs);
> > +
> > +    env->pc_w = 0;
> > +    env->sregI = 1;
> > +    env->sregC = 0;
> > +    env->sregZ = 0;
> > +    env->sregN = 0;
> > +    env->sregV = 0;
> > +    env->sregS = 0;
> > +    env->sregH = 0;
> > +    env->sregT = 0;
> > +
> > +    env->rampD = 0;
> > +    env->rampX = 0;
> > +    env->rampY = 0;
> > +    env->rampZ = 0;
> > +    env->eind = 0;
> > +    env->sp = 0;
> > +
> > +    env->skip = 0;
> > +
> > +    memset(env->r, 0, sizeof(env->r));
> > +
> > +    tlb_flush(cs);
> > +}
Michael Rolnik Jan. 29, 2020, 3:34 p.m. UTC | #7
Sounds good.

Sent from my cell phone, please ignore typos

On Wed, Jan 29, 2020, 5:12 PM Aleksandar Markovic <
aleksandar.m.mail@gmail.com> wrote:

> On Wed, Jan 29, 2020 at 1:20 PM Sarah Harris <seh53@kent.ac.uk> wrote:
> >
> > Hi,
> >
> > I think I've found a minor bug: the stack pointer should be initialised
> to the size of SRAM in some or most cases.
> > Currently, SP is initialised to zero.
> >
>
> Very good (that you spotted the bug)!
>
> > It seems modern AVRs set SP to the size of SRAM (RAMEND) at power-on,
> though a few older ones initialise to zero.
> > The ATmega328 (from 2009) [1], ATmega2560 (from 2005) [2], ATtiny2313
> (from 2003) [6], and ATtiny85 (from 2005) [3] all use RAMEND.
> > The ATmega8 (from 2001) [4], ATmega8535 (from 2002) [5], and AT90S8535
> (from 1998) [7] use zero.
>
> It looks we'll have tremendous "fun" dealing with huge variety of AVR SoCs.
>
> > I haven't found a list of which AVRs use which value (other than reading
> every datasheet).
>
> Yes, I find this to be a big practical problem, this glaring lack of
> some summary documentation, and, unfortunately, this affects not only
> this particular case (SP initialization). It appears to me that for
> many features and details (especially regarding SoC devices) one will
> have to carefully examine multiple documents, as you did in this
> email.
>
> >
> > Given that GCC performs this initialisation in software anyway (so what
> the hardware does doesn't matter), I think this is a minor issue.
> > It will only affect hand written assembly programs that don't do their
> own initialisation (which seems to be discouraged as not all resets are
> power-on events).
> > I'm not sure what, if anything, needs to be done about it but it might
> be worth fixing now we're emulating specific chips.
> >
>
> Let's think a little bit about it. In my honest opinion, this may be
> handled after the series merge, and if you guys agree, there is no
> rush.
>
> > Kind regards,
> > Sarah Harris
> >
>
> Thanks!
> Aleksandar
>
> > [1]
> http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
> (section 6.5.1)
> > [2]
> http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf
> (section 7.6)
> > [3]
> http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf
> (section 4.6.1)
> > [4]
> http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2486-8-bit-AVR-microcontroller-ATmega8_L_datasheet.pdf
> (page 13)
> > [5] http://ww1.microchip.com/downloads/en/DeviceDoc/doc2502.pdf (page
> 12)
> > [6]
> http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2543-AVR-ATtiny2313_Datasheet.pdf
> (page 11)
> > [7] http://ww1.microchip.com/downloads/en/DeviceDoc/doc1041.pdf (page
> 20)
> >
> > On Sun, 26 Jan 2020 23:54:43 +0100
> > Aleksandar Markovic <aleksandar.markovic@rt-rk.com> wrote:
> >
> > > +static void avr_cpu_reset(CPUState *cs)
> > > +{
> > > +    AVRCPU *cpu = AVR_CPU(cs);
> > > +    AVRCPUClass *mcc = AVR_CPU_GET_CLASS(cpu);
> > > +    CPUAVRState *env = &cpu->env;
> > > +
> > > +    mcc->parent_reset(cs);
> > > +
> > > +    env->pc_w = 0;
> > > +    env->sregI = 1;
> > > +    env->sregC = 0;
> > > +    env->sregZ = 0;
> > > +    env->sregN = 0;
> > > +    env->sregV = 0;
> > > +    env->sregS = 0;
> > > +    env->sregH = 0;
> > > +    env->sregT = 0;
> > > +
> > > +    env->rampD = 0;
> > > +    env->rampX = 0;
> > > +    env->rampY = 0;
> > > +    env->rampZ = 0;
> > > +    env->eind = 0;
> > > +    env->sp = 0;
> > > +
> > > +    env->skip = 0;
> > > +
> > > +    memset(env->r, 0, sizeof(env->r));
> > > +
> > > +    tlb_flush(cs);
> > > +}
>
diff mbox series

Patch

diff --git a/target/avr/cpu-qom.h b/target/avr/cpu-qom.h
new file mode 100644
index 0000000..e28b58c
--- /dev/null
+++ b/target/avr/cpu-qom.h
@@ -0,0 +1,54 @@ 
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2019 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#ifndef QEMU_AVR_QOM_H
+#define QEMU_AVR_QOM_H
+
+#include "hw/core/cpu.h"
+
+#define TYPE_AVR_CPU "avr-cpu"
+
+#define AVR_CPU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(AVRCPUClass, (klass), TYPE_AVR_CPU)
+#define AVR_CPU(obj) \
+    OBJECT_CHECK(AVRCPU, (obj), TYPE_AVR_CPU)
+#define AVR_CPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(AVRCPUClass, (obj), TYPE_AVR_CPU)
+
+/**
+ *  AVRCPUClass:
+ *  @parent_realize: The parent class' realize handler.
+ *  @parent_reset: The parent class' reset handler.
+ *  @vr: Version Register value.
+ *
+ *  A AVR CPU model.
+ */
+typedef struct AVRCPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+    DeviceRealize parent_realize;
+    void (*parent_reset)(CPUState *cpu);
+} AVRCPUClass;
+
+typedef struct AVRCPU AVRCPU;
+
+
+#endif /* !defined (QEMU_AVR_CPU_QOM_H) */
diff --git a/target/avr/cpu.c b/target/avr/cpu.c
new file mode 100644
index 0000000..3f3d840
--- /dev/null
+++ b/target/avr/cpu.c
@@ -0,0 +1,220 @@ 
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2019 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/qemu-print.h"
+#include "exec/exec-all.h"
+#include "cpu.h"
+#include "disas/dis-asm.h"
+
+static void avr_cpu_set_pc(CPUState *cs, vaddr value)
+{
+    AVRCPU *cpu = AVR_CPU(cs);
+
+    cpu->env.pc_w = value / 2; /* internally PC points to words */
+}
+
+static bool avr_cpu_has_work(CPUState *cs)
+{
+    AVRCPU *cpu = AVR_CPU(cs);
+    CPUAVRState *env = &cpu->env;
+
+    return (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RESET))
+            && cpu_interrupts_enabled(env);
+}
+
+static void avr_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
+{
+    AVRCPU *cpu = AVR_CPU(cs);
+    CPUAVRState *env = &cpu->env;
+
+    env->pc_w = tb->pc / 2; /* internally PC points to words */
+}
+
+static void avr_cpu_reset(CPUState *cs)
+{
+    AVRCPU *cpu = AVR_CPU(cs);
+    AVRCPUClass *mcc = AVR_CPU_GET_CLASS(cpu);
+    CPUAVRState *env = &cpu->env;
+
+    mcc->parent_reset(cs);
+
+    env->pc_w = 0;
+    env->sregI = 1;
+    env->sregC = 0;
+    env->sregZ = 0;
+    env->sregN = 0;
+    env->sregV = 0;
+    env->sregS = 0;
+    env->sregH = 0;
+    env->sregT = 0;
+
+    env->rampD = 0;
+    env->rampX = 0;
+    env->rampY = 0;
+    env->rampZ = 0;
+    env->eind = 0;
+    env->sp = 0;
+
+    env->skip = 0;
+
+    memset(env->r, 0, sizeof(env->r));
+
+    tlb_flush(cs);
+}
+
+static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
+{
+    info->mach = bfd_arch_avr;
+    info->print_insn = NULL;
+}
+
+static void avr_cpu_realizefn(DeviceState *dev, Error **errp)
+{
+    CPUState *cs = CPU(dev);
+    AVRCPUClass *mcc = AVR_CPU_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    cpu_exec_realizefn(cs, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    qemu_init_vcpu(cs);
+    cpu_reset(cs);
+
+    mcc->parent_realize(dev, errp);
+}
+
+static void avr_cpu_set_int(void *opaque, int irq, int level)
+{
+    AVRCPU *cpu = opaque;
+    CPUAVRState *env = &cpu->env;
+    CPUState *cs = CPU(cpu);
+
+    uint64_t mask = (1ull << irq);
+    if (level) {
+        env->intsrc |= mask;
+        cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+    } else {
+        env->intsrc &= ~mask;
+        if (env->intsrc == 0) {
+            cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+        }
+    }
+}
+
+static void avr_cpu_initfn(Object *obj)
+{
+    AVRCPU *cpu = AVR_CPU(obj);
+
+    cpu_set_cpustate_pointers(cpu);
+
+#ifndef CONFIG_USER_ONLY
+    /* Set the number of interrupts supported by the CPU. */
+    qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int,
+            sizeof(cpu->env.intsrc) * 8);
+#endif
+}
+
+static ObjectClass *avr_cpu_class_by_name(const char *cpu_model)
+{
+    ObjectClass *oc;
+
+    oc = object_class_by_name(cpu_model);
+    if (object_class_dynamic_cast(oc, TYPE_AVR_CPU) == NULL ||
+        object_class_is_abstract(oc)) {
+        oc = NULL;
+    }
+    return oc;
+}
+
+static void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags)
+{
+    AVRCPU *cpu = AVR_CPU(cs);
+    CPUAVRState *env = &cpu->env;
+    int i;
+
+    qemu_fprintf(f, "\n");
+    qemu_fprintf(f, "PC:    %06x\n", env->pc_w);
+    qemu_fprintf(f, "SP:      %04x\n", env->sp);
+    qemu_fprintf(f, "rampD:     %02x\n", env->rampD >> 16);
+    qemu_fprintf(f, "rampX:     %02x\n", env->rampX >> 16);
+    qemu_fprintf(f, "rampY:     %02x\n", env->rampY >> 16);
+    qemu_fprintf(f, "rampZ:     %02x\n", env->rampZ >> 16);
+    qemu_fprintf(f, "EIND:      %02x\n", env->eind >> 16);
+    qemu_fprintf(f, "X:       %02x%02x\n", env->r[27], env->r[26]);
+    qemu_fprintf(f, "Y:       %02x%02x\n", env->r[29], env->r[28]);
+    qemu_fprintf(f, "Z:       %02x%02x\n", env->r[31], env->r[30]);
+    qemu_fprintf(f, "SREG:    [ %c %c %c %c %c %c %c %c ]\n",
+                        env->sregI ? 'I' : '-',
+                        env->sregT ? 'T' : '-',
+                        env->sregH ? 'H' : '-',
+                        env->sregS ? 'S' : '-',
+                        env->sregV ? 'V' : '-',
+                        env->sregN ? '-' : 'N', /* Zf has negative logic */
+                        env->sregZ ? 'Z' : '-',
+                        env->sregC ? 'I' : '-');
+    qemu_fprintf(f, "SKIP:    %02x\n", env->skip);
+
+    qemu_fprintf(f, "\n");
+    for (i = 0; i < ARRAY_SIZE(env->r); i++) {
+        qemu_fprintf(f, "R[%02d]:  %02x   ", i, env->r[i]);
+
+        if ((i % 8) == 7) {
+            qemu_fprintf(f, "\n");
+        }
+    }
+    qemu_fprintf(f, "\n");
+}
+
+static void avr_cpu_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    CPUClass *cc = CPU_CLASS(oc);
+    AVRCPUClass *mcc = AVR_CPU_CLASS(oc);
+
+    mcc->parent_realize = dc->realize;
+    dc->realize = avr_cpu_realizefn;
+
+    mcc->parent_reset = cc->reset;
+    cc->reset = avr_cpu_reset;
+
+    cc->class_by_name = avr_cpu_class_by_name;
+
+    cc->has_work = avr_cpu_has_work;
+    cc->do_interrupt = avr_cpu_do_interrupt;
+    cc->cpu_exec_interrupt = avr_cpu_exec_interrupt;
+    cc->dump_state = avr_cpu_dump_state;
+    cc->set_pc = avr_cpu_set_pc;
+#if !defined(CONFIG_USER_ONLY)
+    cc->memory_rw_debug = avr_cpu_memory_rw_debug;
+#endif
+#ifdef CONFIG_USER_ONLY
+    cc->handle_mmu_fault = avr_cpu_handle_mmu_fault;
+#else
+    cc->get_phys_page_debug = avr_cpu_get_phys_page_debug;
+#endif
+    cc->disas_set_info = avr_cpu_disas_set_info;
+    cc->tlb_fill = avr_cpu_tlb_fill;
+    cc->tcg_initialize = avr_cpu_tcg_init;
+    cc->synchronize_from_tb = avr_cpu_synchronize_from_tb;
+}
diff --git a/target/avr/cpu.h b/target/avr/cpu.h
index d122611..f7a403a 100644
--- a/target/avr/cpu.h
+++ b/target/avr/cpu.h
@@ -69,4 +69,138 @@ 
 
 #define EF_AVR_MACH 0x7F
 
+typedef struct CPUAVRState CPUAVRState;
+
+struct CPUAVRState {
+    uint32_t pc_w; /* 0x003fffff up to 22 bits */
+
+    uint32_t sregC; /* 0x00000001 1 bit */
+    uint32_t sregZ; /* 0x00000001 1 bit */
+    uint32_t sregN; /* 0x00000001 1 bit */
+    uint32_t sregV; /* 0x00000001 1 bit */
+    uint32_t sregS; /* 0x00000001 1 bit */
+    uint32_t sregH; /* 0x00000001 1 bit */
+    uint32_t sregT; /* 0x00000001 1 bit */
+    uint32_t sregI; /* 0x00000001 1 bit */
+
+    uint32_t rampD; /* 0x00ff0000 8 bits */
+    uint32_t rampX; /* 0x00ff0000 8 bits */
+    uint32_t rampY; /* 0x00ff0000 8 bits */
+    uint32_t rampZ; /* 0x00ff0000 8 bits */
+    uint32_t eind; /* 0x00ff0000 8 bits */
+
+    uint32_t r[NUMBER_OF_CPU_REGISTERS]; /* 8 bits each */
+    uint32_t sp; /* 16 bits */
+
+    uint32_t skip; /* if set skip instruction */
+
+    uint64_t intsrc; /* interrupt sources */
+    bool fullacc; /* CPU/MEM if true MEM only otherwise */
+
+    uint32_t features;
+};
+
+/**
+ *  AVRCPU:
+ *  @env: #CPUAVRState
+ *
+ *  A AVR CPU.
+ */
+typedef struct AVRCPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+
+    CPUNegativeOffsetState neg;
+    CPUAVRState env;
+} AVRCPU;
+
+void avr_cpu_do_interrupt(CPUState *cpu);
+bool avr_cpu_exec_interrupt(CPUState *cpu, int int_req);
+hwaddr avr_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+
+#define cpu_list avr_cpu_list
+#define cpu_signal_handler cpu_avr_signal_handler
+#define cpu_mmu_index avr_cpu_mmu_index
+
+static inline int avr_cpu_mmu_index(CPUAVRState *env, bool ifetch)
+{
+    return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX;
+}
+
+void avr_cpu_tcg_init(void);
+
+void avr_cpu_list(void);
+int cpu_avr_exec(CPUState *cpu);
+int cpu_avr_signal_handler(int host_signum, void *pinfo, void *puc);
+int avr_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
+                                int rw, int mmu_idx);
+int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf,
+                                int len, bool is_write);
+
+enum {
+    TB_FLAGS_FULL_ACCESS = 1,
+    TB_FLAGS_SKIP = 2,
+};
+
+static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc,
+                                target_ulong *cs_base, uint32_t *pflags)
+{
+    uint32_t flags = 0;
+
+    *pc = env->pc_w * 2;
+    *cs_base = 0;
+
+    if (env->fullacc) {
+        flags |= TB_FLAGS_FULL_ACCESS;
+    }
+    if (env->skip) {
+        flags |= TB_FLAGS_SKIP;
+    }
+
+    *pflags = flags;
+}
+
+static inline int cpu_interrupts_enabled(CPUAVRState *env)
+{
+    return env->sregI != 0;
+}
+
+static inline uint8_t cpu_get_sreg(CPUAVRState *env)
+{
+    uint8_t sreg;
+    sreg = (env->sregC) << 0
+         | (env->sregZ) << 1
+         | (env->sregN) << 2
+         | (env->sregV) << 3
+         | (env->sregS) << 4
+         | (env->sregH) << 5
+         | (env->sregT) << 6
+         | (env->sregI) << 7;
+    return sreg;
+}
+
+static inline void cpu_set_sreg(CPUAVRState *env, uint8_t sreg)
+{
+    env->sregC = (sreg >> 0) & 0x01;
+    env->sregZ = (sreg >> 1) & 0x01;
+    env->sregN = (sreg >> 2) & 0x01;
+    env->sregV = (sreg >> 3) & 0x01;
+    env->sregS = (sreg >> 4) & 0x01;
+    env->sregH = (sreg >> 5) & 0x01;
+    env->sregT = (sreg >> 6) & 0x01;
+    env->sregI = (sreg >> 7) & 0x01;
+}
+
+bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+                        MMUAccessType access_type, int mmu_idx,
+                        bool probe, uintptr_t retaddr);
+
+typedef CPUAVRState CPUArchState;
+typedef AVRCPU ArchCPU;
+
+#include "exec/cpu-all.h"
+
+const char *avr_flags_to_cpu_type(uint32_t flags, const char *def_cpu_type);
+
 #endif /* !defined (QEMU_AVR_CPU_H) */