diff --git a/cmd_parser.c b/cmd_parser.c index bfa75b06..c3de5ebe 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -5,6 +5,8 @@ // #define DEBUG // #define REGDBG 1 +#include <8051.h> +#include "cmd_parser.h" #include "rtl837x_common.h" #include "rtl837x_port.h" #include "rtl837x_flash.h" @@ -1847,6 +1849,8 @@ void cmd_parser(void) __banked } } else if (cmd_compare(0, "stp")) { stp_parse(); + } else if (cmd_compare(0, "health")) { + health_show(); } else if (cmd_compare(0, "pvid")) { if (cmd_words_len == 3 && cmd_parse_port_separator(cmd_words_b[1]) != 0 && atoi_short(cmd_words_b[2]) && atoi_results_short && atoi_results_short <= 4094) @@ -2045,3 +2049,158 @@ void execute_commands(__xdata uint8_t *p) __banked { p++; }; } + +/* + * Health instrumentation. The counters live in xdata and are fed from the + * main loop through the banked entry points below; the "health" command + * prints everything in one go. All figures are raw hex, diffing two dumps + * gives the rates. Phase times are in system ticks (SYS_TICK_HZ = 200, + * 5 ms each); a phase counts as slow when it spans 2 or more ticks. + */ +extern __xdata uint16_t len_left; /* httpd: bytes still to send of the current file */ +extern __xdata uint8_t entry; /* httpd: index of the file being sent */ + +static __xdata uint32_t health_loops; +__xdata uint16_t health_rx_frames; + +static __xdata uint16_t ph_last_t; +static __xdata uint8_t ph_max[HEALTH_PHASES]; +static __xdata uint16_t ph_slow[HEALTH_PHASES]; + +/* The ISR increments ticks between the two byte reads of a 16-bit load, so + * read until two loads agree. */ +static uint16_t ticks_lo(void) +{ + uint16_t a, b; + a = (uint16_t)ticks; + do { + b = a; + a = (uint16_t)ticks; + } while (a != b); + return a; +} + + +void health_loop_start(void) __banked +{ + health_loops++; + ph_last_t = ticks_lo(); +} + + +void health_phase(uint8_t id) __banked +{ + __xdata uint16_t now = ticks_lo(); + __xdata uint16_t d = now - ph_last_t; + ph_last_t = now; + if (d > 0xff) + d = 0xff; + if ((uint8_t)d > ph_max[id]) + ph_max[id] = d; + if (d >= 2) + ph_slow[id]++; +} + + +/* Fill the stack area above the current frame with a pattern; the dump scans + * for how much of it survived. The 8051 stack grows upward, so everything + * above SP is free at this point. Interrupts are held off so an ISR does not + * push into the area mid-paint. */ +void health_stack_paint(void) __banked +{ + __xdata uint8_t from = SP + 4; + __xdata uint8_t ea = EA; + EA = 0; + for (uint8_t i = 0xff; i >= from; i--) + *(__idata uint8_t *)i = 0xa5; + EA = ea; +} + + +static void ph_line(__code const char *name, uint8_t id) +{ + print_string(name); + print_string(" max "); + print_byte(ph_max[id]); + print_string(" slow "); + print_short(ph_slow[id]); + write_char('\n'); +} + + +void health_show(void) __banked +{ + __xdata uint8_t i; + + print_string("up "); + reg_read_m(RTL837X_REG_SEC_COUNTER); + print_byte(sfr_data[0]); print_byte(sfr_data[1]); + print_byte(sfr_data[2]); print_byte(sfr_data[3]); + print_string(" ticks "); + print_long(ticks); + print_string(" loops "); + print_long(health_loops); + print_string(" rx "); + print_short(health_rx_frames); + write_char('\n'); + + /* Phase timing: max duration seen (ticks of 5 ms) and slow-pass count */ + ph_line("link", HEALTH_PH_LINK); + ph_line("sfp ", HEALTH_PH_SFP); + ph_line("rx ", HEALTH_PH_RX); + ph_line("tx ", HEALTH_PH_TX); + ph_line("stp ", HEALTH_PH_STP); + ph_line("cmd ", HEALTH_PH_CMD); + + /* Stack: current depth plus how much of the painted area is untouched */ + print_string("sp "); + print_byte(SP); + print_string(" untouched "); + i = 0; + while (*(__idata uint8_t *)(0xff - i) == 0xa5 && i < 0x86) + i++; + print_byte(i); + write_char('\n'); + + /* The TCP connection slots (a single one on this configuration) */ + for (i = 0; i < UIP_CONNS; i++) { + print_string("tcp "); + if (UIP_CONNS > 1) { + print_byte(i); + write_char(' '); + } + print_string("st "); + print_byte(uip_conns[i].tcpstateflags); + print_string(" tmr "); + print_byte(uip_conns[i].timer); + print_string(" rtx "); + print_byte(uip_conns[i].nrtx); + print_string(" rto "); + print_byte(uip_conns[i].rto); + print_string(" len "); + print_short(uip_conns[i].len); + print_string(" mss "); + print_short(uip_conns[i].mss); + write_char('\n'); + print_string(" lport "); + print_short(HTONS(uip_conns[i].lport)); + print_string(" rport "); + print_short(HTONS(uip_conns[i].rport)); + print_string(" rip "); + print_short(HTONS(uip_conns[i].ripaddr[0])); + write_char(' '); + print_short(HTONS(uip_conns[i].ripaddr[1])); + write_char('\n'); + } + + /* httpd transfer state and the protocol flags */ + print_string("httpd left "); + print_short(len_left); + print_string(" entry "); + print_byte(entry); + print_string(" stp "); + print_byte(stp_enabled); + print_string(" mvlan "); + print_short(management_vlan); + write_char('\n'); +} diff --git a/cmd_parser.h b/cmd_parser.h index 7564b458..9f140135 100644 --- a/cmd_parser.h +++ b/cmd_parser.h @@ -9,6 +9,22 @@ extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE]; extern __xdata uint8_t cmd_available; extern __xdata uint8_t err_status; +/* Phase ids of the health instrumentation, in main-loop order */ +#define HEALTH_PH_LINK 0 +#define HEALTH_PH_SFP 1 +#define HEALTH_PH_RX 2 +#define HEALTH_PH_TX 3 +#define HEALTH_PH_STP 4 +#define HEALTH_PH_CMD 5 +#define HEALTH_PHASES 6 + +extern __xdata uint16_t health_rx_frames; + +void health_loop_start(void) __banked; /* once per idle() pass, resets the phase baseline */ +void health_phase(uint8_t id) __banked; /* after each phase; accounts ticks since last checkpoint */ +void health_stack_paint(void) __banked; /* paint the unused stack area once at boot */ +void health_show(void) __banked; /* "health" CLI dump */ + void cmd_tokenize(void) __banked; void cmd_parser(void) __banked; void execute_config(void) __banked; diff --git a/rtlplayground.c b/rtlplayground.c index 6b0bf2df..79a3d0b9 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -1004,6 +1004,7 @@ void handle_rx(void) REG_SET(RTL837X_REG_NIC_RXCMD, 1); return; } + health_rx_frames++; #ifdef RXTXDBG print_string("\n<< "); @@ -1165,6 +1166,7 @@ void idle(void) reg_read(RTL837X_REG_NIC_RX_BUFF_DATA); if (!SFR_DATA_U16) PCON |= 1; + health_loop_start(); if (sec_counter >= SYS_TICK_HZ) { sec_counter -= SYS_TICK_HZ; reg_read_m(RTL837X_REG_SEC_COUNTER); @@ -1240,13 +1242,18 @@ void idle(void) } } + health_phase(HEALTH_PH_LINK); + // Check for changes with SFP modules handle_sfp(); + health_phase(HEALTH_PH_SFP); // Check new Packets RX handle_rx(); + health_phase(HEALTH_PH_RX); // Check UIP for packets to transmit handle_tx(); + health_phase(HEALTH_PH_TX); // If STP protocol enabled, decrease STP timers to trigger actions if (stp_enabled) { if (!stp_clock) { @@ -1256,6 +1263,7 @@ void idle(void) stp_clock--; } } + health_phase(HEALTH_PH_STP); // Check whether a command is waiting in the cmd_buffer and execute if (cmd_available) { cmd_available = 0; @@ -1264,6 +1272,7 @@ void idle(void) cmd_parser(); print_cmd_prompt(); } + health_phase(HEALTH_PH_CMD); } @@ -1758,6 +1767,7 @@ void main(void) cmd_editor_init(); + health_stack_paint(); while (1) { cmd_edit(); idle(); // Enter Idle mode until interrupt occurs