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,
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),
/* 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;
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;
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;
}
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;
}
}
+/* 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 = {
/* 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];
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;
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 */
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,
.usage = "",
.chain = arc_jtag_command_group,
},
+ {
+ .name = "cache",
+ .mode = COMMAND_ANY,
+ .help = "cache command group",
+ .usage = "",
+ .chain = arc_cache_group_handlers,
+ },
COMMAND_REGISTRATION_DONE
};
/* 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;
}
/* 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. */
(addr + i * sizeof(uint16_t)) & ~3u, 1, &buffer_he));
}
+ /* Invalidate caches. */
+ CHECK_RETVAL(arc_cache_invalidate(target));
+
return ERROR_OK;
}
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. */
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;
}
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)));