mips32: optimized cache code for pracc access
Follows the the same rules of optimization used by all pracc functions. Solves some bugs in previous code and adds support for write through caches. Change-Id: If88c6738ca8c8197f327f22b766120a24f71b567 Signed-off-by: Salvador Arroyo <sarroyofdez@yahoo.es> Reviewed-on: http://openocd.zylin.com/1557 Tested-by: jenkins Reviewed-by: Freddie Chopin <freddie.chopin@gmail.com>__archive__
parent
92ea548aaf
commit
12f4564e88
|
@ -93,11 +93,6 @@ struct mips32_pracc_context {
|
||||||
struct mips_ejtag *ejtag_info;
|
struct mips_ejtag *ejtag_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int mips32_pracc_sync_cache(struct mips_ejtag *ejtag_info,
|
|
||||||
uint32_t start_addr, uint32_t end_addr);
|
|
||||||
static int mips32_pracc_clean_invalidate_cache(struct mips_ejtag *ejtag_info,
|
|
||||||
uint32_t start_addr, uint32_t end_addr);
|
|
||||||
|
|
||||||
static int wait_for_pracc_rw(struct mips_ejtag *ejtag_info, uint32_t *ctrl)
|
static int wait_for_pracc_rw(struct mips_ejtag *ejtag_info, uint32_t *ctrl)
|
||||||
{
|
{
|
||||||
uint32_t ejtag_ctrl;
|
uint32_t ejtag_ctrl;
|
||||||
|
@ -598,136 +593,116 @@ exit:
|
||||||
* to write back any containing D-cache line and invalidate any locations
|
* to write back any containing D-cache line and invalidate any locations
|
||||||
* already in the I-cache.
|
* already in the I-cache.
|
||||||
*
|
*
|
||||||
* You can do that with cache instructions, but those instructions are only available in kernel mode,
|
* If the cache coherency attribute (CCA) is set to zero, it's a write through cache, there is no need
|
||||||
* and a loader writing instructions for the use of its own process need not be privileged software.
|
* to write back.
|
||||||
*
|
*
|
||||||
* In the latest MIPS32/64 CPUs, MIPS provides the synci instruction,
|
* In the latest MIPS32/64 CPUs, MIPS provides the synci instruction,
|
||||||
* which does the whole job for a cache-line-sized chunk of the memory you just loaded:
|
* which does the whole job for a cache-line-sized chunk of the memory you just loaded:
|
||||||
* That is, it arranges a D-cache write-back and an I-cache invalidate.
|
* That is, it arranges a D-cache write-back (if CCA = 3) and an I-cache invalidate.
|
||||||
*
|
*
|
||||||
* To employ synci at user level, you need to know the size of a cache line,
|
* The line size is obtained with the rdhwr SYNCI_Step in release 2 or from cp0 config 1 register in release 1.
|
||||||
* and that can be obtained with a rdhwr SYNCI_Step
|
|
||||||
* from one of the standard “hardware registers”.
|
|
||||||
*/
|
*/
|
||||||
static int mips32_pracc_sync_cache(struct mips_ejtag *ejtag_info,
|
static int mips32_pracc_synchronize_cache(struct mips_ejtag *ejtag_info,
|
||||||
uint32_t start_addr, uint32_t end_addr)
|
uint32_t start_addr, uint32_t end_addr, int cached, int rel)
|
||||||
{
|
{
|
||||||
static const uint32_t code[] = {
|
struct pracc_queue_info ctx = {.max_code = 256 * 2 + 6};
|
||||||
/* start: */
|
pracc_queue_init(&ctx);
|
||||||
MIPS32_MTC0(15, 31, 0), /* move $15 to COP0 DeSave */
|
if (ctx.retval != ERROR_OK)
|
||||||
MIPS32_LUI(15, UPPER16(MIPS32_PRACC_STACK)), /* $15 = MIPS32_PRACC_STACK */
|
goto exit;
|
||||||
MIPS32_ORI(15, 15, LOWER16(MIPS32_PRACC_STACK)),
|
/** Find cache line size in bytes */
|
||||||
MIPS32_SW(8, 0, 15), /* sw $8,($15) */
|
uint32_t clsiz;
|
||||||
MIPS32_SW(9, 0, 15), /* sw $9,($15) */
|
if (rel) { /* Release 2 (rel = 1) */
|
||||||
MIPS32_SW(10, 0, 15), /* sw $10,($15) */
|
pracc_add(&ctx, 0, MIPS32_MTC0(15, 31, 0)); /* move $15 to COP0 DeSave */
|
||||||
MIPS32_SW(11, 0, 15), /* sw $11,($15) */
|
pracc_add(&ctx, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
|
||||||
|
|
||||||
MIPS32_LUI(8, UPPER16(MIPS32_PRACC_PARAM_IN)), /* $8 = MIPS32_PRACC_PARAM_IN */
|
pracc_add(&ctx, 0, MIPS32_RDHWR(8, MIPS32_SYNCI_STEP)); /* load synci_step value to $8 */
|
||||||
MIPS32_ORI(8, 8, LOWER16(MIPS32_PRACC_PARAM_IN)),
|
|
||||||
MIPS32_LW(9, 0, 8), /* Load write start_addr to $9 */
|
|
||||||
MIPS32_LW(10, 4, 8), /* Load write end_addr to $10 */
|
|
||||||
|
|
||||||
MIPS32_RDHWR(11, MIPS32_SYNCI_STEP), /* $11 = MIPS32_SYNCI_STEP */
|
pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT,
|
||||||
MIPS32_BEQ(11, 0, 6), /* beq $11, $0, end */
|
MIPS32_SW(8, PRACC_OUT_OFFSET, 15)); /* store $8 to pracc_out */
|
||||||
MIPS32_NOP,
|
|
||||||
/* synci_loop : */
|
|
||||||
MIPS32_SYNCI(0, 9), /* synci 0($9) */
|
|
||||||
MIPS32_SLTU(8, 10, 9), /* sltu $8, $10, $9 # $8 = $10 < $9 ? 1 : 0 */
|
|
||||||
MIPS32_BNE(8, 0, NEG16(3)), /* bne $8, $0, synci_loop */
|
|
||||||
MIPS32_ADDU(9, 9, 11), /* $9 += MIPS32_SYNCI_STEP */
|
|
||||||
MIPS32_SYNC,
|
|
||||||
/* end: */
|
|
||||||
MIPS32_LW(11, 0, 15), /* lw $11,($15) */
|
|
||||||
MIPS32_LW(10, 0, 15), /* lw $10,($15) */
|
|
||||||
MIPS32_LW(9, 0, 15), /* lw $9,($15) */
|
|
||||||
MIPS32_LW(8, 0, 15), /* lw $8,($15) */
|
|
||||||
MIPS32_B(NEG16(24)), /* b start */
|
|
||||||
MIPS32_MFC0(15, 31, 0), /* move COP0 DeSave to $15 */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* TODO remove array */
|
pracc_add(&ctx, 0, MIPS32_LUI(8, UPPER16(ejtag_info->reg8))); /* restore upper 16 bits of $8 */
|
||||||
uint32_t *param_in = malloc(2 * sizeof(uint32_t));
|
pracc_add(&ctx, 0, MIPS32_ORI(8, 8, LOWER16(ejtag_info->reg8))); /* restore lower 16 bits of $8 */
|
||||||
int retval;
|
pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
|
||||||
param_in[0] = start_addr;
|
pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* move COP0 DeSave to $15 */
|
||||||
param_in[1] = end_addr;
|
|
||||||
|
|
||||||
retval = mips32_pracc_exec(ejtag_info, ARRAY_SIZE(code), code, 2, param_in, 0, NULL, 1);
|
ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, &clsiz);
|
||||||
|
if (ctx.retval != ERROR_OK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
free(param_in);
|
} else { /* Release 1 (rel = 0) */
|
||||||
|
uint32_t conf;
|
||||||
|
ctx.retval = mips32_cp0_read(ejtag_info, &conf, 16, 1);
|
||||||
|
if (ctx.retval != ERROR_OK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
return retval;
|
uint32_t dl = (conf & MIPS32_CONFIG1_DL_MASK) >> MIPS32_CONFIG1_DL_SHIFT;
|
||||||
|
|
||||||
|
/* dl encoding : dl=1 => 4 bytes, dl=2 => 8 bytes, etc... max dl=6 => 128 bytes cache line size */
|
||||||
|
clsiz = 0x2 << dl;
|
||||||
|
if (dl == 0)
|
||||||
|
clsiz = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (clsiz == 0)
|
||||||
* \b mips32_pracc_clean_invalidate_cache
|
goto exit; /* Nothing to do */
|
||||||
*
|
|
||||||
* Writeback D$ and Invalidate I$
|
|
||||||
* so that the instructions written can be visible to CPU
|
|
||||||
*/
|
|
||||||
static int mips32_pracc_clean_invalidate_cache(struct mips_ejtag *ejtag_info,
|
|
||||||
uint32_t start_addr, uint32_t end_addr)
|
|
||||||
{
|
|
||||||
static const uint32_t code[] = {
|
|
||||||
/* start: */
|
|
||||||
MIPS32_MTC0(15, 31, 0), /* move $15 to COP0 DeSave */
|
|
||||||
MIPS32_LUI(15, UPPER16(MIPS32_PRACC_STACK)), /* $15 = MIPS32_PRACC_STACK */
|
|
||||||
MIPS32_ORI(15, 15, LOWER16(MIPS32_PRACC_STACK)),
|
|
||||||
MIPS32_SW(8, 0, 15), /* sw $8,($15) */
|
|
||||||
MIPS32_SW(9, 0, 15), /* sw $9,($15) */
|
|
||||||
MIPS32_SW(10, 0, 15), /* sw $10,($15) */
|
|
||||||
MIPS32_SW(11, 0, 15), /* sw $11,($15) */
|
|
||||||
|
|
||||||
MIPS32_LUI(8, UPPER16(MIPS32_PRACC_PARAM_IN)), /* $8 = MIPS32_PRACC_PARAM_IN */
|
/* make sure clsiz is power of 2 */
|
||||||
MIPS32_ORI(8, 8, LOWER16(MIPS32_PRACC_PARAM_IN)),
|
if (clsiz & (clsiz - 1)) {
|
||||||
MIPS32_LW(9, 0, 8), /* Load write start_addr to $9 */
|
LOG_DEBUG("clsiz must be power of 2");
|
||||||
MIPS32_LW(10, 4, 8), /* Load write end_addr to $10 */
|
ctx.retval = ERROR_FAIL;
|
||||||
MIPS32_LW(11, 8, 8), /* Load write clsiz to $11 */
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
/* cache_loop: */
|
/* make sure start_addr and end_addr have the same offset inside de cache line */
|
||||||
MIPS32_SLTU(8, 10, 9), /* sltu $8, $10, $9 : $8 <- $10 < $9 ? */
|
start_addr |= clsiz - 1;
|
||||||
MIPS32_BGTZ(8, 6), /* bgtz $8, end */
|
end_addr |= clsiz - 1;
|
||||||
MIPS32_NOP,
|
|
||||||
|
|
||||||
MIPS32_CACHE(MIPS32_CACHE_D_HIT_WRITEBACK, 0, 9), /* cache Hit_Writeback_D, 0($9) */
|
ctx.code_count = 0;
|
||||||
MIPS32_CACHE(MIPS32_CACHE_I_HIT_INVALIDATE, 0, 9), /* cache Hit_Invalidate_I, 0($9) */
|
int count = 0;
|
||||||
|
uint32_t last_upper_base_addr = UPPER16((start_addr + 0x8000));
|
||||||
|
|
||||||
MIPS32_ADDU(9, 9, 11), /* $9 += $11 */
|
pracc_add(&ctx, 0, MIPS32_MTC0(15, 31, 0)); /* move $15 to COP0 DeSave */
|
||||||
|
pracc_add(&ctx, 0, MIPS32_LUI(15, last_upper_base_addr)); /* load upper memory base address to $15 */
|
||||||
|
|
||||||
MIPS32_B(NEG16(7)), /* b cache_loop */
|
while (start_addr <= end_addr) { /* main loop */
|
||||||
MIPS32_NOP,
|
uint32_t upper_base_addr = UPPER16((start_addr + 0x8000));
|
||||||
/* end: */
|
if (last_upper_base_addr != upper_base_addr) { /* if needed, change upper address in $15 */
|
||||||
MIPS32_LW(11, 0, 15), /* lw $11,($15) */
|
pracc_add(&ctx, 0, MIPS32_LUI(15, upper_base_addr));
|
||||||
MIPS32_LW(10, 0, 15), /* lw $10,($15) */
|
last_upper_base_addr = upper_base_addr;
|
||||||
MIPS32_LW(9, 0, 15), /* lw $9,($15) */
|
}
|
||||||
MIPS32_LW(8, 0, 15), /* lw $8,($15) */
|
if (rel)
|
||||||
MIPS32_B(NEG16(25)), /* b start */
|
pracc_add(&ctx, 0, MIPS32_SYNCI(LOWER16(start_addr), 15)); /* synci instruction, offset($15) */
|
||||||
MIPS32_MFC0(15, 31, 0), /* move COP0 DeSave to $15 */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
else {
|
||||||
* Find cache line size in bytes
|
if (cached == 3)
|
||||||
*/
|
pracc_add(&ctx, 0, MIPS32_CACHE(MIPS32_CACHE_D_HIT_WRITEBACK,
|
||||||
uint32_t conf;
|
LOWER16(start_addr), 15)); /* cache Hit_Writeback_D, offset($15) */
|
||||||
uint32_t dl, clsiz;
|
|
||||||
|
|
||||||
mips32_cp0_read(ejtag_info, &conf, 16, 1);
|
pracc_add(&ctx, 0, MIPS32_CACHE(MIPS32_CACHE_I_HIT_INVALIDATE,
|
||||||
dl = (conf & MIPS32_CONFIG1_DL_MASK) >> MIPS32_CONFIG1_DL_SHIFT;
|
LOWER16(start_addr), 15)); /* cache Hit_Invalidate_I, offset($15) */
|
||||||
|
}
|
||||||
|
start_addr += clsiz;
|
||||||
|
count++;
|
||||||
|
if (count == 256 && start_addr <= end_addr) { /* more ?, then execute code list */
|
||||||
|
pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
|
||||||
|
pracc_add(&ctx, 0, MIPS32_NOP); /* nop in delay slot */
|
||||||
|
|
||||||
/* dl encoding : dl=1 => 4 bytes, dl=2 => 8 bytes, etc... */
|
ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
|
||||||
clsiz = 0x2 << dl;
|
if (ctx.retval != ERROR_OK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
/* TODO remove array */
|
ctx.code_count = 0;
|
||||||
uint32_t *param_in = malloc(3 * sizeof(uint32_t));
|
count = 0;
|
||||||
int retval;
|
}
|
||||||
param_in[0] = start_addr;
|
}
|
||||||
param_in[1] = end_addr;
|
pracc_add(&ctx, 0, MIPS32_SYNC);
|
||||||
param_in[2] = clsiz;
|
pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
|
||||||
|
pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave*/
|
||||||
|
|
||||||
retval = mips32_pracc_exec(ejtag_info, ARRAY_SIZE(code), code, 3, param_in, 0, NULL, 1);
|
ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
|
||||||
|
exit:
|
||||||
free(param_in);
|
pracc_queue_free(&ctx);
|
||||||
|
return ctx.retval;
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mips32_pracc_write_mem_generic(struct mips_ejtag *ejtag_info,
|
static int mips32_pracc_write_mem_generic(struct mips_ejtag *ejtag_info,
|
||||||
|
@ -806,9 +781,9 @@ int mips32_pracc_write_mem(struct mips_ejtag *ejtag_info, uint32_t addr, int siz
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If we are in the cachable regoion and cache is activated,
|
* If we are in the cacheable region and cache is activated,
|
||||||
* we must clean D$ + invalidate I$ after we did the write,
|
* we must clean D$ (if Cache Coherency Attribute is set to 3) + invalidate I$ after we did the write,
|
||||||
* so that changes do not continue to live only in D$, but to be
|
* so that changes do not continue to live only in D$ (if CCA = 3), but to be
|
||||||
* replicated in I$ also (maybe we wrote the istructions)
|
* replicated in I$ also (maybe we wrote the istructions)
|
||||||
*/
|
*/
|
||||||
uint32_t conf = 0;
|
uint32_t conf = 0;
|
||||||
|
@ -836,32 +811,19 @@ int mips32_pracc_write_mem(struct mips_ejtag *ejtag_info, uint32_t addr, int siz
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check cachablitiy bits coherency algorithm -
|
* Check cachablitiy bits coherency algorithm
|
||||||
* is the region cacheable or uncached.
|
* is the region cacheable or uncached.
|
||||||
* If cacheable we have to synchronize the cache
|
* If cacheable we have to synchronize the cache
|
||||||
*/
|
*/
|
||||||
if (cached == 0x3) {
|
if (cached == 3 || cached == 0) { /* Write back cache or write through cache */
|
||||||
uint32_t start_addr, end_addr;
|
uint32_t start_addr = addr;
|
||||||
uint32_t rel;
|
uint32_t end_addr = addr + count * size;
|
||||||
|
uint32_t rel = (conf & MIPS32_CONFIG0_AR_MASK) >> MIPS32_CONFIG0_AR_SHIFT;
|
||||||
start_addr = addr;
|
if (rel > 1) {
|
||||||
end_addr = addr + count * size;
|
LOG_DEBUG("Unknown release in cache code");
|
||||||
|
return ERROR_FAIL;
|
||||||
/** select cache synchronisation mechanism based on Architecture Release */
|
|
||||||
rel = (conf & MIPS32_CONFIG0_AR_MASK) >> MIPS32_CONFIG0_AR_SHIFT;
|
|
||||||
switch (rel) {
|
|
||||||
case MIPS32_ARCH_REL1:
|
|
||||||
/* MIPS32/64 Release 1 - we must use cache instruction */
|
|
||||||
mips32_pracc_clean_invalidate_cache(ejtag_info, start_addr, end_addr);
|
|
||||||
break;
|
|
||||||
case MIPS32_ARCH_REL2:
|
|
||||||
/* MIPS32/64 Release 2 - we can use synci instruction */
|
|
||||||
mips32_pracc_sync_cache(ejtag_info, start_addr, end_addr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* what ? */
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
retval = mips32_pracc_synchronize_cache(ejtag_info, start_addr, end_addr, cached, rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
|
Loading…
Reference in New Issue