]> git.gag.com Git - fw/openocd/commitdiff
target/arc: Introduce L1I,L1D,L2 caches support
authorEvgeniy Didin <didin@synopsys.com>
Fri, 15 May 2020 20:04:01 +0000 (23:04 +0300)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 27 Jun 2020 14:34:24 +0000 (15:34 +0100)
With this commit we introduce L1 and L2 cache
flush and invalidate operations which are necessary for
getting/setting actual data during memory r/w operations.

We introduce L2 cache support, which is not presented
on currently support EMSK board. But L2 is presented
on HSDK board, which soon will be introduced.

Change-Id: I2fda505a47ecb8833cc9f5ffe24f6a4e22ab6eb0
Signed-off-by: Evgeniy Didin <didin@synopsys.com>
Reviewed-on: http://openocd.zylin.com/5688
Reviewed-by: Oleksij Rempel <linux@rempel-privat.de>
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
src/target/arc.c
src/target/arc.h
src/target/arc_cmd.c
src/target/arc_mem.c

index 6cf0ec7af7376b1dae36a57ae3163a381d41c8de..e9709f485d9c2422d73065a0dfede946eb4d6d07 100644 (file)
@@ -86,6 +86,26 @@ struct reg *arc_reg_get_by_name(struct reg_cache *first,
        return NULL;
 }
 
+/**
+ * Reset internal states of caches. Must be called when entering debugging.
+ *
+ * @param target Target for which to reset caches states.
+ */
+int arc_reset_caches_states(struct target *target)
+{
+       struct arc_common *arc = target_to_arc(target);
+
+       LOG_DEBUG("Resetting internal variables of caches states");
+
+       /* Reset caches states. */
+       arc->dcache_flushed = false;
+       arc->l2cache_flushed = false;
+       arc->icache_invalidated = false;
+       arc->dcache_invalidated = false;
+       arc->l2cache_invalidated = false;
+
+       return ERROR_OK;
+}
 
 /* Initialize arc_common structure, which passes to openocd target instance */
 static int arc_init_arch_info(struct target *target, struct arc_common *arc,
@@ -102,6 +122,15 @@ static int arc_init_arch_info(struct target *target, struct arc_common *arc,
                return ERROR_FAIL;
        }
 
+       /* On most ARC targets there is a dcache, so we enable its flushing
+        * by default. If there no dcache, there will be no error, just a slight
+        * performance penalty from unnecessary JTAG operations. */
+       arc->has_dcache = true;
+       arc->has_icache = true;
+       /* L2$ is not available in a target by default. */
+       arc->has_l2cache = false;
+       arc_reset_caches_states(target);
+
        /* Add standard GDB data types */
        INIT_LIST_HEAD(&arc->reg_data_types);
        struct arc_reg_data_type *std_types = calloc(ARRAY_SIZE(standard_gdb_types),
@@ -900,6 +929,7 @@ static int arc_debug_entry(struct target *target)
 
        /* TODO: reset internal indicators of caches states, otherwise D$/I$
         * will not be flushed/invalidated when required. */
+       CHECK_RETVAL(arc_reset_caches_states(target));
        CHECK_RETVAL(arc_examine_debug_reason(target));
 
        return ERROR_OK;
@@ -1152,6 +1182,11 @@ static int arc_resume(struct target *target, int current, target_addr_t address,
        LOG_DEBUG("current:%i, address:0x%08" TARGET_PRIxADDR ", handle_breakpoints(not supported yet):%i,"
                " debug_execution:%i", current, address, handle_breakpoints, debug_execution);
 
+       /* We need to reset ARC cache variables so caches
+        * would be invalidated and actual data
+        * would be fetched from memory. */
+       CHECK_RETVAL(arc_reset_caches_states(target));
+
        if (target->state != TARGET_HALTED) {
                LOG_WARNING("target not halted");
                return ERROR_TARGET_NOT_HALTED;
@@ -1396,8 +1431,9 @@ static int arc_set_breakpoint(struct target *target,
                LOG_DEBUG("ERROR: setting unknown breakpoint type");
                return ERROR_FAIL;
        }
-       /* core instruction cache is now invalid,
-        * TODO: add cache invalidation function here (when implemented). */
+
+       /* core instruction cache is now invalid. */
+       CHECK_RETVAL(arc_cache_invalidate(target));
 
        return ERROR_OK;
 }
@@ -1462,8 +1498,8 @@ static int arc_unset_breakpoint(struct target *target,
                        return ERROR_FAIL;
        }
 
-       /* core instruction cache is now invalid.
-        * TODO: Add cache invalidation function */
+       /* core instruction cache is now invalid. */
+       CHECK_RETVAL(arc_cache_invalidate(target));
 
        return retval;
 }
@@ -1596,6 +1632,176 @@ int arc_step(struct target *target, int current, target_addr_t address,
 }
 
 
+/* This function invalidates icache. */
+static int arc_icache_invalidate(struct target *target)
+{
+       uint32_t value;
+
+       struct arc_common *arc = target_to_arc(target);
+
+       /* Don't waste time if already done. */
+       if (!arc->has_icache || arc->icache_invalidated)
+           return ERROR_OK;
+
+       LOG_DEBUG("Invalidating I$.");
+
+       value = IC_IVIC_INVALIDATE;     /* invalidate I$ */
+       CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_IC_IVIC_REG, value));
+
+       arc->icache_invalidated = true;
+
+       return ERROR_OK;
+}
+
+/* This function invalidates dcache */
+static int arc_dcache_invalidate(struct target *target)
+{
+       uint32_t value, dc_ctrl_value;
+
+       struct arc_common *arc = target_to_arc(target);
+
+       if (!arc->has_dcache || arc->dcache_invalidated)
+           return ERROR_OK;
+
+       LOG_DEBUG("Invalidating D$.");
+
+       CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, &value));
+       dc_ctrl_value = value;
+       value &= ~DC_CTRL_IM;
+
+       /* set DC_CTRL invalidate mode to invalidate-only (no flushing!!) */
+       CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, value));
+       value = DC_IVDC_INVALIDATE;     /* invalidate D$ */
+       CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_IVDC_REG, value));
+
+       /* restore DC_CTRL invalidate mode */
+       CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, dc_ctrl_value));
+
+       arc->dcache_invalidated = true;
+
+       return ERROR_OK;
+}
+
+/* This function invalidates l2 cache. */
+static int arc_l2cache_invalidate(struct target *target)
+{
+       uint32_t value, slc_ctrl_value;
+
+       struct arc_common *arc = target_to_arc(target);
+
+       if (!arc->has_l2cache || arc->l2cache_invalidated)
+           return ERROR_OK;
+
+       LOG_DEBUG("Invalidating L2$.");
+
+       CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_CTRL, &value));
+       slc_ctrl_value = value;
+       value &= ~L2_CTRL_IM;
+
+       /* set L2_CTRL invalidate mode to invalidate-only (no flushing!!) */
+       CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_CTRL, value));
+       /* invalidate L2$ */
+       CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_INV, L2_INV_IV));
+
+       /* Wait until invalidate operation ends */
+       do {
+           LOG_DEBUG("Waiting for invalidation end.");
+           CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_CTRL, &value));
+       } while (value & L2_CTRL_BS);
+
+       /* restore L2_CTRL invalidate mode */
+       CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_CTRL, slc_ctrl_value));
+
+       arc->l2cache_invalidated = true;
+
+       return ERROR_OK;
+}
+
+
+int arc_cache_invalidate(struct target *target)
+{
+       CHECK_RETVAL(arc_icache_invalidate(target));
+       CHECK_RETVAL(arc_dcache_invalidate(target));
+       CHECK_RETVAL(arc_l2cache_invalidate(target));
+
+       return ERROR_OK;
+}
+
+/* Flush data cache. This function is cheap to call and return quickly if D$
+ * already has been flushed since target had been halted. JTAG debugger reads
+ * values directly from memory, bypassing cache, so if there are unflushed
+ * lines debugger will read invalid values, which will cause a lot of troubles.
+ * */
+int arc_dcache_flush(struct target *target)
+{
+       uint32_t value, dc_ctrl_value;
+       bool has_to_set_dc_ctrl_im;
+
+       struct arc_common *arc = target_to_arc(target);
+
+       /* Don't waste time if already done. */
+       if (!arc->has_dcache || arc->dcache_flushed)
+           return ERROR_OK;
+
+       LOG_DEBUG("Flushing D$.");
+
+       /* Store current value of DC_CTRL */
+       CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, &dc_ctrl_value));
+
+       /* Set DC_CTRL invalidate mode to flush (if not already set) */
+       has_to_set_dc_ctrl_im = (dc_ctrl_value & DC_CTRL_IM) == 0;
+       if (has_to_set_dc_ctrl_im) {
+               value = dc_ctrl_value | DC_CTRL_IM;
+               CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, value));
+       }
+
+       /* Flush D$ */
+       value = DC_IVDC_INVALIDATE;
+       CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_IVDC_REG, value));
+
+       /* Restore DC_CTRL invalidate mode (even of flush failed) */
+       if (has_to_set_dc_ctrl_im)
+           CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DC_CTRL_REG, dc_ctrl_value));
+
+       arc->dcache_flushed = true;
+
+       return ERROR_OK;
+}
+
+/* This function flushes l2cache. */
+static int arc_l2cache_flush(struct target *target)
+{
+       uint32_t value;
+
+       struct arc_common *arc = target_to_arc(target);
+
+       /* Don't waste time if already done. */
+       if (!arc->has_l2cache || arc->l2cache_flushed)
+           return ERROR_OK;
+
+       LOG_DEBUG("Flushing L2$.");
+
+       /* Flush L2 cache */
+       CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_FLUSH, L2_FLUSH_FL));
+
+       /* Wait until flush operation ends */
+       do {
+           LOG_DEBUG("Waiting for flushing end.");
+           CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, SLC_AUX_CACHE_CTRL, &value));
+       } while (value & L2_CTRL_BS);
+
+       arc->l2cache_flushed = true;
+
+       return ERROR_OK;
+}
+
+int arc_cache_flush(struct target *target)
+{
+       CHECK_RETVAL(arc_dcache_flush(target));
+       CHECK_RETVAL(arc_l2cache_flush(target));
+
+       return ERROR_OK;
+}
 
 /* ARC v2 target */
 struct target_type arcv2_target = {
index defa3fa97da9ca4e9b1462d3190f5490dfbe99ca..66414115976cb71dc48db6281cd98238a03d3bf7 100644 (file)
 /* ARC 16bits opcodes */
 #define ARC_SDBBP_16 0x7FFF      /* BRK_S */
 
+/* Cache registers */
+#define AUX_IC_IVIC_REG                        0X10
+#define IC_IVIC_INVALIDATE             0XFFFFFFFF
+
+#define AUX_DC_IVDC_REG                        0X47
+#define DC_IVDC_INVALIDATE             BIT(0)
+#define AUX_DC_CTRL_REG                        0X48
+#define DC_CTRL_IM                     BIT(6)
+
+/* L2 cache registers */
+#define SLC_AUX_CACHE_CTRL             0x903
+#define L2_CTRL_IM                     BIT(6)
+#define L2_CTRL_BS                     BIT(8)          /* Busy flag */
+#define SLC_AUX_CACHE_FLUSH            0x904
+#define L2_FLUSH_FL                    BIT(0)
+#define SLC_AUX_CACHE_INV              0x905
+#define L2_INV_IV                      BIT(0)
+
 struct arc_reg_bitfield {
        struct reg_data_type_bitfield bitfield;
        char name[REG_TYPE_MAX_NAME_LENGTH];
@@ -109,6 +127,22 @@ struct arc_common {
        struct reg_cache *core_and_aux_cache;
        struct reg_cache *bcr_cache;
 
+       /* Cache control */
+       bool has_dcache;
+       bool has_icache;
+       bool has_l2cache;
+       /* If true, then D$ has been already flushed since core has been
+        * halted. */
+       bool dcache_flushed;
+       /* If true, then L2 has been already flushed since core has been
+        * halted. */
+       bool l2cache_flushed;
+       /* If true, then caches have been already flushed since core has been
+        * halted. */
+       bool icache_invalidated;
+       bool dcache_invalidated;
+       bool l2cache_invalidated;
+
        /* Indicate if cach was built (for deinit function) */
        bool core_aux_cache_built;
        bool bcr_cache_built;
@@ -247,4 +281,7 @@ struct reg *arc_reg_get_by_name(struct reg_cache *first,
 int arc_reg_get_field(struct target *target, const char *reg_name,
                const char *field_name, uint32_t *value_ptr);
 
+int arc_cache_flush(struct target *target);
+int arc_cache_invalidate(struct target *target);
+
 #endif /* OPENOCD_TARGET_ARC_H */
index fad8ca947337a38fba600da073c9fe7d42ce21ec..59e1645d67cbfebf9c1db1caa8e8f2fc29fef834 100644 (file)
@@ -909,10 +909,60 @@ static int jim_arc_get_reg_field(Jim_Interp *interp, int argc, Jim_Obj * const *
        return JIM_OK;
 }
 
+COMMAND_HANDLER(arc_l1_cache_disable_auto_cmd)
+{
+       bool value;
+       int retval = 0;
+       struct arc_common *arc = target_to_arc(get_current_target(CMD_CTX));
+       retval = CALL_COMMAND_HANDLER(handle_command_parse_bool,
+               &value, "target has caches enabled");
+       arc->has_l2cache = value;
+       arc->has_dcache = value;
+       arc->has_icache = value;
+       return retval;
+}
+
+COMMAND_HANDLER(arc_l2_cache_disable_auto_cmd)
+{
+       struct arc_common *arc = target_to_arc(get_current_target(CMD_CTX));
+       return CALL_COMMAND_HANDLER(handle_command_parse_bool,
+               &arc->has_l2cache, "target has l2 cache enabled");
+}
+
 /* ----- Exported target commands ------------------------------------------ */
 
+const struct command_registration arc_l2_cache_group_handlers[] = {
+       {
+               .name = "auto",
+               .handler = arc_l2_cache_disable_auto_cmd,
+               .mode = COMMAND_ANY,
+               .usage = "(1|0)",
+               .help = "Disable or enable L2",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+const struct command_registration arc_cache_group_handlers[] = {
+       {
+               .name = "auto",
+               .handler = arc_l1_cache_disable_auto_cmd,
+               .mode = COMMAND_ANY,
+               .help = "Disable or enable L1",
+               .usage = "(1|0)",
+       },
+       {
+               .name = "l2",
+               .mode = COMMAND_ANY,
+               .help = "L2 cache command group",
+               .usage = "",
+               .chain = arc_l2_cache_group_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+
 static const struct command_registration arc_core_command_handlers[] = {
-{
+       {
                .name = "add-reg-type-flags",
                .jim_handler = jim_arc_add_reg_type_flags,
                .mode = COMMAND_CONFIG,
@@ -967,6 +1017,13 @@ static const struct command_registration arc_core_command_handlers[] = {
                .usage = "",
                .chain = arc_jtag_command_group,
        },
+       {
+               .name = "cache",
+               .mode = COMMAND_ANY,
+               .help = "cache command group",
+               .usage = "",
+               .chain = arc_cache_group_handlers,
+       },
        COMMAND_REGISTRATION_DONE
 };
 
index e80bfb4e41561b791a40d01b1971e2f77607ad5b..866c71fc25d547db9d1f888d10d9e05d9d696fd8 100644 (file)
@@ -41,10 +41,18 @@ static int arc_mem_write_block32(struct target *target, uint32_t addr,
        /* Check arguments */
        assert(!(addr & 3));
 
+       /* We need to flush the cache since it might contain dirty
+        * lines, so the cache invalidation may cause data inconsistency. */
+       CHECK_RETVAL(arc_cache_flush(target));
+
+
        /* No need to flush cache, because we don't read values from memory. */
        CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info, addr, count,
                                (uint32_t *)buf));
 
+       /* Invalidate caches. */
+       CHECK_RETVAL(arc_cache_invalidate(target));
+
        return ERROR_OK;
 }
 
@@ -64,6 +72,9 @@ static int arc_mem_write_block16(struct target *target, uint32_t addr,
        /* Check arguments */
        assert(!(addr & 1));
 
+       /* We will read data from memory, so we need to flush the cache. */
+       CHECK_RETVAL(arc_cache_flush(target));
+
        /* non-word writes are less common, than 4-byte writes, so I suppose we can
         * allowe ourselves to write this in a cycle, instead of calling arc_jtag
         * with count > 1. */
@@ -97,6 +108,9 @@ static int arc_mem_write_block16(struct target *target, uint32_t addr,
                        (addr + i * sizeof(uint16_t)) & ~3u, 1, &buffer_he));
        }
 
+       /* Invalidate caches. */
+       CHECK_RETVAL(arc_cache_invalidate(target));
+
        return ERROR_OK;
 }
 
@@ -113,6 +127,9 @@ static int arc_mem_write_block8(struct target *target, uint32_t addr,
        LOG_DEBUG("Write 1-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32,
                        addr, count);
 
+       /* We will read data from memory, so we need to flush the cache. */
+       CHECK_RETVAL(arc_cache_flush(target));
+
        /* non-word writes are less common, than 4-byte writes, so I suppose we can
         * allowe ourselves to write this in a cycle, instead of calling arc_jtag
         * with count > 1. */
@@ -128,6 +145,9 @@ static int arc_mem_write_block8(struct target *target, uint32_t addr,
                CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info, (addr + i) & ~3, 1, &buffer_he));
        }
 
+       /* Invalidate caches. */
+       CHECK_RETVAL(arc_cache_invalidate(target));
+
        return ERROR_OK;
 }
 
@@ -205,6 +225,9 @@ static int arc_mem_read_block(struct target *target, target_addr_t addr,
        assert(!(addr & 3));
        assert(size == 4);
 
+       /* Flush cache before memory access */
+       CHECK_RETVAL(arc_cache_flush(target));
+
        CHECK_RETVAL(arc_jtag_read_memory(&arc->jtag_info, addr, count, buf,
                    arc_mem_is_slow_memory(arc, addr, size, count)));