diff --git a/src/Makefile b/src/Makefile index 6740485f4e4..400a71c772e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -25,6 +25,7 @@ ifeq ($(ENABLE_DEBUG), 1) CFLAGS += -DENABLE_DEBUG endif + SRC = \ adiv5.c \ adiv5_jtag.c \ @@ -132,6 +133,16 @@ CFLAGS += -DPC_HOSTED=0 include platforms/common/Makefile.inc endif +ifdef ENABLE_MEMWATCH +CFLAGS += -DENABLE_MEMWATCH=$(ENABLE_MEMWATCH) +SRC += memwatch.c +ifndef PC_HOSTED +ifeq ($(ENABLE_MEMWATCH), 1) +SRC += ftoa.c +endif +endif +endif + ifeq ($(ENABLE_RTT), 1) CFLAGS += -DENABLE_RTT SRC += rtt.c rtt_if.c diff --git a/src/ftoa.c b/src/ftoa.c new file mode 100644 index 00000000000..7b11f9ec464 --- /dev/null +++ b/src/ftoa.c @@ -0,0 +1,408 @@ +#include "ftoa.h" +#include +#include + +/* Convert IEEE single precison numbers into decimal ASCII strings, while + satisfying the following two properties: + 1) Calling strtof or '(float) strtod' on the result must produce the + original float, independent of the rounding mode used by strtof/strtod. + 2) Minimize the number of produced decimal digits. E.g. the float 0.7f + should convert to "0.7", not "0.69999999". + + To solve this we use a dedicated single precision version of + Florian Loitsch's Grisu2 algorithm. See + http://florian.loitsch.com/publications/dtoa-pldi2010.pdf?attredirects=0 + + The code below is derived from Loitsch's C code, which + implements the same algorithm for IEEE double precision. See + http://florian.loitsch.com/publications/bench.tar.gz?attredirects=0 + + Adapted for 32-bit float by Peter Barfuss(bofh453) + Subnormal numbers by Koen De Vleeschauwer +*/ + +#define DIY_SIGNIFICAND_SIZE 64 +#define SP_SIGNIFICAND_MASK 0x7fffff +#define SP_HIDDEN_BIT 0x800000 /* 2^23 */ + +#define OUTCHAR(ch) \ + { \ + if (len < size) \ + str[len++] = ch; \ + } + +typedef union _f32 { + float f; + unsigned int i; +} _f32; + +#if defined(__x86_64__) || defined(__amd64__) +static uint64_t multiply(uint64_t x, uint32_t y) +{ + uint64_t y0 = ((uint64_t)y << 32), ac, tmp; + __asm__ __volatile__("mulq %3" : "=a"(tmp), "=d"(ac) : "%0"(x), "rm"(y0)); + // tmp += 0x80000000; /* Round. */ + return ac + (tmp >> 63); +} +#else +static uint64_t multiply(uint64_t x, uint32_t y) +{ + uint64_t xlo = (x & 0xffffffff); + uint64_t xhi = (x >> 32); + return ((xhi * y) + ((xlo * y) >> 31)); +} +#endif + +static int k_comp(int n) +{ + /* k = n * log(2); rational approximation using continuous fractions */ + int32_t k = (int32_t)n * 97879 / 325147; + return n < 0 ? k - 1 : k; +} + +/* Cached powers of ten from 10**-37..10**45. + Scaled so the leftmost bit is 1. */ + +/* Significands. */ +static uint64_t powers_ten[84] = { + 0x881cea14545c7575, + 0xaa242499697392d3, + 0xd4ad2dbfc3d07788, + 0x84ec3c97da624ab5, + 0xa6274bbdd0fadd62, + 0xcfb11ead453994ba, + 0x81ceb32c4b43fcf5, + 0xa2425ff75e14fc32, + 0xcad2f7f5359a3b3e, + 0xfd87b5f28300ca0e, + 0x9e74d1b791e07e48, + 0xc612062576589ddb, + 0xf79687aed3eec551, + 0x9abe14cd44753b53, + 0xc16d9a0095928a27, + 0xf1c90080baf72cb1, + 0x971da05074da7bef, + 0xbce5086492111aeb, + 0xec1e4a7db69561a5, + 0x9392ee8e921d5d07, + 0xb877aa3236a4b449, + 0xe69594bec44de15b, + 0x901d7cf73ab0acd9, + 0xb424dc35095cd80f, + 0xe12e13424bb40e13, + 0x8cbccc096f5088cc, + 0xafebff0bcb24aaff, + 0xdbe6fecebdedd5bf, + 0x89705f4136b4a597, + 0xabcc77118461cefd, + 0xd6bf94d5e57a42bc, + 0x8637bd05af6c69b6, + 0xa7c5ac471b478423, + 0xd1b71758e219652c, + 0x83126e978d4fdf3b, + 0xa3d70a3d70a3d70a, + 0xcccccccccccccccd, + 0x8000000000000000, + 0xa000000000000000, + 0xc800000000000000, + 0xfa00000000000000, + 0x9c40000000000000, + 0xc350000000000000, + 0xf424000000000000, + 0x9896800000000000, + 0xbebc200000000000, + 0xee6b280000000000, + 0x9502f90000000000, + 0xba43b74000000000, + 0xe8d4a51000000000, + 0x9184e72a00000000, + 0xb5e620f480000000, + 0xe35fa931a0000000, + 0x8e1bc9bf04000000, + 0xb1a2bc2ec5000000, + 0xde0b6b3a76400000, + 0x8ac7230489e80000, + 0xad78ebc5ac620000, + 0xd8d726b7177a8000, + 0x878678326eac9000, + 0xa968163f0a57b400, + 0xd3c21bcecceda100, + 0x84595161401484a0, + 0xa56fa5b99019a5c8, + 0xcecb8f27f4200f3a, + 0x813f3978f8940984, + 0xa18f07d736b90be5, + 0xc9f2c9cd04674edf, + 0xfc6f7c4045812296, + 0x9dc5ada82b70b59e, + 0xc5371912364ce305, + 0xf684df56c3e01bc7, + 0x9a130b963a6c115c, + 0xc097ce7bc90715b3, + 0xf0bdc21abb48db20, + 0x96769950b50d88f4, + 0xbc143fa4e250eb31, + 0xeb194f8e1ae525fd, + 0x92efd1b8d0cf37be, + 0xb7abc627050305ae, + 0xe596b7b0c643c719, + 0x8f7e32ce7bea5c70, + 0xb35dbf821ae4f38c, + 0xe0352f62a19e306f, +}; + +/* Exponents. */ +static int16_t powers_ten_e[84] = { + -127, + -124, + -121, + -117, + -114, + -111, + -107, + -104, + -101, + -98, + -94, + -91, + -88, + -84, + -81, + -78, + -74, + -71, + -68, + -64, + -61, + -58, + -54, + -51, + -48, + -44, + -41, + -38, + -34, + -31, + -28, + -24, + -21, + -18, + -14, + -11, + -8, + -4, + -1, + 2, + 5, + 9, + 12, + 15, + 19, + 22, + 25, + 29, + 32, + 35, + 39, + 42, + 45, + 49, + 52, + 55, + 59, + 62, + 65, + 69, + 72, + 75, + 79, + 82, + 85, + 89, + 92, + 95, + 98, + 102, + 105, + 108, + 112, + 115, + 118, + 122, + 125, + 128, + 132, + 135, + 138, + 142, + 145, + 148, +}; + +/* + * compute decimal integer m, exp such that: + * f = m*10^exp + * m is as short as possible without losing exactness + */ +uint32_t ftoa(char *str, size_t size, float f) +{ + uint32_t w_lower, w_upper; + uint64_t D_upper, D_lower, delta, c_mk, one, p2; + _f32 f2; + int ve = 0, mk = 0, kabs = 0; + unsigned int len = 0; + unsigned char digit, p1; + char *msg = NULL; + + /* handle negative numbers */ + if (f < 0) { + f = -f; + OUTCHAR('-'); + } + + /* handle nan, infinity, and zero. */ + f2.f = f; + uint32_t w = f2.i & 0x7fffffffU; + if (w == 0U) /* zero */ + msg = "0"; + else if (w < 0x800000U) { /* subnormal number */ + ve = -126 - 1; + f2.i = (f2.i & SP_SIGNIFICAND_MASK); + /* normalize */ + if (f2.i != 0U) /* safe */ + while (!(f2.i & (SP_HIDDEN_BIT >> 1U))) { + f2.i <<= 1; + ve--; + } + } else if (w >= 0x7f800000U) { /* not a number, infinity */ + msg = w > 0x7f800000U ? "nan" : "inf"; + } else { /* normal number */ + ve = (f2.i >> 23) - 127 - 1; + f2.i = ((f2.i & SP_SIGNIFICAND_MASK) | SP_HIDDEN_BIT); + } + + if (msg) { + char ch; + while (1) { + ch = *msg++; + if (ch == 0) + break; + OUTCHAR(ch); + } + if (len < size) + str[len] = 0; + return len; + } + + w_upper = (f2.i << 2) + 2; + w_lower = (f2.i << 2) - 1; + if (f2.i != SP_HIDDEN_BIT) { + w_lower--; + } + w_upper <<= (DIY_SIGNIFICAND_SIZE - 58); + w_lower <<= (DIY_SIGNIFICAND_SIZE - 58); + + mk = k_comp(ve - 1); + int32_t idx = 37 - mk; + if ((idx < 0) || (idx >= (int32_t)(sizeof(powers_ten) / sizeof(powers_ten[0])))) { + /* index out of range, table too small? */ + OUTCHAR('?'); + if (len < size) + str[len] = 0; + return len; + } + ve = ve + powers_ten_e[idx] - DIY_SIGNIFICAND_SIZE + 7; + one = ((uint64_t)1 << -ve) - 1; + + c_mk = powers_ten[idx]; + D_upper = multiply(c_mk, w_upper); + D_lower = multiply(c_mk, w_lower); + + D_upper--; + D_lower++; + + delta = (D_upper - D_lower); + p1 = D_upper >> -ve; + p2 = D_upper & one; + + digit = p1 / 10; + if (digit) + mk++; + + /* decide when to print in decimal or scientific notation */ + bool f_format = (mk < 6) && (mk > -5); + if (f_format) { + /* print in decimal notation */ + int32_t pos = mk; + /* print leading zeroes */ + if (mk < 0) { + for (int i = 0; i <= -mk; i++) + OUTCHAR("0.000"[i]); + pos = mk; /* decimal point already printed */ + } + if (digit) { + OUTCHAR('0' + digit); + if (pos == 0) + OUTCHAR('.'); + pos--; + } + p1 %= 10; + OUTCHAR('0' + p1); + if (pos == 0) + OUTCHAR('.'); + pos--; + do { + p2 *= 10; + OUTCHAR('0' + (p2 >> -ve)); + if (pos == 0) + OUTCHAR('.'); + pos--; + p2 &= one; + delta *= 10; + } while (p2 > delta); + + /* trailing zeroes */ + if (mk > 0) + while (pos > -1) { + OUTCHAR('0'); + if (pos == 0) + OUTCHAR('.'); + pos--; + } + + if (len < size) + str[len] = 0; + return len; + } + + /* print in scientific notation */ + if (digit) { + OUTCHAR('0' + digit); + OUTCHAR('.'); + } + p1 %= 10; + OUTCHAR('0' + p1); + if (!digit) + OUTCHAR('.'); + do { + p2 *= 10; + OUTCHAR('0' + (p2 >> -ve)); + p2 &= one; + delta *= 10; + } while (p2 > delta); + + OUTCHAR('e'); + if (mk < 0) { + OUTCHAR('-'); + kabs = -mk; + } else { + OUTCHAR('+'); + kabs = mk; + } + OUTCHAR('0' + (kabs / 10)); + OUTCHAR('0' + (kabs % 10)); + if (len < size) + str[len] = 0; + + return len; +} diff --git a/src/ftoa.h b/src/ftoa.h new file mode 100644 index 00000000000..855194eacce --- /dev/null +++ b/src/ftoa.h @@ -0,0 +1,6 @@ +#ifndef FTOA_H +#define FTOA_H +#include +#include +uint32_t ftoa(char *s, size_t size, float f); +#endif diff --git a/src/include/memwatch.h b/src/include/memwatch.h new file mode 100644 index 00000000000..ad04107e1ce --- /dev/null +++ b/src/include/memwatch.h @@ -0,0 +1,31 @@ +#ifndef MEMWATCH_H +#define MEMWATCH_H + +#include +#include +#include + +#define MEMWATCH_NUM 8 +/* string length has to be long enough to store an address 0x20000000 */ +#define MEMWATCH_STRLEN 12 + +typedef enum memwatch_format { + MEMWATCH_FMT_SIGNED, + MEMWATCH_FMT_UNSIGNED, + MEMWATCH_FMT_FLOAT, + MEMWATCH_FMT_HEX +} memwatch_format_e; + +typedef struct { + uint32_t addr; + uint32_t value; + char name[MEMWATCH_STRLEN]; + memwatch_format_e format; +} memwatch_s; + +extern memwatch_s memwatch_table[MEMWATCH_NUM]; +extern uint32_t memwatch_cnt; +extern bool memwatch_timestamp; +extern void poll_memwatch(target_s *cur_target); + +#endif diff --git a/src/main.c b/src/main.c index 496cfd02098..ec7d42ecb33 100644 --- a/src/main.c +++ b/src/main.c @@ -29,6 +29,7 @@ #include "gdb_packet.h" #include "morse.h" #include "command.h" +#include "memwatch.h" #ifdef ENABLE_RTT #include "rtt.h" #endif @@ -58,6 +59,10 @@ static void bmp_poll_loop(void) #ifdef ENABLE_RTT if (rtt_enabled) poll_rtt(cur_target); +#endif +#ifdef ENABLE_MEMWATCH + if (memwatch_cnt != 0) + poll_memwatch(cur_target); #endif } diff --git a/src/memwatch.c b/src/memwatch.c new file mode 100644 index 00000000000..7ac6fa3d645 --- /dev/null +++ b/src/memwatch.c @@ -0,0 +1,84 @@ +#include "general.h" +#include "gdb_packet.h" +#include "memwatch.h" +#ifdef ENABLE_RTT +#include "rtt_if.h" +#endif +#if PC_HOSTED == 1 +#include +#else +#include "usb_serial.h" +#include "ftoa.h" +#endif + +memwatch_s memwatch_table[MEMWATCH_NUM]; +uint32_t memwatch_cnt = 0; +bool memwatch_timestamp = false; + +#ifndef ENABLE_RTT +static uint32_t rtt_write(const char *buf, uint32_t len) +{ +#if PC_HOSTED == 1 + return write(1, buf, len); +#else + uint32_t start_ms = platform_time_ms(); + while (usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, buf, len) <= 0) { + if (platform_time_ms() - start_ms >= 25) + return 0; /* drop silently */ + } + return len; +#endif +} +#endif + +void poll_memwatch(target_s *cur_target) +{ + union val32_u { + uint32_t i; + volatile float f; + } val; + + char buf[64]; + char timestamp[64]; + uint32_t len; + if (!cur_target || (memwatch_cnt == 0)) + return; + + for (uint32_t i = 0; i < memwatch_cnt; i++) { + if (!target_mem_read(cur_target, &val.i, memwatch_table[i].addr, sizeof(val.i)) && + (val.i != memwatch_table[i].value)) { + if (memwatch_timestamp) + snprintf(timestamp, sizeof(timestamp), "%" PRIu32 " ", platform_time_ms()); + else + timestamp[0] = '\0'; + switch (memwatch_table[i].format) { + case MEMWATCH_FMT_SIGNED: + len = snprintf(buf, sizeof(buf), "%s%s %" PRId32 "\r\n", timestamp, memwatch_table[i].name, val.i); + break; + case MEMWATCH_FMT_UNSIGNED: + len = snprintf(buf, sizeof(buf), "%s%s %" PRIu32 "\r\n", timestamp, memwatch_table[i].name, val.i); + break; + case MEMWATCH_FMT_FLOAT: +#if ENABLE_MEMWATCH == 1 +#if PC_HOSTED == 1 + len = snprintf(buf, sizeof(buf), "%s%s %g\r\n", timestamp, memwatch_table[i].name, val.f); +#else + char fbuf[32]; + ftoa(fbuf, sizeof(fbuf), val.f); + fbuf[sizeof(fbuf) - 1] = '\0'; + len = snprintf(buf, sizeof(buf), "%s%s %s\r\n", timestamp, memwatch_table[i].name, fbuf); +#endif + break; +#endif + case MEMWATCH_FMT_HEX: + default: + len = snprintf(buf, sizeof(buf), "%s%s 0x%" PRIx32 "\r\n", timestamp, memwatch_table[i].name, val.i); + break; + } + buf[sizeof(buf) - 1] = '\0'; + rtt_write(buf, len); + memwatch_table[i].value = val.i; + } + } + return; +} diff --git a/src/target/cortexm.c b/src/target/cortexm.c index a944db1af66..cb60cb2ac2d 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -41,6 +41,7 @@ #include "command.h" #include "gdb_packet.h" #include "semihosting.h" +#include "memwatch.h" #include "platform.h" #include @@ -53,6 +54,7 @@ #include #include #include +#include #if PC_HOSTED == 1 @@ -72,12 +74,18 @@ #endif static bool cortexm_vector_catch(target_s *target, int argc, const char **argv); +#ifdef ENABLE_MEMWATCH +static bool cortexm_memwatch(target_s *target, int argc, const char **argv); +#endif #if PC_HOSTED == 0 static bool cortexm_redirect_stdout(target_s *target, int argc, const char **argv); #endif const command_s cortexm_cmd_list[] = { {"vector_catch", cortexm_vector_catch, "Catch exception vectors"}, +#ifdef ENABLE_MEMWATCH + {"memwatch", cortexm_memwatch, "Read memory while target running: [/t] [[NAME] [/d|/u|/f|/x] ADDRESS]..."}, +#endif #if PC_HOSTED == 0 {"redirect_stdout", cortexm_redirect_stdout, "Redirect semihosting stdout to USB UART"}, #endif @@ -1387,6 +1395,74 @@ static bool cortexm_vector_catch(target_s *target, int argc, const char **argv) return true; } +#ifdef ENABLE_MEMWATCH +static bool cortexm_memwatch(target_s *target, int argc, const char **argv) +{ + memwatch_format_e fmt = MEMWATCH_FMT_HEX; + char name[MEMWATCH_STRLEN] = {0}; + memset(memwatch_table, 0, sizeof(memwatch_table)); + memwatch_cnt = 0; + memwatch_timestamp = false; + /* target has to support debugger memory access while running */ + if (target_mem_access_needs_halt(target)) { + gdb_out("memwatch not supported for target\n"); + return true; + } + for (int32_t i = 1; i < argc; i++) { + if (argv[i][0] == '/') { + /* format follows */ + switch (argv[i][1]) { + case 'd': + fmt = MEMWATCH_FMT_SIGNED; + break; + case 'u': + fmt = MEMWATCH_FMT_UNSIGNED; + break; + case 'f': + fmt = MEMWATCH_FMT_FLOAT; + break; + case 'x': + fmt = MEMWATCH_FMT_HEX; + break; + case 't': + memwatch_timestamp = true; + break; + default: + break; + } + } else if (isalpha(argv[i][0])) { + /* name follows */ + strncpy(name, argv[i], sizeof(name)); + name[sizeof(name) - 1] = '\0'; + } else if (isdigit(argv[i][0])) { + /* address follows */ + uint32_t addr = strtoul(argv[i], NULL, 0); + + /* add new name, format and address to memwatch table */ + if (name[0] == '\0') { + /* no name given, use address as name */ + snprintf(name, MEMWATCH_STRLEN, "0x%08" PRIx32, addr); + name[MEMWATCH_STRLEN - 1] = '\0'; + } + memwatch_table[memwatch_cnt].addr = addr; + memwatch_table[memwatch_cnt].value = 0xdeadbeef; + memwatch_table[memwatch_cnt].format = fmt; + memcpy(memwatch_table[memwatch_cnt].name, name, MEMWATCH_STRLEN); + memwatch_cnt++; + memset(name, 0, sizeof(name)); + gdb_outf("0x%08" PRIx32 " ", addr); + if (memwatch_cnt == MEMWATCH_NUM) + break; + } else { + gdb_outf("?"); + break; + } + } + gdb_outf("\n"); + return true; +} +#endif + #if PC_HOSTED == 0 static bool cortexm_redirect_stdout(target_s *target, int argc, const char **argv) {