Move top-level .cpp files into their respective folders (#1249)

* Update Makefile, move top-level .cpp files

* Move top-level .cpp files into their respective folders
This commit is contained in:
Uwe Seimet
2023-10-16 18:27:18 +02:00
committed by GitHub
parent 41bdcd4aed
commit aa927cb504
12 changed files with 18 additions and 21 deletions
+19
View File
@@ -0,0 +1,19 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "scsimon/sm_core.h"
using namespace std;
int main(int argc, char *argv[])
{
const vector<char *> args(argv, argv + argc);
return ScsiMon().run(args);
}
+263
View File
@@ -0,0 +1,263 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2021-2022 akuker
//
//---------------------------------------------------------------------------
#include "scsimon/sm_core.h"
#include "hal/gpiobus.h"
#include "hal/gpiobus_factory.h"
#include "scsimon/sm_reports.h"
#include "hal/log.h"
#include "shared/piscsi_version.h"
#include "shared/piscsi_util.h"
#include <climits>
#include <csignal>
#include <getopt.h>
#include <iostream>
#include <sched.h>
#include <sys/time.h>
using namespace std;
using namespace piscsi_util;
void ScsiMon::KillHandler(int)
{
running = false;
}
void ScsiMon::ParseArguments(const vector<char *> &args)
{
int opt;
while ((opt = getopt(static_cast<int>(args.size()), args.data(), "-Hhb:i:")) != -1) {
switch (opt) {
// The three options below are kind of a compound option with two letters
case 'h':
case 'H':
print_help = true;
break;
case 'b':
buff_size = atoi(optarg);
break;
case 'i':
input_file_name = optarg;
import_data = true;
break;
case 1:
file_base_name = optarg;
break;
default:
cout << "default: " << optarg << endl;
break;
}
}
/* Process any remaining command line arguments (not options). */
if (optind < static_cast<int>(args.size())) {
while (optind < static_cast<int>(args.size())) {
file_base_name = args[optind];
optind++;
}
}
vcd_file_name = file_base_name;
vcd_file_name += ".vcd";
json_file_name = file_base_name;
json_file_name += ".json";
html_file_name = file_base_name;
html_file_name += ".html";
}
void ScsiMon::PrintHelpText(const vector<char *> &args) const
{
spdlog::info(string(args[0]) + " -i [input file json] -b [buffer size] [output file]");
spdlog::info(" -i [input file json] - scsimon will parse the json file instead of capturing new data");
spdlog::info(" If -i option is not specified, scsimon will read the gpio pins");
spdlog::info(" -b [buffer size] - Override the default buffer size of " + to_string(buff_size));
spdlog::info(" [output file] - Base name of the output files. The file extension (ex: .json)");
spdlog::info(" will be appended to this file name");
}
void ScsiMon::Banner() const
{
if (import_data) {
spdlog::info("Reading input file: " + input_file_name);
} else {
spdlog::info("Reading live data from the GPIO pins");
spdlog::info(" Connection type: " + CONNECT_DESC);
}
spdlog::info(" Data buffer size: " + to_string(buff_size));
spdlog::info(" ");
spdlog::info("Generating output files:");
spdlog::info(" " + vcd_file_name + " - Value Change Dump file that can be opened with GTKWave");
spdlog::info(" " + json_file_name + " - JSON file with raw data");
spdlog::info(" " + html_file_name + " - HTML file with summary of commands");
}
bool ScsiMon::Init()
{
// Interrupt handler settings
if (signal(SIGINT, KillHandler) == SIG_ERR) {
return false;
}
if (signal(SIGHUP, KillHandler) == SIG_ERR) {
return false;
}
if (signal(SIGTERM, KillHandler) == SIG_ERR) {
return false;
}
bus = GPIOBUS_Factory::Create(BUS::mode_e::TARGET);
if (bus == nullptr) {
spdlog::error("Unable to intiailize the GPIO bus. Exiting....");
return false;
}
running = false;
return true;
}
void ScsiMon::Cleanup() const
{
if (!import_data) {
spdlog::info("Stopping data collection ...");
}
spdlog::info(" ");
spdlog::info("Generating " + vcd_file_name + "...");
scsimon_generate_value_change_dump(vcd_file_name, data_buffer);
spdlog::info("Generating " + json_file_name + "...");
scsimon_generate_json(json_file_name, data_buffer);
spdlog::info("Generating " + html_file_name + "...");
scsimon_generate_html(html_file_name, data_buffer);
bus->Cleanup();
}
void ScsiMon::Reset() const
{
bus->Reset();
}
int ScsiMon::run(const vector<char *> &args)
{
#ifdef DEBUG
spdlog::set_level(spdlog::level::trace);
#else
spdlog::set_level(spdlog::level::info);
#endif
spdlog::set_pattern("%^[%l]%$ %v");
piscsi_util::Banner("(SCSI Monitor Capture Tool)");
ParseArguments(args);
shared_ptr<DataSample> prev_sample = nullptr;
shared_ptr<DataSample> this_sample = nullptr;
timeval start_time;
timeval stop_time;
uint64_t loop_count = 0;
timeval time_diff;
uint64_t elapsed_us;
if (print_help) {
PrintHelpText(args);
exit(0);
}
Banner();
data_buffer.reserve(buff_size);
if (import_data) {
data_idx = scsimon_read_json(input_file_name, data_buffer);
if (data_idx > 0) {
spdlog::debug("Read " + to_string(data_idx) + " samples from '" + input_file_name + "'");
Cleanup();
}
exit(0);
}
spdlog::info(" ");
spdlog::info("Now collecting data.... Press CTRL-C to stop.");
spdlog::info(" ");
// Initialize
int ret = 0;
if (!Init()) {
ret = EPERM;
goto init_exit;
}
// Reset
Reset();
#ifdef __linux__
// Set the affinity to a specific processor core
FixCpu(3);
// Scheduling policy setting (highest priority)
struct sched_param schparam;
schparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &schparam);
#endif
// Start execution
running = true;
bus->SetACK(false);
(void)gettimeofday(&start_time, nullptr);
// Main Loop
while (running) {
loop_count++;
if (loop_count > LLONG_MAX - 1) {
spdlog::info("Maximum amount of time has elapsed. SCSIMON is terminating.");
running = false;
}
if (data_idx >= (buff_size - 2)) {
spdlog::info("Internal data buffer is full. SCSIMON is terminating.");
running = false;
}
this_sample = bus->GetSample(loop_count);
if ((prev_sample == nullptr) || (this_sample->GetRawCapture() != prev_sample->GetRawCapture())) {
data_buffer.push_back(this_sample);
data_idx++;
prev_sample = this_sample;
}
continue;
}
// Collect one last sample, otherwise it looks like the end of the data was cut off
if (data_idx < buff_size) {
data_buffer.push_back(bus->GetSample(loop_count));
data_idx++;
}
(void)gettimeofday(&stop_time, nullptr);
timersub(&stop_time, &start_time, &time_diff);
elapsed_us = ((time_diff.tv_sec * 1000000) + time_diff.tv_usec);
spdlog::info("Elapsed time: " + to_string(elapsed_us) + " microseconds (" + to_string(elapsed_us / 1000000) +
" seconds");
spdlog::info("Collected " + to_string(data_idx) + " changes");
ns_per_loop = (double)(elapsed_us * 1000) / (double)loop_count;
spdlog::info("Read the SCSI bus " + to_string(loop_count) + " times with an average of " +
to_string(ns_per_loop) + " ns for each read");
Cleanup();
init_exit:
exit(ret);
}
+60
View File
@@ -0,0 +1,60 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2022-2023 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include "hal/bus.h"
#include "hal/data_sample.h"
#include <memory>
#include <vector>
#include <atomic>
using namespace std;
// TODO Make static fields/methods non-static
class ScsiMon
{
public:
ScsiMon() = default;
~ScsiMon() = default;
int run(const vector<char *> &);
inline static double ns_per_loop;
private:
void ParseArguments(const vector<char *> &);
void PrintHelpText(const vector<char *> &) const;
void Banner() const;
bool Init();
void Cleanup() const;
void Reset() const;
static void KillHandler(int);
static inline atomic<bool> running;
shared_ptr<BUS> bus;
uint32_t buff_size = 1000000;
vector<shared_ptr<DataSample>> data_buffer;
uint32_t data_idx = 0;
bool print_help = false;
bool import_data = false;
string file_base_name = "log";
string vcd_file_name;
string json_file_name;
string html_file_name;
string input_file_name;
};
+189
View File
@@ -0,0 +1,189 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2021-2022 akuker
//
//---------------------------------------------------------------------------
#include "hal/log.h"
#include "shared/piscsi_version.h"
#include "sm_reports.h"
#include <iostream>
#include <fstream>
using namespace std;
const static string html_header = R"(
<html>
<head>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
h1 {
text-align: center;
}
h2 {
text-align: center;
}
h3 {
text-align: center;
}
pre {
text-align: center;
}
.collapsible {
background-color: #777;
color: white;
cursor: pointer;
width: 100%;
border: none;
text-align: left;
outline: none;
}
.active, .collapsible:hover {
background-color: #555;
}
.content {
padding: 0;
display: none;
overflow: hidden;
background-color: #f1f1f1;
}
</style>
</head>
)";
static void print_copyright_info(ofstream& html_fp)
{
html_fp << "<table>" << endl \
<< "<h1>PiSCSI scsimon Capture Tool</h1>" << endl \
<< "<pre>Version " << piscsi_get_version_string() \
<< __DATE__ << " " << __TIME__ << endl \
<< "Copyright (C) 2016-2020 GIMONS" << endl \
<< "Copyright (C) 2020-2021 Contributors to the PiSCSI project" << endl \
<< "</pre></table>" << endl \
<< "<br>" << endl;
}
static const string html_footer = R"(
</table>
<script>
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.display === "block") {
content.style.display = "none";
} else {
content.style.display = "block";
}
});
}
</script>
</html>
)";
static void print_html_data(ofstream& html_fp, const vector<shared_ptr<DataSample>> &data_capture_array)
{
shared_ptr<DataSample> data;
bool prev_data_valid = false;
bool curr_data_valid;
uint32_t selected_id = 0;
phase_t prev_phase = phase_t::busfree;
bool close_row = false;
int data_space_count = 0;
bool collapsible_div_active = false;
bool button_active = false;
html_fp << "<table>" << endl;
for (auto data: data_capture_array) {
curr_data_valid = data->GetACK() && data->GetREQ();
phase_t phase = data->GetPhase();
if (phase == phase_t::selection && !data->GetBSY()) {
selected_id = data->GetDAT();
}
if (prev_phase != phase) {
if (close_row) {
if (collapsible_div_active) {
html_fp << "</code></div>";
}
else if (button_active) {
html_fp << "</code></button>";
}
html_fp << "</td>";
if (data_space_count < 1) {
html_fp << "<td>--</td>";
}
else {
html_fp << "<td>wc: " << std::dec << "(0x" << std::hex << data_space_count << ")</td>";
}
html_fp << "</tr>" << endl;
data_space_count = 0;
}
html_fp << "<tr>";
close_row = true; // Close the row the next time around
html_fp << "<td>" << (double)data->GetTimestamp() / 100000 << "</td>";
html_fp << "<td>" << data->GetPhaseStr() << "</td>";
html_fp << "<td>" << std::hex << selected_id << "</td>";
html_fp << "<td>";
}
if (curr_data_valid && !prev_data_valid) {
if (data_space_count == 0) {
button_active = true;
html_fp << "<button type=\"button\" class=\"collapsible\"><code>";
}
if ((data_space_count % 16) == 0) {
html_fp << std::hex << data_space_count << ": ";
}
html_fp << fmt::format("{0:02X}", data->GetDAT());
data_space_count++;
if ((data_space_count % 4) == 0) {
html_fp << " ";
}
if (data_space_count == 16) {
html_fp << "</code></button><div class=\"content\"><code>" << endl;
collapsible_div_active = true;
button_active = false;
}
if (((data_space_count % 16) == 0) && (data_space_count > 17)) {
html_fp << "<br>" << endl;
}
}
prev_data_valid = curr_data_valid;
prev_phase = phase;
}
}
void scsimon_generate_html(const string &filename, const vector<shared_ptr<DataSample>> &data_capture_array)
{
spdlog::info("Creating HTML report file (" + filename + ")");
ofstream html_ofstream;
html_ofstream.open(filename.c_str(), ios::out);
html_ofstream << html_header;
print_copyright_info(html_ofstream);
print_html_data(html_ofstream, data_capture_array);
html_ofstream << html_footer;
html_ofstream.close();
}
+92
View File
@@ -0,0 +1,92 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2021-2022 akuker
//
//---------------------------------------------------------------------------
#include "hal/data_sample_raspberry.h"
#include "hal/log.h"
#include "sm_reports.h"
#include "string.h"
#include <fstream>
#include <iostream>
using namespace std;
const string timestamp_label = "\"timestamp\":\"0x";
const string data_label = "\"data\":\"0x";
uint32_t scsimon_read_json(const string &json_filename, vector<shared_ptr<DataSample>> &data_capture_array)
{
std::ifstream json_file(json_filename);
uint32_t sample_count = 0;
while (json_file) {
string str_buf;
std::getline(json_file, str_buf);
string timestamp;
string data;
uint64_t timestamp_uint;
uint32_t data_uint;
char *ptr;
size_t timestamp_pos = str_buf.find(timestamp_label);
if (timestamp_pos == string::npos)
continue;
timestamp = str_buf.substr(timestamp_pos + timestamp_label.length(), 16);
timestamp_uint = strtoull(timestamp.c_str(), &ptr, 16);
size_t data_pos = str_buf.find(data_label);
if (data_pos == string::npos)
continue;
data = str_buf.substr(data_pos + data_label.length(), 8);
data_uint = static_cast<uint32_t>(strtoul(data.c_str(), &ptr, 16));
// For reading in JSON files, we'll just assume raspberry pi data types
data_capture_array.push_back(make_unique<DataSample_Raspberry>(data_uint, timestamp_uint));
sample_count++;
if (sample_count == UINT32_MAX) {
spdlog::warn("Maximum number of samples read. Some data may not be included.");
break;
}
}
json_file.close();
return sample_count;
}
//---------------------------------------------------------------------------
//
// Generate JSON Output File
//
//---------------------------------------------------------------------------
void scsimon_generate_json(const string &filename, const vector<shared_ptr<DataSample>> &data_capture_array)
{
spdlog::trace("Creating JSON file (" + filename + ")");
ofstream json_ofstream;
json_ofstream.open(filename.c_str(), ios::out);
json_ofstream << "[" << endl;
size_t i = 0;
size_t capture_count = data_capture_array.size();
for (auto data : data_capture_array) {
json_ofstream << fmt::format("{{\"id\": \"{0:d}\", \"timestamp\":\"{1:#016x}\", \"data\":\"{2:#08x}\"}}", i,
data->GetTimestamp(), data->GetRawCapture());
if (i != (capture_count - 1)) {
json_ofstream << ",";
}
json_ofstream << endl;
i++;
}
json_ofstream << "]" << endl;
json_ofstream.close();
}
+19
View File
@@ -0,0 +1,19 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2020-2021 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#include "hal/data_sample.h"
#include <memory>
uint32_t scsimon_read_json(const string &json_filename, vector<shared_ptr<DataSample>> &data_capture_array);
void scsimon_generate_html(const string &filename, const vector<shared_ptr<DataSample>> &data_capture_array);
void scsimon_generate_json(const string &filename, const vector<shared_ptr<DataSample>> &data_capture_array);
void scsimon_generate_value_change_dump(const string &filename, const vector<shared_ptr<DataSample>> &data_capture_array);
+147
View File
@@ -0,0 +1,147 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2020-2021 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#include "hal/data_sample.h"
#include "hal/gpiobus.h"
#include "hal/log.h"
#include "sm_core.h"
#include "sm_reports.h"
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
//---------------------------------------------------------------------------
//
// Constant declarations
//
//---------------------------------------------------------------------------
// Symbol definition for the VCD file
// These are just arbitrary symbols. They can be anything allowed by the VCD file format,
// as long as they're consistently used.
const char SYMBOL_PIN_DAT = '#';
const char SYMBOL_PIN_ATN = '+';
const char SYMBOL_PIN_RST = '$';
const char SYMBOL_PIN_ACK = '%';
const char SYMBOL_PIN_REQ = '^';
const char SYMBOL_PIN_MSG = '&';
const char SYMBOL_PIN_CD = '*';
const char SYMBOL_PIN_IO = '(';
const char SYMBOL_PIN_BSY = ')';
const char SYMBOL_PIN_SEL = '-';
const char SYMBOL_PIN_PHASE = '=';
// We'll use position 0 in the prev_value array to store the previous phase
const int PIN_PHASE = 0;
//---------------------------------------------------------------------------
//
// Variable declarations
//
//---------------------------------------------------------------------------
static array<uint8_t,32> prev_value = {0xFF};
static void vcd_output_if_changed_phase(ofstream &fp, phase_t data, int pin, char symbol)
{
if (prev_value[pin] != static_cast<uint8_t>(data)) {
prev_value[pin] = static_cast<uint8_t>(data);
fp << "s" << BUS::GetPhaseStrRaw(data) << " " << symbol << endl;
}
}
static void vcd_output_if_changed_bool(ofstream &fp, bool data, int pin, char symbol)
{
if (prev_value[pin] != data) {
prev_value[pin] = data;
fp << data << symbol << endl;
}
}
static void vcd_output_if_changed_byte(ofstream &fp, uint8_t data, int pin, char symbol)
{
if (prev_value[pin] != data) {
prev_value[pin] = data;
fp << "b" << fmt::format("{0:8b}", data) << " " << symbol << endl;
}
}
void scsimon_generate_value_change_dump(const string &filename, const vector<shared_ptr<DataSample>> &data_capture_array)
{
spdlog::trace("Creating Value Change Dump file (" + filename + ")");
ofstream vcd_ofstream;
vcd_ofstream.open(filename.c_str(), ios::out);
// Get the current time
time_t rawtime;
time(&rawtime);
struct tm timeinfo;
localtime_r(&rawtime, &timeinfo);
string timestamp;
timestamp.resize(256);
strftime(&timestamp[0], timestamp.size(), "%d-%m-%Y %H-%M-%S", &timeinfo);
vcd_ofstream << "$date" << endl
<< timestamp << endl
<< "$end" << endl
<< "$version" << endl
<< " VCD generator tool version info text." << endl
<< "$end" << endl
<< "$comment" << endl
<< " Tool build date:" << __TIMESTAMP__ << endl
<< "$end" << endl
<< "$timescale 1 ns $end" << endl
<< "$scope module logic $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_BSY << " BSY $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_SEL << " SEL $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_CD << " CD $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_IO << " IO $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_MSG << " MSG $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_REQ << " REQ $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_ACK << " ACK $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_ATN << " ATN $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_RST << " RST $end" << endl
<< "$var wire 8 " << SYMBOL_PIN_DAT << " data $end" << endl
<< "$var string 1 " << SYMBOL_PIN_PHASE << " phase $end" << endl
<< "$upscope $end" << endl
<< "$enddefinitions $end" << endl;
// Initial values - default to zeros
vcd_ofstream << "$dumpvars" << endl
<< "0" << SYMBOL_PIN_BSY << endl
<< "0" << SYMBOL_PIN_SEL << endl
<< "0" << SYMBOL_PIN_CD << endl
<< "0" << SYMBOL_PIN_IO << endl
<< "0" << SYMBOL_PIN_MSG << endl
<< "0" << SYMBOL_PIN_REQ << endl
<< "0" << SYMBOL_PIN_ACK << endl
<< "0" << SYMBOL_PIN_ATN << endl
<< "0" << SYMBOL_PIN_RST << endl
<< "b00000000 " << SYMBOL_PIN_DAT << endl
<< "$end" << endl;
for (shared_ptr<DataSample> cur_sample : data_capture_array) {
vcd_ofstream << "#" << (double)cur_sample->GetTimestamp() * ScsiMon::ns_per_loop << endl;
vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetBSY(), PIN_BSY, SYMBOL_PIN_BSY);
vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetSEL(), PIN_SEL, SYMBOL_PIN_SEL);
vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetCD(), PIN_CD, SYMBOL_PIN_CD);
vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetIO(), PIN_IO, SYMBOL_PIN_IO);
vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetMSG(), PIN_MSG, SYMBOL_PIN_MSG);
vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetREQ(), PIN_REQ, SYMBOL_PIN_REQ);
vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetACK(), PIN_ACK, SYMBOL_PIN_ACK);
vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetATN(), PIN_ATN, SYMBOL_PIN_ATN);
vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetRST(), PIN_RST, SYMBOL_PIN_RST);
vcd_output_if_changed_byte(vcd_ofstream, cur_sample->GetDAT(), PIN_DT0, SYMBOL_PIN_DAT);
vcd_output_if_changed_phase(vcd_ofstream, cur_sample->GetPhase(), PIN_PHASE, SYMBOL_PIN_PHASE);
}
vcd_ofstream.close();
}