aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEvgeniy Didin <didin@synopsys.com>2020-05-15 23:04:01 +0300
committerAntonio Borneo <borneo.antonio@gmail.com>2020-06-27 15:34:24 +0100
commit057aed11a2f80645322ff76c7dd0c7908582d0a4 (patch)
tree1dbf0f12ea59e53d4c3dfe25c6700448ac6722e5 /src
parent2e6904eef5e81e71453168ed8c6f649e3a5c0f6c (diff)
downloadriscv-openocd-057aed11a2f80645322ff76c7dd0c7908582d0a4.zip
riscv-openocd-057aed11a2f80645322ff76c7dd0c7908582d0a4.tar.gz
riscv-openocd-057aed11a2f80645322ff76c7dd0c7908582d0a4.tar.bz2
target/arc: Introduce L1I,L1D,L2 caches support
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>
Diffstat (limited to 'src')
-rw-r--r--src/target/arc.c214
-rw-r--r--src/target/arc.h37
-rw-r--r--src/target/arc_cmd.c59
-rw-r--r--src/target/arc_mem.c23
4 files changed, 328 insertions, 5 deletions
diff --git a/src/target/arc.c b/src/target/arc.c
index 6cf0ec7..e9709f4 100644
--- a/src/target/arc.c
+++ b/src/target/arc.c
@@ -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 = {
diff --git a/src/target/arc.h b/src/target/arc.h
index defa3fa..6641411 100644
--- a/src/target/arc.h
+++ b/src/target/arc.h
@@ -60,6 +60,24 @@
/* 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 */
diff --git a/src/target/arc_cmd.c b/src/target/arc_cmd.c
index fad8ca9..59e1645 100644
--- a/src/target/arc_cmd.c
+++ b/src/target/arc_cmd.c
@@ -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
};
diff --git a/src/target/arc_mem.c b/src/target/arc_mem.c
index e80bfb4..866c71f 100644
--- a/src/target/arc_mem.c
+++ b/src/target/arc_mem.c
@@ -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)));