Log GPIO assert events in device and CSV logs
Add log messages when GPIO pins are pulled low by an external source.
Bug: b/324483249
Change-Id: Iedb509c1640c7f32797386d456b42fb75e0d7e6e
Reviewed-on: https://pigweed-review.googlesource.com/c/gonk/+/229391
Commit-Queue: Anthony DiGirolamo <tonymd@google.com>
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Reviewed-by: Taylor Cramer <cramertj@google.com>
diff --git a/README.md b/README.md
index 158ae6d..1a16e1e 100644
--- a/README.md
+++ b/README.md
@@ -168,6 +168,44 @@
## Appendix
+### CSV Log Format
+
+#### Fields
+
+1. Host timestamp with format `%Y%m%d %H:%M:%S.%f`
+2. Delta microseconds (elapsed microseconds since the last ADC read) on the STM32 microcontroller side.
+3. 7 Voltage values
+4. 7 Current values
+5. 7 Power values
+6. Comment text
+
+Example separated into multiple lines (with an empty comment field)
+```
+20240813 14:16:35.176433,
+283,
+0.8148437500000001, 0.8892578125, 5.212109375000001, 1.087109375, 0.8904296875000001, 3.3820312500000003, 1.8125,
+0.058447916666666676, 0.031546875, 0.13443125, 0.015902343750000002, 0.0009143518518518518, 0.16081250000000002, 0.004143750000000001,
+0.04762591959635418, 0.02805330505371094, 0.7006703784179689, 0.01728758697509766, 0.0008141660337094908, 0.5438729003906251, 0.007510546875000001,
+
+```
+
+#### GPIO assert events in the CSV
+
+When GPIOs are pulled LOW (to ground) a line in the CSV will appear
+with delta_micros = 0 and empty measurement values. The Comment field will
+contain the GPIO assert log message.
+
+Example:
+
+```
+20240813 14:12:32.930888, 0,
+,,,,,,,
+,,,,,,,
+,,,,,,,
+Header pin assert: 2
+```
+
+
### Compiling the FPGA Toolchain
#### yosys
@@ -231,6 +269,28 @@
|--------|-----------|----------------|----------|
| STATUS | PB13 | GPIO_Output | STAT LED |
+### Top 6 pin header (0.1inch pitch 2x3)
+
+Visual header pin positions:
+
+```
+ +-------------
++------+--------+-----+ |
+| 2 | Ground | 6 | | USB-C
++------+--------+-----+ | Port
+| 1 | 3 | VCC | |
++------+--------+-----+ +-------------
+```
+
+| Header Pin | Net | STM32 Pin |
+|------------|-------------|-----------|
+| 1 | SPI_HDR_IO3 | PB0 |
+| 2 | SPI_HDR_IO1 | PA2 |
+| 3 | SPI_HDR_IO2 | PA3 |
+| 4 | GND | |
+| 5 | VCC_GONK | |
+| 6 | SPI_HDR_IO0 | PA1 |
+
### SPI Flash Connection
| Net | FPGA IO# | STM32 Pin | STM32 Function | Flash Pin |
diff --git a/lib/pin_config/pin_config.cc b/lib/pin_config/pin_config.cc
index 1dc187d..a058fd7 100644
--- a/lib/pin_config/pin_config.cc
+++ b/lib/pin_config/pin_config.cc
@@ -10,6 +10,32 @@
#include "pw_log/log.h"
+namespace {
+
+volatile uint8_t gpio1_assert_ = 0;
+volatile uint8_t gpio2_assert_ = 0;
+volatile uint8_t gpio3_assert_ = 0;
+volatile uint8_t gpio6_assert_ = 0;
+
+void Gpio1Interrupt() {
+ gpio1_assert_ = 1;
+ PW_LOG_INFO("Header pin assert: 1");
+}
+void Gpio2Interrupt() {
+ gpio2_assert_ = 1;
+ PW_LOG_INFO("Header pin assert: 2");
+}
+void Gpio3Interrupt() {
+ gpio3_assert_ = 1;
+ PW_LOG_INFO("Header pin assert: 3");
+}
+void Gpio6Interrupt() {
+ gpio6_assert_ = 1;
+ PW_LOG_INFO("Header pin assert: 6");
+}
+
+} // namespace
+
namespace gonk::pin_config {
PinConfig::PinConfig()
@@ -25,6 +51,24 @@
// Default CS pin to output high.
pinMode(FlashCS, OUTPUT);
digitalWrite(FlashCS, HIGH);
+
+ // Top header GPIO inputs.
+ pinMode(HeaderPin1, INPUT_PULLUP);
+ pinMode(HeaderPin2, INPUT_PULLUP);
+ pinMode(HeaderPin3, INPUT_PULLUP);
+ pinMode(HeaderPin6, INPUT_PULLUP);
+ attachInterrupt(/*pin=*/HeaderPin1,
+ /*callback=*/&Gpio1Interrupt,
+ /*mode=*/LOW);
+ attachInterrupt(/*pin=*/HeaderPin2,
+ /*callback=*/&Gpio2Interrupt,
+ /*mode=*/LOW);
+ attachInterrupt(/*pin=*/HeaderPin3,
+ /*callback=*/&Gpio3Interrupt,
+ /*mode=*/LOW);
+ attachInterrupt(/*pin=*/HeaderPin6,
+ /*callback=*/&Gpio6Interrupt,
+ /*mode=*/LOW);
}
void PinConfig::InitFpgaPins() {
@@ -123,4 +167,11 @@
digitalWrite(FlashMOSI, HIGH);
}
+void PinConfig::ClearGpioInterrupts() {
+ gpio1_assert_ = 0;
+ gpio2_assert_ = 0;
+ gpio3_assert_ = 0;
+ gpio6_assert_ = 0;
+}
+
} // namespace gonk::pin_config
diff --git a/lib/pin_config/public/gonk/pin_config.h b/lib/pin_config/public/gonk/pin_config.h
index bdb4398..67683ce 100644
--- a/lib/pin_config/public/gonk/pin_config.h
+++ b/lib/pin_config/public/gonk/pin_config.h
@@ -24,6 +24,11 @@
constexpr uint16_t FpgaIoMode = PB11;
constexpr uint16_t FpgaIoValid = PB6; // was PB10;
+constexpr uint16_t HeaderPin1 = PB0;
+constexpr uint16_t HeaderPin2 = PA2;
+constexpr uint16_t HeaderPin3 = PA3;
+constexpr uint16_t HeaderPin6 = PA1;
+
class PinConfig {
public:
PinConfig();
@@ -41,6 +46,7 @@
void FpgaEnable();
void SPIEnable();
void SPIDisable();
+ void ClearGpioInterrupts();
SPIClass flash_spi;
SPIClass fpga_spi;
diff --git a/tools/gonk_tools/gonk_log_stream.py b/tools/gonk_tools/gonk_log_stream.py
index 9a6d049..a4d7637 100644
--- a/tools/gonk_tools/gonk_log_stream.py
+++ b/tools/gonk_tools/gonk_log_stream.py
@@ -16,6 +16,7 @@
from datetime import datetime
import logging
import time
+from typing import Callable
from google.protobuf.message import DecodeError
from pw_hdlc.decode import FrameDecoder
@@ -26,6 +27,7 @@
from gonk_adc.adc_measurement_pb2 import FramedProto
from gonk_tools.adc import (
+ ADC_COUNT,
calc_power,
get_bus_voltages,
get_shunt_currents,
@@ -64,6 +66,8 @@
self,
detokenizer: detokenize.Detokenizer | None = None,
adc_time_format: str = '%Y%m%d %H:%M:%S.%f',
+ log_message_handlers: list[Callable[[pw_log.log_decoder.Log], None]]
+ | None = None,
) -> None:
self.detokenizer = detokenizer
self.log_decoder: LogStreamDecoder | None = None
@@ -80,6 +84,14 @@
self.proto_update_count: int = 0
self.proto_update_time = time.monotonic()
+ self.log_message_handlers: list[
+ Callable[[pw_log.log_decoder.Log], None]
+ ] = []
+ self.log_message_handlers.append(self._log_csv_gpio_assert)
+ self.empty_measurements_str = ',' * (ADC_COUNT * 3 - 1)
+
+ if log_message_handlers:
+ self.log_message_handlers.extend(log_message_handlers)
def _maybe_log_update_rate(self) -> None:
self.proto_update_count += 1
@@ -171,6 +183,8 @@
log = self.log_decoder.parse_log_entry_proto(log_entry)
emit_python_log(log)
+ for log_message_handler in self.log_message_handlers:
+ log_message_handler(log)
def _handle_proto_packets(self) -> None:
# If no proto has been found, check for a new one.
@@ -195,6 +209,9 @@
self._parse_and_log_adc_proto(proto_bytes)
self._maybe_log_update_rate()
+ def _get_host_time(self) -> str:
+ return datetime.now().strftime(self.adc_time_format)
+
def _parse_and_log_adc_proto(self, proto_bytes: bytes) -> None:
"""Parse an ADC proto message and log."""
framed_proto = FramedProto()
@@ -205,7 +222,7 @@
_LOG.error('ADC FramedProto.DecodeError: %s', proto_bytes.hex())
return
- host_time = datetime.now().strftime(self.adc_time_format)
+ host_time = self._get_host_time()
packet_size = len(proto_bytes)
delta_micros = framed_proto.payload.timestamp
@@ -247,7 +264,7 @@
)
_CSV_LOG.info(
- '%s, %s, %s, %s, %s',
+ '%s, %s, %s, %s, %s, %s',
host_time,
delta_micros_str,
# Volts
@@ -256,4 +273,23 @@
vshunt_list_str,
# Power
power_list_str,
+ # Empty Comment
+ ' ',
+ )
+
+ def _log_csv_gpio_assert(self, log: pw_log.log_decoder.Log) -> None:
+ if 'Header pin assert' not in log.message:
+ return
+
+ _CSV_LOG.info(
+ ', '.join(
+ [
+ self._get_host_time(),
+ '0',
+ # Empty Volts, Current, and Power
+ self.empty_measurements_str,
+ # Comment is the log message
+ log.message.replace(',', ' '),
+ ]
+ )
)