This is the mail archive of the
ecos-bugs@sources.redhat.com
mailing list for the eCos project.
[Bug 19695] New: Different semantics of cache lock feature of TX39
- From: bugzilla-daemon at ecoscentric dot com
- To: ecos-bugs at sources dot redhat dot com
- Date: Thu, 24 Apr 2003 15:55:56 +0100 (BST)
- Subject: [Bug 19695] New: Different semantics of cache lock feature of TX39
http://bugs.ecos.sourceware.org/show_bug.cgi?id=19695
Summary: Different semantics of cache lock feature of TX39
Product: eCos
Version: 0.2
Platform: jmr3904 (Toshiba JMR-TX3904 board)
OS/Version: MIPS
Status: ASSIGNED
Severity: normal
Priority: normal
Component: Other
AssignedTo: nickg at ecoscentric dot com
ReportedBy: support at ecoscentric dot com
CC: akira dot yokosawa at toshiba dot co dot jp
This was found while I was checking output of kcache2.c
I wondered why numbers of dcache-locked and -unlocked differ only a
little. Looking at kcache2.c, I found that TX39's dcache lock feature
can not be tested this way. I also wondered why this test is placed
in kernel/tests.
TX39's dcache lock feature has a different semantics than is assumed
in kcache2.
Attached please find a program that can exhibit the effect of dcache
lock of TX39.
There are a few points I want to clarify: (See Section 7.2.1 of Users
Manual of TLCS-R3900 Family --- I admit that the description here is
not easy to understand)
1) Instruction cache of TX3904 does not have lock feature.
2) Cache lock can only be enabled/disabled in the Cache register.
After one of two ways of one cache set is locked, no more locks
can be set at the cache set. (sorry I use "set" in two meanings here)
In other words, you can't explicitly specify an address range you want
to lock. You have to access the region after enabling lock.
3) Once a lock is established, write operation to that line only
modifies the cache without doing memory access.
4) When you want to clear a lock, you have to use the "cache"
instruction of IndexLockBitClear. But clearing the lock doesn't
synchronize the memory. You have to
o Read data in a line from cache memory
o Clear the Lock bit
o Store the data to memory
Care must to be taken since a Lock bit corresponds to a 16 byte cache
line. You need to read all the 16 byte data before clearing the lock
bit. Otherwise, the data in cache line might be lost.
To make matters worse, there is no way to know the address of a locked
cache line.
It seems that the expected way of using this feature is to lock
critical data once at boot time. I can't think of any way to swap
locks between multiple purposes.
------
/* dcache lock test program */
#include <cyg/kernel/kapi.h>
#include <cyg/infra/diag.h>
#include <cyg/infra/testcase.h>
#include <pkgconf/hal.h>
#include <cyg/hal/hal_cache.h>
#define DCACHE_SIZE 1024/4
#define LOOPS 10000
int data1[DCACHE_SIZE];
int data2[2*DCACHE_SIZE];
int loops = LOOPS;
int k = 0;
cyg_uint32 tick_acc;
void dloop1(){ // read --- write --- invalidate
int i,j;
cyg_uint32 tick0, tick1, delta;
for (j=0; j<loops; j++){
for (i=0; i<DCACHE_SIZE; i++){
k += data1[i];
}
HAL_CLOCK_READ(&tick0);
for (i=0; i<DCACHE_SIZE; i++){
data1[i] = i;
}
HAL_CLOCK_READ(&tick1);
for (i=0; i<2*DCACHE_SIZE; i++){
k += data2[i]; // intend to invalidate data1[] in dcache
}
if (tick1 < tick0) {
delta = (tick1 + CYGNUM_KERNEL_COUNTERS_RTC_PERIOD) - tick0;
} else {
delta = tick1 - tick0;
}
tick_acc += delta;
}
}
void dloop2(){ // write --- read --- invalidate
int i,j;
cyg_uint32 tick0, tick1, delta;
for (j=0; j<loops; j++){
HAL_CLOCK_READ(&tick0);
for (i=0; i<DCACHE_SIZE; i++){
data1[i] = i;
}
HAL_CLOCK_READ(&tick1);
for (i=0; i<DCACHE_SIZE; i++){
k += data1[i];
}
for (i=0; i<2*DCACHE_SIZE; i++){
k += data2[i]; // intend to invalidate data1[] in dcache
}
if (tick1 < tick0) {
delta = (tick1 + CYGNUM_KERNEL_COUNTERS_RTC_PERIOD) - tick0;
} else {
delta = tick1 - tick0;
}
tick_acc += delta;
}
}
void lock_data1(){
int i;
HAL_DCACHE_LOCK(0,0);
for (i=0; i<DCACHE_SIZE; i++) {
k += data1[i];
}
HAL_DCACHE_UNLOCK(0,0);
}
int main(void)
{
volatile long * p1;
int i;
cyg_tick_count_t count0, count1;
#if 1
p1 = (void *) 0xffff9100;// RCCR1
*p1 = 0x00000500; // force 5 wait to SRAM
#endif
CYG_TEST_INIT();
CYG_TEST_INFO("Now initialize data1 and data2 area");
for (i=0; i < DCACHE_SIZE; i++) {
data1[i] = i;
}
for (i=0; i < DCACHE_SIZE; i++) {
data2[i] = data1[i];
}
HAL_DCACHE_DISABLE();
diag_printf("Now read/write the area with d-cache OFF (%d loop)\n", loops);
tick_acc = 0;
count0 = cyg_current_time();
dloop1();
count1 = cyg_current_time();
diag_printf("time: %d ", (int) (count1 - count0));
diag_printf("ticks for write: %d\n", (int) tick_acc);
HAL_DCACHE_ENABLE();
diag_printf("Now read/write the area with d-cache ON (%d loop)\n", loops);
tick_acc = 0;
count0 = cyg_current_time();
dloop1();
count1 = cyg_current_time();
diag_printf("time: %d ", (int) (count1 - count0));
diag_printf("ticks for write: %d\n", (int) tick_acc);
diag_printf("Now write/read the area with d-cache ON (%d loop)\n", loops);
tick_acc = 0;
count0 = cyg_current_time();
dloop2();
count1 = cyg_current_time();
diag_printf("time: %d ", (int) (count1 - count0));
diag_printf("ticks for write: %d\n", (int) tick_acc);
diag_printf("Now read/write the area with d-cache lock ON (%d loop)\n", loops);
lock_data1();
tick_acc = 0;
count0 = cyg_current_time();
dloop1();
count1 = cyg_current_time();
diag_printf("time: %d ", (int) (count1 - count0));
diag_printf("ticks for write: %d\n", (int) tick_acc);
diag_printf("Now write/read the area with d-cache lock ON (%d loop)\n", loops);
tick_acc = 0;
count0 = cyg_current_time();
dloop2();
count1 = cyg_current_time();
diag_printf("time: %d ", (int) (count1 - count0));
diag_printf("ticks for write: %d\n", (int) tick_acc);
CYG_TEST_EXIT("done");
return 0;
}
--------
Output example (JMR 50MHz, Tick-rate is increased x8 from default)
--------
INFO:<Now initialize data1 and data2 area>
Now read/write the area with d-cache OFF (10000 loop)
time: 632 ticks for write: 5785803
Now read/write the area with d-cache ON (10000 loop)
time: 458 ticks for write: 5774762
Now write/read the area with d-cache ON (10000 loop)
time: 459 ticks for write: 5776364
Now read/write the area with d-cache lock ON (10000 loop)
time: 305 ticks for write: 4112355
Now write/read the area with d-cache lock ON (10000 loop)
time: 306 ticks for write: 4104723
EXIT:<done>
-------------
End of PR
Originator:
Akira Yokosawa
Organization:
Toshiba
Audit-Trail:
Responsible-Changed-From-To: alexs->nickg
Responsible-Changed-By: alexs
Responsible-Changed-When: Fri Mar 26 03:18:29 PST 1999
Responsible-Changed-Why:
Nick to investigate and respond
From: Akira Yokosawa <akira dot yokosawa at toshiba dot co dot jp>
To: bugs at cygnus dot com
Cc: alexs at cygnus dot com
Subject: Re: ecos/19695: Different semantics of cache lock feature of TX39
Date: Mon, 29 Mar 1999 12:16:42 +0900
The program I attached to the PR was not sorted out well, and I made
a mistake in DCACHE way size. Please try this one instead.
In the meantime, I found a bug in the definition of
HAL_DCACH_INVALIDATE() in hal_cache.h. It ended up with an infinete loop.
I also added a macro called HAL_DCACHE_LOCK_CLEAR to see the effect of
IndexLockBitClear.
As for the semantics difference, I'm beginning to think that it would
be better to use other names of macros. Something like
HAL_DCACHE_LOCK_ENABLE() and HAL_DCACHE_LOCK_DISABLE() would be more
suitable than HAL_DCACHE_LOCK(_base_,_size_) and
HAL_DCACHE_UNLOCK(_base_,_size_) for TX3904. This idea is not yet
reflected in the attached program.
Anyway, an expected output of the program is as follows:
----
read/write the area with d-cache OFF (10000 loops)
time: 237, ticks for write: 1924010
write/read the area with d-cache OFF (10000 loops)
time: 237, ticks for write: 1921805
read/write the area with d-cache ON (10000 loops)
time: 174, ticks for write: 1918665
write/read the area with d-cache ON (10000 loops)
time: 174, ticks for write: 1916935
read/write the area with d-cache lock ON (10000 loops)
time: 117, ticks for write: 1316271
write/read the area with d-cache lock ON (10000 loops)
time: 116, ticks for write: 1310799
write/read the area after clearing lock (10000 loops)
time: 174, ticks for write: 1916872
write/read the area with d-cach lock again (10000 loops)
time: 116, ticks for write: 1311870
write/read the area after invalidate without clearing lock (10000 loops)
time: 143, ticks for write: 1918593
write/read the area after clearing lock again (10000 loops)
time: 174, ticks for write: 1916935
EXIT:<done>
-----
You can see that invalidating without clearing lock causes the dcache
to be in a mysterious state (undocumented). Read access to the locked
entry still hits, but writing to that entry causes a real memory
access.
Here is the revised program:
-----
/* dcache lock test program */
#include <cyg/kernel/kapi.h>
#include <cyg/infra/diag.h>
#include <cyg/infra/testcase.h>
#include <pkgconf/hal.h>
#include <cyg/hal/hal_cache.h>
#define DCACHE_SIZE 1024/4
#define LOOPS 10000
#define DATA1_SIZE DCACHE_SIZE/2
#define DATA2_SIZE DCACHE_SIZE
int data1[DATA1_SIZE];
int data2[DATA2_SIZE];
int k = 0;
cyg_uint32 tick_acc;
cyg_tick_count_t count0, count1;
void dloop1(char * message){ // read --- write --- invalidate
int i,j;
cyg_uint32 tick0, tick1, delta;
diag_printf("%s", message);
diag_printf(" (%d loops)\n", LOOPS);
tick_acc = 0;
count0 = cyg_current_time();
for (j=0; j<LOOPS; j++){
for (i=0; i<DATA1_SIZE; i++){
k += data1[i];
}
HAL_CLOCK_READ(&tick0);
for (i=0; i<DATA1_SIZE; i++){
data1[i] = i;
}
HAL_CLOCK_READ(&tick1);
for (i=0; i<DATA2_SIZE; i++){
k += data2[i]; // intend to invalidate data1[] in dcache
}
if (tick1 < tick0) {
delta = (tick1 + CYGNUM_KERNEL_COUNTERS_RTC_PERIOD) - tick0;
} else {
delta = tick1 - tick0;
}
tick_acc += delta;
}
count1 = cyg_current_time();
diag_printf("time: %d, ",(int) (count1 - count0));
diag_printf("ticks for write: %d\n", (int) tick_acc);
}
void dloop2(char * message){ // write --- read --- invalidate
int i,j;
cyg_uint32 tick0, tick1, delta;
diag_printf("%s", message);
diag_printf(" (%d loops)\n", LOOPS);
tick_acc = 0;
count0 = cyg_current_time();
for (j=0; j<LOOPS; j++){
HAL_CLOCK_READ(&tick0);
for (i=0; i<DATA1_SIZE; i++){
data1[i] = i;
}
HAL_CLOCK_READ(&tick1);
for (i=0; i<DATA1_SIZE; i++){
k += data1[i];
}
for (i=0; i<DATA2_SIZE; i++){
k += data2[i]; // intend to invalidate data1[] in dcache
}
if (tick1 < tick0) {
delta = (tick1 + CYGNUM_KERNEL_COUNTERS_RTC_PERIOD) - tick0;
} else {
delta = tick1 - tick0;
}
tick_acc += delta;
}
count1 = cyg_current_time();
diag_printf("time: %d, ",(int) (count1 - count0));
diag_printf("ticks for write: %d\n", (int) tick_acc);
}
void lock_data1(){
int i;
HAL_DCACHE_LOCK(0,0);
for (i=0; i<DATA1_SIZE; i++) {
k += data1[i];
}
HAL_DCACHE_UNLOCK(0,0);
}
int main(void)
{
int i;
#if 1
{
volatile long * p1;
p1 = (void *) 0xffff9100;
*p1 = 0x00000200; // force SRAM 2 wait cycle
}
#endif
CYG_TEST_INIT();
for (i=0; i < DATA1_SIZE; i++) {
data1[i] = i;
}
for (i=0; i < DATA2_SIZE; i++) {
data2[i] = i;
}
HAL_DCACHE_DISABLE();
dloop1("read/write the area with d-cache OFF");
dloop2("write/read the area with d-cache OFF");
HAL_DCACHE_ENABLE();
dloop1("read/write the area with d-cache ON");
dloop2("write/read the area with d-cache ON");
lock_data1();
dloop1("read/write the area with d-cache lock ON");
dloop2("write/read the area with d-cache lock ON");
HAL_DCACHE_LOCK_CLEAR( data1, 4*DATA1_SIZE);
dloop2("write/read the area after clearing lock");
lock_data1();
dloop2("write/read the area with d-cach lock again");
HAL_DCACHE_INVALIDATE( data1, 4*DATA1_SIZE);
HAL_DCACHE_INVALIDATE( data2, 8*DATA2_SIZE);
dloop2("write/read the area after invalidate without clearing lock");
HAL_DCACHE_LOCK_CLEAR( data1, 4*DATA1_SIZE);
dloop2("write/read the area after clearing lock again");
CYG_TEST_EXIT("done");
return 0;
}
------
Here is the modifications to hal_cache.h:
------
// Invalidate cache lines in the given range without writing to memory.
#define HAL_DCACHE_INVALIDATE( _base_ , _asize_ ) \
{ \
register CYG_ADDRESS _addr_ = (CYG_ADDRESS)(_base_); \
register CYG_ADDRESS _end_ = (CYG_ADDRESS)(_base_) + (_asize_); \
HAL_DCACHE_DISABLE(); \
for( ; _addr_ <= _end_; _addr_ += HAL_DCACHE_LINE_SIZE ) \
{ \
asm volatile ("cache 17,0(%0)" : : "r"(_addr_) ); \
} \
HAL_DCACHE_ENABLE(); \
}
// Clear locks of the indexes corresponding to the given range.
#define HAL_DCACHE_LOCK_CLEAR( _base_ , _asize_ ) \
{ \
register CYG_ADDRESS _addr_ = (CYG_ADDRESS)(_base_); \
register CYG_ADDRESS _end_ = (CYG_ADDRESS)(_base_) + (_asize_); \
HAL_DCACHE_DISABLE(); \
for( ; _addr_ <= _end_; _addr_ += HAL_DCACHE_LINE_SIZE ) \
{ \
asm volatile ("cache 9,0(%0)" : : "r"(_addr_) ); \
} \
HAL_DCACHE_ENABLE(); \
}
-----
End of message
State-Changed-From-To: open-analyzed
State-Changed-By: nickg
State-Changed-When: Mon Mar 29 10:17:15 BST 1999
State-Changed-Why:
I need to think about all this when I have the time and things are
not quite so busy (fat chance!).
Meanwhile moved PR to analyzed state to keep PRMS happy.
From: Akira Yokosawa <akira dot yokosawa at toshiba dot co dot jp>
To: nickg at cygnus dot co dot uk
Cc: bugs at cygnus dot com
Subject: Re: ecos/19695
Date: Mon, 29 Mar 1999 19:15:20 +0900
Hi Nick,
It seems you are quite busy right now, and I'm sending what I prepared
as a separate PR as a follow up to PR 19695 so that you won't be
bothered by PRMS :-). Please give considerations to them when you
have the time.
I have found several bugs in hal_cache.h other than I had reported in
original PR 19695.
1) In HAL_DCACHE_INVALIDATE_ALL(), the ROM address of 0xbfc00000 was
used. But this address is in kseg1 region which is un-cacheable and
it had no effects to the dcache.
Use ROM address of 0x9fc00000 instead, which is mapped to the same
physical address as 0xbfc00000.
2) HAL_DCACHE_SYNC() was defined as HAL_DCACHE_INVALIDATE_ALL(). But
when the locking feature is used, HAL_DCACHE_INVALIDATE_ALL() does not
affect locked lines. Once the locking feature is used, I don't think
there is a general way to synchronize. Or, If you make a software
layer that wraps the dcache lock feature, it should be possible. Then you
can implement HAL_DCACHE_LOCK and HAL_DCACHE_UNLOCK that have the same
semantics as is documented now. This problem is not fixed in the
attached diff.
3) Disabling dcache before using "cache" instruction that manipulates
dcache is not necessary. Disabling is needed only when invalidating
icache.
4) When invalidating icache, cares should be taken to ensure that the
icache is actually disabled. In the instraction stream below, icache
might not be disabled yet at the "cache" instruction.
MTC0 Rn, Config (clear ICE bit)
CACHE IndexInvalidate,offset(base)
This is because the cache state change takes effect at the end of
current cache refill cycles. If this "MTC0" is at 16 byte align
boundary and is fetched from memory, icache is disabled after the
current refill cycle ends. The refill cycle contains the "CACHE"
instruction. When it gets to execution stage, the cycle might still be
in progress. And something weired can happen if the invalidation
involves the cache line being filled.
To avoid such a rare occasions, you need to program this way.
MTC0 Rn, Config (clear ICE bit)
J 0f
nop
...
.balign 16
0: CACHE IndexInvalidate,offset(base)
In PR 19222 (report of a bug in tx39-cache.S of CygMon),
I didn't mention the alignment of CACHE instruction. But the
alignment seems necessary. I'll submit another PR of CygMon later.
5) HAL_ICACHE_INVALIDATE(_base_,_asize_) looped forever. This was the
same bug as I reported in the previous follow up to PR 19695.
6) Locking of icache is not supported in TX3904.
HAL_ICACHE_(LOCK|UNLOCK) shouled not be defined for TX3904.
Attached is a context diff of hal_cache.h:
-----
*** hal_cache.h.orig Tue Mar 16 03:39:40 1999
--- hal_cache.h Mon Mar 29 18:43:44 1999
***************
*** 229,235 ****
// most likely to be code, and will not get out of sync even if it is not.
#define HAL_DCACHE_INVALIDATE_ALL() \
{ \
! CYG_BYTE volatile *addr = (CYG_BYTE *)(0xbfc00000); \
CYG_BYTE volatile tmp = 0; \
int i; \
for( i = 0; i < (HAL_DCACHE_SIZE*2); i += HAL_DCACHE_LINE_SIZE ) \
--- 229,235 ----
// most likely to be code, and will not get out of sync even if it is not.
#define HAL_DCACHE_INVALIDATE_ALL() \
{ \
! CYG_BYTE volatile *addr = (CYG_BYTE *)(0x9fc00000); \
CYG_BYTE volatile tmp = 0; \
int i; \
for( i = 0; i < (HAL_DCACHE_SIZE*2); i += HAL_DCACHE_LINE_SIZE ) \
***************
*** 294,308 ****
#define HAL_DCACHE_INVALIDATE( _base_ , _asize_ ) \
{ \
register CYG_ADDRESS _addr_ = (CYG_ADDRESS)(_base_); \
! register CYG_WORD _size_ = (_asize_); \
! HAL_DCACHE_DISABLE(); \
! for( ; _addr_ <= _addr_+_size_; _addr_ += HAL_DCACHE_LINE_SIZE ) \
{ \
asm volatile ("cache 17,0(%0)" : : "r"(_addr_) ); \
} \
- HAL_DCACHE_ENABLE(); \
}
// Write dirty cache lines to memory for the given address range.
//#define HAL_DCACHE_STORE( _base_ , _size_ )
--- 294,318 ----
#define HAL_DCACHE_INVALIDATE( _base_ , _asize_ ) \
{ \
register CYG_ADDRESS _addr_ = (CYG_ADDRESS)(_base_); \
! register CYG_ADDRESS _end_ = (CYG_ADDRESS)(_base_) + (_asize_); \
! for( ; _addr_ <= _end_; _addr_ += HAL_DCACHE_LINE_SIZE ) \
{ \
asm volatile ("cache 17,0(%0)" : : "r"(_addr_) ); \
} \
}
+ // Clear locks in the index corresponds to the given range.
+ #define HAL_DCACHE_LOCK_CLEAR( _base_ , _asize_ ) \
+ { \
+ register CYG_ADDRESS _addr_ = (CYG_ADDRESS)(_base_); \
+ register CYG_ADDRESS _end_ = (CYG_ADDRESS)(_base_) + (_asize_); \
+ for( ; _addr_ < _end_; _addr_ += HAL_DCACHE_LINE_SIZE ) \
+ { \
+ asm volatile ("cache 9,0(%0)" : : "r"(_addr_) ); \
+ } \
+ }
+
+
// Write dirty cache lines to memory for the given address range.
//#define HAL_DCACHE_STORE( _base_ , _size_ )
***************
*** 340,345 ****
--- 350,357 ----
"la $3,0xFFFFFFDF;" \
"and $2,$2,$3;" \
"mtc0 $2,$3;" \
+ "j 0f;" \
+ "0:" \
: \
: \
: "$2", "$3" \
***************
*** 354,360 ****
HAL_ICACHE_DISABLE(); \
for( _addr_ = 0; _addr_ < HAL_ICACHE_SIZE; _addr_ += HAL_ICACHE_LINE_SIZE ) \
{ \
! asm volatile ("cache 0,0(%0)" : : "r"(_addr_) ); \
} \
HAL_ICACHE_ENABLE(); \
}
--- 366,372 ----
HAL_ICACHE_DISABLE(); \
for( _addr_ = 0; _addr_ < HAL_ICACHE_SIZE; _addr_ += HAL_ICACHE_LINE_SIZE ) \
{ \
! asm volatile (".balign 16,0;cache 0,0(%0)" : : "r"(_addr_) ); \
} \
HAL_ICACHE_ENABLE(); \
}
***************
*** 367,395 ****
// Load the contents of the given address range into the instruction cache
// and then lock the cache so that it stays there.
! #define HAL_ICACHE_LOCK(_base_, _size_) \
! { \
! asm volatile ("mfc0 $2,$7;" \
! "ori $2,$2,0x0200;" \
! "mtc0 $2,$7;" \
! : \
! : \
! : "$2" \
! ); \
! }
// Undo a previous lock operation
! #define HAL_ICACHE_UNLOCK(_base_, _size_) \
! { \
! asm volatile ("mfc0 $2,$7;" \
! "la $3,0xFFFFFDFF;" \
! "and $2,$2,$3;" \
! "mtc0 $2,$7;" \
! : \
! : \
! : "$2", "$3" \
! ); \
! }
// Unlock entire cache
#define HAL_ICACHE_UNLOCK_ALL()
--- 379,388 ----
// Load the contents of the given address range into the instruction cache
// and then lock the cache so that it stays there.
! //#define HAL_ICACHE_LOCK(_base_, _size_)
// Undo a previous lock operation
! //#define HAL_ICACHE_UNLOCK(_base_, _size_)
// Unlock entire cache
#define HAL_ICACHE_UNLOCK_ALL()
***************
*** 401,411 ****
#define HAL_ICACHE_INVALIDATE( _base_ , _asize_ ) \
{ \
register CYG_ADDRESS _addr_ = (CYG_ADDRESS)(_base_); \
! register CYG_WORD _size_ = (_asize_); \
HAL_ICACHE_DISABLE(); \
! for( ; _addr_ <= _addr_+_size_; _addr_ += HAL_ICACHE_LINE_SIZE ) \
{ \
! asm volatile ("cache 0,0(%0)" : : "r"(_addr_) ); \
} \
HAL_ICACHE_ENABLE(); \
}
--- 394,404 ----
#define HAL_ICACHE_INVALIDATE( _base_ , _asize_ ) \
{ \
register CYG_ADDRESS _addr_ = (CYG_ADDRESS)(_base_); \
! register CYG_ADDRESS _end_ = (CYG_ADDRESS)(_base_) + _asize_; \
HAL_ICACHE_DISABLE(); \
! for( ; _addr_ <= _end_; _addr_ += HAL_ICACHE_LINE_SIZE ) \
{ \
! asm volatile (".balign 16,0;cache 0,0(%0)" : : "r"(_addr_) ); \
} \
HAL_ICACHE_ENABLE(); \
}
------- You are receiving this mail because: -------
You are the assignee for the bug, or are watching the assignee.