@ -38,6 +38,11 @@
* M100 C x Corrupts x locations within the free memory block . This is useful to check the
* correctness of the M100 F and M100 D commands .
*
* Also , there are two support functions that can be called from a developer ' s C code .
*
* uint16_t check_for_free_memory_corruption ( char * const ptr ) ;
* void M100_dump_free_memory ( char * ptr , char * sp ) ;
*
* Initial version by Roxy - 3 D
*/
# define M100_FREE_MEMORY_DUMPER // Enable for the `M110 D` Dump sub-command
@ -47,7 +52,7 @@
# if ENABLED(M100_FREE_MEMORY_WATCHER)
# define TEST_BYTE 0xE5
# define TEST_BYTE ((uint8_t) 0xE5)
extern char * __brkval ;
extern size_t __heap_start , __heap_end , __flp ;
@ -61,6 +66,7 @@ extern char __bss_end;
//
# define END_OF_HEAP() (__brkval ? __brkval : &__bss_end)
int check_for_free_memory_corruption ( char * title ) ;
// Location of a variable on its stack frame. Returns a value above
// the stack (once the function returns to the caller).
@ -70,7 +76,7 @@ char* top_of_stack() {
}
// Count the number of test bytes at the specified location.
int16_t count_test_bytes ( const char * const ptr ) {
int16_t count_test_bytes ( const uint8_t * const ptr ) {
for ( uint16_t i = 0 ; i < 32000 ; i + + )
if ( ptr [ i ] ! = TEST_BYTE )
return i - 1 ;
@ -78,27 +84,6 @@ int16_t count_test_bytes(const char * const ptr) {
return - 1 ;
}
// Return a count of free memory blocks.
uint16_t free_memory_is_corrupted ( char * const ptr , const uint16_t size ) {
// Find the longest block of test bytes in the given buffer
uint16_t block_cnt = 0 ;
for ( uint16_t i = 0 ; i < size ; i + + ) {
if ( ptr [ i ] = = TEST_BYTE ) {
const uint16_t j = count_test_bytes ( ptr + i ) ;
if ( j > 8 ) {
//SERIAL_ECHOPAIR("Found ", j);
//SERIAL_ECHOLNPAIR(" bytes free at 0x", hex_word((uint16_t)ptr + i));
i + = j ;
block_cnt + + ;
}
}
}
//if (block_cnt > 1) {
// SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
// SERIAL_ECHOLNPAIR("\nLargest free block is ", max_cnt);
//}
return block_cnt ;
}
//
// M100 sub-commands
@ -114,13 +99,13 @@ uint16_t free_memory_is_corrupted(char * const ptr, const uint16_t size) {
* the block . If so , it may indicate memory corruption due to a bad pointer .
* Unexpected bytes are flagged in the right column .
*/
void dump_free_memory ( char * ptr , char * sp ) {
void dump_free_memory ( uint8_t * ptr , uint8_t * sp ) {
//
// Start and end the dump on a nice 16 byte boundary
// (even though the values are not 16-byte aligned).
//
ptr = ( char * ) ( ( uint16_t ) ptr & 0xFFF0 ) ; // Align to 16-byte boundary
sp = ( char * ) ( ( uint16_t ) sp | 0x000F ) ; // Align sp to the 15th byte (at or above sp)
ptr = ( uint8_t * ) ( ( uint16_t ) ptr & 0xFFF0 ) ; // Align to 16-byte boundary
sp = ( uint8_t * ) ( ( uint16_t ) sp | 0x000F ) ; // Align sp to the 15th byte (at or above sp)
// Dump command main loop
while ( ptr < sp ) {
@ -131,14 +116,30 @@ uint16_t free_memory_is_corrupted(char * const ptr, const uint16_t size) {
print_hex_byte ( ptr [ i ] ) ;
SERIAL_CHAR ( ' ' ) ;
}
safe_delay ( 25 ) ;
SERIAL_CHAR ( ' | ' ) ; // Point out non test bytes
for ( uint8_t i = 0 ; i < 16 ; i + + )
SERIAL_CHAR ( ptr [ i ] = = TEST_BYTE ? ' ' : ' ? ' ) ;
SERIAL_EOL ;
ptr + = 16 ;
safe_delay ( 25 ) ;
idle ( ) ;
}
}
void M100_dump_routine ( char * title , char * start , char * end ) {
unsigned char c ;
int i ;
//
// Round the start and end locations to produce full lines of output
//
start = ( char * ) ( ( uint16_t ) start & 0xfff0 ) ;
end = ( char * ) ( ( uint16_t ) end | 0x000f ) ;
SERIAL_ECHOLN ( title ) ;
dump_free_memory ( start , end ) ;
}
# endif // M100_FREE_MEMORY_DUMPER
/**
@ -172,7 +173,7 @@ void free_memory_pool_report(const char * const ptr, const uint16_t size) {
SERIAL_ECHOPAIR ( " \n Largest free block is " , max_cnt ) ;
SERIAL_ECHOLNPAIR ( " bytes at 0x " , hex_word ( ( uint16_t ) max_addr ) ) ;
}
SERIAL_ECHOLNPAIR ( " free_memory_is_corrupted() = " , free_memory_is_corrupted ( ptr , size ) ) ;
SERIAL_ECHOLNPAIR ( " check_for_free_memory_corruption() = " , check_for_free_memory_corruption ( " M100 F " ) ) ;
}
# if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
@ -202,13 +203,17 @@ void free_memory_pool_report(const char * const ptr, const uint16_t size) {
* M100 I
* Init memory for the M100 tests . ( Automatically applied on the first M100 . )
*/
void init_free_memory ( char * ptr , int16_t size ) {
void init_free_memory ( uint8_t * ptr , int16_t size ) {
SERIAL_ECHOLNPGM ( " Initializing free memory block. \n \n " ) ;
size - = 250 ; // -250 to avoid interrupt activity that's altered the stack.
if ( size < 0 ) return ;
if ( size < 0 ) {
SERIAL_ECHOLNPGM ( " Unable to initialize. \n " ) ;
return ;
}
ptr + = 8 ;
ptr + = 8 ; // move a few bytes away from the heap just because we don't want
// to be altering memory that close to it.
memset ( ptr , TEST_BYTE , size ) ;
SERIAL_ECHO ( size ) ;
@ -217,8 +222,7 @@ void init_free_memory(char *ptr, int16_t size) {
for ( uint16_t i = 0 ; i < size ; i + + ) {
if ( ptr [ i ] ! = TEST_BYTE ) {
SERIAL_ECHOPAIR ( " ? address : 0x " , hex_word ( ( uint16_t ) ptr + i ) ) ;
SERIAL_ECHOPAIR ( " = " , hex_byte ( ptr [ i ] ) ) ;
SERIAL_EOL ; SERIAL_EOL ;
SERIAL_ECHOLNPAIR ( " = " , hex_byte ( ptr [ i ] ) ) ;
}
}
}
@ -230,7 +234,7 @@ void gcode_M100() {
SERIAL_ECHOPAIR ( " \n __brkval : 0x " , hex_word ( ( uint16_t ) __brkval ) ) ;
SERIAL_ECHOPAIR ( " \n __bss_end: 0x " , hex_word ( ( uint16_t ) & __bss_end ) ) ;
char * ptr = END_OF_HEAP ( ) , * sp = top_of_stack ( ) ;
uint8_t * ptr = END_OF_HEAP ( ) , * sp = top_of_stack ( ) ;
SERIAL_ECHOPAIR ( " \n start of free space : 0x " , hex_word ( ( uint16_t ) ptr ) ) ;
SERIAL_ECHOLNPAIR ( " \n Stack Pointer : 0x " , hex_word ( ( uint16_t ) sp ) ) ;
@ -243,10 +247,8 @@ void gcode_M100() {
}
# if ENABLED(M100_FREE_MEMORY_DUMPER)
if ( code_seen ( ' D ' ) )
return dump_free_memory ( ptr , sp ) ;
# endif
if ( code_seen ( ' F ' ) )
@ -260,4 +262,67 @@ void gcode_M100() {
# endif
}
int check_for_free_memory_corruption ( char * title ) {
char * sp , * ptr ;
int block_cnt = 0 , i , j , n ;
SERIAL_ECHO ( title ) ;
ptr = __brkval ? __brkval : & __bss_end ;
sp = top_of_stack ( ) ;
n = sp - ptr ;
SERIAL_ECHOPAIR ( " \n fmc() n= " , n ) ;
SERIAL_ECHOPAIR ( " \n &__brkval: 0x " , hex_word ( ( uint16_t ) & __brkval ) ) ;
SERIAL_ECHOPAIR ( " =0x " , hex_word ( ( uint16_t ) __brkval ) ) ;
SERIAL_ECHOPAIR ( " \n __bss_end: 0x " , hex_word ( ( uint16_t ) & __bss_end ) ) ;
SERIAL_ECHOPAIR ( " sp= " , hex_word ( sp ) ) ;
if ( sp < ptr ) {
SERIAL_ECHOPGM ( " sp < Heap " ) ;
// SET_INPUT_PULLUP(63); // if the developer has a switch wired up to their controller board
// safe_delay(5); // this code can be enabled to pause the display as soon as the
// while ( READ(63)) // malfunction is detected. It is currently defaulting to a switch
// idle(); // being on pin-63 which is unassigend and available on most controller
// safe_delay(20); // boards.
// while ( !READ(63))
// idle();
safe_delay ( 20 ) ;
M100_dump_routine ( " Memory corruption detected with sp<Heap \n " , ( char * ) 0x1b80 , 0x21ff ) ;
}
// Scan through the range looking for the biggest block of 0xE5's we can find
for ( i = 0 ; i < n ; i + + ) {
if ( * ( ptr + i ) = = ( char ) 0xe5 ) {
j = count_test_bytes ( ptr + i ) ;
if ( j > 8 ) {
// SERIAL_ECHOPAIR("Found ", j);
// SERIAL_ECHOLNPAIR(" bytes free at 0x", hex_word((uint16_t)(ptr + i)));
i + = j ;
block_cnt + + ;
SERIAL_ECHOPAIR ( " ( " , block_cnt ) ;
SERIAL_ECHOPAIR ( " ) found= " , j ) ;
SERIAL_ECHOPGM ( " " ) ;
}
}
}
SERIAL_ECHOPAIR ( " block_found= " , block_cnt ) ;
if ( ( block_cnt ! = 1 ) | | ( __brkval ! = 0x0000 ) )
SERIAL_ECHOLNPGM ( " \n Memory Corruption detected in free memory area. " ) ;
if ( ( block_cnt = = 0 ) ) // Make sure the special case of no free blocks shows up as an
block_cnt = - 1 ; // error to the calling code!
if ( block_cnt = = 1 ) {
SERIAL_ECHOPGM ( " return=0 \n " ) ; // if the block_cnt is 1, nothing has broken up the free memory
return 0 ; // area and it is appropriate to say 'no corruption'.
}
SERIAL_ECHOPGM ( " return=true \n " ) ;
return block_cnt ;
}
# endif // M100_FREE_MEMORY_WATCHER