/* * Copyright 2020 Birte Kristina Friesel * * SPDX-License-Identifier: BSD-2-Clause */ #ifndef PTALOG_H #define PTALOG_H #include #include "driver/stdout.h" #ifdef PTALOG_GPIO #include "driver/gpio.h" #if defined(PTALOG_GPIO_BEFORE) || defined(PTALOG_GPIO_BAR) #include "arch.h" #endif #endif #ifdef PTALOG_TIMING #include "driver/counter.h" #endif class PTALog { private: PTALog(const PTALog& copy); #ifdef PTALOG_GPIO uint8_t sync_pin; #endif public: typedef struct { uint8_t transition_id; #ifdef PTALOG_TIMING counter_value_t timer; counter_overflow_t overflow; counter_value_t prev_state_timer; counter_overflow_t prev_state_overflow; #endif #ifdef PTALOG_WITH_RETURNVALUES uint16_t return_value; #endif } log_entry; int const max_entry = 15; log_entry log[16]; uint8_t log_index; #ifdef PTALOG_GPIO PTALog(uint8_t pin_number) : sync_pin(pin_number), log_index(0) {} #else PTALog() : log_index(0) {} #endif void passTransition(uint8_t transition_id) { log[log_index].transition_id = transition_id; if (log_index < max_entry) { log_index++; } } #ifdef PTALOG_TIMING /* * This function is meant to estimate the overhead of counter.start() * and counter.stop(). It immediately restarts the counter to ensure that * state durations are captured accurately. * This means that the overflow value is unusable. We assume that there * are no overflows in this short time span. */ inline void passNop() { counter.start(); counter.stop(); counter.start(); // .value is kept until .stop kout << "[PTA] nop=" << counter.value << "/" << 0 << endl; } #endif void reset() { log_index = 0; } void startBenchmark(uint8_t id) { kout << "[PTA] benchmark start, id=" << dec << id << endl; #ifdef PTALOG_GPIO gpio.output(sync_pin); #endif } void stopBenchmark() { #ifdef PTALOG_TIMING counter.stop(); kout << "[PTA] benchmark stop, cycles=" << counter.value << "/" << counter.overflow << endl; #else kout << "[PTA] benchmark stop" << endl; #endif } void dump(uint16_t trace_id) { kout << "[PTA] trace=" << dec << trace_id << " count=" << log_index << endl; for (uint8_t i = 0; i < log_index; i++) { kout << "[PTA] transition=" << log[i].transition_id; #ifdef PTALOG_TIMING kout << " prevcycles=" << log[i].prev_state_timer << "/" << log[i].prev_state_overflow; kout << " cycles=" << log[i].timer << "/" << log[i].overflow; #endif #ifdef PTALOG_WITH_RETURNVALUES kout << " return=" << log[i].return_value; #endif kout << endl; } } #ifdef PTALOG_GPIO_BAR void startTransition(char const *code, uint8_t code_length) { counter.stop(); log[log_index - 1].prev_state_timer = counter.value; log[log_index - 1].prev_state_overflow = counter.overflow; // Quiet zone (must last at least 10x longer than a module) arch.sleep_ms(60); for (uint8_t byte = 0; byte < code_length; byte++) { for (uint16_t mask = 0x01; mask <= 0x80; mask <<= 1) { // a single module, which is part of a bar gpio.write(sync_pin, code[byte] & mask ? 1 : 0); arch.sleep_ms(5); } } gpio.write(sync_pin, 0); // Quiet zone arch.sleep_ms(60); counter.start(); } #else inline void startTransition() { #ifdef PTALOG_GPIO_BEFORE gpio.write(sync_pin, 1); arch.sleep_ms(10); gpio.write(sync_pin, 0); arch.sleep_ms(10); #endif #ifdef PTALOG_TIMING counter.stop(); log[log_index - 1].prev_state_timer = counter.value; log[log_index - 1].prev_state_overflow = counter.overflow; counter.start(); #endif #if defined(PTALOG_GPIO) && !defined(PTALOG_GPIO_BEFORE) gpio.write(sync_pin, 1); #endif } #endif #ifdef PTALOG_WITH_RETURNVALUES inline void logReturn(uint16_t ret) { log[log_index - 1].return_value = ret; } #endif inline void stopTransition() { #if defined(PTALOG_GPIO) && !defined(PTALOG_GPIO_BEFORE) && !defined(PTALOG_GPIO_BAR) gpio.write(sync_pin, 0); #endif #ifdef PTALOG_TIMING counter.stop(); log[log_index - 1].timer = counter.value; log[log_index - 1].overflow = counter.overflow; counter.start(); #endif } }; #endif