qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v1 25/43] target/loongarch: Add LoongArch CSR instruction


From: Richard Henderson
Subject: Re: [PATCH v1 25/43] target/loongarch: Add LoongArch CSR instruction
Date: Tue, 19 Apr 2022 10:05:25 -0700
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.8.0

On 4/19/22 00:33, yangxiaojuan wrote:

On 2022/4/16 上午9:04, Richard Henderson wrote:
On 4/15/22 02:40, Xiaojuan Yang wrote:
...
+void  helper_csr_update(CPULoongArchState *env, target_ulong new_val,
+                        target_ulong csr_offset)
+{
+    uint64_t *csr = (void *)env + csr_offset;
+
+    *csr = new_val;
+}
This function should not exist
...
+    switch (a->csr) {
+    case LOONGARCH_CSR_ESTAT:
+        gen_helper_csrwr_estat(dest, cpu_env, new_val);
+        break;
+    case LOONGARCH_CSR_ASID:
+        gen_helper_csrwr_asid(dest, cpu_env, new_val);
+        break;
+    case LOONGARCH_CSR_TCFG:
+        gen_helper_csrwr_tcfg(dest, cpu_env, new_val);
+        break;
+    case LOONGARCH_CSR_TICLR:
+        gen_helper_csrwr_ticlr(dest, cpu_env, new_val);
+        break;
+    default:
+        tcg_gen_mov_tl(dest, old_val);
+    }
+
+    gen_helper_csr_update(cpu_env, new_val, tcg_constant_tl(csr_offset));

Note that helper_csr_update is nothing more than the store to csr_offset.
On trans_csrxchg() , I am don't know how to use a TCGv value 'new_val 'to update an uint64_t value "CSR_XXX",  So I use helper_csr_update(),

You'd use a store, just like you were already doing in trans_csrwr.

But here's how I'd improve this. For avoidance of doubt, all of this would go in trans_priviledged.c.inc -- there's no use of csr_offsets[] outside of that file.


r~

----


typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env);
typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src);

typedef struct {
    int offset;
    int flags;
    GenCSRRead readfn;
    GenCSRWrite writefn;
} CSRInfo;

enum {
    CSRFL_READONLY  = (1 << 0),
    CSRFL_EXITTB    = (1 << 1),
    CSRFL_IO        = (1 << 2),
};

#define CSR_OFF_FUNCS(NAME, FL, RD, WR) \
    [LOONGARCH_CSR_##NAME] = {                             \
        .offset = offsetof(CPULoongArchState, CSR_##NAME), \
        .flags = FL, .readfn = RD, .writefn = WR           \
    }
#define CSR_OFF_FLAGS(NAME, FL) \
    CSR_OFF_FUNCS(NAME, FL, NULL, NULL)
#define CSR_OFF(NAME) \
    CSR_OFF_FLAGS(NAME, 0)

static const CSRInfo csr_info[] = {
    CSR_OFF(CRMD),
    CSR_OFF_FLAGS(CPUID, CSRFL_READONLY),
    CSR_OFF_FUNCS(TCFG, CSRFL_IO, NULL, gen_helper_csrwr_tcfg),
    CSR_OFF_FUNCS(TVAL, CSRFL_READONLY | CSRFL_IO, gen_helper_csrrd_tval, NULL),
    CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr),
    CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat),
    ...
};

static const CSRInfo *get_csr(unsigned csr_num)
{
    const CSRInfo *csr;
    if (csr_num < ARRAY_SIZE(csr_info)) {
        return NULL;
    }
    csr = &csr_info[csr_num];
    if (csr->offset == 0) {
        return NULL; /* undefined */
    }
    return csr;
}

static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
{
    if ((info->flags & CSRFL_READONLY) && write) {
        return false;
    }
    if ((info->flags & CSRFL_IO) &&
        (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT)) {
        gen_io_start();
        ctx->base.is_jmp = DISAS_EXIT_UPDATE;
    } else if ((info->flags & CSRFL_EXITTB) && write) {
        ctx->base.is_jmp = DISAS_EXIT_UPDATE;
    }
    return true;
}

static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
{
    TCGv dest;
    const CSRInfo *csr;

    if (check_plv(ctx)) {
        return false;
    }
    csr = get_csr(a->csr);
    if (csr == NULL) {
        /* CSR is undefined: read as 0 */
        dest = tcg_constant_tl(0);
    } else {
        check_csr_flags(ctx, csr, false);
        dest = gpr_dst(ctx, a->rd, EXT_NONE);
        if (csr->readfn) {
            csr_readfn(dest, cpu_env);
        } else {
            tcg_gen_ld_tl(dest, cpu_env, csr->offset);
        }
    }
    gen_set_gpr(a->rd, dest, EXT_NONE);
    return true;
}

static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
{
    TCGv dest, src1;
    const CSRInfo *csr;

    if (check_plv(ctx)) {
        return false;
    }
    csr = get_csr_info(a->csr);
    if (csr == NULL) {
        /* CSR is undefined: write ignored, read old value as 0. */
        gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
        return true;
    }
    if (!check_csr_flags(ctx, csr, true)) {
        /* CSR is readonly: trap. */
        return false;
    }
    src1 = gpr_src(ctx, a->rd, EXT_NONE);
    if (csr->writefn) {
        dest = gpr_dst(ctx, a->rd, EXT_NONE);
        csr->writefn(dest, cpu_env, src1);
    } else {
         dest = temp_new(ctx);
         tcg_gen_ld_tl(dest, cpu_env, csr->offset);
         tcg_gen_st_tl(src1, cpu_env, csr->offset);
    }
    gen_set_gpr(a->rd, dest, EXT_NONE);
    return true;
}

static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
{
    TCGv src1, mask, oldv, newv, temp;
    const CSRInfo *csr;

    if (check_plv(ctx)) {
        return false;
    }
    csr = get_csr_info(a->csr);
    if (csr == NULL) {
        /* CSR is undefined: write ignored, read old value as 0. */
        gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
        return true;
    }
    if (!check_csr_flags(ctx, csr, true)) {
        /* CSR is readonly: trap. */
        return false;
    }

    /* So far only readonly csrs have readfn. */
    assert(csr->readfn == NULL);

    src1 = gpr_src(ctx, a->rd, EXT_NONE);
    mask = gpr_src(ctx, a->rj, EXT_NONE);
    oldv = tcg_temp_new();
    newv = tcg_temp_new();
    temp = tcg_temp_new();

    tcg_gen_ld_tl(oldv, cpu_env, csr->offset);
    tcg_gen_and_tl(newv, src1, mask);
    tcg_gen_andc_tl(temp, oldv, mask);
    tcg_gen_or_tl(newv, newv, temp);

    if (csr->writefn) {
        csr->writefn(oldv, cpu_env, newv);
    } else {
        tcg_gen_st_tl(newv, cpu_env, csr->offset);
    }
    gen_set_gpr(a->rd, oldv, EXT_NONE);

    tcg_temp_free(temp);
    tcg_temp_free(newv);
    tcg_temp_free(oldv);
    return true;
}

and then in loongarch_tr_tb_stop:

    case DISAS_EXIT_UPDATE:
        tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);

        /* fall through */
    case DISAS_EXIT:

        tcg_gen_exit_tb(NULL, 0);

        break;





reply via email to

[Prev in Thread] Current Thread [Next in Thread]