BUG in arm_dcache_delete

One possible update:
Code:
__attribute__((always_inline, unused))
static inline void arm_dcache_delete(void *addr, uint32_t size)
{
	if (((uint32_t)addr | size) & 0x1f) {
		arm_dcache_flush_delete(addr, size);
		return;
	}
	uint32_t location = (uint32_t)addr & 0xFFFFFFE0;
	uint32_t end_addr = (uint32_t)addr + size;
	asm volatile("": : :"memory");
	asm("dsb");
	do {
		SCB_CACHE_DCIMVAC = location;
		location += 32;
	} while (location < end_addr);
	asm("dsb");
	asm("isb");
}
For those cases you don't want the extra overhead and you know it is aligned for 32 bytes
You could have copy of original one...

Obviously probably other optimizations that one could do...
Not sure with the or of the two at start to avoid to ands helps or not. Also if the size is some compile time known size will it optimize it to one part or the other...
 
Had an Idea - but can't find the Source to test it ???

The problem is the malloc() returns DMAMEM ptr, and it works storing malloc() data lists in DMAMEM.

So this sample code is writing malloc() list details to cache - and the cache is being deleted before those critical values are being saved to actual RAM, this Crashes on subsequent calls as the list data is parsed.

Solution ??? : On malloc() return do a dcache_flush to save the malloc() lists to actual RAM before giving the user a chance to delete them - as is done in this case?
 
As stated in #4, flush before delete is an option. If you care about speed, don't flush more than necessary.

I think Kurt's code would work.
 
Flush before delete makes not any sense if you dont need it flushed. The function is used to *prevent* a flush, for speed reasons.

It just invalidates the cache lines.
 
Why not just this, or has it a bug I don't see?
Code:
__attribute__((always_inline, unused))
static inline void arm_dcache_delete(void *addr, uint32_t size)
{
    uintptr_t location = (31 + (uintptr_t)addr) & 0xFFFFFFE0;
    uintptr_t end_addr = ((uintptr_t)addr + size) & 0xFFFFFFE0;
    asm volatile("": : :"memory");
    asm("dsb");
    while (location < end_addr) {
        SCB_CACHE_DCIMVAC = location;
        location += 32;
    };
    asm("dsb");
    asm("isb");
}
The code from #19 works with that.
Adding more attributes (i.e. nonnull ) would probably be good, and gives the compiler useful hints. And to use size_t, perhaps, for the size.


At least it needs a while loop, otherwise size=zero will never work. The goal is to delete _maximimum_ the touched cache lines.
Attention the other two functions need a different handling. The goal is there to flush _minimum_ the touched cache lines.
 
Last edited:
@Kurt's change would protect the leading data in the block - basically doing the essential part of this that works in the sketch:
Code:
  arm_dcache_flush(p2, 100);
  arm_dcache_delete(p2, 100);

The Corruption/Crash is from loss of malloc() linked list data regarding allocations.

Can anyone find the code where the malloc() lists are maintained in the self same DMAMEM? If before returning the ptr to the caller the changed data in cache were written (in all places changed) - it would be immune to loss as the values would be in physical RAM.
 
My code protects all data, not only the leading :)
And the malloc is only one possible issue. Its just nice as example. But the bug has nothing to do with malloc.
Only the cache lines count. Its not interesting what they are used for.
 
At least it needs a while loop, otherwise size=zero will never work. The goal is to delete _maximimum_ the touched cache lines.
Attention the other two functions need a different handling. The goal is there to flush _minimum_ the touched cache lines.

Now to the remaining two:

Rethinking about them.. no, I think they work as they are. They may flush more than needed, and this is ok. Objections?
 
Last edited:
Any use of _delete() is the user doing something risky. This is no different than the user abusing a pointer - in this case instead of running off the end, code corrupts prior to start pointer.

Recall the other work in prior releases where buffer alignment was considered by the coder. Not sure if that was LittleFS or video buffering with DMA or other ... maybe those and more.

In this case the malloc() code in DMAMEM should assure edits are _flushed and 'this' "malloc lists corruption" problem would be avoided.
 
With 1024 and 20 as address/size inputs, your code does nothing.

Yes, and that's good. You can't delete a part of a cache line.
So, deleting 32 bytes would mean to delete *more* than wanted. And this is *not* wanted.
 
In this case the malloc() code in DMAMEM should assure edits are _flushed and 'this' "malloc lists corruption" problem would be avoided.
We had this discussion. Scroll up.
Why should I call *delete* then? There is already a better function "flush and delete". its totally against its intention. and it's just not needed. there is absolutely not reason to flush when it's ok to just do nothing.
It's intention is *speed*, not flushing. a flush is *not* wanted.

even an simple {return;} (empty function) would be better than flushing. as said several times, it's ok to delete less than requested. the problem is only when it deletes *more* than requested.
 
I think i stop here now.
37 posts for a 3 minute edit.
that's enough. I'm tired about such discussions.
I posted a solution.

Or use a other , *correct* one., I don't care.
But fix it.
 
So you would flush & delete the whole 32 Bytes.
Interesting. Tell us why it's "not harmless" to delete less. despite the user *wanted* to delete and not to flush.
Hm... what happens with a DMA access wich by accidentent uses just these unwanted flushed bytes at just this moment?

intersting enough, please find a example where "delete less" not work? Show us!
I had to post examples, too, so I think my request is fair.
WOuld be really cool to see valid code that crashes because of the "delete less" variant.



btw, my examples work with my variant. yes, with DMA too.



Forget it.
I promised to stop here ;-)
 
We had this discussion. Scroll up.
Why should I call *delete* then? There is already a better function "flush and delete". its totally against its intention. and it's just not needed. there is absolutely not reason to flush when it's ok to just do nothing.
It's intention is *speed*, not flushing. a flush is *not* wanted.

even an simple {return;} (empty function) would be better than flushing. as said several times, it's ok to delete less than requested. the problem is only when it deletes *more* than requested.

... missing my point it seems ... the malloc code should protect itself and FLUSH its updated pointer list areas - just the size of any updated struct { free and alloc list } - nothing to do with all user data.
 
again: it has nothing to do with malloc.
This thread is about dcache_flush - not malloc.

Maybe use it with a temp file in your "RAM"-littleFS - its the same. Oh, wait, your littlFS should protect itself... eh..NO!

So, here too:
I stop this. Post #42 about a dead easy thing.. A good number. Way too much. For me. I should have stopend this some 10th posts earlier.
I believe, Paul is smart enough to know that a flush is not wanted here. No, I know that he knows to fo fix it.

edit: and, I know he will not touch malloc, as there is no reason to do so. when the delete works as it should ("delete less"), it happens nothing to malloc, and it will work perfectly.
edit: And it does not destroy DMA data that is written to the memory location in question.
 
Last edited:
It is all about corruption of critical malloc structure stored in cache where they are destroyed by user code.

If malloc() code did a flush() before returning the pointer - the delete() could not hurt anything, it would already be in RAM.
 
Wow, 43 messages on this just yesterday. Just want to confirm I've put this on my list of issues to investigate... though I haven't read this thread yet. I will, but probably not until next week. This all happened while I was off the forum and trying to get MacOS to talk to a locked IMXRT chip (which is finally working - a silly bug in my code took 2 days to find & fix).
 
Saw this example of alternate way to destroy data deleting the cache of DMAMEM.
Fixed with commented bold flush line.
Nothing is committed to DMAMEM for sure until _flush. And alternatively - anything in DMAMEM on warm restart is still there! That is running this as 'fixed' and then 'broken' showed it working after upload as the residual bits persisted.

as always ... Not Arguing for/against any fix - just that DMAMEM has an awesome potential for use and misunderstanding that is worthy of documentation.

Other interesting note made the other day - the Build determines where DMAMEM physically goes, not the placement in the source code line ordering as both ptr1 and ptr2 are allocated addresses after buffer[], see first line of Output below. Not sure what decides goes where based on 'source code' - but note it is subject to change by whatever that 'hidden logic' is.

Code:
DMAMEM uint8_t *ptr1;
DMAMEM uint8_t buffer[100];
DMAMEM uint8_t *ptr2;

void setup() {
  while(!Serial);
  Serial.begin(115200);
  Serial.printf("%x %x %x\n", (uint32_t)&ptr1, (uint32_t)buffer, (uint32_t)&ptr2);
  ptr1 = buffer;
  ptr2 = buffer;
  [B]// arm_dcache_flush(buffer, sizeof(buffer));[/B]
  Serial.printf("%x %x\n", (uint32_t)ptr1, (uint32_t)ptr2);
  arm_dcache_delete(buffer, sizeof(buffer));
  Serial.printf("%x %x\n", (uint32_t)ptr1, (uint32_t)ptr2);
}
void loop() {
}

Output when ptr's destroyed by _delete:
Code:
[B]20200064 20200000 20200068[/B]
20200000 20200000
72f9abc5 4d414091
 
Thanks @defragster,

I had not posted that test case yet, but was pass one of some testing I have been doing.

It was also suggested that I go ahead and post some some stuff from email as well... Minus the email types stuff:

Short version: It would be great if some of these cache functions were described in more details somewhere. Maybe they are, but I did not
find anything using google... Probably the best one was: https://www.nxp.com/docs/en/application-note/AN12042.pdf

<Warning start of rambling ;) >

As I mentioned earlier in this thread, I really don’t understand enough about how these function work:
Yes I can read the code like:
Code:
__attribute__((always_inline, unused))
static inline void arm_dcache_delete(void *addr, uint32_t size)
{
            uint32_t location = (uint32_t)addr & 0xFFFFFFE0;
            uint32_t end_addr = (uint32_t)addr + size;
            asm volatile("": : :"memory");
            asm("dsb");
            do {
                        SCB_CACHE_DCIMVAC = location;
                        location += 32;
            } while (location < end_addr);
            asm("dsb");
            asm("isb");
}
But I have no clue on what exactly SCB_CACHE_DCIMVAC = location does...
Yes I can read imxrt.h and see: #define SCB_CACHE_DCIMVAC (*(volatile uint32_t *)0xE000EF5C)

So I know it is writing the location out to some specific memory location, which somehow deletes that location and inference on how this function works 32 bytes from the cache.
And the code implies that it needs to align the address passed in to 32 byte boundary…

What happens if you pass in address that is not aligned to 32 bit address? Nothing? Those bits have special meaning to this function? Still clears that same 32 bytes? Only to end of that 32 byte page? 32 bytes from that location? …

How much of a time hit is there to do the flush and delete versus just delete. My guess is … it depends on which memory… DMAMEM or EXTMEM…
Also probably depends on if the memory actually is in the cache? Does not also depend on how many bytes have been written to within that 32 bytes?

I would assume that: doing a flush followed by a delete is slower than flush_delete
(SCB_CACHE_DCCMVAC = location; SCB_CACHE_DCIMVAC = location; ) versus (SCB_CACHE_DCCIMVAC = location;

Again using Frank’s example like: arm_dcache_delete(p2, 100);

We know that this will clear 4 pages with one or two of them not full 32 bytes. Now my assumption is that if he is asking to delete 100 bytes from cache he must be doing this for some DMA operation, so you need all 100 bytes cleared from the cache.

So on forum I posted one possible fix that simply says if either the address or the size are not multiples of 32 bytes it calls off to flush_delete… Using the KISS…

Question is would it be better to do a more complete change? Something like:
Code:
__attribute__((always_inline, unused))
static inline void arm_dcache_delete(void *addr, uint32_t size)
{
                uint32_t location = (uint32_t)addr & 0xFFFFFFE0;
                uint32_t end_addr = (uint32_t)addr + size;
                asm volatile("": : :"memory");
                asm("dsb");
                if (location != (uint32_t)addr) SCB_CACHE_DCCMVAC = location;  // make sure it is flushed first if unaligned
                if (end_addr & 0x1f) SCB_CACHE_DCCMVAC = (end_addr & 0xFFFFFFE0);  // make sure flush if end unaligned
                do {
                                SCB_CACHE_DCIMVAC = location;
                                location += 32;
                } while (location < end_addr);
                asm("dsb");
                asm("isb");
}
But this can probably be improved on, like this does possible flush and first and last pages if they are unaligned, but what if they are on same page?
Also maybe better to do flush/delete here, but then need to update start/end of loop code…

So question is, does adding this complexity speed up things over simply calling flush_delete? It may very likely depend on how big… That is if I want to read in the whole frame buffer for a 320x240*2 bytes display than probably. But for his 100 byte case, it might be curious.

In a follow up message, I extended the possible change to the function, to maybe something like:
Code:
__attribute__((always_inline, unused))
static inline void arm_dcache_delete(void *addr, uint32_t size)
{
    uint32_t location = (uint32_t)addr & 0xFFFFFFE0;
    uint32_t end_addr = (uint32_t)addr + size;
    asm volatile("": : :"memory");
    asm("dsb");
        if (location != (uint32_t)addr) {
            SCB_CACHE_DCCIMVAC = location;  // make sure it is flushed and delete it
             location += 32;  // don't process this one again. 
        }
        if (end_addr & 0x1f) {
            end_addr &= 0xFFFFFFE0;
            // see if there was a first write that it did not cover this one as well
            if (end_addr > location) SCB_CACHE_DCCIMVAC = end_addr;  // flush and delete it and decrement size back to not do this page again.
        }
        while (location < end_addr);
        {
            SCB_CACHE_DCIMVAC = location;
            location += 32;
        }
        asm("dsb");
        asm("isb");
}

Note: All of these updates were typed in on the fly so not compiled yet nor tested...

End rambling &#55357;&#56835;
 
Somethings that caught my eye when looking up DCache functions especially the built in funtions.

https://www.keil.com/pack/doc/CMSIS...s__m7.html#gaf5585be5547cc60585d702a6129f4c17
https://www.st.com/resource/en/application_note/DM00272913-.pdf

https://github.com/ARM-software/CMSIS_5/issues/280 ( SCB_CleanDCache_by_Addr.)
https://github.com/ARM-software/CMSIS_5/commit/ab35be31712f978b8b031d6438ea180c107cf171


https://elixir.bootlin.com/linux/latest/source/arch/arm/include/asm/v7m.h
Code:
/* Cache opeartions */
#define	V7M_SCB_ICIALLU		0x250	/* I-cache invalidate all to PoU */
#define	V7M_SCB_ICIMVAU		0x258	/* I-cache invalidate by MVA to PoU */
#define	V7M_SCB_DCIMVAC		0x25c	/* D-cache invalidate by MVA to PoC */
#define	V7M_SCB_DCISW		0x260	/* D-cache invalidate by set-way */
#define	V7M_SCB_DCCMVAU		0x264	/* D-cache clean by MVA to PoU */
#define	V7M_SCB_DCCMVAC		0x268	/* D-cache clean by MVA to PoC */
#define	V7M_SCB_DCCSW		0x26c	/* D-cache clean by set-way */
#define	V7M_SCB_DCCIMVAC	0x270	/* D-cache clean and invalidate by MVA to PoC */
#define	V7M_SCB_DCCISW		0x274	/* D-cache clean and invalidate by set-way */
#define	V7M_SCB_BPIALL		0x278	/* D-cache clean and invalidate by set-way */
 
@mjs513 sounds like some more fun reading.

I wanted to next test doing an DMA operation to that memory to see what makes it through. But so far not having luck with DMA memory to memory...
Code:
#include <DMAChannel.h>
DMAChannel dma;
DMAMEM char *ptr1;
DMAMEM char dmaBuffer[100];
DMAMEM char *ptr2;

char low_buffer[100];

void setup() {
  while (!Serial);
  Serial.begin(115200);
  Serial.printf("%x %x %x\n", (uint32_t)&ptr1, (uint32_t)dmaBuffer, (uint32_t)&ptr2);
  ptr1 = dmaBuffer;
  ptr2 = dmaBuffer;
  Serial.printf("%x %x\n", (uint32_t)ptr1, (uint32_t)ptr2);
  arm_dcache_delete(dmaBuffer, sizeof(dmaBuffer));
  Serial.printf("%x %x\n", (uint32_t)ptr1, (uint32_t)ptr2);

  strcpy(low_buffer, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz0123456789");
  dma.sourceBuffer((uint8_t*)low_buffer, sizeof(low_buffer));
  dma.destinationBuffer((uint8_t*)dmaBuffer, sizeof(dmaBuffer));
  dma.transferCount(sizeof(dmaBuffer));
  dma.disableOnCompletion();
  dma.enable();
  dma.clearComplete();
  dma.triggerContinuously();
  delay(100);
  Serial.println((char*)low_buffer);
  Serial.println((char*)dmaBuffer);
}
void loop() {
}
Probably something obvious... One thing I noticed is the triggerContinuously() is not implemented... Other ways people do this?

Kurt
 
Fair warning going to show something and assumes I did it right. Also found that to test accurately you have to cycle the power otherwise gives you a false sense that it works. For instance if I run Kurt's example (first example in post #46) and then run the SCB_CleanDCache example shows both works but if you cycle power it fails.

SCB_CleanDCache
Code:
20200064 20200000 20200068
20200000 20200000
9f21fe51 8488cc5

Kurt's example
Code:
20200064 20200000 20200068
20200000 20200000
20200000 20200000

Here is the sketch - this assumes I did it right on the scb version.
Code:
/**
  \brief   Clean a D-Cache region
  \details Cleans D-Cache for the given address and size.
  \param[in]   addr    address
  \param[in]   dsize   size of memory block (in number of bytes)
*/
void SCB_CleanDCache_Region(void *addr, uint32_t dsize)
{
  //#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
    if ( dsize > 0 ) {
      const uint32_t linesize = 32; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */
      uint32_t op_addr = ((uint32_t)addr) & ~(linesize - 1);
      int32_t  op_size = (int32_t)(dsize + (((uint32_t)addr) & (linesize - 1)));

        asm("dsb");

      do {
        SCB_CACHE_DCIMVAC  = op_addr;
        op_addr +=          linesize;
        op_size -= (int32_t)linesize;
      } while ( op_size > 0 );

        asm("dsb");
        asm("isb");
    }
  //#endif
}

void larm_dcache_delete(void *addr, uint32_t size)
{
    uint32_t location = (uint32_t)addr & 0xFFFFFFE0;
    uint32_t end_addr = (uint32_t)addr + size;
    asm volatile("": : :"memory");
    asm("dsb");
    if (location != (uint32_t)addr) SCB_CACHE_DCCMVAC = location;  // make sure it is flushed first if unaligned
    if (end_addr & 0x1f) SCB_CACHE_DCCMVAC = (end_addr & 0xFFFFFFE0);  // make sure flush if end unaligned
    do {
      SCB_CACHE_DCIMVAC = location;
      location += 32;
    } while (location < end_addr);
    asm("dsb");
    asm("isb");
}

const int ledPin = 13;

DMAMEM uint8_t *ptr1;
DMAMEM uint8_t buffer[100];
DMAMEM uint8_t *ptr2;

void setup() {
  // initialize the digital pin as an output.
  pinMode(ledPin, OUTPUT);

  while(!Serial);
  Serial.begin(115200);
  Serial.printf("%x %x %x\n", (uint32_t)&ptr1, (uint32_t)buffer, (uint32_t)&ptr2);
  ptr1 = buffer;
  ptr2 = buffer;
  //arm_dcache_flush(buffer, sizeof(buffer));
  Serial.printf("%x %x\n", (uint32_t)ptr1, (uint32_t)ptr2);
  //arm_dcache_delete(buffer, sizeof(buffer));
  //SCB_CleanDCache_Region(buffer, sizeof(buffer));
  larm_dcache_delete(buffer, sizeof(buffer));
  Serial.printf("%x %x\n", (uint32_t)ptr1, (uint32_t)ptr2);
}

// the loop() methor runs over and over again,
// as long as the board has power

void loop() {
  digitalWrite(ledPin, HIGH);   // set the LED on
  delay(1000);                  // wait for a second
  digitalWrite(ledPin, LOW);    // set the LED off
  delay(1000);                  // wait for a second
}
 
Out of curiosity I rewrote the above sketch to use 4 sets of buffers and 4 sets of pointers so you could do the comparison from the same sketch with out have to recycle power I also added Frank's method in post 30

Code:
-------------  Franks arm_dcache_delete in post 30-----------------
20200064 20200000 20200068
2020006c 2020006c
2020006c 2020006c

-------------  arm_dcache_delete -----------------
202000d0 20200000 202000d4
20200000 20200000
20200000 20200000

-------------  SCB_CleanDCache_Region -----------------
202000d8 20200000 202000dc
202000e0 202000e0
20200000 20200000

-------------  Kurt's first complete function in post 46 -----------------
20200144 20200000 20200148
2020014c 2020014c
2020014c 2020014c
The interesting thing to me and unexpected is that after clearing cache in the middle 2 cases the pointers are the same value. But it clearly shows Kurt's and Frank's method seems to work.
Code:
/**
  \brief   Clean a D-Cache region
  \details Cleans D-Cache for the given address and size.
  \param[in]   addr    address
  \param[in]   dsize   size of memory block (in number of bytes)
*/
void SCB_CleanDCache_Region(void *addr, uint32_t dsize)
{
  //#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
    if ( dsize > 0 ) {
      const uint32_t linesize = 32; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */
      uint32_t op_addr = ((uint32_t)addr) & ~(linesize - 1);
      int32_t  op_size = (int32_t)(dsize + (((uint32_t)addr) & (linesize - 1)));

        asm("dsb");

      do {
        SCB_CACHE_DCIMVAC  = op_addr;
        op_addr +=          linesize;
        op_size -= (int32_t)linesize;
      } while ( op_size > 0 );

        asm("dsb");
        asm("isb");
    }
  //#endif
}

void larm_dcache_delete(void *addr, uint32_t size)
{
    uint32_t location = (uint32_t)addr & 0xFFFFFFE0;
    uint32_t end_addr = (uint32_t)addr + size;
    asm volatile("": : :"memory");
    asm("dsb");
    if (location != (uint32_t)addr) SCB_CACHE_DCCMVAC = location;  // make sure it is flushed first if unaligned
    if (end_addr & 0x1f) SCB_CACHE_DCCMVAC = (end_addr & 0xFFFFFFE0);  // make sure flush if end unaligned
    do {
      SCB_CACHE_DCIMVAC = location;
      location += 32;
    } while (location < end_addr);
    asm("dsb");
    asm("isb");
}

void farm_dcache_delete(void *addr, uint32_t size)
{
    uintptr_t location = (31 + (uintptr_t)addr) & 0xFFFFFFE0;
    uintptr_t end_addr = ((uintptr_t)addr + size) & 0xFFFFFFE0;
    asm volatile("": : :"memory");
    asm("dsb");
    while (location < end_addr) {
        SCB_CACHE_DCIMVAC = location;
        location += 32;
    };
    asm("dsb");
    asm("isb");
}

const int ledPin = 13;

DMAMEM uint8_t *ptr1;
DMAMEM uint8_t buffer[100];
DMAMEM uint8_t *ptr2;
DMAMEM uint8_t *ptr3;
DMAMEM uint8_t buffer1[100];
DMAMEM uint8_t *ptr4;
DMAMEM uint8_t *ptr5;
DMAMEM uint8_t buffer2[100];
DMAMEM uint8_t *ptr6;
DMAMEM uint8_t *ptr7;
DMAMEM uint8_t buffer3[100];
DMAMEM uint8_t *ptr8;

void setup() {
  // initialize the digital pin as an output.
  pinMode(ledPin, OUTPUT);

  while(!Serial);
  Serial.begin(115200);

  Serial.printf("\n-------------  Franks arm_dcache_delete in post 30-----------------\n");
  Serial.printf("%x %x %x\n", (uint32_t)&ptr7, (uint32_t)buffer, (uint32_t)&ptr8);
  ptr7 = buffer3;
  ptr8 = buffer3;
  //arm_dcache_flush(buffer, sizeof(buffer));
  Serial.printf("%x %x\n", (uint32_t)ptr7, (uint32_t)ptr8);
  farm_dcache_delete(buffer3, sizeof(buffer3));
  Serial.printf("%x %x\n", (uint32_t)ptr7, (uint32_t)ptr8);

  
  
  Serial.printf("\n-------------  arm_dcache_delete -----------------\n");
  Serial.printf("%x %x %x\n", (uint32_t)&ptr1, (uint32_t)buffer, (uint32_t)&ptr2);
  ptr1 = buffer;
  ptr2 = buffer;
  //arm_dcache_flush(buffer, sizeof(buffer));
  Serial.printf("%x %x\n", (uint32_t)ptr1, (uint32_t)ptr2);
  arm_dcache_delete(buffer, sizeof(buffer));
  Serial.printf("%x %x\n", (uint32_t)ptr1, (uint32_t)ptr2);

  Serial.printf("\n-------------  SCB_CleanDCache_Region -----------------\n");
  Serial.printf("%x %x %x\n", (uint32_t)&ptr3, (uint32_t)buffer, (uint32_t)&ptr4);
  ptr3 = buffer1;
  ptr4 = buffer1;
  Serial.printf("%x %x\n", (uint32_t)ptr3, (uint32_t)ptr4);
  SCB_CleanDCache_Region(buffer1, sizeof(buffer1));
  Serial.printf("%x %x\n", (uint32_t)ptr1, (uint32_t)ptr2);

  Serial.printf("\n-------------  Kurt's first complete function in post 46 -----------------\n");
  Serial.printf("%x %x %x\n", (uint32_t)&ptr5, (uint32_t)buffer, (uint32_t)&ptr6);
  ptr5 = buffer2;
  ptr6 = buffer2;
  Serial.printf("%x %x\n", (uint32_t)ptr5, (uint32_t)ptr6);
  larm_dcache_delete(buffer2, sizeof(buffer2));
  Serial.printf("%x %x\n", (uint32_t)ptr5, (uint32_t)ptr6);
}

// the loop() methor runs over and over again,
// as long as the board has power

void loop() {
  digitalWrite(ledPin, HIGH);   // set the LED on
  delay(1000);                  // wait for a second
  digitalWrite(ledPin, LOW);    // set the LED off
  delay(1000);                  // wait for a second
}
 
Back
Top