From 4f53d4255cd88c35ea9c0b473c1469297ea4b159 Mon Sep 17 00:00:00 2001 From: Peter Wittich Date: Wed, 15 Jul 2020 12:34:03 -0400 Subject: [PATCH] Power supply state machine clean-up and debugging code (#62) * Rewrite of PowerSupplyTask to clean it up * Code to understand behavior of LGA80D power supply modules. note that the LGA80D power supply module code needs a mod to the 3.3V I2C lines on the board and will not work with an un-modded board. --- common/power_ctl.c | 225 +++++++++++++----------- common/power_ctl.h | 19 +- common/smbus_units.c | 29 +++- common/utils.c | 16 +- projects/boot_loader/bl_config.h | 2 +- projects/cm_mcu/CommandLineTask.c | 214 ++++++++++++++++------- projects/cm_mcu/FireFlyTask.c | 6 +- projects/cm_mcu/FreeRTOSConfig.h | 2 +- projects/cm_mcu/I2CSlaveTask.c | 10 +- projects/cm_mcu/LocalTasks.c | 145 +++++++++++++++- projects/cm_mcu/MonitorTask.c | 22 ++- projects/cm_mcu/MonitorTask.h | 6 +- projects/cm_mcu/PowerSupplyTask.c | 280 +++++++++++++++++++++++------- projects/cm_mcu/Tasks.h | 3 + projects/cm_mcu/cm_mcu.c | 1 + projects/cm_mcu/startup_gcc.c | 2 +- 16 files changed, 728 insertions(+), 254 deletions(-) diff --git a/common/power_ctl.c b/common/power_ctl.c index 161b87ee..3fd9f8f9 100644 --- a/common/power_ctl.c +++ b/common/power_ctl.c @@ -32,48 +32,46 @@ void Print(const char* ); // needs to be implemented in each project // if you update this you need to update N_PS_ENABLES static const struct gpio_pin_t enables[] = { - { CTRL_K_VCCINT_PWR_EN, 1}, - { CTRL_V_VCCINT_PWR_EN, 1}, - { CTRL_VCC_1V8_PWR_EN, 2}, - { CTRL_VCC_3V3_PWR_EN, 2}, - { CTRL_V_MGTY1_VCCAUX_PWR_EN, 3}, - { CTRL_V_MGTY2_VCCAUX_PWR_EN, 3}, - { CTRL_K_MGTY_VCCAUX_PWR_EN, 3}, - { CTRL_K_MGTH_VCCAUX_PWR_EN, 3}, - { CTRL_V_MGTY1_AVCC_PWR_EN, 4}, - { CTRL_V_MGTY2_AVCC_PWR_EN, 4}, - { CTRL_K_MGTY_AVCC_PWR_EN, 4}, - { CTRL_K_MGTH_AVCC_PWR_EN, 4}, // this one is broken on S/N 001 - { CTRL_K_MGTY_AVTT_PWR_EN, 5}, - { CTRL_K_MGTH_AVTT_PWR_EN, 5}, - { CTRL_V_MGTY1_AVTT_PWR_EN, 5}, - { CTRL_V_MGTY2_AVTT_PWR_EN, 5} + { CTRL_K_VCCINT_PWR_EN, 1}, + { CTRL_V_VCCINT_PWR_EN, 1}, + { CTRL_VCC_1V8_PWR_EN, 2}, + { CTRL_VCC_3V3_PWR_EN, 2}, + { CTRL_V_MGTY1_VCCAUX_PWR_EN, 3}, + { CTRL_V_MGTY2_VCCAUX_PWR_EN, 3}, + { CTRL_K_MGTY_VCCAUX_PWR_EN, 3}, + { CTRL_K_MGTH_VCCAUX_PWR_EN, 3}, + { CTRL_V_MGTY1_AVCC_PWR_EN, 4}, + { CTRL_V_MGTY2_AVCC_PWR_EN, 4}, + { CTRL_K_MGTY_AVCC_PWR_EN, 4}, + { CTRL_K_MGTH_AVCC_PWR_EN, 4}, // this one is broken on S/N 001 + { CTRL_K_MGTY_AVTT_PWR_EN, 5}, + { CTRL_K_MGTH_AVTT_PWR_EN, 5}, + { CTRL_V_MGTY1_AVTT_PWR_EN, 5}, + { CTRL_V_MGTY2_AVTT_PWR_EN, 5} }; //if you update this you need to update N_PS_OKS too const struct gpio_pin_t oks[] = { - { K_VCCINT_PG_A, 1}, - { K_VCCINT_PG_B, 1}, - { V_VCCINT_PG_A, 1}, - { V_VCCINT_PG_B, 1}, - { VCC_1V8_PG, 2}, - { VCC_3V3_PG, 2}, - { V_MGTY1_AVCC_OK, 4}, - { V_MGTY2_AVCC_OK, 4}, - { K_MGTY_AVCC_OK, 4}, - { K_MGTH_AVCC_OK, 4}, - { K_MGTY_AVTT_OK, 5}, - { K_MGTH_AVTT_OK, 5}, - { V_MGTY1_AVTT_OK, 5}, - { V_MGTY2_AVTT_OK, 5} + { K_VCCINT_PG_A, 1}, + { K_VCCINT_PG_B, 1}, + { V_VCCINT_PG_A, 1}, + { V_VCCINT_PG_B, 1}, + { VCC_1V8_PG, 2}, + { VCC_3V3_PG, 2}, + { V_MGTY1_AVCC_OK, 4}, + { V_MGTY2_AVCC_OK, 4}, + { K_MGTY_AVCC_OK, 4}, + { K_MGTH_AVCC_OK, 4}, + { K_MGTY_AVTT_OK, 5}, + { K_MGTH_AVTT_OK, 5}, + { V_MGTY1_AVTT_OK, 5}, + { V_MGTY2_AVTT_OK, 5} }; const int num_priorities = 5; -// these arrays hold the current and old status of these power supplies -static enum ps_state new_states[N_PS_OKS] = { PWR_UNKNOWN }; -static enum ps_state states[N_PS_OKS] = { PWR_UNKNOWN }; - +//static enum ps_state new_states[N_PS_OKS] = { PWR_UNKNOWN }; +#ifdef NOTDEF // this variable holds the current lowest enabled power supply static int lowest_enabled_ps_prio = 0; @@ -81,67 +79,68 @@ int getLowestEnabledPSPriority() { return lowest_enabled_ps_prio; } +#endif // 0 - +// these arrays hold the current and old status of these power supplies +static enum ps_state states[N_PS_OKS] = { PWR_UNKNOWN }; enum ps_state getPSStatus(int i) { if ( i < 0 || i >= N_PS_OKS) return PWR_UNKNOWN; return states[i]; } -#if 0 + void setPSStatus(int i, enum ps_state theState) { if ( i < 0 || i >= N_PS_OKS) return; states[i] = theState; } -#endif - +#ifdef NOTDEF bool update_failed_ps(int prio){ - bool ku_enable = (read_gpio_pin(TM4C_DIP_SW_1) == 1); - bool vu_enable = (read_gpio_pin(TM4C_DIP_SW_2) == 1); - bool failure = false; - - for ( int o = 0; o < N_PS_OKS; ++o ) { - if ( oks[o].priority <= prio ) { - int8_t val = read_gpio_pin(oks[o].name); - if ( val == 0 ) { - // if this is a VU7P supply and dip switch says ignore it, continue - if (!vu_enable && (strncmp(pin_names[oks[o].name], "V_", 2) == 0) ) { - new_states[o] = PWR_DISABLED; - continue; - } - // ditto for KU15P - if ( !ku_enable && (strncmp(pin_names[oks[o].name], "K_", 2) == 0) ) { - new_states[o] = PWR_DISABLED; - continue; - } - // remember the VCC_ supplies - if ((states[o]==PWR_ON)||(states[o]==PWR_UNKNOWN)){ - new_states[o]=PWR_FAILED; - errbuffer_put(EBUF_PWR_FAILURE,o); - failure=true; - } - else { - new_states[o]=PWR_OFF; - } - } - else { - new_states[o] = PWR_ON; - } - } - } - memcpy(states, new_states, sizeof(states)); - return failure; -} + bool ku_enable = (read_gpio_pin(TM4C_DIP_SW_1) == 1); + bool vu_enable = (read_gpio_pin(TM4C_DIP_SW_2) == 1); + bool failure = false; + for ( int o = 0; o < N_PS_OKS; ++o ) { + if ( oks[o].priority <= prio ) { + int8_t val = read_gpio_pin(oks[o].name); + if ( val == 0 ) { + // if this is a VU7P supply and dip switch says ignore it, continue + if (!vu_enable && (strncmp(pin_names[oks[o].name], "V_", 2) == 0) ) { + new_states[o] = PWR_DISABLED; + continue; + } + // ditto for KU15P + if ( !ku_enable && (strncmp(pin_names[oks[o].name], "K_", 2) == 0) ) { + new_states[o] = PWR_DISABLED; + continue; + } + // remember the VCC_ supplies + if ((states[o]==PWR_ON)||(states[o]==PWR_UNKNOWN)){ + new_states[o]=PWR_FAILED; + errbuffer_put(EBUF_PWR_FAILURE,o); + failure=true; + } + else { + new_states[o]=PWR_OFF; + } + } + else { + new_states[o] = PWR_ON; + } + } + } + memcpy(states, new_states, sizeof(states)); + return failure; +} +#endif // 0 +#ifdef NOTDEF // // check the power supplies and turn them on one by one // Assert BLADE_POWER_OK if you are successful. // Return immediately if BLADE_POWER_EN is not asserted by the SM. bool set_ps() { - bool success = true; // return value // read two dip switches to see if we are powering either or both FPGAs bool ku_enable = (read_gpio_pin(TM4C_DIP_SW_1) == 1); bool vu_enable = (read_gpio_pin(TM4C_DIP_SW_2) == 1); @@ -150,14 +149,13 @@ bool set_ps() bool blade_power_en = (read_gpio_pin(BLADE_POWER_EN)==1); if ( ! blade_power_en ) { write_gpio_pin(BLADE_POWER_OK, 0x0); - success = false; - return success; + return false; } // loop over the enables for ( int prio = 1; prio <= num_priorities; ++prio ) { // enable the supplies at the relevant priority - lowest_enabled_ps_prio = prio; + //lowest_enabled_ps_prio = prio; for ( int e = 0; e < N_PS_ENABLES; ++e ) { if ( enables[e].priority == prio ) { // if the supply is for VU7P and dip switch says ignore it, continue @@ -174,12 +172,12 @@ bool set_ps() ShortDelay(); // check power good at this level or higher priority (lower number) - bool ps_failure = update_failed_ps(prio); + //bool ps_failure = update_failed_ps(prio); - // loop over 'ok' bits + // loop over 'ok' bits if ( ps_failure ) { - Print("set_ps: Power supply check failed. "); + Print("turn_on_ps: Power supply check failed. "); Print("Turning off all supplies at this level or lower.\r\n"); // turn off all supplies at current priority level or lower // that is probably overkill since they should not all be @@ -200,14 +198,10 @@ bool set_ps() break; } } // loop over priorities - - if ( success ) - write_gpio_pin(BLADE_POWER_OK, 0x1); - else - write_gpio_pin(BLADE_POWER_OK, 0x0); - return success; - + write_gpio_pin(BLADE_POWER_OK, 0x1); + return true; } + // check_ps(Void) // in this function we check the status of the supplies. The function returns // true if all supplies it expects to be good, are good. That means that if one @@ -252,8 +246,8 @@ check_ps(void) for ( int o = 0; o < N_PS_OKS; ++o ) { if ( (new_states[o] != states[o]) && (states[o] != PWR_UNKNOWN) && - (states[o] != PWR_DISABLED) - ) { + (states[o] != PWR_DISABLED)) { + errbuffer_put(EBUF_PWR_FAILURE,o); char tmp[128]; snprintf(tmp, 128, "check_ps: New failed supply %s (level %d)\r\n", pin_names[oks[o].name], oks[o].priority); @@ -268,7 +262,6 @@ check_ps(void) if ( states[o] == PWR_OFF ) { if ( oks[o].priority < min_good_prio) min_good_prio = oks[o].priority; - } } if ( ! success ) { @@ -286,7 +279,7 @@ check_ps(void) return success; } - +#endif // NOTDEF // turn off all power supplies in the proper order // de-assert BLADE_POWER_OK on successful exit. bool @@ -317,17 +310,17 @@ disable_ps(void) while ( ! ready_to_proceed ) { bool all_ready = true; for ( int o = 0; o < N_PS_OKS; ++o ) { - if ( oks[o].priority >= prio ) { - int8_t val = read_gpio_pin(oks[o].name); - if ( val == 1 ) { // all supplies are supposed to be off now - all_ready = false; - states[o] = PWR_UNKNOWN; - } - } - } // loop over 'ok' bits + if ( oks[o].priority >= prio ) { + int8_t val = read_gpio_pin(oks[o].name); + if ( val == 1 ) { // all supplies are supposed to be off now + all_ready = false; + states[o] = PWR_UNKNOWN; + } + } + } // loop over 'ok' bits if ( all_ready) ready_to_proceed = true; } - lowest_enabled_ps_prio = prio; + //lowest_enabled_ps_prio = prio; } // loop over priorities // turn off POWER_OK when we are done @@ -338,3 +331,33 @@ disable_ps(void) return success; } +// check the power supplies and turn them on one by one +// Assert BLADE_POWER_OK if you are successful. +// Return immediately if BLADE_POWER_EN is not asserted by the SM. +// Which supplies to enable is based on the ps_en_mask (passed in), +// which references entries in the enables[] array. +bool turn_on_ps(uint16_t ps_en_mask) +{ + // if blade_power_en is false, return with failure + bool blade_power_en = (read_gpio_pin(BLADE_POWER_EN)==1); + if ( ! blade_power_en ) { + write_gpio_pin(BLADE_POWER_OK, 0x0); + return false; + } + + // loop over the enables + for ( int prio = 1; prio <= num_priorities; ++prio ) { + // enable the supplies at the relevant priority + for ( int e = 0; e < N_PS_ENABLES; ++e ) { + if ( enables[e].priority == prio ) { + // check if this supply is to be enabled + if ( ((1U<= -1024) && (mantissa <= 1023)) { + break; // stop if mantissa valid + } + exponent++; + mantissa = (int)(input_val / pow(2.0, exponent)); + } while (exponent < 15); + // Format the exponent of the L11 + uint16_t uExponent = exponent << 11; // Format the mantissa of the L11 + uint16_t uMantissa = mantissa & 0x07FF; + // Compute value as exponent | mantissa + return uExponent | uMantissa; +} diff --git a/common/utils.c b/common/utils.c index 906ae9ce..b62ed5e3 100644 --- a/common/utils.c +++ b/common/utils.c @@ -140,7 +140,7 @@ const char* ebuf_errstrings[] = { "Buffer Reset", "Power Off - Manual", "Power Off - Temp High", - "Power On - Manual", + "Power On", "Temp Normal", "Hard fault", "Assertion failed", @@ -148,6 +148,7 @@ const char* ebuf_errstrings[] = { "(continue)", "Power Failure", "Temp High (TM4C FPGA FF DCDC)", + "Power Failure CLEAR", }; #define EBUF_N_ERRSTRINGS (sizeof(ebuf_errstrings)/sizeof(ebuf_errstrings[0])) @@ -165,7 +166,7 @@ int errbuffer_get_messagestr(const uint32_t word, char *m, size_t s ) uint16_t minutes = EBUF_ENTRY_TIMESTAMP_MINS(word); if ( errcode > (EBUF_N_ERRSTRINGS-1)) { - return snprintf(m, s, "\r\n\t%s %d", " Invalid error code:", errcode); + return snprintf(m, s, "\r\n\t%s %d (word %d)", " Invalid error code:", errcode, (int)word); } int copied = snprintf(m, s, "\r\n %02u %02u:%02u \t %x %s ", days, hours, minutes, realcount, ebuf_errstrings[errcode]); @@ -174,18 +175,21 @@ int errbuffer_get_messagestr(const uint32_t word, char *m, size_t s ) case EBUF_RESTART: if (errdata & EBUF_RESTART_SW ) copied += snprintf(m+copied, s-copied, "(SW)"); - else if (errdata & EBUF_RESTART_EXT ) + if (errdata & EBUF_RESTART_EXT ) copied += snprintf(m+copied, s-copied, "(EXT)"); - else if (errdata & EBUF_RESTART_WDOG ) + if (errdata & EBUF_RESTART_WDOG ) copied += snprintf(m+copied, s-copied, "(WDOG)"); - else if (errdata & EBUF_RESTART_POR ) + if (errdata & EBUF_RESTART_POR ) copied += snprintf(m+copied, s-copied, "(POR)"); break; case EBUF_HARDFAULT: copied += snprintf(m+copied, s-copied, "(ISRNUM= 0x%x)", errdata); break; case EBUF_PWR_FAILURE: - copied += snprintf(m+copied, s-copied, "(supply %d)", errdata); + copied += snprintf(m+copied, s-copied, "(supply mask 0x%x)", errdata); + break; + case EBUF_ASSERT: + copied += snprintf(m+copied, s-copied, "(pc 0x%x)", errdata); break; default: if (errcode>EBUF_WITH_DATA){ diff --git a/projects/boot_loader/bl_config.h b/projects/boot_loader/bl_config.h index 35f2b1d5..bc10279c 100644 --- a/projects/boot_loader/bl_config.h +++ b/projects/boot_loader/bl_config.h @@ -25,7 +25,7 @@ #ifndef __BL_CONFIG_H__ #define __BL_CONFIG_H__ -#define APOLLO_BL_UART_FP // if you want front panel, otherwise zynq +//#define APOLLO_BL_UART_FP // if you want front panel, otherwise zynq #ifdef APOLLO_BL_UART_FP #define SYSCTL_PERIPH_UARTx SYSCTL_PERIPH_UART4 diff --git a/projects/cm_mcu/CommandLineTask.c b/projects/cm_mcu/CommandLineTask.c index bfbe0f1d..f404e3d8 100644 --- a/projects/cm_mcu/CommandLineTask.c +++ b/projects/cm_mcu/CommandLineTask.c @@ -288,37 +288,45 @@ static BaseType_t power_ctl(int argc, char ** argv) else if ( strncmp(argv[1], "off", 3) == 0 ) { message = PS_OFF; // turn off power supply } - else if ( strncmp(argv[1], "status", 5) == 0 ) { // report status to UART + else if (strncmp(argv[1], "clearfail", 9) == 0) { + message = PS_ANYFAIL_ALARM_CLEAR; + } + else if (strncmp(argv[1], "status", 5) == 0) { // report status to UART int copied = 0; -// copied += snprintf(m+copied, SCRATCH_SIZE-copied, "power_ctl:\r\nLowest ena: %d\r\n", -// getLowestEnabledPSPriority()); bool ku_enable = (read_gpio_pin(TM4C_DIP_SW_1) == 1); bool vu_enable = (read_gpio_pin(TM4C_DIP_SW_2) == 1); static int i=0; if (i==0){ - copied += snprintf(m+copied, SCRATCH_SIZE-copied, "pwr_ctl:\r\nVU_ENABLE:\t%d\r\n" - "KU_ENABLE:\t%d\r\n", vu_enable, ku_enable); + copied += snprintf(m + copied, SCRATCH_SIZE - copied, + "%s:\r\nVU_ENABLE:\t%d\r\n" + "KU_ENABLE:\t%d\r\n", + argv[0], vu_enable, ku_enable); + copied += snprintf(m + copied, SCRATCH_SIZE - copied, + "State machine state: %d\r\n", getPowerControlState()); } for (; i < N_PS_OKS; ++i ) { enum ps_state j = getPSStatus(i); char *c; switch (j) { - case PWR_UNKNOWN: - c = "PWR_UNKNOWN"; - break; - case PWR_ON: - c = "PWR_ON"; - break; - case PWR_OFF: - c = "PWR_OFF"; - break; - case PWR_DISABLED: - c = "PWR_DISABLED"; - break; - default: - c = "UNKNOWN"; - break; - } + case PWR_UNKNOWN: + c = "PWR_UNKNOWN"; + break; + case PWR_ON: + c = "PWR_ON"; + break; + case PWR_OFF: + c = "PWR_OFF"; + break; + case PWR_DISABLED: + c = "PWR_DISABLED"; + break; + case PWR_FAILED: + c = "PWR_FAILED"; + break; + default: + c = "UNKNOWN"; + break; + } copied += snprintf(m+copied, SCRATCH_SIZE-copied, "%15s: %s\r\n", pin_names[oks[i].name], c); @@ -641,8 +649,8 @@ static BaseType_t ff_ctl(int argc, char ** argv) whichFF = NFIREFLIES; } else { // commands with arguments. The last argument is always which FF module. - whichFF = atoi(argv[argc-1]); - if (whichFF>=NFIREFLIES || (whichFF==0 && strncmp(argv[3],"0",1)!=0)){ + whichFF = strtol(argv[argc-1], NULL, 10); + if (whichFF>=NFIREFLIES || (whichFF==0 && strncmp(argv[argc-1],"0",1)!=0)){ snprintf(m+copied, SCRATCH_SIZE-copied, "%s: choose ff number less than %d\r\n", argv[0], NFIREFLIES); return pdFALSE; @@ -809,6 +817,91 @@ static BaseType_t fpga_ctl(int argc, char ** argv) return pdFALSE; } } +#include "common/smbus_units.h" +void snapdump(struct dev_i2c_addr_t *add, uint8_t page, + uint8_t snapshot[32], bool reset); + +typedef struct __attribute__((packed)) { + linear11_val_t v_in; + uint16_t v_out; + linear11_val_t i_out; + linear11_val_t i_out_max; + linear11_val_t duty_cycle; + linear11_val_t temperature; + linear11_val_t unused1; + linear11_val_t freq; + uint8_t v_out_status; + uint8_t i_out_status; + uint8_t input_status; + uint8_t temperature_status; + uint8_t cml_status; + uint8_t mfr_status; + uint8_t flash_status; + uint8_t unused[9]; +} snapshot_t ; + +extern struct dev_i2c_addr_t pm_addrs_dcdc[]; + +static +void float_to_ints(float val, int *tens, int * fraction) +{ + *tens = val; + *fraction = ABS((val - *tens)*100.0); + + return; +} +static BaseType_t snapshot(int argc, char ** argv) +{ + _Static_assert(sizeof(snapshot_t)==32, "sizeof snapshot_t"); + int copied = 0; + int page = strtol(argv[1],NULL,10); // which LGA08D + int which = page/10; + page = page%10; + if ( page < 0 || page > 1 ) { + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "%s: page %d must be between 0-1\r\n", + argv[0], page); + return pdFALSE; + } + if ( which <0 || which >4 ) { + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "%s: device %d must be between 0-4\r\n", + argv[0], which); + return pdFALSE; + } + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "%s: page %d of device %s\r\n", argv[0], + page, pm_addrs_dcdc[which].name); + + bool reset = false; + int ireset = strtol(argv[2],NULL, 10); + if ( ireset == 1 ) + reset = true; + + uint8_t sn[32]; + snapdump(&pm_addrs_dcdc[which],page,sn,reset); + snapshot_t *p0 = (snapshot_t*)&sn[0]; + int tens, fraction; + float_to_ints(linear11_to_float(p0->v_in), &tens, &fraction); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "VIN = %d.%02d\r\n", tens, fraction); + float_to_ints(linear16u_to_float(p0->v_out), &tens, &fraction); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "VOUT = %d.%02d\r\n", tens, fraction); + float_to_ints(linear11_to_float(p0->i_out), &tens, &fraction); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "IOUT = %d.%02d\r\n", tens, fraction); + float_to_ints(linear11_to_float(p0->i_out_max), &tens, &fraction); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "IOUT MAX = %d.%02d\r\n", tens, fraction); + float_to_ints(linear11_to_float(p0->duty_cycle), &tens, &fraction); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "duty cycle = %d.%02d\r\n", tens, fraction); + float_to_ints(linear11_to_float(p0->temperature), &tens, &fraction); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "TEMP = %d.%02d\r\n", tens, fraction); + float_to_ints(linear11_to_float(p0->freq), &tens, &fraction); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "switching freq = %d.%02d\r\n", tens, fraction); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "VOUT STATUS: 0x%02x\r\n", p0->v_out_status); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "I0UT STATUS: 0x%02x\r\n", p0->i_out_status); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "INPUT STATUS: 0x%02x\r\n", p0->input_status); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "TEMP STATUS: 0x%02x\r\n", p0->temperature_status); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "CML STATUS: 0x%02x\r\n", p0->cml_status); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "MFR STATUS: 0x%02x\r\n", p0->mfr_status); + copied += snprintf(m+copied, SCRATCH_SIZE-copied, "flash STATUS: 0x%02x\r\n", p0->flash_status); + return pdFALSE; +} // this command takes no arguments since there is only one command // right now. @@ -1041,22 +1134,7 @@ static BaseType_t errbuff_out(int argc, char **argv) } for (; i>ERRDATA_OFFSET; -// uint16_t o_errdata = o_entry&ERRDATA_MASK; -// uint16_t o_counter = o_entry>>(16-COUNTER_OFFSET); -// uint16_t o_realcount = o_counter*COUNTER_UPDATE+1; -// -// uint16_t entry = EBUF_ENTRY(word);; uint16_t errcode = EBUF_ERRCODE(word); -// uint16_t counter = EBUF_COUNTER(word); -// uint16_t realcount = counter*COUNTER_UPDATE+1; -// -// uint16_t timestamp = EBUF_ENTRY_TIMESTAMP(word); -// uint16_t days = EBUF_ENTRY_TIMESTAMP_DAYS(word); -// uint16_t hours = EBUF_ENTRY_TIMESTAMP_HOURS(word); -// uint16_t minutes = EBUF_ENTRY_TIMESTAMP_MINS(word); // if this is a continuation and it's not the first entry we see if (errcode == EBUF_CONTINUATION && i != 0 ){ uint16_t errdata = EBUF_DATA(word); @@ -1423,49 +1501,49 @@ struct command_t commands[] = { { "i2c_base", i2c_ctl_set_dev, - "i2c_base \r\n Set I2C controller number. Value between 0-9.\r\n", - 1 + "i2c_base \r\n Set I2C controller number. Value between 0-9.\r\n", + 1 }, { - "i2cr", - i2c_ctl_r, + "i2cr", + i2c_ctl_r, "i2cr
\r\n Read I2C controller. Addr in hex.\r\n", 2 }, { - "i2crr", - i2c_ctl_reg_r, - "i2crr
\r\n Read I2C controller. Addr in hex\r\n", - 3 + "i2crr", + i2c_ctl_reg_r, + "i2crr
\r\n Read I2C controller. Addr in hex\r\n", + 3 }, { - "i2cw", - i2c_ctl_w, - "i2cw
\r\n Write I2C controller.\r\n", - 3 + "i2cw", + i2c_ctl_w, + "i2cw
\r\n Write I2C controller.\r\n", + 3 }, { - "i2cwr", - i2c_ctl_reg_w, - "i2cwr
\r\n Write I2C controller.\r\n", - 4 + "i2cwr", + i2c_ctl_reg_w, + "i2cwr
\r\n Write I2C controller.\r\n", + 4 }, { - "i2c_scan", - i2c_scan, - "i2c_scan\r\n Scan current I2C bus.\r\n", - 0, + "i2c_scan", + i2c_scan, + "i2c_scan\r\n Scan current I2C bus.\r\n", + 0, }, { - "help", - help_command_fcn, - "help\r\n This help command\r\n", - -1 + "help", + help_command_fcn, + "help\r\n This help command\r\n", + -1 }, { "pwr", power_ctl, - "pwr (on|off|status)\r\n Turn on or off all power.\r\n", + "pwr (on|off|status|clearfail)\r\n Turn on or off all power, get status or clear failures.\r\n", 1 }, { @@ -1480,6 +1558,12 @@ struct command_t commands[] = { "psmon <#>\r\n Displays a table showing the state of power supplies.\r\n", 1 }, + { + "snapshot", + snapshot, + "snapshot # (0|1)\r\n Dump snapshot register. #: which of 5 LGA80D (10*dev+page). 0|1 decide if to reset snapshot.\r\n", + 2 + }, { "restart_mcu", restart_mcu, @@ -1518,13 +1602,13 @@ struct command_t commands[] = { { "stack_usage", stack_ctl, - "stack_usage \r\n Print out system stack high water mark.\r\n", + "stack_usage\r\n Print out system stack high water mark.\r\n", 0, }, { "task-stats", TaskStatsCommand, - "task-stats \r\n Displays a table showing the state of each FreeRTOS task\r\n", + "task-stats\r\n Displays a table showing the state of each FreeRTOS task\r\n", 0 }, { diff --git a/projects/cm_mcu/FireFlyTask.c b/projects/cm_mcu/FireFlyTask.c index 1700bbad..58c64104 100644 --- a/projects/cm_mcu/FireFlyTask.c +++ b/projects/cm_mcu/FireFlyTask.c @@ -398,13 +398,15 @@ void FireFlyTask(void *parameters) #endif // DEBUG_FIF ff_temp [i] = -55; } - +#define I2C_PULLUP_BUG2 vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS( 2500 ) ); for (;;) { tSMBus *smbus; tSMBusStatus *p_status; +#ifdef I2C_PULLUP_BUG2 bool good = false; +#endif // I2C_PULLUP_BUG // loop over FireFly modules for ( uint8_t ff = 0; ff < NFIREFLIES; ++ ff ) { if (! isEnabledFF(ff)) // skip the FF if it's not enabled via the FF config @@ -415,6 +417,7 @@ void FireFlyTask(void *parameters) else { smbus = &g_sMaster3; p_status = &eStatus3; } +#ifdef I2C_PULLUP_BUG2 if ( getPSStatus(5) != PWR_ON) { if ( good ) { Print("FIF: 3V3 died. Skipping I2C monitoring.\r\n"); @@ -426,6 +429,7 @@ void FireFlyTask(void *parameters) else { good = true; } +#endif // I2C_PULLUP_BUG // check for any messages uint32_t message; if ( xQueueReceive(xFFlyQueueIn, &message, 0) ) { // TODO: what if I receive more than one message diff --git a/projects/cm_mcu/FreeRTOSConfig.h b/projects/cm_mcu/FreeRTOSConfig.h index e93cae4d..062c9fa7 100644 --- a/projects/cm_mcu/FreeRTOSConfig.h +++ b/projects/cm_mcu/FreeRTOSConfig.h @@ -173,7 +173,7 @@ header file. */ //#define CLI_UART UART1_BASE // Zynq #define SYSTEM_STACK_SIZE 128 - +#define I2C_PULLUP_BUG #define pdTICKS_TO_MS( xTicks ) ( ( ( TickType_t ) ( xTicks ) * 1000u ) / configTICK_RATE_HZ ) diff --git a/projects/cm_mcu/I2CSlaveTask.c b/projects/cm_mcu/I2CSlaveTask.c index 0beeb9f9..c97e3206 100644 --- a/projects/cm_mcu/I2CSlaveTask.c +++ b/projects/cm_mcu/I2CSlaveTask.c @@ -49,7 +49,7 @@ void Print(const char* str); static uint8_t testreg = 0x0U; -static int fpga_vu, fpga_ku; +static int local_fpga_vu, local_fpga_ku; static uint8_t getSlaveData(uint8_t address) { @@ -62,11 +62,11 @@ static uint8_t getSlaveData(uint8_t address) value = getADCvalue(20)+0.5; // always valid break; case 0x12U: // FPGA VU temp - value = (uint8_t) fpga_vu>=0?fpga_args.pm_values[fpga_vu]:0U; + value = (uint8_t) local_fpga_vu>=0?fpga_args.pm_values[local_fpga_vu]:0U; if ( value == 0 ) value = 0xFFU; // invalid value break; case 0x14U: // FPGA KU temp - value = (uint8_t) fpga_ku>=0?fpga_args.pm_values[fpga_ku]:0U; + value = (uint8_t) local_fpga_ku>=0?fpga_args.pm_values[local_fpga_ku]:0U; if ( value == 0 ) value = 0xFFU; // invalid value break; case 0x16U: // hottest FF temp @@ -123,8 +123,8 @@ void I2CSlaveTask(void *parameters) TaskNotifyI2CSlave = xTaskGetCurrentTaskHandle(); // struct I2CSlaveTaskArgs_t * args = parameters; - fpga_ku = get_ku_index(); - fpga_vu = get_vu_index(); + local_fpga_ku = get_ku_index(); + local_fpga_vu = get_vu_index(); ROM_I2CSlaveEnable(I2C0_BASE); diff --git a/projects/cm_mcu/LocalTasks.c b/projects/cm_mcu/LocalTasks.c index 9cdc2309..abbedb4a 100644 --- a/projects/cm_mcu/LocalTasks.c +++ b/projects/cm_mcu/LocalTasks.c @@ -9,11 +9,18 @@ * DUE TO LIMITATIONS OF MACOS this file is called LocalTasks.c not Tasks.c, as there is a FreeRTOS * file called tasks.c and MacOS default file system can't tell these apart. */ +#include +#include +#include // memset + #include "Tasks.h" #include "MonitorTask.h" #include "InterruptHandlers.h" #include "common/pinsel.h" +#include "common/smbus_units.h" +// local sprintf prototype +int snprintf( char *buf, unsigned int count, const char *format, ... ); // FPGA arguments for monitoring task @@ -49,6 +56,7 @@ struct MonitorTaskArgs_t fpga_args = { .n_pages = 1, .smbus = &g_sMaster6, .smbus_status = &eStatus6, + .initfcn = NULL, }; // Power supply arguments for Monitoring task @@ -67,17 +75,144 @@ struct dev_i2c_addr_t pm_addrs_dcdc[] = { {"VVCCINT2", 0x70, 4, 0x45}, }; +int apollo_pmbus_rw(tSMBus *smbus, volatile tSMBusStatus *smbus_status, + bool read, struct dev_i2c_addr_t* add, + struct pm_command_t * cmd, uint8_t * value) +{ + // write to the I2C mux + uint8_t data; + // select the appropriate output for the mux + data = 0x1U << add->mux_bit; + tSMBusStatus r = SMBusMasterI2CWrite(smbus, add->mux_addr, &data, 1); + if ( r != SMBUS_OK ) { + return -1; + } + while ( SMBusStatusGet(smbus) == SMBUS_TRANSFER_IN_PROGRESS) { + vTaskDelay( pdMS_TO_TICKS( 10 )); // wait + } + if ( *smbus_status != SMBUS_OK ) { + return -2; + } + // read/write to the device itself + if ( read ) { + r = SMBusMasterByteWordRead(smbus, add->dev_addr, cmd->command, + value, cmd->size); + } + else { // write + r = SMBusMasterByteWordWrite(smbus, add->dev_addr, cmd->command, + value, cmd->size); + } + if ( r != SMBUS_OK ) { + return -3; + } + while ( SMBusStatusGet(smbus) == SMBUS_TRANSFER_IN_PROGRESS) { + vTaskDelay( pdMS_TO_TICKS( 10 )); // wait + } + // this is checking the return from the interrupt + if (*smbus_status != SMBUS_OK ) { + return -4; + } + // if we get here, a successful read/write command + + return 0; +} + +void Print(const char*); + +// this function is run once in the dcdc monitoring task +struct pm_command_t extra_cmds[] = { + {0x0, 1, "PAGE", "", PM_STATUS}, + {0x1, 1, "OPERATION", "", PM_STATUS}, + {0x33, 2, "FREQUENCY_SWITCH", "Hz", PM_LINEAR11}, + {0xEA, 32, "SNAPSHOP", "", PM_STATUS}, + {0xF3, 1, "SNAPSHOP_CONTROL", "", PM_STATUS}, +}; + +void snapdump(struct dev_i2c_addr_t *add, uint8_t page, + uint8_t snapshot[32], bool reset) +{ + while(xSemaphoreTake(xMonSem, (TickType_t) 10) == pdFALSE) + ; + // page register + int r = apollo_pmbus_rw(&g_sMaster1, &eStatus1, + false, add, &extra_cmds[0], &page); + if ( r ) { + Print("error in snapdump (0)\r\n"); + } + + // actual command -- snapshot control copy NVRAM for reading + uint8_t cmd = 0x1; + r = apollo_pmbus_rw(&g_sMaster1, &eStatus1, + false, add, &extra_cmds[4], &cmd); + if ( r ) { + Print("error in scdump 1\r\n"); + } + // actual command -- read snapshot + tSMBusStatus r2 = SMBusMasterBlockRead(&g_sMaster1, add->dev_addr, + extra_cmds[3].command, &snapshot[0]); + if ( r2 != SMBUS_OK ) { + Print("error setting up block read (scdump 2)\r\n"); + } + while ( (r2 = SMBusStatusGet(&g_sMaster1)) == SMBUS_TRANSFER_IN_PROGRESS) { + vTaskDelay( pdMS_TO_TICKS( 10 )); // wait + } + if ( r2 != SMBUS_TRANSFER_COMPLETE ) { + Print("error in scdump(3)\r\n"); + } + if ( reset ) { + // reset SNAPSHOT. This will fail if the device is on. + cmd = 0x3; + r = apollo_pmbus_rw(&g_sMaster1, &eStatus1, + false, add,&extra_cmds[4], &cmd); + if ( r ) { + Print("error in scdump 4\r\n"); + } + } + xSemaphoreGive(xMonSem); +} + +void dcdc_initfcn(void) +{ + // set up the switching frequency + //uint16_t freqlin11 = float_to_linear11(457.14); + uint16_t freqlin11 = float_to_linear11(800.); + for ( int dev = 1; dev < 4; dev += 2 ) { + for (uint8_t page = 0; page < 2; ++ page ) { + // page register + int r = apollo_pmbus_rw(&g_sMaster1, &eStatus1, + false, pm_addrs_dcdc+dev, &extra_cmds[0], &page); + if ( r ) { + Print("error in dcdc_initfcn (0)\r\n"); + } + // actual command -- frequency switch + r = apollo_pmbus_rw(&g_sMaster1, &eStatus1, + false, pm_addrs_dcdc+dev,&extra_cmds[2], (uint8_t*)&freqlin11); + if ( r ) { + Print("error in dcdc_initfcn (1)\r\n"); + } + } + } + return; +} + +// if you change the length of this array, you also need to change +// NCOMMANDS_PS in MonitorTask.h +// TODO make this fix automatic struct pm_command_t pm_command_dcdc[] = { { 0x8d, 2, "READ_TEMPERATURE_1", "C", PM_LINEAR11 }, { 0x8f, 2, "READ_TEMPERATURE_3", "C", PM_LINEAR11 }, { 0x88, 2, "READ_VIN", "V", PM_LINEAR11 }, { 0x8B, 2, "READ_VOUT", "V", PM_LINEAR16U }, { 0x8c, 2, "READ_IOUT", "A", PM_LINEAR11 }, - //{ 0x4F, 2, "OT_FAULT_LIMIT", "C", PM_LINEAR11}, { 0x79, 2, "STATUS_WORD", "", PM_STATUS }, - //{ 0xE7, 2, "IOUT_AVG_OC_FAULT_LIMIT", "A", PM_LINEAR11 }, - //{ 0x95, 2, "READ_FREQUENCY", "Hz", PM_LINEAR11}, + { 0x4F, 2, "OT_FAULT_LIMIT", "C", PM_LINEAR11}, + { 0xE7, 2, "IOUT_AVG_OC_FAULT_LIMIT", "A", PM_LINEAR11 }, + { 0x95, 2, "READ_FREQUENCY", "Hz", PM_LINEAR11}, + { 0x46, 2, "IOUT_OC_FAULT_LIMIT", "A", PM_LINEAR11}, + { 0x44, 2, "VOUT_UV_FAULT_LIMIT", "V", PM_LINEAR16U}, + { 0x37, 2, "INTERLEAVE", "", PM_STATUS}, + { 0x80, 1, "STATUS_MFR_SPECIFIC", "", PM_STATUS}, }; float dcdc_values[NSUPPLIES_PS*NPAGES_PS*NCOMMANDS_PS]; struct MonitorTaskArgs_t dcdc_args = { @@ -91,12 +226,12 @@ struct MonitorTaskArgs_t dcdc_args = { .n_pages = NPAGES_PS, .smbus = &g_sMaster1, .smbus_status = &eStatus1, + //.initfcn = &dcdc_initfcn, + .initfcn = NULL, }; - - static int fpga_ku = -1; static int fpga_vu = -1; int get_ku_index() diff --git a/projects/cm_mcu/MonitorTask.c b/projects/cm_mcu/MonitorTask.c index 7d63cb92..7cab1b69 100644 --- a/projects/cm_mcu/MonitorTask.c +++ b/projects/cm_mcu/MonitorTask.c @@ -48,6 +48,8 @@ void Print(const char* str); // the PAGE command is an SMBUS standard at register 0 #define PAGE_COMMAND 0x0 +SemaphoreHandle_t xMonSem = NULL; + static void SuppressedPrint(const char *str, int *current_error_cnt, bool *logging) { @@ -88,12 +90,22 @@ void MonitorTask(void *parameters) int current_error_cnt = 0; args->updateTick = xLastWakeTime; // initial value + // wait for the power to come up + vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(500)); + if ( args->initfcn != NULL ) + (*args->initfcn)(); + +#ifdef I2C_PULLUP_BUG + bool good = false; +#endif // I2C_PULLUP_BUG for (;;) { + while (xSemaphoreTake(xMonSem, (TickType_t) 10) == pdFALSE) + ; + char tmp[TMPBUFFER_SZ]; +#ifdef I2C_PULLUP_BUG // check if the 3.3V is there or not. If it disappears then nothing works // since that is the I2C pullups. This will be changed with next // rev of the board. - char tmp[TMPBUFFER_SZ]; - static bool good = false; if ( getPSStatus(5) != PWR_ON) { if ( good ) { snprintf(tmp, TMPBUFFER_SZ, "MON(%s): 3V3 died. Skipping I2C monitoring.\r\n", @@ -101,17 +113,21 @@ void MonitorTask(void *parameters) SuppressedPrint(tmp, ¤t_error_cnt, &log); good = false; } + xSemaphoreGive(xMonSem); vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(500)); continue; } else { good = true; } +#endif // I2C_PULLUP_BUG args->updateTick = xTaskGetTickCount(); // current time in ticks // loop over devices for ( uint8_t ps = 0; ps < args->n_devices; ++ ps ) { +#ifdef I2C_PULLUP_BUG if ( getPSStatus(5) != PWR_ON) break; +#endif // I2C_PULLUP_BUG // select the appropriate output for the mux data[0] = 0x1U<devices[ps].mux_bit; @@ -229,6 +245,8 @@ void MonitorTask(void *parameters) } // loop over commands } // loop over pages } // loop over power supplies + xSemaphoreGive(xMonSem); + vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS( 250 ) ); } // infinite loop diff --git a/projects/cm_mcu/MonitorTask.h b/projects/cm_mcu/MonitorTask.h index 5c61d2e0..213ee0ac 100644 --- a/projects/cm_mcu/MonitorTask.h +++ b/projects/cm_mcu/MonitorTask.h @@ -9,10 +9,13 @@ #define PROJECTS_CM_MCU_MONITORTASK_H_ #include "common/smbus.h" +#include "semphr.h" extern float pm_values[]; +extern SemaphoreHandle_t xMonSem; + // pilfered and adapted from http://billauer.co.il/blog/2018/01/c-pmbus-xilinx-fpga-kc705/ enum { PM_VOLTAGE, PM_NONVOLTAGE, PM_STATUS, PM_LINEAR11, PM_LINEAR16U, PM_LINEAR16S } pm_types ; @@ -44,10 +47,11 @@ struct MonitorTaskArgs_t { tSMBus *smbus; volatile tSMBusStatus *smbus_status; volatile TickType_t updateTick; + void (*initfcn) (void); }; // DC-DC converter #define NSUPPLIES_PS (5) // 5 devices, 2 pages each -#define NCOMMANDS_PS 6 // number of entries in above array +#define NCOMMANDS_PS 13 // number of entries in dcdc_ array #define NPAGES_PS 2 // number of pages on the power supplies. extern struct MonitorTaskArgs_t dcdc_args; diff --git a/projects/cm_mcu/PowerSupplyTask.c b/projects/cm_mcu/PowerSupplyTask.c index aa82fb1f..388179e4 100644 --- a/projects/cm_mcu/PowerSupplyTask.c +++ b/projects/cm_mcu/PowerSupplyTask.c @@ -6,123 +6,279 @@ */ // includes for types -#include #include +#include #include +#include // local includes -#include "common/uart.h" -#include "common/utils.h" -#include "common/power_ctl.h" +#include "Tasks.h" #include "common/i2c_reg.h" #include "common/pinout.h" #include "common/pinsel.h" -#include "Tasks.h" +#include "common/power_ctl.h" +#include "common/uart.h" +#include "common/utils.h" // FreeRTOS includes #include "FreeRTOS.h" #include "FreeRTOSConfig.h" #include "queue.h" - - // Holds the handle of the created queue for the power supply task. QueueHandle_t xPwrQueue = NULL; extern QueueHandle_t xLedQueue; -void Print(const char* str); +void Print(const char *str); +// local sprintf prototype +int snprintf( char *buf, unsigned int count, const char *format, ... ); +enum power_system_state currentState = INIT; // start in INIT state + +enum power_system_state getPowerControlState() +{ + return currentState; +} + +extern const struct gpio_pin_t oks[]; +static uint16_t check_ps_oks(void) +{ + uint16_t status = 0U; + for (int i = 0; i < N_PS_OKS; ++i) { + int val = read_gpio_pin(oks[i].name); + if (val) + status |= 1U << i; + } + return status; +} // monitor and control the power supplies void PowerSupplyTask(void *parameters) { + // compile-time sanity check + static_assert(PS_ENS_MASK == (PS_ENS_GEN_MASK|PS_ENS_VU_MASK|PS_ENS_KU_MASK), "mask"); + static_assert(PS_OKS_MASK == (PS_OKS_GEN_MASK|PS_OKS_VU_MASK|PS_OKS_KU_MASK), "mask"); + // initialize to the current tick time TickType_t xLastWakeTime = xTaskGetTickCount(); - TickType_t oldTime = xLastWakeTime; - enum ps_state oldState = PWR_UNKNOWN; - bool alarm = false; + // alarm from outside this task + bool external_alarm = false; + // powerdown request from the CLI bool cli_powerdown_request = false; - // turn on the power supply at the start of the task, if the power enable is sent by the - // zynq - if ( read_gpio_pin(BLADE_POWER_EN) == 1 ) - set_ps() ; + // masks to enable/check appropriate supplies + uint16_t supply_ok_mask = PS_OKS_GEN_MASK; + uint16_t supply_en_mask = PS_ENS_GEN_MASK; + bool ku_enable = (read_gpio_pin(TM4C_DIP_SW_1) == 1); + bool vu_enable = (read_gpio_pin(TM4C_DIP_SW_2) == 1); + if (ku_enable) { + supply_ok_mask |= PS_OKS_KU_MASK; + supply_en_mask |= PS_ENS_KU_MASK; + } + if (vu_enable) { + supply_ok_mask |= PS_OKS_VU_MASK; + supply_en_mask |= PS_ENS_VU_MASK; + } + #ifdef APOLLO10_HACK + // APOLLO 10 HACK + supply_mask &= ~(1<<6); + supply_mask &= ~(1<<7); + supply_mask &= ~(1<<8); + supply_mask &= ~(1<<9); + supply_mask &= ~(1<<10); + supply_mask &= ~(1<<11); + supply_mask &= ~(1<<12); + supply_mask &= ~(1<<13); + char tmp[64]; + snprintf(tmp, 64, "PowerSupplyTask: hack mask is %x\r\n", supply_mask); + Print(tmp); + #endif // APOLLO10_HACK - // this function never returns - for ( ;; ) { - // first check for message on the queue and act on any messages. + bool power_supply_alarm = false; + uint16_t failed_mask = 0x0U; + // this loop never exits + for (;;) { + // first check for message on the queue and collect all messages. // non-blocking call. uint32_t message; - if ( xQueueReceive(xPwrQueue, &message, 0) ) { // TODO: what if I receive more than one message - switch (message ) { + if (xQueueReceive(xPwrQueue, &message, + 0)) { // TODO: what if I receive more than one message + switch (message) { case PS_OFF: - errbuffer_put(EBUF_POWER_OFF,0); cli_powerdown_request = true; - disable_ps(); break; case TEMP_ALARM: - alarm = true; - if(oldState!=PWR_OFF){ - disable_ps(); - errbuffer_put(EBUF_POWER_OFF_TEMP,0); - } + external_alarm = true; break; case TEMP_ALARM_CLEAR: - alarm = false; + external_alarm = false; break; case PS_ON: - errbuffer_put(EBUF_POWER_ON,0); cli_powerdown_request = false; - set_ps(); + break; + case PS_ANYFAIL_ALARM_CLEAR: + power_supply_alarm = false; + failed_mask = 0x0U; break; default: - message = RED_LED_TOGGLE; - xQueueSendToBack(xLedQueue, &message, pdMS_TO_TICKS(10)); // message I don't understand? Toggle red LED break; } } - enum ps_state newstate; - // Check the state of BLADE_POWER_EN. The MCU will - // run the disable command even if the power was already - // off, just to be sure. check_ps() above can return PWR_OFF - // even if some supplies are still on (it really is checking if _all__ - // expected supplies are good.) - // Eventually this will be replaced by a message from an interrupt handler. + // Check the state of BLADE_POWER_EN. bool blade_power_enable = (read_gpio_pin(BLADE_POWER_EN) == 1); - if ( ! blade_power_enable ) { - disable_ps(); + + // now check the actual state of the power supplies + uint16_t supply_bitset = check_ps_oks(); + bool supply_off = false; // are supplies off (besides the ones that are disabled) + if ((supply_bitset&supply_ok_mask) != supply_ok_mask) { + supply_off = true; } - else if ( ! alarm && ! cli_powerdown_request ) { // blade_power_enable and not alarm - TickType_t newTime = xTaskGetTickCount(); - int32_t seconds_passed = (newTime-oldTime)*portTICK_PERIOD_MS/1000; - if ((oldTime==0)||(ABS(seconds_passed)>=SET_PS_RETRY)){ // absolute value to catch timer overflow - set_ps(); - oldTime=newTime; + + // MAIN POWER SUPPLY TASK STATE MACHINE + // +------+ + // | INIT +--------------------------------------------+ + // +---+--+ | + // | v + // | +-----------+ +-----+-----+ + // | | +----------------> | | + // +--------->+ POWER_ON | | POWER_OFF | + // | | <----------------+ | + // +----+------+ +-------+---+ + // | ^ + // | +----------------+ | + // | | | | + // +------->+ POWER_FAILURE +-------+ + // | | + // +----------------+ + // the state you are in is the current state, so for instance + // there is no power_off transition in the power_off state; + // instead it's in the POWER_ON state (e.g.) + enum power_system_state nextState; + switch (currentState) { + case INIT: { + // only run on first boot + if (blade_power_enable) { + turn_on_ps(supply_en_mask); + errbuffer_put(EBUF_POWER_ON, 0); + nextState = POWER_ON; + supply_off = false; } + else { + nextState = POWER_OFF; + } + break; } - // now check the actual state - bool psgood = check_ps(); - newstate = psgood?PWR_ON:PWR_OFF; + case POWER_ON: { + if (supply_off) { + // log erroring supplies + failed_mask = (~supply_bitset) & supply_ok_mask; + char tmp[64]; + snprintf(tmp, 64, "failure set: fail, supply_mask,bitset = %x,%x,%x\r\n", + failed_mask, supply_ok_mask, supply_bitset); + Print(tmp); + errbuffer_put(EBUF_PWR_FAILURE, failed_mask); + // turn off all supplies + disable_ps(); + power_supply_alarm = true; + nextState = POWER_FAILURE; + } + else if (external_alarm) { + errbuffer_put(EBUF_POWER_OFF_TEMP,0); + // turn off all supplies + disable_ps(); + nextState = POWER_FAILURE; + } + else if (!blade_power_enable || cli_powerdown_request) { + disable_ps(); + errbuffer_put(EBUF_POWER_OFF, 0); + nextState = POWER_OFF; + } + else { + nextState = POWER_ON; + } + break; + } + case POWER_OFF: { + if (blade_power_enable && !cli_powerdown_request && !external_alarm && + !power_supply_alarm) { + turn_on_ps(supply_en_mask); + errbuffer_put(EBUF_POWER_ON,0); + nextState = POWER_ON; + } + else { + nextState = POWER_OFF; + } + break; + } + case POWER_FAILURE: { // we go through POWER_OFF state before turning on. + if (!power_supply_alarm && !external_alarm) { // errors cleared + nextState = POWER_OFF; + } + else { // still in failed state + nextState = POWER_FAILURE; + } + break; + } + default: { + //configASSERT(1 == 0); + nextState = INIT; // shut up debugger + break; + } + } - if ( newstate == PWR_OFF && oldState != PWR_OFF) { - Print("\r\nPowerSupplyTask: power supplies turned off.\r\n"); - if ( ! blade_power_enable ) - Print("\r\nPowerSupplyTask: PWR_EN removed by SM\r\n"); - message = PS_BAD; + // update the ps_state variables, for external display + // as well as for usage in ensuring the I2C pullups are on. + supply_bitset = check_ps_oks(); + for (size_t i = 0; i < N_PS_OKS; i++) { + if ((1U << i) & supply_bitset) { + // OK bit is on -- PS is on + setPSStatus(i, PWR_ON); + } + // OK bit is not on and ... + else if (!((1U << i) & supply_ok_mask)) { + // ... it should _not_ be on. Disabled intentionally + setPSStatus(i, PWR_DISABLED); + } + else { + // ... it _should_ be on based on the mask + switch (currentState) { + case POWER_OFF: + case INIT: + // ... but the power state is "off" or + // we are just initializing / turning on + setPSStatus(i, PWR_OFF); + break; + case POWER_ON: + case POWER_FAILURE: + // ... but the power state is is "on," so ... + if ((1U << i) & failed_mask) { + // ... either the supply failed + setPSStatus(i, PWR_FAILED); + } + else { + // ... or it's just off because there was a power failure, + // but this supply is not the root cause. + setPSStatus(i, PWR_OFF); + } + break; + default: + break; + } + } } - else { // either the PS is now good or there was no change. - message = PS_GOOD; + if (currentState != nextState) { + char tmp[64]; + snprintf(tmp, 64, "PowerSupplyTask: change from state %d to %d\r\n", + currentState, nextState); + Print(tmp); } - // only send a message on state change. - if ( oldState != newstate ) - xQueueSendToBack(xLedQueue, &message, pdMS_TO_TICKS(10));// todo: check on fail to send - oldState = newstate; + currentState = nextState; // wait here for the x msec, where x is 2nd argument below. - vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS( 25 ) ); + vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(25)); } } diff --git a/projects/cm_mcu/Tasks.h b/projects/cm_mcu/Tasks.h index 49f1e166..888e79c2 100644 --- a/projects/cm_mcu/Tasks.h +++ b/projects/cm_mcu/Tasks.h @@ -57,6 +57,9 @@ void LedTask(void *parameters); #define SET_PS_RETRY 5 // time interval between each set_ps() attempt void PowerSupplyTask(void *parameters); extern QueueHandle_t xPwrQueue; +enum power_system_state { INIT, POWER_ON, POWER_OFF, POWER_FAILURE }; +enum power_system_state getPowerControlState(); + // --- Semi-generic PMBUS based I2C task void MonitorTask(void *parameters); diff --git a/projects/cm_mcu/cm_mcu.c b/projects/cm_mcu/cm_mcu.c index 674f1165..ad741499 100644 --- a/projects/cm_mcu/cm_mcu.c +++ b/projects/cm_mcu/cm_mcu.c @@ -228,6 +228,7 @@ int main( void ) // mutex for the UART output xUARTMutex = xSemaphoreCreateMutex(); + xMonSem = xSemaphoreCreateMutex(); // Create the stream buffers that sends data from the interrupt to the diff --git a/projects/cm_mcu/startup_gcc.c b/projects/cm_mcu/startup_gcc.c index 11b732f4..67380b6e 100644 --- a/projects/cm_mcu/startup_gcc.c +++ b/projects/cm_mcu/startup_gcc.c @@ -94,7 +94,7 @@ void (* const g_pfnVectors[])(void) = IntDefaultHandler, // UART0 Rx and Tx UART1IntHandler, // UART1 Rx and Tx -- ZYNQ UART IntDefaultHandler, // SSI0 Rx and Tx - I2CSlave0Interrupt, // I2C0 Master and Slave + I2CSlave0Interrupt, // I2C0 Master and Slave IntDefaultHandler, // PWM Fault IntDefaultHandler, // PWM Generator 0 IntDefaultHandler, // PWM Generator 1