lib/adc: Log continuous updates in proto binary - Log adc measurements in encoded proto binary format. - Switch SPI bus baudrate hz variable to uint32. - Use SPI peripheral managed CS pin for ADC spi transactions. - Switch ADC SPI bus speed to 7.5mhz. - Remove ADCTask elapsed time check to run as fast as possible. Bug: b/323017707 Change-Id: I4a740820752dd50b280485443a8be377422227f7 Reviewed-on: https://pigweed-review.googlesource.com/c/gonk/+/188952 Reviewed-by: Eric Holland <hollande@google.com> Commit-Queue: Anthony DiGirolamo <tonymd@google.com>
diff --git a/BUILD.gn b/BUILD.gn index 8a504ba..222e8fc 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -51,6 +51,8 @@ # Python Targets _gonk_python_packages = [ "//tools" ] +_gonk_proto_packages = [ "//lib/adc:protos.python" ] + # Pigweed Python packages to include _pigweed_python_packages = [ "$dir_pw_env_setup:core_pigweed_python_packages" ] @@ -69,7 +71,7 @@ # single super Python package for installation into the bootstrapped virtual # environment. pw_python_distribution("gonk_python_distribution") { - packages = _pigweed_python_packages + packages = _pigweed_python_packages + _gonk_proto_packages generate_setup_cfg = { name = "gonk-dist" version = "0.0.1"
diff --git a/applications/fpga_config/main.cc b/applications/fpga_config/main.cc index 41829fe..5c48e8d 100644 --- a/applications/fpga_config/main.cc +++ b/applications/fpga_config/main.cc
@@ -48,7 +48,7 @@ FpgaControl fpga_control = FpgaControl(&pin_config); SpiFlash spi_flash = SpiFlash(FlashCS, /*baudrate=*/1000000, pin_config.flash_spi); -Adc fpga_adc = Adc(pin_config.fpga_spi, /*fpga_spi_baudrate=*/1000000, +Adc fpga_adc = Adc(Serial, pin_config.fpga_spi, /*fpga_spi_baudrate=*/7500000, FpgaDspiCS, FpgaIoMode, FpgaIoReset, FpgaIoValid); void ice40_done_rising_isr() { PW_LOG_DEBUG("ICE40 Done: HIGH"); } @@ -95,50 +95,15 @@ } void ADCTask() { - static uint32_t last_update = millis(); - static uint32_t this_update = millis(); - static uint16_t update_count = 0; - - this_update = millis(); - - // Check for serial input. - if (Serial.available()) { - int data_in = Serial.read(); - // Press enter to restart the FPGA config. - if (data_in == '\n') { - PW_LOG_INFO("Restarting FPGA Config."); - current_task = &FpgaConfigTask; + pw::Status update_result = fpga_adc.UpdateContinuousMeasurements(); + if (!update_result.ok()) { + PW_LOG_ERROR("UpdateContinuousMeasurements() failed"); + } else { + pw::Status write_result = fpga_adc.WriteMeasurementPacket(); + if (!write_result.ok()) { + PW_LOG_ERROR("WriteMeasurementPacket failed"); } } - - if (this_update > last_update + 1) { - PW_LOG_INFO("ADC update: %d", update_count); - - pw::Status update_result = fpga_adc.UpdateContinuousMeasurements(); - if (!update_result.ok()) { - PW_LOG_ERROR("UpdateContinuousMeasurements() failed"); - } else { - PW_LOG_INFO( - " vbus: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, ", - fpga_adc.vbus_measurements_[0], fpga_adc.vbus_measurements_[1], - fpga_adc.vbus_measurements_[2], fpga_adc.vbus_measurements_[3], - fpga_adc.vbus_measurements_[4], fpga_adc.vbus_measurements_[5], - fpga_adc.vbus_measurements_[6], fpga_adc.vbus_measurements_[7], - fpga_adc.vbus_measurements_[8], fpga_adc.vbus_measurements_[9], - fpga_adc.vbus_measurements_[10]); - PW_LOG_INFO( - "vshunt: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, ", - fpga_adc.vshunt_measurements_[0], fpga_adc.vshunt_measurements_[1], - fpga_adc.vshunt_measurements_[2], fpga_adc.vshunt_measurements_[3], - fpga_adc.vshunt_measurements_[4], fpga_adc.vshunt_measurements_[5], - fpga_adc.vshunt_measurements_[6], fpga_adc.vshunt_measurements_[7], - fpga_adc.vshunt_measurements_[8], fpga_adc.vshunt_measurements_[9], - fpga_adc.vshunt_measurements_[10]); - } - - last_update = this_update; - update_count = (update_count + 1) % UINT16_MAX; - } } void CheckSpiFlash() { @@ -162,6 +127,8 @@ // Init ADCs fpga_adc.InitAdcs(); fpga_adc.CheckAllAdcs(); + + PW_LOG_INFO("Switching to ADC binary log"); fpga_adc.SetContinuousReadMode(); } else { PW_LOG_INFO("Restarting FPGA Config.");
diff --git a/lib/adc/BUILD.gn b/lib/adc/BUILD.gn index 0068028..af17a5d 100644 --- a/lib/adc/BUILD.gn +++ b/lib/adc/BUILD.gn
@@ -15,11 +15,19 @@ import("//build_overrides/pigweed.gni") import("$dir_pw_build/target_types.gni") +import("$dir_pw_protobuf_compiler/proto.gni") config("default_config") { include_dirs = [ "public" ] } +pw_proto_library("protos") { + sources = [ "adc_measurement.proto" ] + inputs = [ "adc_measurement.options" ] + prefix = "gonk_adc" + deps = [ "$dir_pw_protobuf:common_protos" ] +} + pw_source_set("adc") { public_configs = [ ":default_config" ] public = [ "public/gonk/adc.h" ] @@ -33,6 +41,7 @@ "$dir_pw_status", ] public_deps = [ + ":protos.pwpb", "$dir_pw_bytes", "$dir_pw_third_party/arduino:arduino_core_sources", "//third_party:stm32duino_spi",
diff --git a/lib/adc/adc.cc b/lib/adc/adc.cc index 441e1c4..091513e 100644 --- a/lib/adc/adc.cc +++ b/lib/adc/adc.cc
@@ -5,8 +5,9 @@ #include <stdint.h> #include "gonk/adc.h" +#include "gonk_adc/adc_measurement.pwpb.h" -#define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG +#define PW_LOG_LEVEL PW_LOG_LEVEL_INFO #define PW_LOG_MODULE_NAME "Adc" #include "pw_bytes/bit.h" @@ -77,17 +78,17 @@ constexpr pw::span<const uint8_t> kINA229RegisterByteSize(INA229RegisterByteSize); -Adc::Adc(SPIClass &fpga_spi, uint16_t fpga_spi_baudrate, uint16_t fpga_cs_pin, - uint16_t fpga_mode_pin, uint16_t fpga_reset_pin, +Adc::Adc(Stream &serial_stream, SPIClass &fpga_spi, uint32_t fpga_spi_baudrate, + uint16_t fpga_cs_pin, uint16_t fpga_mode_pin, uint16_t fpga_reset_pin, uint16_t fpga_valid_pin) - : fpga_spi_(fpga_spi), fpga_reset_(fpga_reset_pin), + : serial_(serial_stream), fpga_spi_(fpga_spi), fpga_reset_(fpga_reset_pin), fpga_mode_(fpga_mode_pin), fpga_valid_(fpga_valid_pin), fpga_cs_(fpga_cs_pin), spi_settings_(fpga_spi_baudrate, MSBFIRST, SPI_MODE1) { sample_read_index_ = 0; + measurement_timestamp_ = 0; + previous_timestamp_ = 0; sample_read_time_.fill(0); - vbus_measurements_.fill(0); - vshunt_measurements_.fill(0); } void Adc::SetReadWriteMode() { @@ -225,15 +226,9 @@ return INA229RegisterByteSize[adc_register]; } -void Adc::StartSpiTransaction() { - digitalWrite(fpga_cs_, LOW); - fpga_spi_.beginTransaction(spi_settings_); -} +void Adc::StartSpiTransaction() { fpga_spi_.beginTransaction(spi_settings_); } -void Adc::EndSpiTransaction() { - digitalWrite(fpga_cs_, HIGH); - fpga_spi_.endTransaction(); -} +void Adc::EndSpiTransaction() { fpga_spi_.endTransaction(); } void Adc::WriteAddress(uint32_t adc_address) { uint8_t address[3]; @@ -313,7 +308,10 @@ return wait_result; } - for (int i = 0; i < kStreamingAdcCount; i++) { + previous_timestamp_ = measurement_timestamp_; + measurement_timestamp_ = micros(); + measurement_delta_micros_ = measurement_timestamp_ - previous_timestamp_; + for (int i = 0; i < kMaxStreamingAdcCount; i++) { // Read VBUS std::array<std::byte, 3> vbus_read_buffer; const pw::Result<pw::ConstByteSpan> vbus_read_result = @@ -323,10 +321,9 @@ return vbus_read_result.status(); } int32_t vbus_value = VoltageMeasurement(vbus_read_result.value()); - vbus_measurements_[i] = vbus_value; - PW_LOG_INFO("Continuous Read ADC #%02d: VBUS = %02x %02x %02x = %d", i, - vbus_read_result.value()[0], vbus_read_result.value()[1], - vbus_read_result.value()[2], vbus_value); + PW_LOG_DEBUG("Continuous Read ADC #%02d: VBUS = %02x %02x %02x = %d", i, + vbus_read_result.value()[0], vbus_read_result.value()[1], + vbus_read_result.value()[2], vbus_value); // Read VSHUNT std::array<std::byte, 3> vshunt_read_buffer; @@ -337,10 +334,17 @@ return vshunt_read_result.status(); } int32_t vshunt_value = VoltageMeasurement(vshunt_read_result.value()); - vshunt_measurements_[i] = vshunt_value; - PW_LOG_INFO("Continuous Read ADC #%02d: VSHUNT = %02x %02x %02x = %d", i, - vshunt_read_result.value()[0], vshunt_read_result.value()[1], - vshunt_read_result.value()[2], vshunt_value); + PW_LOG_DEBUG("Continuous Read ADC #%02d: VSHUNT = %02x %02x %02x = %d", i, + vshunt_read_result.value()[0], vshunt_read_result.value()[1], + vshunt_read_result.value()[2], vshunt_value); + + // Save this measurement. + measurements_[i].vbus_value_ = vbus_value; + measurements_[i].vshunt_value_ = vshunt_value; + for (size_t i = 0; i < 3; i++) { + measurements_[i].vbus_bytes_[i] = vbus_read_result.value()[i]; + measurements_[i].vshunt_bytes_[i] = vshunt_read_result.value()[i]; + } } // All data has been read: clear the pulse signal variable. @@ -355,6 +359,57 @@ return pw::OkStatus(); } +// Ensure the proto max size is == the expected kMaxStreamingAdcCount +static_assert(Payload::kAdcMeasurementsMaxSize == kMaxStreamingAdcCount); + +Status Adc::WriteMeasurementPacket() { + std::array<std::byte, FramedProto::kMaxEncodedSizeBytes + + (AdcMeasure::kMaxEncodedSizeBytes * + Payload::kAdcMeasurementsMaxSize)> + packet_buffer; + FramedProto::MemoryEncoder packet(packet_buffer); + Status status = packet.WriteMagicStart(kFramedProtoMagicConstant); + if (!status.ok()) { + PW_LOG_ERROR("WriteMagicStart %d", status); + return status; + } + + // Controls lifetime of payload. + { + auto payload = packet.GetPayloadEncoder(); + status = payload.WriteTimestamp(measurement_delta_micros_); + if (!status.ok()) { + PW_LOG_ERROR("WriteTimestamp: %d", status); + return status; + } + for (size_t i = 0; i < kMaxStreamingAdcCount; i++) { + auto adc_encoder = payload.GetAdcMeasurementsEncoder(); + status = adc_encoder.WriteVbusValue(measurements_[i].vbus_value_); + if (!status.ok()) { + PW_LOG_ERROR("WriteVbusValue: #%d %d", i, status); + return status; + } + status = adc_encoder.WriteVshuntValue(measurements_[i].vshunt_value_); + if (!status.ok()) { + PW_LOG_ERROR("WriteVshuntValue: #%d %d", i, status); + return status; + } + } + } + + if (!packet.status().ok()) { + PW_LOG_ERROR("packet.status %d", status); + return packet.status(); + } + + // Write proto packet bytes over serial. + serial_.write(reinterpret_cast<const uint8_t *>(packet.data()), + packet.size()); + serial_.flush(); + + return pw::OkStatus(); +} + Status Adc::WriteRegister(uint8_t adc_number, uint8_t adc_register, pw::ByteSpan write_buffer) { uint32_t adc_address = ADCAddressWrite(adc_number, adc_register); @@ -479,12 +534,23 @@ uint16_t adc_value = pw::bytes::ReadInOrder<uint16_t>( pw::endian::big, adc_config_result.value().data()); PW_LOG_INFO("ADC #%02d: ADC Config = %x", adc_number, adc_value); + } else { + return adc_config_result.status(); } // Read and log the VSHUNT register. - GetShuntVoltageMeasurement(adc_number); + const pw::Result<int32_t> vshunt_result = + GetShuntVoltageMeasurement(adc_number); + if (!vshunt_result.ok()) { + PW_LOG_ERROR("GetShuntVoltageMeasurement failed: %d", + vshunt_result.status()); + } // Read and log the VBUS register. - GetBusVoltageMeasurement(adc_number); + const pw::Result<int32_t> vbus_result = + GetBusVoltageMeasurement(adc_number); + if (!vbus_result.ok()) { + PW_LOG_ERROR("GetBusVoltageMeasurement failed: %d", vbus_result.status()); + } } return pw::OkStatus();
diff --git a/lib/adc/adc_measurement.options b/lib/adc/adc_measurement.options new file mode 100644 index 0000000..344e30d --- /dev/null +++ b/lib/adc/adc_measurement.options
@@ -0,0 +1,15 @@ +// Copyright 2022 The Pigweed Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +gonk.adc.Payload.adc_measurements max_count:11 \ No newline at end of file
diff --git a/lib/adc/adc_measurement.proto b/lib/adc/adc_measurement.proto new file mode 100644 index 0000000..1f1f3e1 --- /dev/null +++ b/lib/adc/adc_measurement.proto
@@ -0,0 +1,34 @@ +// Copyright 2024 The Pigweed Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +syntax = "proto3"; + +package gonk.adc; + +import "pw_protobuf_protos/common.proto"; + +message AdcMeasure { + int32 vbus_value = 1; + int32 vshunt_value = 2; +} + +message Payload { + repeated AdcMeasure adc_measurements = 1; + uint32 timestamp = 2; +} + +message FramedProto { + fixed32 magic_start = 1; + + Payload payload = 2; +} \ No newline at end of file
diff --git a/lib/adc/public/gonk/adc.h b/lib/adc/public/gonk/adc.h index 510b9a3..ee0393f 100644 --- a/lib/adc/public/gonk/adc.h +++ b/lib/adc/public/gonk/adc.h
@@ -14,13 +14,15 @@ namespace gonk::adc { +const uint32_t kFramedProtoMagicConstant = 0x7EAABB99; const uint8_t kTotalAdcCount = 11; -const uint8_t kStreamingAdcCount = 11; +const uint8_t kMaxStreamingAdcCount = 11; class Adc { public: - Adc(SPIClass &fpga_spi, uint16_t fpga_spi_baudrate, uint16_t fpga_cs_pin, - uint16_t fpga_mode_pin, uint16_t fpga_reset_pin, uint16_t fpga_valid_pin); + Adc(Stream &serial_stream, SPIClass &fpga_spi, uint32_t fpga_spi_baudrate, + uint16_t fpga_cs_pin, uint16_t fpga_mode_pin, uint16_t fpga_reset_pin, + uint16_t fpga_valid_pin); Status InitAdcs(); Status CheckAllAdcs(); @@ -28,6 +30,7 @@ void SetReadWriteMode(); void SetContinuousReadMode(); Status UpdateContinuousMeasurements(); + Status WriteMeasurementPacket(); // Writing and reading data over SPI. void StartSpiTransaction(); @@ -51,15 +54,24 @@ pw::Result<int32_t> GetShuntVoltageMeasurement(uint8_t adc_number); pw::Result<int32_t> GetBusVoltageMeasurement(uint8_t adc_number); - std::array<int32_t, kStreamingAdcCount> vbus_measurements_; - std::array<int32_t, kStreamingAdcCount> vshunt_measurements_; - private: + struct Measurement { + std::array<std::byte, 3> vbus_bytes_; + std::array<std::byte, 3> vshunt_bytes_; + int32_t vbus_value_; + int32_t vshunt_value_; + }; + std::array<Measurement, kMaxStreamingAdcCount> measurements_; + uint32_t previous_timestamp_; + uint32_t measurement_timestamp_; + uint32_t measurement_delta_micros_; + uint16_t fpga_reset_; uint16_t fpga_mode_; uint16_t fpga_valid_; uint16_t fpga_cs_; + Stream &serial_; SPIClass &fpga_spi_; SPISettings spi_settings_;
diff --git a/lib/pin_config/pin_config.cc b/lib/pin_config/pin_config.cc index ae70ebe..1dc187d 100644 --- a/lib/pin_config/pin_config.cc +++ b/lib/pin_config/pin_config.cc
@@ -12,7 +12,8 @@ namespace gonk::pin_config { -PinConfig::PinConfig() : fpga_spi(FpgaDspiMOSI, FpgaDspiMISO, FpgaDspiCLK) { +PinConfig::PinConfig() + : fpga_spi(FpgaDspiMOSI, FpgaDspiMISO, FpgaDspiCLK, FpgaDspiCS) { flash_spi = SPIClass(FlashMOSI, FlashMISO, FlashCLK); }
diff --git a/lib/spi_flash/public/gonk/spi_flash.h b/lib/spi_flash/public/gonk/spi_flash.h index f84d842..f9e4c62 100644 --- a/lib/spi_flash/public/gonk/spi_flash.h +++ b/lib/spi_flash/public/gonk/spi_flash.h
@@ -20,7 +20,7 @@ uint8_t product_version; }; - SpiFlash(uint16_t flash_cs, int baudrate, SPIClass &flash_spi); + SpiFlash(uint16_t flash_cs, uint32_t baudrate, SPIClass &flash_spi); Status WriteEnable(); Status WriteDisable();
diff --git a/lib/spi_flash/spi_flash.cc b/lib/spi_flash/spi_flash.cc index 9202ed1..8d75555 100644 --- a/lib/spi_flash/spi_flash.cc +++ b/lib/spi_flash/spi_flash.cc
@@ -16,7 +16,7 @@ namespace gonk::spi_flash { -SpiFlash::SpiFlash(uint16_t flash_cs, int baudrate, SPIClass &flash_spi) +SpiFlash::SpiFlash(uint16_t flash_cs, uint32_t baudrate, SPIClass &flash_spi) : flash_cs_(flash_cs), spi_settings_(baudrate, MSBFIRST, SPI_MODE0), flash_spi_(flash_spi) {}
diff --git a/targets/stm32f730r8tx_arduino/init.cc b/targets/stm32f730r8tx_arduino/init.cc index 437ee3d..eea9aa9 100644 --- a/targets/stm32f730r8tx_arduino/init.cc +++ b/targets/stm32f730r8tx_arduino/init.cc
@@ -18,4 +18,4 @@ // Arduino target specific init. pw_sys_io_arduino_Init() normally calls // Serial.begin(115200). Instead, we init serial to a higher baudrate here. -extern "C" void pw_arduino_Init() { Serial.begin(1000000); } +extern "C" void pw_arduino_Init() { Serial.begin(2000000); }
diff --git a/tools/BUILD.gn b/tools/BUILD.gn index 0b04cfe..3b8c8f1 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn
@@ -34,6 +34,7 @@ "$dir_pw_build/py", "$dir_pw_cli/py", "$dir_pw_presubmit/py", + "//lib/adc:protos.python", ] pylintrc = "$dir_pigweed/.pylintrc" }
diff --git a/tools/gonk_tools/write_fpga.py b/tools/gonk_tools/write_fpga.py index a3852f0..8e6f15c 100644 --- a/tools/gonk_tools/write_fpga.py +++ b/tools/gonk_tools/write_fpga.py
@@ -14,6 +14,7 @@ """Write a binary over serial to provision the Gonk FPGA.""" import argparse +from datetime import datetime from itertools import islice import operator from pathlib import Path @@ -21,9 +22,15 @@ import time from typing import Optional +import serial from serial import Serial from serial.tools.list_ports import comports -from serial.tools.miniterm import Miniterm +from serial.tools.miniterm import Miniterm, Transform + +import pw_cli.color +from gonk_adc.adc_measurement_pb2 import FramedProto + +_COLOR = pw_cli.color.colors() def _parse_args(): @@ -38,7 +45,7 @@ '-b', '--baudrate', type=int, - default=1000000, + default=2000000, help='Sets the baudrate for serial communication.', ) parser.add_argument( @@ -55,6 +62,10 @@ type=Path, help='FPGA Bitstream file.', ) + parser.add_argument( + '--skip-config', + action='store_true', + ) return parser.parse_args() @@ -84,6 +95,9 @@ FILE_START_BYTES = bytes.fromhex(FILE_START_STR) SYNC_START_BYTES = bytes.fromhex(SYNC_START_STR) +BIN_LOG_SYNC_START_STR = '0d 99 bb aa 7e' +BIN_LOG_SYNC_START_BYTES = bytes.fromhex(BIN_LOG_SYNC_START_STR) + class IncorrectBinaryFormat(Exception): """Exception raised when FPGA bitstream file is in an unexpected format.""" @@ -106,12 +120,113 @@ yield batch +class HandleBinaryData(Transform): + """Miniterm transform to handle incoming byte data.""" + def __init__(self) -> None: + self.include_binary = True + self.data = bytes() + self.timestamp_prefix = ' timestamp: ' + self.vbus_prefix = ' vbus:' + self.vshunt_prefix = ' vshunt:' + self.start_time = time.time() + self.time_format = '%Y%m%d %H:%M:%S.%f' + self.binary_format_started = False + + def rx(self, text, data=None): + """text received from serial port""" + if not data: + return text + + # Concat data with any existing captures + self.data = self.data + data + + # Check for binary start bytes. + if BIN_LOG_SYNC_START_BYTES in self.data: + self.binary_format_started = True + if not self.binary_format_started: + return text + + # Check for a complete proto capture. + sections = self.data.split(BIN_LOG_SYNC_START_BYTES, maxsplit=2) + if len(sections) < 3: + # Don't print anything / filter out this text + return '' + + # Section 0 contains previous bytes not between two proto start bytes. + _discard_bytes = sections[0] + # Section 1 is a complete proto packet. + proto_bytes = BIN_LOG_SYNC_START_BYTES + sections[1] + + # Done, reset self.data to the remaining bytes minus the above packet. + self.data = BIN_LOG_SYNC_START_BYTES + sections[2] + + # Parse the proto + try: + framed_proto = FramedProto() + framed_proto.ParseFromString(proto_bytes) + vbus_values = [] + vshunt_values = [] + for adc_measure in framed_proto.payload.adc_measurements: + vbus_values.append(adc_measure.vbus_value) + vshunt_values.append(adc_measure.vshunt_value) + + # TODO(tonymd): Use Python logging and separate output file + output = [ + (datetime.now().strftime(self.time_format) + '.' + + str(len(proto_bytes)) + '.'), + self.timestamp_prefix, + str(framed_proto.payload.timestamp), + self.vbus_prefix, + ','.join(str(value) for value in vbus_values), + self.vshunt_prefix, + ','.join(str(value) for value in vshunt_values), + ] + return ' '.join(output) + '\n' + except FramedProto.DecodeError: + # TODO(tonymd): Handle failed packets. + return 'FramedProto.DecodeError\n' + + def tx(self, text): + """text to be sent to serial port""" + return text + + def echo(self, text): + """text to be sent but displayed on console""" + return text + + +class MinitermBinary(Miniterm): + def reader(self): + """loop and copy serial->console""" + # pylint: disable=too-many-nested-blocks + try: + while self.alive and self._reader_alive: + # read all that is there or wait for one byte + data = self.serial.read(self.serial.in_waiting or 1) + if data: + if self.raw: + self.console.write_bytes(data) + else: + text = self.rx_decoder.decode(data) + for transformation in self.rx_transformations: + if hasattr(transformation, 'include_binary'): + text = transformation.rx(text, data) + else: + text = transformation.rx(text) + self.console.write(text) + except serial.SerialException: + self.alive = False + self.console.cancel() + raise # XXX handle instead of re-raise? + + def main( baudrate: int, bitstream_file: Path, port: Optional[str] = None, product: Optional[str] = None, serial_number: Optional[str] = None, + skip_config: bool = False, ) -> int: """Write a bitstream file over serial while monitoring output.""" @@ -134,7 +249,7 @@ serial_instance = Serial(port=port, baudrate=baudrate, timeout=320) # Use pyserial miniterm to monitor recieved data. - miniterm = Miniterm( + miniterm = MinitermBinary( serial_instance, echo=False, eol='crlf', @@ -142,24 +257,27 @@ ) # Use Ctrl-C as the exit character. (Miniterm default is Ctrl-]) miniterm.exit_character = chr(0x03) - miniterm.set_rx_encoding('utf-8') + miniterm.set_rx_encoding('utf-8', errors='backslashreplace') miniterm.set_tx_encoding('utf-8') # Start monitoring serial data. miniterm.start() - # Wait a couple seconds to print early log messages from Gonk. - time.sleep(2) + if not skip_config: + # Wait a couple seconds to print early log messages from Gonk. + time.sleep(2) - # Write out the bitstream in batches. - print('Sending bitstream...') - written_bytes: int = 0 - for byte_batch in batched(bitstream_bytes, 8): - result = serial_instance.write(byte_batch) - if result: - written_bytes += result - serial_instance.flush() - print(f'Done sending bitstream. Wrote {written_bytes}') + # Write out the bitstream in batches. + print('Sending bitstream...') + written_bytes: int = 0 + for byte_batch in batched(bitstream_bytes, 8): + result = serial_instance.write(byte_batch) + if result: + written_bytes += result + serial_instance.flush() + print(f'Done sending bitstream. Wrote {written_bytes}') + + miniterm.rx_transformations.append(HandleBinaryData()) # Wait for ctrl-c, then shutdown miniterm. try: