blob: 2219e660d267f441e91b299bf32b864df8174758 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "otbn_trace_entry.h"
#include <algorithm>
#include <cassert>
#include <iostream>
#include <regex>
#include <sstream>
void OtbnTraceEntry::from_rtl_trace(const std::string &trace) {
size_t eol = trace.find('\n');
hdr_ = trace.substr(0, eol);
while (eol != std::string::npos) {
size_t bol = eol + 1;
eol = trace.find('\n', bol);
size_t line_len =
(eol == std::string::npos) ? std::string::npos : eol - bol;
std::string line = trace.substr(bol, line_len);
if (line.size() > 0 && line[0] == '>')
writes_.push_back(line);
}
std::sort(writes_.begin(), writes_.end());
auto last = std::unique(writes_.begin(), writes_.end());
writes_.erase(last, writes_.end());
}
bool OtbnTraceEntry::operator==(const OtbnTraceEntry &other) const {
return hdr_ == other.hdr_ && writes_ == other.writes_;
}
void OtbnTraceEntry::print(const std::string &indent, std::ostream &os) const {
os << indent << hdr_ << "\n";
for (const std::string &write : writes_) {
os << indent << write << "\n";
}
}
void OtbnTraceEntry::take_writes(const OtbnTraceEntry &other) {
if (!other.writes_.empty()) {
for (const std::string &write : other.writes_) {
writes_.push_back(write);
}
std::sort(writes_.begin(), writes_.end());
auto last = std::unique(writes_.begin(), writes_.end());
writes_.erase(last, writes_.end());
}
}
bool OtbnTraceEntry::empty() const { return hdr_.empty(); }
bool OtbnTraceEntry::is_stall() const { return !empty() && hdr_[0] == 'S'; }
bool OtbnTraceEntry::is_exec() const { return !empty() && hdr_[0] == 'E'; }
bool OtbnTraceEntry::is_compatible(const OtbnTraceEntry &prev) const {
// Two entries are compatible if they might both come from the multi-cycle
// execution of one instruction. For example, you might expect to see these
// two lines:
//
// S PC: 0x00000010, insn: 0x00107db8
// E PC: 0x00000010, insn: 0x00107db8
//
// which show an instruction at 0x10 stalling for a cycle and then managing
// to execute.
//
// As an added complication, if we see an IMEM fetch error, the entry will
// become
//
// E PC: 0x00000010, insn: ??
//
// and that's fine. So the rule is:
//
// - Compare the two lines from character 1 onwards.
// - If they match: accept.
// - Otherwise, if the second line has no '?' then reject.
// - If there is a '?' and they match up to that point, accept.
// - Otherwise: reject
//
// (This wrongly accepts some malformed examples, but that's fine: it's just
// meant as a quick check to make sure our trace machinery isn't dropping
// entries)
bool exact_match =
0 == hdr_.compare(1, std::string::npos, prev.hdr_, 1, std::string::npos);
if (exact_match)
return true;
size_t first_qm = hdr_.find('?', 1);
if (first_qm == std::string::npos)
return false;
return 0 == hdr_.compare(1, first_qm - 1, prev.hdr_, 1, first_qm - 1);
}
bool OtbnIssTraceEntry::from_iss_trace(const std::vector<std::string> &lines) {
// Read FSM. state 0 = read header; state 1 = read mnemonic (for E
// lines); state 2 = read writes
int state = 0;
std::regex re("# @0x([0-9a-f]{8}): (.*)");
std::smatch match;
for (const std::string &line : lines) {
switch (state) {
case 0:
hdr_ = line;
state = (!line.empty() && line[0] == 'E') ? 1 : 2;
break;
case 1:
// This some "special" extra data from the ISS that we use for
// functional coverage calculations. The line should be of the form
//
// # @ADDR: MNEMONIC
//
// where ADDR is an 8-digit instruction address (in hex) and mnemonic
// is the string mnemonic.
if (!std::regex_match(line, match, re)) {
std::cerr << "Bad 'special' line for ISS trace with header `" << hdr_
<< "': `" << line << "'.\n";
return false;
}
assert(match.size() == 3);
data_.insn_addr =
(uint32_t)strtoul(match[1].str().c_str(), nullptr, 16);
data_.mnemonic = match[2].str();
state = 2;
break;
default: {
assert(state == 2);
// Ignore '!' lines (which are used to tell the simulation about
// external register changes, not tracked by the RTL core simulation)
bool is_bang = (line.size() > 0 && line[0] == '!');
if (!is_bang) {
writes_.push_back(line);
}
break;
}
}
}
// We shouldn't be in state 1 here: that would mean an E line with no
// follow-up '#' line.
if (state == 1) {
std::cerr << "No 'special' line for ISS trace with header `" << hdr_
<< "'.\n";
return false;
}
std::sort(writes_.begin(), writes_.end());
return true;
}