My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

M100_Free_Mem_Chk.cpp 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. #define M100_FREE_MEMORY_DUMPER // Comment out to remove Dump sub-command
  2. #define M100_FREE_MEMORY_CORRUPTOR // Comment out to remove Corrupt sub-command
  3. // M100 Free Memory Watcher
  4. //
  5. // This code watches the free memory block between the bottom of the heap and the top of the stack.
  6. // This memory block is initialized and watched via the M100 command.
  7. //
  8. // M100 I Initializes the free memory block and prints vitals statistics about the area
  9. // M100 F Identifies how much of the free memory block remains free and unused. It also
  10. // detects and reports any corruption within the free memory block that may have
  11. // happened due to errant firmware.
  12. // M100 D Does a hex display of the free memory block along with a flag for any errant
  13. // data that does not match the expected value.
  14. // M100 C x Corrupts x locations within the free memory block. This is useful to check the
  15. // correctness of the M100 F and M100 D commands.
  16. //
  17. // Initial version by Roxy-3DPrintBoard
  18. //
  19. //
  20. #include "Marlin.h"
  21. #if ENABLED(M100_FREE_MEMORY_WATCHER)
  22. extern void *__brkval;
  23. extern size_t __heap_start, __heap_end, __flp;
  24. //
  25. // Declare all the functions we need from Marlin_Main.cpp to do the work!
  26. //
  27. float code_value();
  28. long code_value_long();
  29. bool code_seen(char );
  30. void serial_echopair_P(const char *, float );
  31. void serial_echopair_P(const char *, double );
  32. void serial_echopair_P(const char *, unsigned long );
  33. void serial_echopair_P(const char *, int );
  34. void serial_echopair_P(const char *, long );
  35. //
  36. // Utility functions used by M100 to get its work done.
  37. //
  38. unsigned char *top_of_stack();
  39. void prt_hex_nibble( unsigned int );
  40. void prt_hex_byte(unsigned int );
  41. void prt_hex_word(unsigned int );
  42. int how_many_E5s_are_here( unsigned char *);
  43. void gcode_M100()
  44. {
  45. static int m100_not_initialized=1;
  46. unsigned char *sp, *ptr;
  47. int i, j, n;
  48. //
  49. // M100 D dumps the free memory block from __brkval to the stack pointer.
  50. // malloc() eats memory from the start of the block and the stack grows
  51. // up from the bottom of the block. Solid 0xE5's indicate nothing has
  52. // used that memory yet. There should not be anything but 0xE5's within
  53. // the block of 0xE5's. If there is, that would indicate memory corruption
  54. // probably caused by bad pointers. Any unexpected values will be flagged in
  55. // the right hand column to help spotting them.
  56. //
  57. #if ENABLED(M100_FREE_MEMORY_DUMPER) // Disable to remove Dump sub-command
  58. if ( code_seen('D') ) {
  59. ptr = (unsigned char *) __brkval;
  60. //
  61. // We want to start and end the dump on a nice 16 byte boundry even though
  62. // the values we are using are not 16 byte aligned.
  63. //
  64. SERIAL_ECHOPGM("\n__brkval : ");
  65. prt_hex_word( (unsigned int) ptr );
  66. ptr = (unsigned char *) ((unsigned long) ptr & 0xfff0);
  67. sp = top_of_stack();
  68. SERIAL_ECHOPGM("\nStack Pointer : ");
  69. prt_hex_word( (unsigned int) sp );
  70. SERIAL_ECHOPGM("\n");
  71. sp = (unsigned char *) ((unsigned long) sp | 0x000f);
  72. n = sp - ptr;
  73. //
  74. // This is the main loop of the Dump command.
  75. //
  76. while ( ptr < sp ) {
  77. prt_hex_word( (unsigned int) ptr); // Print the address
  78. SERIAL_ECHOPGM(":");
  79. for(i=0; i<16; i++) { // and 16 data bytes
  80. prt_hex_byte( *(ptr+i));
  81. SERIAL_ECHOPGM(" ");
  82. delay(2);
  83. }
  84. SERIAL_ECHO("|"); // now show where non 0xE5's are
  85. for(i=0; i<16; i++) {
  86. delay(2);
  87. if ( *(ptr+i)==0xe5)
  88. SERIAL_ECHOPGM(" ");
  89. else
  90. SERIAL_ECHOPGM("?");
  91. }
  92. SERIAL_ECHO("\n");
  93. ptr += 16;
  94. delay(2);
  95. }
  96. SERIAL_ECHOLNPGM("Done.\n");
  97. return;
  98. }
  99. #endif
  100. //
  101. // M100 F requests the code to return the number of free bytes in the memory pool along with
  102. // other vital statistics that define the memory pool.
  103. //
  104. if ( code_seen('F') ) {
  105. int max_addr = (int) __brkval;
  106. int max_cnt = 0;
  107. int block_cnt = 0;
  108. ptr = (unsigned char *) __brkval;
  109. sp = top_of_stack();
  110. n = sp - ptr;
  111. // Scan through the range looking for the biggest block of 0xE5's we can find
  112. for(i=0; i<n; i++) {
  113. if ( *(ptr+i) == (unsigned char) 0xe5) {
  114. j = how_many_E5s_are_here( (unsigned char *) ptr+i );
  115. if ( j>8) {
  116. SERIAL_ECHOPAIR("Found ", j );
  117. SERIAL_ECHOPGM(" bytes free at 0x");
  118. prt_hex_word( (int) ptr+i );
  119. SERIAL_ECHOPGM("\n");
  120. i += j;
  121. block_cnt++;
  122. }
  123. if ( j>max_cnt) { // We don't do anything with this information yet
  124. max_cnt = j; // but we do know where the biggest free memory block is.
  125. max_addr = (int) ptr+i;
  126. }
  127. }
  128. }
  129. if (block_cnt>1)
  130. SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.\n");
  131. SERIAL_ECHO("\nDone.\n");
  132. return;
  133. }
  134. //
  135. // M100 C x Corrupts x locations in the free memory pool and reports the locations of the corruption.
  136. // This is useful to check the correctness of the M100 D and the M100 F commands.
  137. //
  138. #if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
  139. if ( code_seen('C') ) {
  140. int x; // x gets the # of locations to corrupt within the memory pool
  141. x = code_value();
  142. SERIAL_ECHOLNPGM("Corrupting free memory block.\n");
  143. ptr = (unsigned char *) __brkval;
  144. SERIAL_ECHOPAIR("\n__brkval : ",(long) ptr );
  145. ptr += 8;
  146. sp = top_of_stack();
  147. SERIAL_ECHOPAIR("\nStack Pointer : ",(long) sp );
  148. SERIAL_ECHOLNPGM("\n");
  149. n = sp - ptr - 64; // -64 just to keep us from finding interrupt activity that
  150. // has altered the stack.
  151. j = n / (x+1);
  152. for(i=1; i<=x; i++) {
  153. *(ptr+(i*j)) = i;
  154. SERIAL_ECHO("\nCorrupting address: 0x");
  155. prt_hex_word( (unsigned int) (ptr+(i*j)) );
  156. }
  157. SERIAL_ECHOLNPGM("\n");
  158. return;
  159. }
  160. #endif
  161. //
  162. // M100 I Initializes the free memory pool so it can be watched and prints vital
  163. // statistics that define the free memory pool.
  164. //
  165. if (m100_not_initialized || code_seen('I') ) { // If no sub-command is specified, the first time
  166. SERIAL_ECHOLNPGM("Initializing free memory block.\n"); // this happens, it will Initialize.
  167. ptr = (unsigned char *) __brkval; // Repeated M100 with no sub-command will not destroy the
  168. SERIAL_ECHOPAIR("\n__brkval : ",(long) ptr ); // state of the initialized free memory pool.
  169. ptr += 8;
  170. sp = top_of_stack();
  171. SERIAL_ECHOPAIR("\nStack Pointer : ",(long) sp );
  172. SERIAL_ECHOLNPGM("\n");
  173. n = sp - ptr - 64; // -64 just to keep us from finding interrupt activity that
  174. // has altered the stack.
  175. SERIAL_ECHO( n );
  176. SERIAL_ECHOLNPGM(" bytes of memory initialized.\n");
  177. for(i=0; i<n; i++)
  178. *(ptr+i) = (unsigned char) 0xe5;
  179. for(i=0; i<n; i++) {
  180. if ( *(ptr+i) != (unsigned char) 0xe5 ) {
  181. SERIAL_ECHOPAIR("? address : ", (unsigned long) ptr+i );
  182. SERIAL_ECHOPAIR("=", *(ptr+i) );
  183. SERIAL_ECHOLNPGM("\n");
  184. }
  185. }
  186. m100_not_initialized = 0;
  187. SERIAL_ECHOLNPGM("Done.\n");
  188. return;
  189. }
  190. return;
  191. }
  192. // top_of_stack() returns the location of a variable on its stack frame. The value returned is above
  193. // the stack once the function returns to the caller.
  194. unsigned char *top_of_stack() {
  195. unsigned char x;
  196. return &x + 1; // x is pulled on return;
  197. }
  198. //
  199. // 3 support routines to print hex numbers. We can print a nibble, byte and word
  200. //
  201. void prt_hex_nibble( unsigned int n )
  202. {
  203. if ( n <= 9 )
  204. SERIAL_ECHO(n);
  205. else
  206. SERIAL_ECHO( (char) ('A'+n-10) );
  207. delay(2);
  208. }
  209. void prt_hex_byte(unsigned int b)
  210. {
  211. prt_hex_nibble( ( b & 0xf0 ) >> 4 );
  212. prt_hex_nibble( b & 0x0f );
  213. }
  214. void prt_hex_word(unsigned int w)
  215. {
  216. prt_hex_byte( ( w & 0xff00 ) >> 8 );
  217. prt_hex_byte( w & 0x0ff );
  218. }
  219. // how_many_E5s_are_here() is a utility function to easily find out how many 0xE5's are
  220. // at the specified location. Having this logic as a function simplifies the search code.
  221. //
  222. int how_many_E5s_are_here( unsigned char *p)
  223. {
  224. int n;
  225. for(n=0; n<32000; n++) {
  226. if ( *(p+n) != (unsigned char) 0xe5)
  227. return n-1;
  228. }
  229. return -1;
  230. }
  231. #endif