diff --git a/hw/arc/boot.c b/hw/arc/boot.c index 39cae230fa7..387c212d751 100644 --- a/hw/arc/boot.c +++ b/hw/arc/boot.c @@ -41,7 +41,7 @@ void arc_cpu_reset(void *opaque) * via CPU registers we have to do it here. */ - if (info->kernel_cmdline && strlen(info->kernel_cmdline)) { + if (info && info->kernel_cmdline && strlen(info->kernel_cmdline)) { /* Load "cmdline" far enough from the kernel image. */ hwaddr cmdline_offset, cmdline_addr; const hwaddr max_page_size = 64 * KiB; @@ -68,6 +68,7 @@ void arc_cpu_reset(void *opaque) void arc_load_kernel(ARCCPU *cpu, struct arc_boot_info *info) { hwaddr entry; + CPUState *cs; int elf_machine, kernel_size; if (!info->kernel_filename) { @@ -98,10 +99,11 @@ void arc_load_kernel(ARCCPU *cpu, struct arc_boot_info *info) exit(EXIT_FAILURE); } - cpu->env.boot_info = info; - /* Set CPU's PC to point to the entry-point */ - cpu->env.pc = entry; + CPU_FOREACH(cs) { + ARC_CPU(cs)->env.pc = entry; + ARC_CPU(cs)->env.boot_info = info; + } } diff --git a/hw/arc/virt.c b/hw/arc/virt.c index 05ffb70705f..73661d1602a 100644 --- a/hw/arc/virt.c +++ b/hw/arc/virt.c @@ -26,6 +26,8 @@ #include "hw/pci-host/gpex.h" #include "hw/sysbus.h" #include "hw/arc/virt.h" +#include "target/arc/arconnect.h" +#include "target/arc/cpu.h" #define VIRT_IO_BASE 0xf0000000 #define VIRT_IO_SIZE 0x10000000 @@ -124,6 +126,17 @@ static void virt_init(MachineState *machine) boot_info.kernel_filename = machine->kernel_filename; boot_info.kernel_cmdline = machine->kernel_cmdline; + /* + * HS4x and HS5x reside in one TARGET_ARC32 machine class. Maximum number + * of CPUs in MachineClass is the highest number for all ARC families (12). + * For HS4x it's 4. Thus, we have to check whether a valid CPUs number is + * passed for HS4x. + */ + if (!g_strcmp0(machine->cpu_type, TYPE_ARC_CPU_ARCHS) && smp_cpus > 4) { + error_report("ARCv2 supports only up to 4 cores! %u is passed.", smp_cpus); + exit(EXIT_FAILURE); + } + for (n = 0; n < smp_cpus; n++) { #if defined(TARGET_ARC32) cpu = ARC_CPU(cpu_create(machine->cpu_type)); @@ -133,10 +146,12 @@ static void virt_init(MachineState *machine) #error "Should not happen. Something is wrong." #endif if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition!\n"); - exit(1); + error_report("Unable to find CPU definition!"); + exit(EXIT_FAILURE); } + cpu->core_id = n; + /* Initialize internal devices. */ cpu_arc_pic_init(cpu); cpu_arc_clock_init(cpu); @@ -157,32 +172,38 @@ static void virt_init(MachineState *machine) /* Init IO area */ system_io = g_new(MemoryRegion, 1); - memory_region_init_io(system_io, NULL, NULL, NULL, "arc.io", - VIRT_IO_SIZE); + memory_region_init_io(system_io, NULL, NULL, NULL, "arc.io", VIRT_IO_SIZE); memory_region_add_subregion(system_memory, VIRT_IO_BASE, system_io); + /* + * Initialize all devices on the fist CPU since there is no complete + * support of IDU in ARConnect in case of SMP configuration. In real + * hardware such interrupts are connect to the distinct IDU interrupt + * controller. + */ + for (n = 0; n < VIRT_UART_NUMBER; n++) { serial_mm_init(system_io, VIRT_UART_OFFSET + VIRT_UART_SIZE * n, 2, - cpu->env.irq[VIRT_UART_IRQ + n], 115200, serial_hd(n), - DEVICE_NATIVE_ENDIAN); + ARC_CPU(first_cpu)->env.irq[VIRT_UART_IRQ + n], 115200, + serial_hd(n), DEVICE_NATIVE_ENDIAN); } for (n = 0; n < VIRT_VIRTIO_NUMBER; n++) { sysbus_create_simple("virtio-mmio", VIRT_VIRTIO_BASE + VIRT_VIRTIO_SIZE * n, - cpu->env.irq[VIRT_VIRTIO_IRQ + n]); + ARC_CPU(first_cpu)->env.irq[VIRT_VIRTIO_IRQ + n]); } - create_pcie(cpu); + create_pcie(ARC_CPU(first_cpu)); - arc_load_kernel(cpu, &boot_info); + arc_load_kernel(ARC_CPU(first_cpu), &boot_info); } static void virt_machine_init(MachineClass *mc) { mc->desc = "ARC Virtual Machine"; mc->init = virt_init; - mc->max_cpus = 1; + mc->max_cpus = ARC_MAX_CORES_NUMBER; mc->is_default = true; mc->default_ram_size = 2 * GiB; } diff --git a/target/arc/arconnect.c b/target/arc/arconnect.c index 0f89ff75dfd..fffb481d2bd 100644 --- a/target/arc/arconnect.c +++ b/target/arc/arconnect.c @@ -1,7 +1,7 @@ /* * QEMU ARC CPU * - * Copyright (c) 2021 Synppsys Inc. + * Copyright (c) 2021 Synopsys Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,272 +17,612 @@ * License along with this library; if not, see * http://www.gnu.org/licenses/lgpl-2.1.html */ + #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/error-report.h" #include "target/arc/regs.h" #include "target/arc/cpu.h" #include "target/arc/arconnect.h" +#include "target/arc/irq.h" #include "hw/irq.h" +#include "exec/exec-all.h" +#include "qemu/main-loop.h" +#include "target/arc/timer.h" -#define ICI_IRQ 19 +#ifndef CONFIG_USER_ONLY +#include "hw/boards.h" +#endif -struct lpa_lf_entry lpa_lfs[LPA_LFS_SIZE]; +/* + * Definition of ARConnect's state structures: a general state, LPA/LF hash + * table and an array of common interrupts. + */ -enum arconnect_commands { - CMD_CHECK_CORE_ID = 0x0, - CMD_INTRPT_GENERATE_IRQ = 0x1, - CMD_INTRPT_GENERATE_ACK, - CMD_INTRPT_READ_STATUS, - CMD_INTRPT_CHECK_SOURCE, - CMD_SEMA_CLAIM_AND_READ = 0x11, - CMD_SEMA_SEMA_RELEASE, - CMD_SEMA_FORCE_RELEASE, - CMD_MSG_SRAM_SET_ADDR = 0x21, - CMD_MSG_SRAM_READ_ADDR, - CMD_MSG_SRAM_SET_ADDR_OFFSET, - CMD_MSG_SRAM_READ_ARRR_OFFSET, - CMD_MSG_SRAM_WRITE, - CMD_MSG_SRAM_WRITE_INC, - CMD_MSG_SRAM_WRITE_IMM, - CMD_MSG_SRAM_READ, - CMD_MSG_SRAM_READ_INC, - CMD_MSG_SRAM_READ_IMM, - CMD_MSG_SET_ECC_CTRL, - CMD_MSG_READ_ECC_CTRL, - CMD_MSG_READ_ECC_SBE_CNT, - CMD_MSG_CLEAR_ECC_SBE_CNT, - CMD_DEBUG_RESET = 0x31, - CMD_DEBUG_HALT, - CMD_DEBUG_RUN, - CMD_DEBUG_SET_MASK, - CMD_DEBUG_READ_MASK, - CMD_DEBUG_SET_SELECT, - CMD_DEBUG_READ_SELECT, - CMD_DEBUG_READ_EN, - CMD_DEBUG_READ_CMD, - CMD_DEBUG_READ_CORE, - CMD_GFRC_CLEAR = 0x41, - CMD_GFRC_READ_LO, - CMD_GFRC_READ_HI, - CMD_GFRC_ENABLE, - CMD_GFRC_DISABLE, - CMD_GFRC_READ_DISABLE, - CMD_GFRC_SET_CORE, - CMD_GFRC_READ_CORE, - CMD_GFRC_READ_HALT, - CMD_PMU_SET_PUCNT = 0x51, - CMD_PMU_READ_PICNT, - CMD_PMU_SET_RSTCNT, - CMD_PMU_READ_RSTCNT, - CMD_PMU_SET_PDCNT, - CMD_PMU_READ_PDCNT, - CMD_IDU_ENABLE = 0x71, - CMD_IDU_DISABLE, - CMD_IDU_READ_ENABLE, - CMD_IDU_SET_MODE, - CMD_IDU_READ_MODE, - CMD_IDU_SET_DEST, - CMD_IDU_READ_DEST, - CMD_IDU_GEN_CIRQ, - CMD_IDU_ACK_CIRQ, - CMD_IDU_CHECK_STATUS, - CMD_IDU_CHECK_SOURCE, - CMD_IDU_SET_MASK, - CMD_IDU_READ_MASK, - CMD_IDU_CHECK_FIRST, - CMD_IDU_SET_PM = 0x81, - CMD_IDU_READ_PSTATUS -}; +ARCArconnectGlobalState arconnect_state; +ARCArconnectCommonIRQ arconnect_cirq_array[MAX_NR_OF_COMMON_IRQS]; +struct lpa_lf_entry lpa_lfs[LPA_LFS_SIZE]; /* - * Setup SMP (arconnect) related data structures + * A function for initializing of ARConnect state. ARConnect state may be + * initialized only once. */ -void arc_arconnect_init(ARCCPU *cpu) + +static void init_arconnect_state(ARCArconnectGlobalState *state) { - cpu->env.arconnect.intrpt_status = 0; + /* + * Get a number of cores connected to ARConnect from machine's + * MachineState structure. It's not available and meaningless in user mode, + * thus prevent reading MachineState in this mode. + */ +#ifndef CONFIG_USER_ONLY + uint32_t corenum = MACHINE(qdev_get_machine())->smp.cpus; +#else + uint32_t corenum = 1; +#endif - /* Initialize all llock/scond lpa entries and respective mutexes */ + uint32_t reg; int i; - for(i = 0; i < LPA_LFS_SIZE; i++) { - lpa_lfs[i].lpa_lf = 0; - qemu_mutex_init(&lpa_lfs[i].mutex); - } - cpu->env.arconnect.lpa_lf = &(lpa_lfs[0]); - cpu->env.arconnect.locked_mutex = &(lpa_lfs[0].mutex); -} + if (!qatomic_xchg(&state->initialized, true)) { + /* Initialize subsystems' mutexes*/ + qemu_mutex_init(&state->ici_mutex); -/* TODO: Find a better way to get cpu for core. */ -static ARCCPU *get_cpu_for_core(uint8_t core_id) -{ - CPUState *cs; - ARCCPU *ret = NULL; - CPU_FOREACH(cs) { - if(ARC_CPU(cs)->core_id == core_id) { - ret = ARC_CPU(cs); + /* Initialize ARConnect BCR */ + reg = FIELD_DP32(0, CONNECT_SYSTEM_BUILD, VERSION, 0x3); + reg = FIELD_DP32(reg, CONNECT_SYSTEM_BUILD, ICI, 0x1); + reg = FIELD_DP32(reg, CONNECT_SYSTEM_BUILD, GFRC, 0x1); + reg = FIELD_DP32(reg, CONNECT_SYSTEM_BUILD, CORENUM, corenum); + reg = FIELD_DP32(reg, CONNECT_SYSTEM_BUILD, IDU, 0x1); + state->system_build = reg; + + /* Initialize IDU BCR */ + reg = FIELD_DP32(0, CONNECT_IDU_BUILD, VERSION, ARCONNECT_IDU_VERSION); + reg = FIELD_DP32(reg, CONNECT_IDU_BUILD, CIRQNUM, ARCONNECT_IDU_CIRQNUM); + state->idu_build = reg; + + /* Initialize GFRC BCR */ + reg = FIELD_DP32(0, CONNECT_GFRC_BUILD, VERSION, ARCONNECT_GFRC_VERSION); + state->gfrc_build = reg; + + /* Initialize ICI BCR */ + reg = FIELD_DP32(0, CONNECT_ICI_BUILD, VERSION, ARCONNECT_ICI_VERSION); + state->ici_build = reg; + + /* By default, IDU is disabled. */ + state->idu_enabled = false; + + /* Initialize all llock/scond lpa entries and respective mutexes */ + for(i = 0; i < LPA_LFS_SIZE; i++) { + lpa_lfs[i].lpa_lf = 0; + qemu_mutex_init(&lpa_lfs[i].mutex); } } - return ret; } -static void arcon_status_set(CPUARCState *env, uint8_t status_core_id, uint8_t sender_core_id) -{ - ARCCPU *cpu = env_archcpu(env); - qemu_log_mask(CPU_LOG_INT, - "[ICI %d] Set intrpt_status in core %d for sender %d\n", - cpu->core_id, status_core_id, sender_core_id); - qatomic_or(&(get_cpu_for_core(status_core_id)->env.arconnect.intrpt_status), - 1 << sender_core_id); -} -static void arcon_status_clr(CPUARCState *env, uint8_t status_core_id, uint8_t sender_core_id) + +/* + * Get ARCCPU object core that corresponds to the particular CORE_ID. It's a + * a useful helper for ARConnect commands since some commands address CPU's by + * CORE_ID. + */ + +static ARCCPU *core_id_to_cpu[ARC_MAX_CORES_NUMBER]; + +static ARCCPU *get_cpu_for_core(uint8_t core_id) { - ARCCPU *cpu = env_archcpu(env); - qemu_log_mask(CPU_LOG_INT, - "[ICI %d] Clear intrpt_status in core %d for sender %d\n", - cpu->core_id, status_core_id, sender_core_id); - qatomic_and(&(get_cpu_for_core(status_core_id)->env.arconnect.intrpt_status), - ~(1 << sender_core_id)); + assert(core_id < ARC_MAX_CORES_NUMBER); + + return core_id_to_cpu[core_id]; } -static uint64_t arcon_status_read(CPUARCState *env, uint8_t core_id) + +/* + * This function is used to initialize per-CPU ARConnect structures. Global + * ARConnect state is initialized by init_arconnect_state only once. + */ + +void arc_arconnect_init(ARCCPU *cpu) { - ARCCPU *cpu = env_archcpu(env); - uint64_t ret = qatomic_read(&(get_cpu_for_core(core_id)->env.arconnect.intrpt_status)); - qemu_log_mask(CPU_LOG_INT, - "[ICI %d] Reading intrpt_status in core %d. (read: 0x%lx)\n", - cpu->core_id, core_id, ret); - return ret; + init_arconnect_state(&arconnect_state); + + assert(cpu->core_id < ARC_MAX_CORES_NUMBER); + + core_id_to_cpu[cpu->core_id] = cpu; + cpu->env.arconnect.pending_senders = 0; + cpu->env.arconnect.lpa_lf = &(lpa_lfs[0]); + cpu->env.arconnect.locked_mutex = &(lpa_lfs[0].mutex); } +/* + * An ARConnect command is sent to ARConnect and processed when a command with + * an optional parameter is saved to CONNECT_CMD auxilary register. Depending + * on a command, CONNECT_WDATA may be used to send extra data to ARConnect. + * An answer for ARConnect is stored CONNECT_READBACK. + * + * Each ARConnect subsystem has its own set of ARConnect commands. + * arconnect_command_handler receives a command and passes it to a corresponding + * subsystem handler: + * + * ici_command_handler - Inter-Core Interrupt unit commands + * idu_command_handler - Interrupt Distribution Unit commands + * gfrc_command_handler - Global Free Running Counter commands + */ + +#define ARCONNECT_COMMAND(NAME, VALUE) [VALUE] = #NAME, +const char *arc_arconnect_command_name_array[] = { +#include "arconnect_commands.def" +}; +#undef ARCONNECT_COMMAND -#define CMD_COREID(V) ((V >> 8) & 0xff) -#define MCIP_IRQ 19 +static void ici_command_handler(CPUARCState *env, uint8_t command, uint16_t parameter); +static void idu_command_handler(CPUARCState *env, uint8_t command, uint16_t parameter); +static void gfrc_command_handler(CPUARCState *env, uint8_t command, uint16_t parameter); -static void arconnect_intercore_intr_unit_cmd(CPUARCState *env, enum arconnect_commands cmd, uint16_t param) +static void arconnect_command_handler(CPUARCState *env) { - ARCCPU *cpu = env_archcpu(env); + uint8_t command = FIELD_EX32(env->arconnect.cmd, CONNECT_CMD, COMMAND); + uint16_t parameter = FIELD_EX32(env->arconnect.cmd, CONNECT_CMD, PARAMETER); + uint8_t core_id = env_archcpu(env)->core_id; - switch(cmd) { - - case CMD_INTRPT_GENERATE_IRQ: - { - if(((param & 0x80) == 0) && ((param & 0x1f) != cpu->core_id)) { - uint8_t core_id = param & 0x1f; - arcon_status_set(env, core_id, cpu->core_id); - qemu_set_irq(get_cpu_for_core(core_id)->env.irq[MCIP_IRQ], 1); - } - //uint8_t core_id = CMD_COREID(cmd); - //CPUState *cs; - //CPU_FOREACH(cs) { - // ARCCPU *cpu = ARC_CPU(cs); - //} - } + qemu_log_mask(CPU_LOG_INT, + "[ICI %d] Process command %s with param %d.\n", + core_id, arc_arconnect_command_name_array[command], parameter); + + switch(command) { + case ARCONNECT_CMD_CHECK_CORE_ID: + env->arconnect.readback = core_id & 0x1f; break; - case CMD_INTRPT_GENERATE_ACK: - { - uint8_t core_id = param & 0x1f; - arcon_status_clr(env, cpu->core_id, core_id); - } + case ARCONNECT_CMD_INTRPT_GENERATE_IRQ: + case ARCONNECT_CMD_INTRPT_GENERATE_ACK: + case ARCONNECT_CMD_INTRPT_READ_STATUS: + case ARCONNECT_CMD_INTRPT_CHECK_SOURCE: + case ARCONNECT_CMD_INTRPT_GENERATE_ACK_BIT_MASK: + case ARCONNECT_CMD_INTRPT_EXT_MODE: + case ARCONNECT_CMD_INTRPT_SET_PULSE_CNT: + case ARCONNECT_CMD_INTRPT_READ_PULSE_CNT: + ici_command_handler(env, command, parameter); break; - case CMD_INTRPT_READ_STATUS: - { - uint8_t core_id = param & 0x1f; - env->readback = (arcon_status_read(env, core_id) >> cpu->core_id) & 0x1; - } + case ARCONNECT_CMD_GFRC_CLEAR: + case ARCONNECT_CMD_GFRC_READ_LO: + case ARCONNECT_CMD_GFRC_READ_HI: + case ARCONNECT_CMD_GFRC_ENABLE: + case ARCONNECT_CMD_GFRC_DISABLE: + case ARCONNECT_CMD_GFRC_READ_DISABLE: + case ARCONNECT_CMD_GFRC_SET_CORE: + case ARCONNECT_CMD_GFRC_READ_CORE: + case ARCONNECT_CMD_GFRC_READ_HALT: + case ARCONNECT_CMD_GFRC_CLK_ENABLE: + case ARCONNECT_CMD_GFRC_CLK_DISABLE: + case ARCONNECT_CMD_GFRC_READ_CLK_STATUS: + case ARCONNECT_CMD_GFRC_READ_FULL: + gfrc_command_handler(env, command, parameter); break; - - case CMD_INTRPT_CHECK_SOURCE: - { - env->readback = arcon_status_read(env, cpu->core_id); - } - + case ARCONNECT_CMD_IDU_ENABLE: + case ARCONNECT_CMD_IDU_DISABLE: + case ARCONNECT_CMD_IDU_READ_ENABLE: + case ARCONNECT_CMD_IDU_SET_MODE: + case ARCONNECT_CMD_IDU_READ_MODE: + case ARCONNECT_CMD_IDU_SET_DEST: + case ARCONNECT_CMD_IDU_READ_DEST: + case ARCONNECT_CMD_IDU_GEN_CIRQ: + case ARCONNECT_CMD_IDU_ACK_CIRQ: + case ARCONNECT_CMD_IDU_CHECK_STATUS: + case ARCONNECT_CMD_IDU_CHECK_SOURCE: + case ARCONNECT_CMD_IDU_SET_MASK: + case ARCONNECT_CMD_IDU_READ_MASK: + case ARCONNECT_CMD_IDU_CHECK_FIRST: + idu_command_handler(env, command, parameter); break; + case ARCONNECT_CMD_SEMA_CLAIM_AND_READ: + case ARCONNECT_CMD_SEMA_RELEASE: + case ARCONNECT_CMD_SEMA_FORCE_RELEASE: + error_report("Inter-Core Semaphore unit is not supported yet."); + exit(EXIT_FAILURE); + case ARCONNECT_CMD_MSG_SRAM_SET_ADDR: + case ARCONNECT_CMD_MSG_SRAM_READ_ADDR: + case ARCONNECT_CMD_MSG_SRAM_SET_ADDR_OFFSET: + case ARCONNECT_CMD_MSG_SRAM_READ_ADDR_OFFSET: + case ARCONNECT_CMD_MSG_SRAM_WRITE: + case ARCONNECT_CMD_MSG_SRAM_WRITE_INC: + case ARCONNECT_CMD_MSG_SRAM_WRITE_IMM: + case ARCONNECT_CMD_MSG_SRAM_READ: + case ARCONNECT_CMD_MSG_SRAM_READ_INC: + case ARCONNECT_CMD_MSG_SRAM_READ_IMM: + case ARCONNECT_CMD_MSG_SET_ECC_CTRL: + case ARCONNECT_CMD_MSG_READ_ECC_CTRL: + case ARCONNECT_CMD_MSG_READ_ECC_SBE_CNT: + case ARCONNECT_CMD_MSG_CLEAR_ECC_SBE_CNT: + error_report("Inter-Core Message unit is not supported yet."); + exit(EXIT_FAILURE); + case ARCONNECT_CMD_DEBUG_RESET: + case ARCONNECT_CMD_DEBUG_HALT: + case ARCONNECT_CMD_DEBUG_RUN: + case ARCONNECT_CMD_DEBUG_SET_MASK: + case ARCONNECT_CMD_DEBUG_READ_MASK: + case ARCONNECT_CMD_DEBUG_SET_SELECT: + case ARCONNECT_CMD_DEBUG_READ_SELECT: + case ARCONNECT_CMD_DEBUG_READ_EN: + case ARCONNECT_CMD_DEBUG_READ_CMD: + case ARCONNECT_CMD_DEBUG_READ_CORE: + error_report("Inter-Core Debug unit is not supported yet."); + exit(EXIT_FAILURE); + case ARCONNECT_CMD_PDM_SET_PM: + case ARCONNECT_CMD_PDM_READ_PSTATUS: + error_report("Power Domain Management commands are not supported yet."); + exit(EXIT_FAILURE); + case ARCONNECT_CMD_IVC_SET: + case ARCONNECT_CMD_IVC_READ: + case ARCONNECT_CMD_IVC_SET_HI: + case ARCONNECT_CMD_IVC_READ_HI: + case ARCONNECT_CMD_IVC_READ_FULL: + error_report("Interrupt Vector Base Control unit is not supported yet."); + exit(EXIT_FAILURE); + case ARCONNECT_CMD_PMU_SET_PUCNT: + case ARCONNECT_CMD_PMU_READ_PUCNT: + case ARCONNECT_CMD_PMU_SET_RSTCNT: + case ARCONNECT_CMD_PMU_READ_RSTCNT: + case ARCONNECT_CMD_PMU_SET_PDCNT: + case ARCONNECT_CMD_PMU_READ_PDCNT: + error_report("Power Management Unit is not supported yet."); + exit(EXIT_FAILURE); default: - assert(0); - break; + error_report("Unsupported ARConnect command: %d.", command); + exit(EXIT_FAILURE); }; } -#define ARCON_COMMAND(V) (V & 0xff) -#define ARCON_PARAM(V) ((V >> 8) & 0xffff) +/* + * Inter-Core Interrupt (ICI) unit allows sending and interrupt from one core + * to another to notify about an event. + * + * Each core is assigned an internal INTRPT_STATUS register. CMD_INTRPT_GENERATE_IRQ + * command sets one bit in the interrupt initiator core's INTRPT_STATUS register + * corresponding to the interrupt receiver core. + * + * The interrupt line of the interrupt receiver core is asserted after + * ARConnect issues this command and is asserted until the interrupt receiver + * core acknowledges all the inter-core interrupts it received. + * + * Implemented commands: + * + * CMD_INTRPT_GENERATE_IRQ + * + * Command field value: 0x01 + * + * Parameter field value: + * + * PARAMETER[4:0] (PARAMETER[5:0] for ARCv3) specifies the CORE_ID + * of the receiver core. PARAMETER[8] defines whether the interrupt + * is generated to the external system (in QEMU it must be 0). + * + * Description: + * + * Generate an interrupt to another core or to the external system. + * If PARAMETER[8] is set then CORE_ID value is ignored (this case + * is not considered by QEMU). The command sets one bit in the + * interrupt initiator core's INTRPT_STATUS register corresponding + * to the interrupt receiver core + * + * CMD_INTRPT_GENERATE_ACK + * + * Command field value: 0x02 + * + * Parameter field value: + * + * PARAMETER[4:0] (PARAMETER[5:0] for ARCv3) specifies the CORE_ID + * of the interrupt initiator core. PARAMETER[8] defines whether + * the interrupt is generated to the external system (in QEMU it + * must be 0). + * + * Description: + * + * Clear the bit that corresponds to the interrupt receiver core + * in the interrupt initiator core's INTRPT_STATUS register. + * If PARAMETER[8] is set then CORE_ID value is ignored (this case + * is not considered by QEMU) + * + * CMD_INTRPT_READ_STATUS + * + * Command field value: 0x03 + * + * Parameter field value: + * + * PARAMETER[4:0] (PARAMETER[5:0] for ARCv3) specifies the CORE_ID + * of the interrupt receiver core. + * + * Description: + * + * The CONNECT_READBACK register returns the value of the + * INTRPT_STATUS[CORE_ID] bit. + * + * CMD_INTRPT_CHECK_SOURCE + * + * Command field value: 0x04 + * + * Description: + * + * The CONNECT_READBACK register returns a vector gathered from + * the internal INTRPT_STATUS registers of all interrupt initiator + * cores. + * + * Not implemented commands: + * + * Name Command field value + * CMD_INTRPT_GENERATE_ACK_BIT_MASK 0x05 + * CMD_INTRPT_EXT_MODE 0x06 + * CMD_INTRPT_SET_PULSE_CNT 0x07 + */ -static void arconnect_command_process(CPUARCState *env, uint32_t data) +static void ici_command_handler(CPUARCState *env, uint8_t command, uint16_t parameter) { - enum arconnect_commands cmd = ARCON_COMMAND(data); - uint16_t param = ARCON_PARAM(data); ARCCPU *cpu = env_archcpu(env); + uint8_t parameter_core_id = parameter & ARCONNECT_CMD_INTRPT_PARAMETER_CORE_ID_MASK; + uint8_t current_core_id = cpu->core_id; + uint32_t *senders; - qemu_log_mask(CPU_LOG_INT, - "[ICI %d] Process command %d with param %d.\n", - cpu->core_id, cmd, param); - - switch(ARCON_COMMAND(cmd)) { + switch(command) { + case ARCONNECT_CMD_INTRPT_GENERATE_IRQ: + /* We don't support external systems for ICI */ + if (parameter & ARCONNECT_CMD_INTRPT_PARAMETER_EXTERNAL_RECEIVER_MASK) { + error_report("CMD_INTRPT_GENERATE_IRQ command is not support for external systems yet."); + exit(EXIT_FAILURE); + } - case CMD_CHECK_CORE_ID: - env->readback = cpu->core_id & 0x1f; - break; + /* Do nothing if ICI is sent to the same core. */ + if (parameter_core_id == current_core_id) { + break; + } - case CMD_INTRPT_GENERATE_IRQ: - case CMD_INTRPT_GENERATE_ACK: - case CMD_INTRPT_READ_STATUS: + qemu_mutex_lock(&arconnect_state.ici_mutex); + senders = &(get_cpu_for_core(parameter_core_id)->env.arconnect.pending_senders); + *senders |= 1 << current_core_id; + qemu_mutex_lock_iothread(); + qemu_irq_raise(get_cpu_for_core(parameter_core_id)->env.irq[ICI_IRQ]); + qemu_mutex_unlock_iothread(); + qemu_mutex_unlock(&arconnect_state.ici_mutex); + break; + case ARCONNECT_CMD_INTRPT_GENERATE_ACK: + qemu_mutex_lock(&arconnect_state.ici_mutex); + senders = &env->arconnect.pending_senders; + *senders &= ~(1 << parameter_core_id); + /* + * If all ICI interrupts are processed by the current core then the + * interrupt line may be safely deasserted. + */ + if ((*senders) == 0) { + qemu_mutex_lock_iothread(); + qemu_irq_lower(env->irq[ICI_IRQ]); + qemu_mutex_unlock_iothread(); + } - case CMD_INTRPT_CHECK_SOURCE: - arconnect_intercore_intr_unit_cmd(env, cmd, param); + qemu_mutex_unlock(&arconnect_state.ici_mutex); break; - - case CMD_IDU_ENABLE: - case CMD_IDU_DISABLE: - case CMD_IDU_SET_MASK: - /* TODO: Implement */ + case ARCONNECT_CMD_INTRPT_READ_STATUS: + qemu_mutex_lock(&arconnect_state.ici_mutex); + senders = &(get_cpu_for_core(parameter_core_id)->env.arconnect.pending_senders); + env->arconnect.readback = ((*senders) >> current_core_id) & 0x1; + qemu_mutex_unlock(&arconnect_state.ici_mutex); + break; + case ARCONNECT_CMD_INTRPT_CHECK_SOURCE: + qemu_mutex_lock(&arconnect_state.ici_mutex); + env->arconnect.readback = env->arconnect.pending_senders; + qemu_mutex_unlock(&arconnect_state.ici_mutex); break; + default: + error_report("ICI command %s (0x%x) is not implemented yet.", + arc_arconnect_command_name_array[command], command); + exit(EXIT_FAILURE); + }; +} +/* + * Interrupt Distribution Unit (IDU) is a distinct interrupt controller in + * ARConnect. Interrupts (common IRQs) connected to IDU are shared between + * cores. In turn, each common IRQ (CIRQ) of IDU is connected to each core + * starting from IRQ 24 (up to 128 common IRQs). + * + * ARConnect commands may be used to control distributing of common interrupts + * between cores. However, there is no a distinct IDU interrupt controller in + * QEMU yet - all devices are connected to the first core. All implemented IDE + * are just stubs for setting/reading internal common IRQs' registers (these + * registers are described in arconnect.h) to keep Linux working without errors. + * Actually, Linux works well with such implementation of IDU until a full + * hardware emulation is needed. + * + * Implemented commands stubs: + * + * Name Command field value + * CMD_IDU_ENABLE 0x71 + * CMD_IDU_DISABLE 0x72 + * CMD_IDU_READ_ENABLE 0x73 + * CMD_IDU_SET_MODE 0x74 + * CMD_IDU_READ_MODE 0x75 + * CMD_IDU_SET_DEST 0x76 + * CMD_IDU_READ_DEST 0x77 + * CMD_IDU_ACK_CIRQ 0x79 + * CMD_IDU_SET_MASK 0x7C + * CMD_IDU_READ_MASK 0x7D + * + * Not implemented commands: + * + * Name Command field value + * CMD_IDU_GEN_CIRQ 0x78 + * CMD_IDU_CHECK_STATUS 0x7A + * CMD_IDU_CHECK_SOURCE 0x7B + * CMD_IDU_CHECK_FIRST 0x7E + */ + +static void idu_command_handler(CPUARCState *env, uint8_t command, uint16_t parameter) +{ + switch(command) { + case ARCONNECT_CMD_IDU_SET_MASK: + qatomic_set(&arconnect_cirq_array[parameter].mask, env->arconnect.wdata); + break; + case ARCONNECT_CMD_IDU_READ_MASK: + env->arconnect.readback = arconnect_cirq_array[parameter].mask; + break; + case ARCONNECT_CMD_IDU_SET_DEST: + qatomic_set(&arconnect_cirq_array[parameter].dest, env->arconnect.wdata); + break; + case ARCONNECT_CMD_IDU_READ_DEST: + env->arconnect.readback = arconnect_cirq_array[parameter].dest; + break; + case ARCONNECT_CMD_IDU_SET_MODE: + qatomic_set(&arconnect_cirq_array[parameter].mode, env->arconnect.wdata); + break; + case ARCONNECT_CMD_IDU_READ_MODE: + env->arconnect.readback = arconnect_cirq_array[parameter].mode; + break; + case ARCONNECT_CMD_IDU_ENABLE: + qatomic_set(&arconnect_state.idu_enabled, true); + break; + case ARCONNECT_CMD_IDU_DISABLE: + qatomic_set(&arconnect_state.idu_enabled, false); + break; + case ARCONNECT_CMD_IDU_READ_ENABLE: + env->arconnect.readback = arconnect_state.idu_enabled; + break; + case ARCONNECT_CMD_IDU_ACK_CIRQ: + /* + * TODO: This command must be processed by IDU interrupt controller. + * Anyway, it must be processed by QEMU to conform a standard common + * IRQ handling flow. + */ + break; default: - assert(0); + error_report("IDU command %s (0x%x) is not implemented yet.", + arc_arconnect_command_name_array[command], command); + } +} + +/* + * Global Free Running Counter (GFRC) is a clock that is shared between all + * cores connected to ARConnect. + * + * Implemented commands: + * + * CMD_GFRC_READ_LO + * + * Command field value: 0x42 + * + * Description: + * + * Read the lower 32-bits of the GFRC counter. Result is saved in + * CONNECT_READBACK register. + * + * CMD_GFRC_READ_HI + * + * Command field value: 0x43 + * + * Description: + * + * Read the higher 32-bits of the GFRC counter. Result is saved in + * CONNECT_READBACK register. + * + * Not implemented commands: + * + * Name Command field value + * CMD_GFRC_ENABLE 0x44 + * CMD_GFRC_DISABLE 0x45 + * CMD_GFRC_READ_DISABLE 0x46 + * CMD_GFRC_SET_CORE 0x47 + * CMD_GFRC_READ_CORE 0x48 + * CMD_GFRC_READ_HALT 0x49 + * CMD_GFRC_CLK_ENABLE 0x4A + * CMD_GFRC_CLK_DISABLE 0x4B + * CMD_GFRC_READ_CLK_STATUS 0x4C + * CMD_GFRC_READ_FULL (HS6x) 0x4D + */ + +static void gfrc_command_handler(CPUARCState *env, uint8_t command, uint16_t parameter) +{ + switch(command) { + case ARCONNECT_CMD_GFRC_READ_LO: + env->arconnect.gfrc_snapshot = arc_get_global_cycles(); + env->arconnect.readback = (uint32_t) (env->arconnect.gfrc_snapshot & 0xffffffff); break; - }; + case ARCONNECT_CMD_GFRC_READ_HI: + env->arconnect.readback = (uint32_t) ((env->arconnect.gfrc_snapshot >> 32) & 0xffffffff); + break; + default: + error_report("GFRC command %s (0x%x) is not implemented yet.", + arc_arconnect_command_name_array[command], command); + } } -target_ulong -arconnect_regs_get(const struct arc_aux_reg_detail *aux_reg_detail, void *data) +/* + * There is a set of auxilary registers in ARConnect. For a detailed information + * refer to arconnect.h header file that contains a description for implemented + * registers. + * + * Name Address Access Implemented ID in QEMU + * CONNECT_SYSTEM_BUILD 0xD0 R Yes AUX_ID_mcip_bcr + * CONNECT_SEMA_BUILD 0xD1 R No + * CONNECT_MESSAGE_BUILD 0xD2 R No + * CONNECT_PMU_BUILD 0xD3 R No + * CONNECT_IDU_BUILD 0xD5 R Yes AUX_ID_mcip_idu_bcr + * CONNECT_GFRC_BUILD 0xD6 R Yes AUX_ID_mcip_gfrc_bcr + * CONNECT_IVC_BUILD 0xD7 R No + * CONNECT_ICI_BUILD 0xE0 R Yes AUX_ID_mcip_ici_bcr + * CONNECT_ICD_BUILD 0xE1 R No + * CONNECT_ASI_BUILD 0xE2 R No + * CONNECT_PDM_BUILD 0xE3 R No + * CONNECT_CMD 0x600 RW Yes AUX_ID_mcip_cmd + * CONNECT_WDATA 0x601 RW Yes AUX_ID_mcip_wdata + * CONNECT_READBACK 0x602 R Yes AUX_ID_mcip_readback + */ + +/* + * A helper for getting ARConnect auxilary registers + */ + +target_ulong arconnect_regs_get(const struct arc_aux_reg_detail *aux_reg_detail, + void *data) { CPUARCState *env = (CPUARCState *) data; + switch(aux_reg_detail->id) { case AUX_ID_mcip_bcr: - return 0x00800000 /* IDU */ - | 0x00040000; + return arconnect_state.system_build; + case AUX_ID_mcip_idu_bcr: + return arconnect_state.idu_build; + case AUX_ID_mcip_gfrc_bcr: + return arconnect_state.gfrc_build; + case AUX_ID_mcip_ici_bcr: + return arconnect_state.ici_build; case AUX_ID_mcip_cmd: - assert(0); /* TODO: raise exception */ - break; + return env->arconnect.cmd; case AUX_ID_mcip_wdata: - break; + return env->arconnect.wdata; case AUX_ID_mcip_readback: - return env->readback; - break; - + return env->arconnect.readback; default: - assert(0); - break; + error_report("Unsupported ARConnect AUX register: 0x%x.", aux_reg_detail->id); + exit(EXIT_FAILURE); } + return 0; } -void -arconnect_regs_set(const struct arc_aux_reg_detail *aux_reg_detail, - target_ulong val, void *data) + +/* + * A helper for setting ARConnect auxilary registers + */ + +void arconnect_regs_set(const struct arc_aux_reg_detail *aux_reg_detail, + target_ulong val, void *data) { CPUARCState *env = (CPUARCState *) data; + switch(aux_reg_detail->id) { case AUX_ID_mcip_cmd: - arconnect_command_process(env, val); + env->arconnect.cmd = val; + arconnect_command_handler(env); break; case AUX_ID_mcip_wdata: - /* TODO: To implement */ - break; - case AUX_ID_mcip_readback: - assert(0); /* TODO: raise exception */ + env->arconnect.wdata = val; break; - default: - assert(0); - break; + error_report("Unsupported ARConnect AUX register: 0x%x.", aux_reg_detail->id); + exit(EXIT_FAILURE); } } diff --git a/target/arc/arconnect.h b/target/arc/arconnect.h index a26a187cca0..dd1ac5266f6 100644 --- a/target/arc/arconnect.h +++ b/target/arc/arconnect.h @@ -23,19 +23,55 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "hw/registerfields.h" +#include "target/arc/irq.h" -struct lpa_lf_entry { - QemuMutex mutex; - target_ulong lpa_lf; - uint64_t read_value; -}; +/* + * ARC HS3x and HS4x families support up to 4 cores in SMP configuration despite + * the fact that ARConnect's commands support a larger number of cores. In turn, + * ARC HS5x and HS6x families support up to 12 cores in SMP configuration. + */ -struct arc_arcconnect_info { - uint64_t intrpt_status; +#define ARCV2_MAX_CORES_NUMBER 4 +#define ARCV3_MAX_CORES_NUMBER 12 +#define ARC_MAX_CORES_NUMBER ARCV3_MAX_CORES_NUMBER - struct lpa_lf_entry *lpa_lf; - QemuMutex *locked_mutex; -}; +/* + * ARConnect's state consist of a set of build configuration registers and + * other internal registers. Registers that are implemented in QEMU are + * described in details further. A summary of build configuration registers + * (they are accessed by the guest through arconnect_regs_get and + * arconnect_regs_set helpers): + * + * Name Address Access Implemented ID in QEMU + * CONNECT_SYSTEM_BUILD 0xD0 R Yes AUX_ID_mcip_bcr + * CONNECT_SEMA_BUILD 0xD1 R No + * CONNECT_MESSAGE_BUILD 0xD2 R No + * CONNECT_PMU_BUILD 0xD3 R No + * CONNECT_IDU_BUILD 0xD5 R Yes AUX_ID_mcip_idu_bcr + * CONNECT_GFRC_BUILD 0xD6 R Yes AUX_ID_mcip_gfrc_bcr + * CONNECT_IVC_BUILD 0xD7 R No + * CONNECT_ICI_BUILD 0xE0 R Yes AUX_ID_mcip_ici_bcr + * CONNECT_ICD_BUILD 0xE1 R No + * CONNECT_ASI_BUILD 0xE2 R No + * CONNECT_PDM_BUILD 0xE3 R No + */ + +typedef struct { + uint32_t system_build; /* ARConnect Build Configuration Register, CONNECT_SYSTEM_BUILD */ + uint32_t idu_build; /* Interrupt Distribution Unit BCR, CONNECT_IDU_BUILD */ + uint32_t gfrc_build; /* Global Free Running Counter BCR, CONNECT_GFRC_BUILD */ + uint32_t ici_build; /* Inter-Core Interrupt Unit BCR, CONNECT_ICI_BUILD */ + bool idu_enabled; /* Internal IDU enable/disable register */ + bool initialized; /* Is ARConnect initialized? */ + QemuMutex ici_mutex; +} ARCArconnectGlobalState; + +extern ARCArconnectGlobalState arconnect_state; + +/* + * LPA/LF hash table. + */ #define LPA_LFS_ALIGNEMENT_BITS 2 /* Inforced alignement */ #define LPA_LFS_ALIGNEMENT_MASK ((1 << LPA_LFS_ALIGNEMENT_BITS) - 1) @@ -46,11 +82,224 @@ struct arc_arcconnect_info { ^ (PA >> (LPA_LFS_SIZE_IN_BITS + LPA_LFS_ALIGNEMENT_BITS))) \ & ((1 << LPA_LFS_SIZE_IN_BITS) - 1)) -#define LPA_LF_GET_ENTRY_FOR_VADDR(VADDR) \ - - +struct lpa_lf_entry { + QemuMutex mutex; + target_ulong lpa_lf; + uint64_t read_value; +}; + extern struct lpa_lf_entry lpa_lfs[LPA_LFS_SIZE]; +/* + * Each core has its own set of internal registers associated with ARConnect. + * A summary of internal control registers (they are accessed by the guest + * through arconnect_regs_get and arconnect_regs_set helpers too): + * + * Name Address Access Implemented ID in QEMU + * CONNECT_CMD 0x600 RW Yes AUX_ID_mcip_cmd + * CONNECT_WDATA 0x601 RW Yes AUX_ID_mcip_wdata + * CONNECT_READBACK 0x602 R Yes AUX_ID_mcip_readback + * CONNECT_READBACK_64 0x603 R No + * + * An ARConnect command is sent to ARConnect and processed when a command with + * an optional parameter is saved to CONNECT_CMD auxilary register. Depending + * on a command, CONNECT_WDATA may be used to send extra data to ARConnect. + * An answer for ARConnect is stored CONNECT_READBACK. + * + * CONNECT_READBACK_64 is used by ARC HS6x family for storing a 64-bit value + * of GFRC clock, so the value may be read using just one command. + */ + +typedef struct { + uint32_t cmd; /* ARConnect Command Register, CONNECT_CMD */ + uint32_t wdata; /* ARConnect Write Data Register, CONNECT_WDATA */ + uint32_t readback; /* ARConnect Read Data Register, CONNECT_READBACK */ + + /* + * Each core is assigned an internal INTRPT_STATUS register in ICI to + * record the status of the interrupt (acknowledged or not) from it to all + * other cores. The CMD_INTRPT_GENERATE_IRQ command sets one bit in the + * interrupt initiator core’s INTRPT_STATUS register corresponding to the + * interrupt receiver core. + * + * However, it's not efficient to store INTRPT_STATUS register as is. + * Instead, there is pending_senders variable. N-th bit of the variable + * corresponds to the N-th core that is waiting for an acknowledgement from + * the core this variable belongs to. + * + * For example, if pending_senders of the core is 0b0000...101 then it + * means that cores with CORE_ID 0 and 2 sent ICI interrupt to the core and + * are waiting for an acknowledgement. + */ + uint32_t pending_senders; + + /* + * GFRC snapshot is store here after reading a low word using + * CMD_GFRC_READ_LO ARConnect command. + */ + uint64_t gfrc_snapshot; + + struct lpa_lf_entry *lpa_lf; + QemuMutex *locked_mutex; +} ARCArconnectCPUState; + +/* + * Interrupt Distribution Unit (IDU) is a distinct interrupt controller in + * ARConnect. Interrupts (common IRQs) connected to IDU are shared between + * cores. In turn, each common IRQ (CIRQ) of IDU is connected to each core + * starting from IRQ 24 (up to 128 common IRQs). + * + * Each common interrupt is assigned a number of internal registers: + * + * MODE - Configures a triggering mode (level triggered or edge triggered) + * and a distribution mode (round-robin, first-acknowledge or + * all-destination). It's configured by CMD_IDU_SET_MODE command and + * read by CMD_IDU_READ_MODE command + * + * DEST - Configures the target cores to receive the specified + * common interrupt when it is triggered. It's configured by + * CMD_IDU_SET_DEST command and read by CMD_IDU_READ_DEST command + * + * MASK - Each core can use the CMD_IDU_SET_MASK command to mask or unmask + * the specified common interrupt. It 1 is stored in MASK, then + * the common interrupt is masked. CMD_IDU_READ_MASK is used + * to read content of the common interrupts MASK register. + */ + +#define FIRST_COMMON_IRQ 24 +#define MAX_NR_OF_COMMON_IRQS 128 + +typedef struct { + uint32_t mode; + uint32_t dest; + uint32_t mask; +} ARCArconnectCommonIRQ; + +extern ARCArconnectCommonIRQ arconnect_cirq_array[MAX_NR_OF_COMMON_IRQS]; + +/* + * ARConnect Build Configuration Register bits + * CONNECT_SYSTEM_BUILD, AUX address: 0xD0, access: R + * + * VERSION[7:0] - 0x1 for the first version, 0x2 for the current version + * ASI[8] - ARConnect Slave Interface unit + * ICI[9] - Inter-Core Interrupt unit + * ICS[10] - Inter-Core Semaphore unit + * ICM[11] - Inter-Core Message unit + * PMU[12] - Power Management unit + * ICD[13] - Inter-Code Debug unit + * GFRC[14] - Global Free-Running Counter + * R[15] - Reserved + * CORENUM[21:16] - Number of cores connected to ARConnect + * R[22] - Reserved + * IDU[23] - Interrupt Distribution unit + * R[24] - Reserved + * PDM[25] - Power Domain Management unit + * IVC[26] - Interrupt Vector base Control unit + * R[31:27] - Reserved + */ + +FIELD(CONNECT_SYSTEM_BUILD, VERSION, 0, 8) +FIELD(CONNECT_SYSTEM_BUILD, ASI, 8, 1) +FIELD(CONNECT_SYSTEM_BUILD, ICI, 9, 1) +FIELD(CONNECT_SYSTEM_BUILD, ICS, 10, 1) +FIELD(CONNECT_SYSTEM_BUILD, ICM, 11, 1) +FIELD(CONNECT_SYSTEM_BUILD, PMU, 12, 1) +FIELD(CONNECT_SYSTEM_BUILD, ICD, 13, 1) +FIELD(CONNECT_SYSTEM_BUILD, GFRC, 14, 1) +FIELD(CONNECT_SYSTEM_BUILD, CORENUM, 16, 6) +FIELD(CONNECT_SYSTEM_BUILD, IDU, 23, 1) +FIELD(CONNECT_SYSTEM_BUILD, PDM, 25, 1) +FIELD(CONNECT_SYSTEM_BUILD, IVC, 26, 1) + +/* + * ARConnect Interrupt Distribution Unit Build Configuration Register bits + * CONNECT_IDU_BUILD, AUX address: 0xD5, access: R + * + * VERSION[7:0] - 0x1 or 0x2 + * CIRQNUM[10:8] - Number of common interrupts supported: + * 0x0 = 4 common interrupts + * 0x1 = 8 common interrupts + * 0x2 = 16 common interrupts + * 0x3 = 32 common interrupts + * 0x4 = 64 common interrupts + * 0x5 = 128 common interrupts + * R[31:11] - Reserved + */ + +FIELD(CONNECT_IDU_BUILD, VERSION, 0, 8) +FIELD(CONNECT_IDU_BUILD, CIRQNUM, 8, 3) + +#define ARCONNECT_IDU_VERSION 0x1 +#define ARCONNECT_IDU_CIRQNUM 0x5 + +/* + * ARConnect Global Free Running Counter Build Configuration Register bits + * CONNECT_GFRC_BUILD, AUX address: 0xD6, access: R + * + * VERSION[7:0] - 0x1, 0x2, 0x3, 0x4 or 0x5 + * R[31:8] - Reserved + */ + +FIELD(CONNECT_GFRC_BUILD, VERSION, 0, 8) + +#define ARCONNECT_GFRC_VERSION 0x2 + +/* + * ARConnect Inter-Core Interrupt Unit Build Configuration Register bits + * CONNECT_ICI_BUILD, AUX address: 0xE0, access: R + * + * VERSION[7:0] - 0x2, 0x3 or 0x4 + * R[31:8] - Reserved + */ + +FIELD(CONNECT_ICI_BUILD, VERSION, 0, 8) + +#define ARCONNECT_ICI_VERSION 0x2 + +/* + * ARConnect Command Register bits + * CONNECT_CMD, AUX address: 0x600, access: RW + * + * The register is used for sending commands to ARConnect. + * + * COMMAND[7:0] - Command to ARConnect + * PARAMETER[23:8] - Parameter field of the command to ARConnect + * R[31:24] - Reserved + */ + +FIELD(CONNECT_CMD, COMMAND, 0, 8) +FIELD(CONNECT_CMD, PARAMETER, 8, 16) + +/* + * ARConnect Inter-Core Interrupt unit commands use PARAMETER field in + * COMMAND_CMD register for setting a type of ICI interrupts and a receiver's + * core number. + * + * if ARCONNECT_CMD_INTRPT_PARAMETER_CORE_ID_MASK bit is set, then a receiver is an + * external system (not supported yet) and a receiver's CORE_ID, encoded + * in ARCONNECT_CMD_INTRPT_PARAMETER_EXTERNAL_RECEIVER_MASK bits, is ignored. + */ + +#define ARCONNECT_CMD_INTRPT_PARAMETER_CORE_ID_MASK 0x1f +#define ARCONNECT_CMD_INTRPT_PARAMETER_EXTERNAL_RECEIVER_MASK 0x80 + +/* + * The enumeration of all ARConnect commands, even unsupported by QEMU. + */ + +#define ARCONNECT_COMMAND(NAME, VALUE) ARCONNECT_ ## NAME = VALUE, +enum ARCArconnectCommandEnum { +#include "arconnect_commands.def" +}; +#undef ARCONNECT_COMMAND + +/* + * This array matches ARConnect command's number to command's symbolic name. + */ + +extern const char *arc_arconnect_command_name_array[]; + void arc_arconnect_init(ARCCPU *cpu); #endif /* __ARC_ARCONNECT_H__ */ diff --git a/target/arc/arconnect_commands.def b/target/arc/arconnect_commands.def new file mode 100644 index 00000000000..80072aae04c --- /dev/null +++ b/target/arc/arconnect_commands.def @@ -0,0 +1,76 @@ +ARCONNECT_COMMAND(CMD_CHECK_CORE_ID, 0x0) +ARCONNECT_COMMAND(CMD_INTRPT_GENERATE_IRQ, 0x1) +ARCONNECT_COMMAND(CMD_INTRPT_GENERATE_ACK, 0x2) +ARCONNECT_COMMAND(CMD_INTRPT_READ_STATUS, 0x3) +ARCONNECT_COMMAND(CMD_INTRPT_CHECK_SOURCE, 0x4) +ARCONNECT_COMMAND(CMD_INTRPT_GENERATE_ACK_BIT_MASK, 0x5) +ARCONNECT_COMMAND(CMD_INTRPT_EXT_MODE, 0x6) +ARCONNECT_COMMAND(CMD_INTRPT_SET_PULSE_CNT, 0x7) +ARCONNECT_COMMAND(CMD_INTRPT_READ_PULSE_CNT, 0x8) +ARCONNECT_COMMAND(CMD_SEMA_CLAIM_AND_READ, 0x11) +ARCONNECT_COMMAND(CMD_SEMA_RELEASE, 0x12) +ARCONNECT_COMMAND(CMD_SEMA_FORCE_RELEASE, 0x13) +ARCONNECT_COMMAND(CMD_MSG_SRAM_SET_ADDR, 0x21) +ARCONNECT_COMMAND(CMD_MSG_SRAM_READ_ADDR, 0x22) +ARCONNECT_COMMAND(CMD_MSG_SRAM_SET_ADDR_OFFSET, 0x23) +ARCONNECT_COMMAND(CMD_MSG_SRAM_READ_ADDR_OFFSET, 0x24) +ARCONNECT_COMMAND(CMD_MSG_SRAM_WRITE, 0x25) +ARCONNECT_COMMAND(CMD_MSG_SRAM_WRITE_INC, 0x26) +ARCONNECT_COMMAND(CMD_MSG_SRAM_WRITE_IMM, 0x27) +ARCONNECT_COMMAND(CMD_MSG_SRAM_READ, 0x28) +ARCONNECT_COMMAND(CMD_MSG_SRAM_READ_INC, 0x29) +ARCONNECT_COMMAND(CMD_MSG_SRAM_READ_IMM, 0x2a) +ARCONNECT_COMMAND(CMD_MSG_SET_ECC_CTRL, 0x2b) +ARCONNECT_COMMAND(CMD_MSG_READ_ECC_CTRL, 0x2c) +ARCONNECT_COMMAND(CMD_MSG_READ_ECC_SBE_CNT, 0x2d) +ARCONNECT_COMMAND(CMD_MSG_CLEAR_ECC_SBE_CNT, 0x2e) +ARCONNECT_COMMAND(CMD_DEBUG_RESET, 0x31) +ARCONNECT_COMMAND(CMD_DEBUG_HALT, 0x32) +ARCONNECT_COMMAND(CMD_DEBUG_RUN, 0x33) +ARCONNECT_COMMAND(CMD_DEBUG_SET_MASK, 0x34) +ARCONNECT_COMMAND(CMD_DEBUG_READ_MASK, 0x35) +ARCONNECT_COMMAND(CMD_DEBUG_SET_SELECT, 0x36) +ARCONNECT_COMMAND(CMD_DEBUG_READ_SELECT, 0x37) +ARCONNECT_COMMAND(CMD_DEBUG_READ_EN, 0x38) +ARCONNECT_COMMAND(CMD_DEBUG_READ_CMD, 0x39) +ARCONNECT_COMMAND(CMD_DEBUG_READ_CORE, 0x3a) +ARCONNECT_COMMAND(CMD_GFRC_CLEAR, 0x41) +ARCONNECT_COMMAND(CMD_GFRC_READ_LO, 0x42) +ARCONNECT_COMMAND(CMD_GFRC_READ_HI, 0x43) +ARCONNECT_COMMAND(CMD_GFRC_ENABLE, 0x44) +ARCONNECT_COMMAND(CMD_GFRC_DISABLE, 0x45) +ARCONNECT_COMMAND(CMD_GFRC_READ_DISABLE, 0x46) +ARCONNECT_COMMAND(CMD_GFRC_SET_CORE, 0x47) +ARCONNECT_COMMAND(CMD_GFRC_READ_CORE, 0x48) +ARCONNECT_COMMAND(CMD_GFRC_READ_HALT, 0x49) +ARCONNECT_COMMAND(CMD_GFRC_CLK_ENABLE, 0x4A) +ARCONNECT_COMMAND(CMD_GFRC_CLK_DISABLE, 0x4B) +ARCONNECT_COMMAND(CMD_GFRC_READ_CLK_STATUS, 0x4C) +ARCONNECT_COMMAND(CMD_GFRC_READ_FULL, 0x4D) +ARCONNECT_COMMAND(CMD_PMU_SET_PUCNT, 0x51) +ARCONNECT_COMMAND(CMD_PMU_READ_PUCNT, 0x52) +ARCONNECT_COMMAND(CMD_PMU_SET_RSTCNT, 0x53) +ARCONNECT_COMMAND(CMD_PMU_READ_RSTCNT, 0x54) +ARCONNECT_COMMAND(CMD_PMU_SET_PDCNT, 0x55) +ARCONNECT_COMMAND(CMD_PMU_READ_PDCNT, 0x56) +ARCONNECT_COMMAND(CMD_IDU_ENABLE, 0x71) +ARCONNECT_COMMAND(CMD_IDU_DISABLE, 0x72) +ARCONNECT_COMMAND(CMD_IDU_READ_ENABLE, 0x73) +ARCONNECT_COMMAND(CMD_IDU_SET_MODE, 0x74) +ARCONNECT_COMMAND(CMD_IDU_READ_MODE, 0x75) +ARCONNECT_COMMAND(CMD_IDU_SET_DEST, 0x76) +ARCONNECT_COMMAND(CMD_IDU_READ_DEST, 0x77) +ARCONNECT_COMMAND(CMD_IDU_GEN_CIRQ, 0x78) +ARCONNECT_COMMAND(CMD_IDU_ACK_CIRQ, 0x79) +ARCONNECT_COMMAND(CMD_IDU_CHECK_STATUS, 0x7a) +ARCONNECT_COMMAND(CMD_IDU_CHECK_SOURCE, 0x7b) +ARCONNECT_COMMAND(CMD_IDU_SET_MASK, 0x7c) +ARCONNECT_COMMAND(CMD_IDU_READ_MASK, 0x7d) +ARCONNECT_COMMAND(CMD_IDU_CHECK_FIRST, 0x7e) +ARCONNECT_COMMAND(CMD_PDM_SET_PM, 0x81) +ARCONNECT_COMMAND(CMD_PDM_READ_PSTATUS, 0x82) +ARCONNECT_COMMAND(CMD_IVC_SET, 0x91) +ARCONNECT_COMMAND(CMD_IVC_READ, 0x92) +ARCONNECT_COMMAND(CMD_IVC_SET_HI, 0x93) +ARCONNECT_COMMAND(CMD_IVC_READ_HI, 0x94) +ARCONNECT_COMMAND(CMD_IVC_READ_FULL, 0x95) diff --git a/target/arc/cpu.c b/target/arc/cpu.c index 3cb5d459679..a95fec0e0b3 100644 --- a/target/arc/cpu.c +++ b/target/arc/cpu.c @@ -65,7 +65,7 @@ static Property arc_cpu_properties[] = { DEFINE_PROP_UINT32("pc-size", ARCCPU, cfg.pc_size, 32), DEFINE_PROP_UINT32("num-regs", ARCCPU, cfg.rgf_num_regs, 32), DEFINE_PROP_UINT32("num-banks", ARCCPU, cfg.rgf_num_banks, 0), - DEFINE_PROP_BOOL("rtc-opt", ARCCPU, cfg.rtc_option, false), + DEFINE_PROP_BOOL("rtc-opt", ARCCPU, cfg.rtc_option, true), DEFINE_PROP_UINT32("freq_hz", ARCCPU, cfg.freq_hz, 4600000), DEFINE_PROP_STRING("mmuv6-version", ARCCPU, cfg.mmuv6_version), diff --git a/target/arc/cpu.h b/target/arc/cpu.h index 69383a12bcd..961f2057838 100644 --- a/target/arc/cpu.h +++ b/target/arc/cpu.h @@ -213,6 +213,15 @@ typedef struct { uint64_t last_clk; } ARCTimer; +typedef struct { + uint32_t low; + uint32_t high; + uint32_t ctrl; + uint64_t stop_cycles; + uint64_t stop_time_ns; + uint64_t base_time_ns; +} ARCRealTimeCounter; + /* ARC PIC interrupt bancked regs. */ typedef struct { target_ulong priority; @@ -259,6 +268,7 @@ struct CPUArchState { #define TMR_IP (1 << 3) #define TMR_PD (1 << 4) ARCTimer timer[2]; /* ARC CPU-Timer 0/1 */ + ARCRealTimeCounter rtc; /* TODO: Verify correctness of this types for both ARCv2 and v3. */ ARCIrq irq_bank[256]; /* IRQ register bank */ @@ -269,9 +279,6 @@ struct CPUArchState { uint32_t aux_irq_hint; /* AUX register, used to trigger soft irq */ target_ulong aux_user_sp; uint32_t aux_irq_ctrl; - uint32_t aux_rtc_ctrl; - uint32_t aux_rtc_low; - uint32_t aux_rtc_high; /* TODO: This one in particular. */ /* Fields required by exception handling. */ @@ -282,20 +289,17 @@ struct CPUArchState { struct arc_mmu v3; struct arc_mmuv6 v6; } mmu; - struct ARCMPU mpu; /* mpu.h */ - struct arc_arcconnect_info arconnect; /* arconnect.h */ - struct arc_cache cache; /* cache.h */ + struct ARCMPU mpu; /* mpu.h */ + ARCArconnectCPUState arconnect; /* arconnect.h */ + struct arc_cache cache; /* cache.h */ bool stopped; /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; - uint64_t last_clk_rtc; - void *irq[256]; QEMUTimer *cpu_timer[2]; /* Internal timer. */ - QEMUTimer *cpu_rtc; /* Internal RTC. */ const struct arc_boot_info *boot_info; @@ -303,8 +307,6 @@ struct CPUArchState { target_ulong tls_backup; #endif - target_ulong readback; - target_ulong exclusive_addr; target_ulong exclusive_val; target_ulong exclusive_val_hi; diff --git a/target/arc/irq.c b/target/arc/irq.c index 5ac1f5e9996..a355d847d27 100644 --- a/target/arc/irq.c +++ b/target/arc/irq.c @@ -539,12 +539,12 @@ bool arc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return false; } - qemu_log_mask(CPU_LOG_INT, "[IRQ] interrupt at pc=0x" TARGET_FMT_lx - "\n", env->pc); - /* Adjust vector number. */ vectno += NR_OF_EXCEPTIONS; + qemu_log_mask(CPU_LOG_INT, "[IRQ] interrupt %d at pc=0x" TARGET_FMT_lx " at core %d" + "\n", vectno, env->pc, cpu->core_id); + /* Set the AUX_IRQ_ACT. */ if ((env->aux_irq_act & 0xffff) == 0) { env->aux_irq_act |= GET_STATUS_BIT(env->stat, Uf) << 31; diff --git a/target/arc/irq.h b/target/arc/irq.h index 1c869d65083..64e3987c031 100644 --- a/target/arc/irq.h +++ b/target/arc/irq.h @@ -34,6 +34,8 @@ void arc_resetIRQ(ARCCPU *); #define NR_OF_EXCEPTIONS 16 #define TIMER0_IRQ 16 #define TIMER1_IRQ 17 +#define ICI_IRQ 19 +#define PMU_IRQ 23 #if defined(TARGET_ARC32) #define OFFSET_FOR_VECTOR(VECNO) (VECNO << 2) diff --git a/target/arc/mmu-v6.c b/target/arc/mmu-v6.c index c7c78ca9cdf..cefe6434552 100644 --- a/target/arc/mmu-v6.c +++ b/target/arc/mmu-v6.c @@ -100,42 +100,6 @@ struct mmu_version_info *mmu_v6_version; (EXCP).parameter = P; \ } -uint32_t mmu_ctrl; - -#define MMU_ENABLED_BIT 0 -#define MMU_ENABLED_MASK (1 << MMU_ENABLED_BIT) -#define MMU_ENABLED ((mmu_ctrl & MMU_ENABLED_MASK) != 0) -#define MMU_IN_USER_KERNEL_MODE ((mmu_ctrl >> 1) & 1) - -static void disable_mmuv6(void) -{ - mmu_ctrl &= ~MMU_ENABLED_MASK; -} -int mmuv6_enabled(void) -{ - return MMU_ENABLED; -} - - -uint32_t mmu_ttbcr; - -#define MMU_TTBCR_TNSZ(I) ((mmu_ttbcr >> (I * 16)) & 0x1f) -#define MMU_TTBCR_TNSH(I) (((mmu_ttbcr >> 4) >> (I * 16)) & 0x3) - -#define MMU_TTBCR_A1 ((mmu_ttbcr >> 15) & 0x1) - -/* -static void init_mmu_ttbcr(void) -{ - // TODO BE DONE -} -*/ - -uint64_t mmu_rtp0; -uint64_t mmu_rtp1; - -uint64_t mmu_fault_status; - //static target_ulong mask_for_root_address(char x) { // switch(mmu_v6_version->id) { // case MMUV6_52_64K: @@ -150,34 +114,6 @@ uint64_t mmu_fault_status; // // } //} -#define MASK_FOR_ROOT_ADDRESS(X) \ - (((1ull << VADDR_SIZE()) - 1) & (~((1 << X) - 1))) - -/* TODO: This is for MMU48 only. */ - -#ifndef CONFIG_USER_ONLY -static char x_for_ttbc(unsigned char i) -{ - const char xs[MMUV6_VERSION_SIZE][2] = { - [MMUV6_52_64K] = { 13, 16 }, - [MMUV6_48_4K] = { 12, 12 }, - [MMUV6_48_16K] = {4, 6 }, - [MMUV6_48_64K] = { 9, 13 }, - }; - switch(mmu_v6_version->id) { - case MMUV6_32_4K: - if(MMU_TTBCR_TNSZ(i) > 1) - return (14 - MMU_TTBCR_TNSZ(i)); - else - return (5 - MMU_TTBCR_TNSZ(i)); - break; - default: - return xs[mmu_v6_version->id][i]; - break; - } - assert("This should not happen !!!" == 0); -} -#endif // Grab Root Table Address for RTPN #ifndef CONFIG_USER_ONLY @@ -194,13 +130,6 @@ static uint64_t root_address(uint64_t rtp) } #endif -#define MMU_RTPN_ROOT_ADDRESS(N) \ - (root_address(mmu_rtp##N) & MASK_FOR_ROOT_ADDRESS(x_for_ttbc(N))) - -/* TODO: This is for MMU48/52 only. */ -#define MMU_RTPN_ASID(VADDR, N) \ - ((mmu_rtp##N >> 48) & 0xffff) - /* Table descriptors accessor macros */ #ifndef CONFIG_USER_ONLY @@ -313,48 +242,51 @@ mmuv6_tlb_command(CPUARCState *env, enum MMUv6_TLBCOMMAND command) target_ulong arc_mmuv6_aux_get(const struct arc_aux_reg_detail *aux_reg_detail, void *data) { - target_ulong reg = 0; - switch(aux_reg_detail->id) - { - case AUX_ID_mmuv6_build: - reg = 0; - reg |= (0x10 << 24); /* Version: 0x10 (MMUv6) */ - /* Type: 1 (MMUv48) */ - reg |= (mmu_v6_version->type << 21); - reg |= (0 << 9); /* TC: 0 (no translation cache) */ - reg |= (0 << 6); /* L2TLB: 0 (256 entries) */ - reg |= (1 << 3); /* ITLB: 1 (4 entries) */ - reg |= 2; /* DTLB: 2 (8 entries) */ - - qemu_log_mask(CPU_LOG_MMU, "\n[MMUV3] BUILD read " TARGET_FMT_lu " \n\n", reg); - break; - case AUX_ID_mmu_rtp0: - reg = mmu_rtp0; - qemu_log_mask(CPU_LOG_MMU, "\n[MMUV3] RTP0 read %lx\n\n", mmu_rtp0); - break; - case AUX_ID_mmu_rtp0hi: - reg = (mmu_rtp0 >> 32); - break; - case AUX_ID_mmu_rtp1: - qemu_log_mask(CPU_LOG_MMU, "\n[MMUV3] RTP1 read %lx\n\n", mmu_rtp1); - reg = mmu_rtp1; - break; - case AUX_ID_mmu_rtp1hi: - reg = (mmu_rtp1 >> 32); - break; - case AUX_ID_mmu_ctrl: - reg = mmu_ctrl; - break; - case AUX_ID_mmu_ttbcr: - reg = mmu_ttbcr; - break; - case AUX_ID_mmu_fault_status: - reg = mmu_fault_status; - break; - default: - break; - } - return reg; + CPUARCState *env = (CPUARCState *) data; + target_ulong reg = 0; + + switch(aux_reg_detail->id) + { + case AUX_ID_mmuv6_build: + reg = 0; + reg |= (0x10 << 24); /* Version: 0x10 (MMUv6) */ + /* Type: 1 (MMUv48) */ + reg |= (mmu_v6_version->type << 21); + reg |= (0 << 9); /* TC: 0 (no translation cache) */ + reg |= (0 << 6); /* L2TLB: 0 (256 entries) */ + reg |= (1 << 3); /* ITLB: 1 (4 entries) */ + reg |= 2; /* DTLB: 2 (8 entries) */ + + qemu_log_mask(CPU_LOG_MMU, "\n[MMUV3] BUILD read " TARGET_FMT_lu " \n\n", reg); + break; + case AUX_ID_mmu_rtp0: + reg = env->mmu.v6.rtp0; + qemu_log_mask(CPU_LOG_MMU, "\n[MMUV3] RTP0 read %lx\n\n", env->mmu.v6.rtp1); + break; + case AUX_ID_mmu_rtp0hi: + reg = (env->mmu.v6.rtp0 >> 32); + break; + case AUX_ID_mmu_rtp1: + reg = env->mmu.v6.rtp1; + qemu_log_mask(CPU_LOG_MMU, "\n[MMUV3] RTP1 read %lx\n\n", env->mmu.v6.rtp1); + break; + case AUX_ID_mmu_rtp1hi: + reg = (env->mmu.v6.rtp1 >> 32); + break; + case AUX_ID_mmu_ctrl: + reg = env->mmu.v6.ctrl; + break; + case AUX_ID_mmu_ttbcr: + reg = env->mmu.v6.ttbcr; + break; + case AUX_ID_mmu_fault_status: + reg = env->mmu.v6.fault_status; + break; + default: + break; + } + + return reg; } void @@ -369,36 +301,36 @@ arc_mmuv6_aux_set(const struct arc_aux_reg_detail *aux_reg_detail, { case AUX_ID_mmu_rtp0: qemu_log_mask(CPU_LOG_MMU, "\n[MMUV3] RTP0 update %lx" - " ==> " TARGET_FMT_lx "\n\n", mmu_rtp0, val); - if (mmu_rtp0 != u64_val) + " ==> " TARGET_FMT_lx "\n\n", env->mmu.v6.rtp0, val); + if (env->mmu.v6.rtp0 != u64_val) tlb_flush(cs); - mmu_rtp0 = u64_val; + env->mmu.v6.rtp0 = u64_val; break; case AUX_ID_mmu_rtp0hi: - if ((mmu_rtp0 >> 32) != u64_val) + if ((env->mmu.v6.rtp0 >> 32) != u64_val) tlb_flush(cs); - mmu_rtp0 &= ~0xffffffff00000000; - mmu_rtp0 |= (u64_val << 32); + env->mmu.v6.rtp0 &= ~0xffffffff00000000; + env->mmu.v6.rtp0 |= (u64_val << 32); break; case AUX_ID_mmu_rtp1: - if (mmu_rtp1 != u64_val) + if (env->mmu.v6.rtp1 != u64_val) tlb_flush(cs); - mmu_rtp1 = u64_val; + env->mmu.v6.rtp1 = u64_val; break; case AUX_ID_mmu_rtp1hi: - if ((mmu_rtp1 >> 32) != u64_val) + if ((env->mmu.v6.rtp1 >> 32) != u64_val) tlb_flush(cs); - mmu_rtp1 &= ~0xffffffff00000000; - mmu_rtp1 |= (u64_val << 32); + env->mmu.v6.rtp1 &= ~0xffffffff00000000; + env->mmu.v6.rtp1 |= (u64_val << 32); break; case AUX_ID_mmu_ctrl: - if (mmu_ctrl != val) + if (env->mmu.v6.ctrl != val) tlb_flush(cs); - mmu_ctrl = val; + env->mmu.v6.ctrl = val; qemu_log_mask(CPU_LOG_MMU, "mmu_ctrl = 0x" TARGET_FMT_lx "\n", val); break; case AUX_ID_mmu_ttbcr: - mmu_ttbcr = val; + env->mmu.v6.ttbcr = val; break; case AUX_ID_mmuv6_tlbcommand: mmuv6_tlb_command(env, val); @@ -415,30 +347,76 @@ arc_mmuv6_aux_set(const struct arc_aux_reg_detail *aux_reg_detail, #define ALL1_64BIT (0xffffffffffffffff) +/* TODO: This is for MMU48 only. */ +static char x_for_ttbc(CPUARCState *env, unsigned char i) +{ + uint32_t tnsz; + const char xs[MMUV6_VERSION_SIZE][2] = { + [MMUV6_52_64K] = { 13, 16 }, + [MMUV6_48_4K] = { 12, 12 }, + [MMUV6_48_16K] = {4, 6 }, + [MMUV6_48_64K] = { 9, 13 }, + }; + + switch (i) { + case 0: + tnsz = FIELD_EX32(env->mmu.v6.ttbcr, MMU_TTBC, T0SZ); + break; + case 1: + tnsz = FIELD_EX32(env->mmu.v6.ttbcr, MMU_TTBC, T1SZ); + break; + default: + assert(0); + break; + } + + switch(mmu_v6_version->id) { + case MMUV6_32_4K: + if(tnsz > 1) + return (14 - tnsz); + else + return (5 - tnsz); + break; + default: + return xs[mmu_v6_version->id][i]; + break; + } + + assert("This should not happen !!!" == 0); +} + +#define MASK_FOR_ROOT_ADDRESS(X) \ + (((1ull << VADDR_SIZE()) - 1) & (~((1 << X) - 1))) + +#define MMU_RTPN_ROOT_ADDRESS(N) \ + (root_address(env->mmu.v6.rtp##N) & MASK_FOR_ROOT_ADDRESS(x_for_ttbc(env, N))) + static uint64_t -root_ptr_for_vaddr(uint64_t vaddr, bool *valid) +root_ptr_for_vaddr(CPUARCState *env, uint64_t vaddr, bool *valid) { + uint32_t t0sz = FIELD_EX32(env->mmu.v6.ttbcr, MMU_TTBC, T0SZ); + uint32_t t1sz = FIELD_EX32(env->mmu.v6.ttbcr, MMU_TTBC, T1SZ); + /* TODO: This is only for MMUv48 */ assert(mmu_v6_version->id != MMUV6_48_4K || ( - MMU_TTBCR_TNSZ(0) == MMU_TTBCR_TNSZ(1) - && (MMU_TTBCR_TNSZ(0) == 16 || MMU_TTBCR_TNSZ(0) == 25))); + t0sz == t1sz && (t0sz == 16 || t0sz == 25))); switch(mmu_v6_version->id) { case MMUV6_52_64K: case MMUV6_48_4K: case MMUV6_48_16K: case MMUV6_48_64K: - if ((vaddr >> (64-MMU_TTBCR_TNSZ(0))) == 0) + if ((vaddr >> (64 - t0sz)) == 0) return MMU_RTPN_ROOT_ADDRESS(0); - if ((vaddr >> (64-MMU_TTBCR_TNSZ(1))) == ((1 << MMU_TTBCR_TNSZ(1)) - 1)) + if ((vaddr >> (64 - t1sz)) == ((1 << t1sz) - 1)) return MMU_RTPN_ROOT_ADDRESS(1); break; case MMUV6_32_4K: - if ((vaddr >> (32-MMU_TTBCR_TNSZ(0))) == 0) + if ((vaddr >> (32 - t0sz)) == 0) return MMU_RTPN_ROOT_ADDRESS(0); - if ((vaddr >> (32-MMU_TTBCR_TNSZ(1))) == ((1 << MMU_TTBCR_TNSZ(1)) - 1)) + if ((vaddr >> (32 - t1sz)) == ((1 << t1sz) - 1)) return MMU_RTPN_ROOT_ADDRESS(1); break; default: @@ -567,10 +545,10 @@ page_table_traverse(CPUARCState *env, { bool found_block_descriptor = false; uint64_t pte, pte_addr; - int l; + int level; int overwrite_permitions = 0; bool valid_root = true; - uint64_t root = root_ptr_for_vaddr(vaddr, &valid_root); + uint64_t root = root_ptr_for_vaddr(env, vaddr, &valid_root); ARCCPU *cpu = env_archcpu (env); unsigned char remainig_bits = VADDR_SIZE(); @@ -589,8 +567,8 @@ page_table_traverse(CPUARCState *env, } } - for(l = 0; l < NLEVELS(); l++) { - unsigned char bits_to_compare = N_BITS_ON_LEVEL(l); + for(level = 0; level < NLEVELS(); level++) { + unsigned char bits_to_compare = N_BITS_ON_LEVEL(level); remainig_bits = remainig_bits - bits_to_compare; unsigned offset = (vaddr >> remainig_bits) & ((1< %lx\n", l, offset, pte_addr, pte); + qemu_log_mask(CPU_LOG_MMU, "[MMUV3] == Level: %d, offset: %d, pte_addr: %lx ==> %lx\n", level, offset, pte_addr, pte); } - if(pte_is_invalid(pte, l)) { + if(pte_is_invalid(pte, level)) { if(rwe != MMU_MEM_IRRELEVANT_TYPE) { qemu_log_mask(CPU_LOG_MMU, "[MMUV3] PTE seems invalid\n"); } - mmu_fault_status = (l & 0x7); + env->mmu.v6.fault_status = (level & 0x7); if(rwe == MMU_MEM_FETCH || rwe == MMU_MEM_IRRELEVANT_TYPE) { SET_MMU_EXCEPTION(*excp, EXCP_IMMU_FAULT, 0x00, 0x00); return -1; @@ -617,13 +595,13 @@ page_table_traverse(CPUARCState *env, } - if(PTE_IS_BLOCK_DESCRIPTOR(pte, l) || PTE_IS_PAGE_DESCRIPTOR(pte, l)) { + if(PTE_IS_BLOCK_DESCRIPTOR(pte, level) || PTE_IS_PAGE_DESCRIPTOR(pte, level)) { if(PTE_BLK_AF(pte) != 0) { found_block_descriptor = true; break; } else { qemu_log_mask(CPU_LOG_MMU, "[MMUV3] PTE AF is not set\n"); - mmu_fault_status = (l & 0x7); + env->mmu.v6.fault_status = (level & 0x7); if(rwe == MMU_MEM_FETCH || rwe == MMU_MEM_IRRELEVANT_TYPE) { SET_MMU_EXCEPTION(*excp, EXCP_IMMU_FAULT, 0x10, 0x00); return -1; @@ -634,7 +612,7 @@ page_table_traverse(CPUARCState *env, } } - if(PTE_IS_TABLE_DESCRIPTOR(pte, l)) { + if(PTE_IS_TABLE_DESCRIPTOR(pte, level)) { if(PTE_TBL_KERNEL_EXECUTE_NEVER_NEXT(pte)) { overwrite_permitions |= RESTRICT_TBL_KERNEL_EXECUTE_NEVER; } @@ -652,7 +630,7 @@ page_table_traverse(CPUARCState *env, } } - if(pte_is_invalid(pte, l)) { + if(pte_is_invalid(pte, level)) { if(rwe == MMU_MEM_FETCH || rwe == MMU_MEM_IRRELEVANT_TYPE) { SET_MMU_EXCEPTION(*excp, EXCP_IMMU_FAULT, 0x00, 0x00); return -1; @@ -662,12 +640,12 @@ page_table_traverse(CPUARCState *env, } } - root = pte_tbl_next_level_table_address(l, pte); + root = pte_tbl_next_level_table_address(level, pte); } if(found_block_descriptor) { - if(protv_violation(env, pte, l, overwrite_permitions, rwe)) { + if(protv_violation(env, pte, level, overwrite_permitions, rwe)) { qemu_log_mask(CPU_LOG_MMU, "\n[MMUV3] [PC "TARGET_FMT_lx "] PTE Protection violation: vaddr "TARGET_FMT_lx " pte [addr %lx val %lx]\n", env->pc, vaddr, pte_addr, pte); @@ -694,10 +672,14 @@ void arc_mmu_init_v6(CPUARCState *env) { ARCCPU *cpu = env_archcpu(env); + cpu->env.mmu.v6.ctrl = 0; + cpu->env.mmu.v6.ttbcr = 0; + cpu->env.mmu.v6.fault_status = 0; + cpu->env.mmu.v6.rtp0 = 0; + cpu->env.mmu.v6.rtp1 = 0; + switch(cpu->family) { case ARC_OPCODE_ARC64: - //mmu_v6_version = &mmuv6_info[MMUV6_48_4K]; - if(cpu->cfg.mmuv6_version == NULL) { mmu_v6_version = &mmuv6_info[MMUV6_48_4K]; } else if(!g_strcmp0(cpu->cfg.mmuv6_version, "48_4k")) { @@ -736,12 +718,13 @@ arc_mmuv6_translate(CPUARCState *env, int *prot, struct mem_exception *excp) { target_ulong paddr; + uint32_t enabled = FIELD_EX32(env->mmu.v6.ctrl, MMU_CTRL, EN); /* This is really required. Fail in non singlestep without in_asm. */ env->mmu.v6.exception.number = EXCP_NO_EXCEPTION; - if(!MMU_ENABLED) { - paddr = vaddr; + if(!enabled) { + paddr = vaddr; } paddr = (target_ulong) page_table_traverse(env, vaddr, rwe, prot, excp); @@ -760,14 +743,14 @@ typedef enum { MMU_ACTION, } ACTION; -static int mmuv6_decide_action(const CPUARCState *env, - target_ulong addr, - int mmu_idx) +static int mmuv6_decide_action(const CPUARCState *env, target_ulong addr, int mmu_idx) { - if (MMU_ENABLED) - return MMU_ACTION; - else - return DIRECT_ACTION; + uint32_t enabled = FIELD_EX32(env->mmu.v6.ctrl, MMU_CTRL, EN); + + if (enabled) + return MMU_ACTION; + else + return DIRECT_ACTION; } #endif @@ -902,8 +885,9 @@ bool arc_cpu_tlb_fill_v6(CPUState *cs, vaddr address, int size, hwaddr arc_mmu_debug_translate_v6(CPUARCState *env, vaddr addr) { struct mem_exception excp; + uint32_t enabled = FIELD_EX32(env->mmu.v6.ctrl, MMU_CTRL, EN); - if(mmuv6_enabled()) { + if(enabled) { return arc_mmuv6_translate(env, addr, MMU_MEM_IRRELEVANT_TYPE, NULL, &excp); } else { return addr; @@ -912,7 +896,7 @@ hwaddr arc_mmu_debug_translate_v6(CPUARCState *env, vaddr addr) #endif /* CONFIG_USER_ONLY */ void arc_mmu_disable_v6(CPUARCState *env) { - disable_mmuv6(); + env->mmu.v6.ctrl = FIELD_DP32(env->mmu.v6.ctrl, MMU_CTRL, EN, 0); } /*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/ diff --git a/target/arc/mmu-v6.h b/target/arc/mmu-v6.h index 40e8e619295..3df308ddc52 100644 --- a/target/arc/mmu-v6.h +++ b/target/arc/mmu-v6.h @@ -22,6 +22,47 @@ #define ARC64_MMUV6_H #include "target/arc/mmu-common.h" +#include "hw/registerfields.h" + +/* + * MMU Control Register bits + * MMU_CTRL, AUX address: 0x468, access: RW + * + * EN[0] - Enabled + * KU[1] - Kernel mode access to user mode data + * WX[2] - Writeable pages execution behavior + * TCE[3] - Translation cache enable + * R[31:4] - Reserved + */ + +FIELD(MMU_CTRL, EN, 0, 1) +FIELD(MMU_CTRL, KU, 1, 1) +FIELD(MMU_CTRL, WX, 2, 1) +FIELD(MMU_CTRL, TCE, 3, 1) + +/* + * MMU Translation Table Base Control Register bits + * MMU_TTBC, AUX address: 0x469, access: RW + * + * T0SZ[4:0] - Size of the virtual region mapped by translation table 0 + * T0SH[6:5] - Share attributes for translation table 0 + * T0C[7] - Cache attribute for translation table 0 + * R[14:8] - Reserved + * A1[15] - When this bit is asserted, the execute permissions are + * not influenced by the Access Permissions bits + * T1SZ[20:16] - Size of the virtual region mapped by translation table 1 + * T1SH[22:21] - Share attributes for translation table 1 + * T1C[23] - Cache attribute for translation table 1 + * R[31:24] - Reserved + */ + +FIELD(MMU_TTBC, T0SZ, 0, 5) +FIELD(MMU_TTBC, T0SH, 5, 2) +FIELD(MMU_TTBC, T0C, 7, 1) +FIELD(MMU_TTBC, A1, 15, 1) +FIELD(MMU_TTBC, T1SZ, 16, 5) +FIELD(MMU_TTBC, T1SH, 21, 2) +FIELD(MMU_TTBC, T1C, 23, 1) struct arc_mmuv6 { struct mmuv6_exception { @@ -29,9 +70,12 @@ struct arc_mmuv6 { uint8_t causecode; uint8_t parameter; } exception; -}; - -int mmuv6_enabled(void); + uint32_t ctrl; /* Control Register */ + uint32_t ttbcr; /* Translation Table Base Control Register */ + uint64_t fault_status; /* Fault Status Register */ + uint64_t rtp0; /* Root Translation Pointer0 Register (Low + High) */ + uint64_t rtp1; /* Root Translation Pointer1 Register (Low + High) */ +}; #endif /* ARC64_MMUV6_H */ diff --git a/target/arc/op_helper.c b/target/arc/op_helper.c index e0860cc4771..8a38d1696ce 100644 --- a/target/arc/op_helper.c +++ b/target/arc/op_helper.c @@ -92,7 +92,7 @@ target_ulong helper_llock(CPUARCState *env, target_ulong addr) target_ulong ret = cpu_ldl_data_ra(env, addr, GETPC()); entry->lpa_lf = (haddr & (~LPA_LFS_ALIGNEMENT_MASK)); - entry->lpa_lf += 1; /* least significant bit is LF flag */ + entry->lpa_lf |= 1; /* least significant bit is LF flag */ entry->read_value = ret; qemu_mutex_unlock(&entry->mutex); return ret; @@ -149,7 +149,7 @@ target_ulong helper_llockl(CPUARCState *env, target_ulong addr) target_ulong ret = cpu_ldq_data_ra(env, addr, GETPC()); entry->lpa_lf = (haddr & (~LPA_LFS_ALIGNEMENT_MASK)); - entry->lpa_lf += 1; /* least significant bit is LF flag */ + entry->lpa_lf |= 1; /* least significant bit is LF flag */ entry->read_value = ret; qemu_mutex_unlock(&entry->mutex); return ret; @@ -194,7 +194,7 @@ uint64_t helper_llockd(CPUARCState *env, target_ulong addr) uint64_t ret = cpu_ldq_data_ra(env, addr, GETPC()); entry->lpa_lf = (haddr & (~LPA_LFS_ALIGNEMENT_MASK)); - entry->lpa_lf += 1; /* least significant bit is LF flag */ + entry->lpa_lf |= 1; /* least significant bit is LF flag */ entry->read_value = ret; qemu_mutex_unlock(&entry->mutex); diff --git a/target/arc/regs-detail.def b/target/arc/regs-detail.def index 7a15e0fc431..cd7c4a82329 100644 --- a/target/arc/regs-detail.def +++ b/target/arc/regs-detail.def @@ -403,6 +403,16 @@ DEF(0x542, ARC_OPCODE_ARC700, NONE, aux_cabac_cod_param) DEF(0x543, ARC_OPCODE_ARC700, NONE, aux_cabac_misc0) DEF(0x544, ARC_OPCODE_ARC700, NONE, aux_cabac_misc1) DEF(0x545, ARC_OPCODE_ARC700, NONE, aux_cabac_misc2) + +/* ARConnect */ +DEF(0xd0, ARC_OPCODE_ARCALL, NONE, mcip_bcr) +DEF(0xd5, ARC_OPCODE_ARCALL, NONE, mcip_idu_bcr) +DEF(0xd6, ARC_OPCODE_ARCALL, NONE, mcip_gfrc_bcr) +DEF(0xe0, ARC_OPCODE_ARCALL, NONE, mcip_ici_bcr) +DEF(0x600, ARC_OPCODE_ARCALL, NONE, mcip_cmd) +DEF(0x601, ARC_OPCODE_ARCALL, NONE, mcip_wdata) +DEF(0x602, ARC_OPCODE_ARCALL, NONE, mcip_readback) + DEF(0x700, ARC_OPCODE_ARCALL, NONE, smart_control) /* DEF (0x701, ARC_OPCODE_ARC700, NONE, smart_data_0) @@ -460,13 +470,10 @@ DEF(0xcc, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xcd, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xce, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xcf, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) -DEF(0xd0, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xd1, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xd2, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xd3, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xd4, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) -DEF(0xd5, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) -DEF(0xd6, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xd7, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xd8, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xd9, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) @@ -476,7 +483,6 @@ DEF(0xdc, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xdd, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xde, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xdf, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) -DEF(0xe0, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xe1, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xe2, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) DEF(0xe3, ARC_OPCODE_DEFAULT, NONE, unimp_bcr) diff --git a/target/arc/regs-impl.c b/target/arc/regs-impl.c index c33cfdd04f6..5456495df02 100644 --- a/target/arc/regs-impl.c +++ b/target/arc/regs-impl.c @@ -30,8 +30,10 @@ static target_ulong get_identity(CPUARCState *env) { - target_ulong chipid = 0xffff, arcnum = 0, arcver, res; + target_ulong chipid = 0xffff; + target_ulong arcver, res; ARCCPU *cpu = env_archcpu(env); + target_ulong arcnum = cpu->core_id; switch (cpu->family) { case ARC_OPCODE_ARC700: @@ -56,7 +58,6 @@ static target_ulong get_identity(CPUARCState *env) } - /* TODO: in SMP, arcnum depends on the cpu instance. */ res = ((chipid & 0xFFFF) << 16) | ((arcnum & 0xFF) << 8) | (arcver & 0xFF); return res; } diff --git a/target/arc/regs.def b/target/arc/regs.def index a5d5dd429f6..b479540ff43 100644 --- a/target/arc/regs.def +++ b/target/arc/regs.def @@ -442,7 +442,10 @@ AUX_REG (mmu_fault_status, arc_mmuv6_aux_get, arc_mmuv6_aux_set) AUX_REG (mmu_mem_attr_lo, arc_mmuv6_aux_get, arc_mmuv6_aux_set) AUX_REG (mmu_mem_attr_hi, arc_mmuv6_aux_get, arc_mmuv6_aux_set) -AUX_REG (mcip_bcr, arconnect_regs_get, arconnect_regs_set) +AUX_REG (mcip_bcr, arconnect_regs_get, NULL) +AUX_REG (mcip_idu_bcr, arconnect_regs_get, NULL) +AUX_REG (mcip_gfrc_bcr, arconnect_regs_get, NULL) +AUX_REG (mcip_ici_bcr, arconnect_regs_get, NULL) AUX_REG (mcip_cmd, arconnect_regs_get, arconnect_regs_set) AUX_REG (mcip_wdata, arconnect_regs_get, arconnect_regs_set) AUX_REG (mcip_readback, arconnect_regs_get, NULL) diff --git a/target/arc/timer.c b/target/arc/timer.c index f1469d525c1..9dd4ffcb1c4 100644 --- a/target/arc/timer.c +++ b/target/arc/timer.c @@ -46,6 +46,27 @@ static uint64_t get_ns(CPUARCState *env) #endif } +uint64_t arc_get_cycles(CPUARCState *env) +{ + uint64_t diff = get_ns(env) - env->rtc.base_time_ns; + + /* + * In user mode host's cycles are stored in base_time_ns and get_ns() + * returns host cycles. Thus, return just a difference in this case + * without converting from nanoseconds to cycles. + */ +#ifndef CONFIG_USER_ONLY + return NS_TO_CYCLE(diff); +#else + return diff; +#endif +} + +uint64_t arc_get_global_cycles(void) +{ + return arc_get_cycles(&ARC_CPU(first_cpu)->env); +} + static uint32_t get_t_count(CPUARCState *env, uint32_t t) { #ifndef CONFIG_USER_ONLY @@ -141,78 +162,6 @@ static void arc_timer1_cb(void *opaque) } #endif -/* RTC counter update. */ -static void cpu_rtc_count_update(CPUARCState *env) -{ - uint64_t now; - uint64_t llreg; - - assert((env_archcpu(env)->timer_build & TB_RTC) && env->cpu_rtc); - now = get_ns(env); - - if (!(env->aux_rtc_ctrl & 0x01)) { - return; - } - - llreg = ((now - env->last_clk_rtc) / TIMER_PERIOD(FREQ_HZ)); - llreg += env->aux_rtc_low + ((uint64_t)env->aux_rtc_high << 32); - env->aux_rtc_high = llreg >> 32; - env->aux_rtc_low = (uint32_t) llreg; - - env->last_clk_rtc = now; - qemu_log_mask(LOG_UNIMP, "[RTC] RTC count-regs update\n"); -} - -#ifndef CONFIG_USER_ONLY -/* Update the next timeout time as difference between Count and Limit */ -static void cpu_rtc_update(CPUARCState *env) -{ - uint64_t wait = 0; - uint64_t now, period; - uint64_t next; - - assert(env->cpu_rtc); - now = get_ns(env); - - if (!(env->aux_rtc_ctrl & 0x01)) { - return; - } - - period = TIMER_PERIOD(FREQ_HZ); - wait = UINT64_MAX - ((((uint64_t) env->aux_rtc_high) << 32) - + env->aux_rtc_low); - wait -= (now - env->last_clk_rtc) / period; - - /* Limit timeout rate. */ - if ((wait * period) < TIMEOUT_LIMIT) { - period = TIMEOUT_LIMIT / wait; - } - - next = now + (uint64_t) wait * period; - timer_mod(env->cpu_rtc, next); - qemu_log_mask(LOG_UNIMP, "[RTC] RTC update\n"); -} -#endif - -#ifndef CONFIG_USER_ONLY -/* RTC call back routine. */ -static void arc_rtc_cb(void *opaque) -{ - CPUARCState *env = (CPUARCState *) opaque; - - if (!(env_archcpu(env)->timer_build & TB_RTC)) { - return; - } - - qemu_log_mask(LOG_UNIMP, "[RTC] RTC expired\n"); - - env->aux_rtc_high = 0; - env->aux_rtc_low = 0; - env->last_clk_rtc = get_ns(env); - cpu_rtc_update(env); -} -#endif - /* Helper used when resetting the system. */ static void cpu_arc_count_reset(CPUARCState *env, uint32_t timer) { @@ -284,35 +233,88 @@ static void cpu_arc_control_set(CPUARCState *env, } } -/* Get The RTC count value. */ -static uint32_t arc_rtc_count_get(CPUARCState *env, bool lower) +#if defined(TARGET_ARC64) +static uint64_t arc_rtc_count_get_low(CPUARCState *env) { - cpu_rtc_count_update(env); - return lower ? env->aux_rtc_low : env->aux_rtc_high; + uint8_t enabled = FIELD_EX32(env->rtc.ctrl, ARC_RTC_CTRL, ENABLE); + + if (enabled) { + return arc_get_cycles(env); + } else { + return env->rtc.stop_cycles; + } } +#else +static uint32_t arc_rtc_count_get_low(CPUARCState *env) +{ + uint8_t enabled = FIELD_EX32(env->rtc.ctrl, ARC_RTC_CTRL, ENABLE); + uint64_t cycles; + + if (enabled) { + cycles = arc_get_cycles(env); + } else { + cycles = env->rtc.stop_cycles; + } + + /* + * If RTC is enabled then update (high,low) pair with the latest cycles + * count. Otherwise, don't update it and use the old value. + */ + env->rtc.low = cycles & 0xFFFFFFFF; + env->rtc.high = (cycles >> 32) & 0xFFFFFFFF; + + return env->rtc.low; +} + +static uint32_t arc_rtc_count_get_high(CPUARCState *env) +{ + return env->rtc.high; +} +#endif /* Set the RTC control bits. */ static void arc_rtc_ctrl_set(CPUARCState *env, uint32_t val) { #ifndef CONFIG_USER_ONLY + uint8_t enable = FIELD_EX32(val, ARC_RTC_CTRL, ENABLE); + uint8_t enabled = FIELD_EX32(env->rtc.ctrl, ARC_RTC_CTRL, ENABLE); + uint8_t clear = FIELD_EX32(val, ARC_RTC_CTRL, CLEAR); + assert(GET_STATUS_BIT(env->stat, Uf) == 0); - if (val & 0x02) { - env->aux_rtc_low = 0; - env->aux_rtc_high = 0; - env->last_clk_rtc = get_ns(env); + /* Case: RTC is enabled and it's going to be disabled. + * Remember stop time and save the latest cycles count for further using. + */ + if (enabled && !enable) { + env->rtc.stop_time_ns = get_ns(env); + env->rtc.stop_cycles = arc_get_cycles(env); } - if (!(val & 0x01)) { - timer_del(env->cpu_rtc); + + if (clear) { + env->rtc.base_time_ns = get_ns(env); + + /* + * If RTC is stopped then remember stop time - it will allow to reset + * the counter after reactivating the RTC. + */ + if (!enabled) { + env->rtc.stop_time_ns = env->rtc.base_time_ns; + env->rtc.stop_cycles = 0; + } } - /* Restart RTC, update last clock. */ - if ((env->aux_rtc_ctrl & 0x01) == 0 && (val & 0x01)) { - env->last_clk_rtc = get_ns(env); + /* + * Case: RTC is disabled and it's going to be enabled. + * Increase base time to fill the gap between stop and now. + */ + if (!enabled && enable) { + env->rtc.base_time_ns += get_ns(env) - env->rtc.stop_time_ns; } - env->aux_rtc_ctrl = 0xc0000000 | (val & 0x01); - cpu_rtc_update(env); + /* Always set atomicity bits since. */ + env->rtc.ctrl = FIELD_DP32(env->rtc.ctrl, ARC_RTC_CTRL, ENABLE, enable); + env->rtc.ctrl = FIELD_DP32(env->rtc.ctrl, ARC_RTC_CTRL, A0, 1); + env->rtc.ctrl = FIELD_DP32(env->rtc.ctrl, ARC_RTC_CTRL, A1, 1); #endif } @@ -324,19 +326,12 @@ cpu_arc_clock_init(ARCCPU *cpu) CPUARCState *env = &cpu->env; #ifndef CONFIG_USER_ONLY - if (env_archcpu(env)->timer_build & TB_T0) { - env->cpu_timer[0] = - timer_new_ns(QEMU_CLOCK_VIRTUAL, &arc_timer0_cb, env); + if (FIELD_EX32(cpu->timer_build, ARC_TIMER_BUILD, T0)) { + env->cpu_timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, &arc_timer0_cb, env); } - if (env_archcpu(env)->timer_build & TB_T1) { - env->cpu_timer[1] = - timer_new_ns(QEMU_CLOCK_VIRTUAL, &arc_timer1_cb, env); - } - - if (env_archcpu(env)->timer_build & TB_RTC) { - env->cpu_rtc = - timer_new_ns(QEMU_CLOCK_VIRTUAL, &arc_rtc_cb, env); + if (FIELD_EX32(cpu->timer_build, ARC_TIMER_BUILD, T1)) { + env->cpu_timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, &arc_timer1_cb, env); } #endif @@ -344,27 +339,52 @@ cpu_arc_clock_init(ARCCPU *cpu) env->timer[1].last_clk = get_ns(env); } -void -arc_initializeTIMER(ARCCPU *cpu) +/* + * TODO: Implement setting default interrupt priorities for Timer 0 and Timer 1. + */ + +void arc_initializeTIMER(ARCCPU *cpu) { - CPUARCState *env = &cpu->env; + uint32_t build = 0; + uint8_t version; + + switch (cpu->family) { + case ARC_OPCODE_ARC64: + version = 0x7; + break; + case ARC_OPCODE_ARC32: + version = 0x6; + break; + default: + version = 0x4; + } + + build = FIELD_DP32(build, ARC_TIMER_BUILD, VERSION, version); + + if (cpu->cfg.has_timer_0) { + build = FIELD_DP32(build, ARC_TIMER_BUILD, T0, 1); + } + + if (cpu->cfg.has_timer_1) { + build = FIELD_DP32(build, ARC_TIMER_BUILD, T1, 1); + } + + if (cpu->cfg.rtc_option) { + build = FIELD_DP32(build, ARC_TIMER_BUILD, RTC, 1); + } - /* FIXME! add default timer priorities. */ - env_archcpu(env)->timer_build = 0x04 | (cpu->cfg.has_timer_0 ? TB_T0 : 0) | - (cpu->cfg.has_timer_1 ? TB_T1 : 0) | - (cpu->cfg.rtc_option ? TB_RTC : 0); + cpu->timer_build = build; } -void -arc_resetTIMER(ARCCPU *cpu) +void arc_resetTIMER(ARCCPU *cpu) { CPUARCState *env = &cpu->env; - if (env_archcpu(env)->timer_build & TB_T0) { + if (FIELD_EX32(cpu->timer_build, ARC_TIMER_BUILD, T0)) { cpu_arc_count_reset(env, 0); } - if (env_archcpu(env)->timer_build & TB_T1) { + if (FIELD_EX32(cpu->timer_build, ARC_TIMER_BUILD, T1)) { cpu_arc_count_reset(env, 1); } } @@ -405,15 +425,18 @@ aux_timer_get(const struct arc_aux_reg_detail *aux_reg_detail, void *data) break; case AUX_ID_aux_rtc_low: - return arc_rtc_count_get(env, true); + return arc_rtc_count_get_low(env); break; +#if !defined(TARGET_ARC64) + /* AUX_RTC_HIGH register is not presented on ARC HS6x processors. */ case AUX_ID_aux_rtc_high: - return arc_rtc_count_get(env, false); + return arc_rtc_count_get_high(env); break; +#endif case AUX_ID_aux_rtc_ctrl: - return env->aux_rtc_ctrl; + return env->rtc.ctrl; break; default: diff --git a/target/arc/timer.h b/target/arc/timer.h index 01baf73d37f..1b76a333f64 100644 --- a/target/arc/timer.h +++ b/target/arc/timer.h @@ -21,7 +21,63 @@ #ifndef __ARC_TIMER_H__ #define __ARC_TIMER_H__ +/* + * Timer Configuration Register bits + * TIMER_BUILD, AUX address: 0x75, access: R + * + * VERSION[7:0] - Version of timers: + * 0x4 - ARCv2 + * 0x6 - ARCv2 with support of TD bit for timers + * 0x6 - ARCv3 HS5x + * 0x7 - ARCv3 HS6x + * T0[8] - Timer 0 present + * T1[9] - Timer 1 present + * RTC[10] - 64-bit RTC present + * R[15:11] - Reserved + * P0[19:16] - Indicates the interrupt priority level of Timer 0 + * P1[23:20] - Indicates the interrupt priority level of Timer 1 + * R[31:24] - Reserved + */ + +FIELD(ARC_TIMER_BUILD, VERSION, 0, 8) +FIELD(ARC_TIMER_BUILD, T0, 8, 1) +FIELD(ARC_TIMER_BUILD, T1, 9, 1) +FIELD(ARC_TIMER_BUILD, RTC, 10, 1) +FIELD(ARC_TIMER_BUILD, P0, 16, 4) +FIELD(ARC_TIMER_BUILD, P1, 20, 4) + +/* + * Real-Time Counter Control Register bits + * AUX_RTC_CTRL, AUX address: 0x103, access: RW + * + * E[0] - Enable: 0 - disabled counting , 1 - enabled counting + * C[1] - A value of 1 clears the AUX_RTC_LOW and AUX_RTC_HIGH registers + * R[29:2] - Reserved + * A0[30] - A bit of atomicity of reads for AUX_RTC_LOW + * A1[31] - A bit of atomicity of reads for AUX_RTC_HIGH + */ + +FIELD(ARC_RTC_CTRL, ENABLE, 0, 1) +FIELD(ARC_RTC_CTRL, CLEAR, 1, 1) +FIELD(ARC_RTC_CTRL, A0, 30, 1) +FIELD(ARC_RTC_CTRL, A1, 31, 1) + + void arc_initializeTIMER(ARCCPU *); void arc_resetTIMER(ARCCPU *); +/* + * Return the number of global clock cycles passed for a particular CPU. + */ + +uint64_t arc_get_cycles(CPUARCState *env); + +/* + * Return the number of global clock cycles passed common for all CPUs. It's + * used by Global Free Running Counter (GFRC) subsystem of ARConnect that is + * used by CPUs the global clock source. + */ + +uint64_t arc_get_global_cycles(void); + #endif