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>
This commit is contained in:
Evgeniy Didin 2020-05-15 23:04:01 +03:00 committed by Antonio Borneo
parent 2e6904eef5
commit 057aed11a2
4 changed files with 328 additions and 5 deletions

View 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 = {

View File

@ -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 */

View 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
};

View 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)));