From d1523790f286552bdaae4ea51539aa1935120f05 Mon Sep 17 00:00:00 2001 From: Yuriy Kolerov Date: Wed, 13 Sep 2023 17:23:23 +0400 Subject: [PATCH 1/4] target/arc: v3: Use a distinct MMU state for each core This patch moves data for MMU to core's state storage. It's necessary for adding support of SMP since each core requires it's own MMU state. Signed-off-by: Yuriy Kolerov --- target/arc/mmu-v6.c | 304 +++++++++++++++++++++----------------------- target/arc/mmu-v6.h | 50 +++++++- 2 files changed, 191 insertions(+), 163 deletions(-) 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 */ From ce7d330e0cf599cdc496bad289db691c06d7c41a Mon Sep 17 00:00:00 2001 From: Yuriy Kolerov Date: Sat, 7 Oct 2023 09:49:32 +0400 Subject: [PATCH 2/4] target/arc: Don't use a timer for RTC and enable it by default Using QTimer for RTC implementation is redundent and meaningless since it's just free-running clock. Also, enable RTC by default. If it's not available then Linux uses Timer 1 as fallback which is much less accurate. Signed-off-by: Yuriy Kolerov --- target/arc/cpu.c | 2 +- target/arc/cpu.h | 16 +-- target/arc/timer.c | 247 +++++++++++++++++++++++++-------------------- target/arc/timer.h | 56 ++++++++++ 4 files changed, 202 insertions(+), 119 deletions(-) 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..7bbeff35d4c 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. */ @@ -291,11 +298,8 @@ struct CPUArchState { /* 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; 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 From 8d452e781cea868f770f11995a02a462e7b67b48 Mon Sep 17 00:00:00 2001 From: Yuriy Kolerov Date: Thu, 14 Sep 2023 14:42:22 +0400 Subject: [PATCH 3/4] target/arc: Add support of SMP This commit introduces a support of SMP for ARCv2 and ARCv3 targets in QEMU. A partial support of ICI, IDU and GFRC subsystems is presented enough for running Linux. Known issues: * Some IDU commands like CMD_IDU_GEN_CIRQ, CMD_IDU_ACK_CIRQ and CMD_IDU_CHECK_FIRST are not supported yet. They may be implemented correctly only with a distinct interrupt controller for IDU. All devices are connected to the first core because of the same reason. It means that Linux cannot really control distributing of interrupts. * Only reading operations for GFRC are supported. * Only a small subset of ICI commands is supported. * SMP for HS4x is slower than for HS5x and HS6x. SMP mode may be turned on by -smp N option where N stands for a number of cores. Co-authored-by: Cupertino Miranda Co-authored-by: Jose Abreu Signed-off-by: Yuriy Kolerov --- hw/arc/boot.c | 10 +- hw/arc/virt.c | 41 +- target/arc/arconnect.c | 732 ++++++++++++++++++++++-------- target/arc/arconnect.h | 275 ++++++++++- target/arc/arconnect_commands.def | 76 ++++ target/arc/cpu.h | 8 +- target/arc/irq.c | 6 +- target/arc/irq.h | 2 + target/arc/regs-detail.def | 14 +- target/arc/regs-impl.c | 5 +- target/arc/regs.def | 5 +- 11 files changed, 936 insertions(+), 238 deletions(-) create mode 100644 target/arc/arconnect_commands.def 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.h b/target/arc/cpu.h index 7bbeff35d4c..961f2057838 100644 --- a/target/arc/cpu.h +++ b/target/arc/cpu.h @@ -289,9 +289,9 @@ 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; @@ -307,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/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) From 6492f3975012eb116d0f66e4564b72c75e69319a Mon Sep 17 00:00:00 2001 From: Yuriy Kolerov Date: Mon, 9 Oct 2023 21:50:22 +0400 Subject: [PATCH 4/4] target/arc: Fix setting LF bit for spinlock Signed-off-by: Yuriy Kolerov --- target/arc/op_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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);