Message ID | 1580079311-20447-3-git-send-email-aleksandar.markovic@rt-rk.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | target/avr merger | expand |
> +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 >
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
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.
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 > > >
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); > +}
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); > > +}
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 --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) */