Skip to content

Add Instruction Test #120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/machine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ if(NOT ${WASM})
PRIVATE ${QtLib}::Core ${QtLib}::Test)
add_test(NAME cache COMMAND cache_test)

add_executable(instruction_test_gendata
instruction.test.gendata.cpp
)
target_link_libraries(instruction_test_gendata
PRIVATE machine ${QtLib}::Core
)

add_executable(instruction_test
csr/controlstate.cpp
csr/controlstate.h
Expand Down Expand Up @@ -216,5 +223,5 @@ if(NOT ${WASM})
add_test(NAME core COMMAND core_test)

add_custom_target(machine_unit_tests
DEPENDS alu_test registers_test memory_test cache_test instruction_test program_loader_test core_test)
DEPENDS alu_test registers_test memory_test cache_test instruction_test_gendata instruction_test program_loader_test core_test)
endif()
6 changes: 3 additions & 3 deletions src/machine/csr/controlstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,16 +255,16 @@ namespace machine { namespace CSR {

class RegisterMapByName {
bool initialized = false;
std::unordered_map<const char *, size_t> map;
std::unordered_map<std::string, size_t> map;

void init() {
for (size_t i = 0; i < REGISTERS.size(); i++) {
map.emplace(REGISTERS[i].name, i);
map.emplace(std::string(REGISTERS[i].name), i);
}
initialized = true;
}
public:
size_t at(const char* name) {
size_t at(std::string name) {
if (!initialized) init();
return map.at(name);
}
Expand Down
108 changes: 56 additions & 52 deletions src/machine/instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,55 @@ Instruction &Instruction::operator=(const Instruction &c) {
return *this;
}

QString field_to_string(int32_t field, const ArgumentDesc* arg_desc, Address inst_addr, bool symbolic_registers_enabled) {
QString res = "";
if (arg_desc->min < 0) {
field = extend(field, [&]() {
int sum = (int)arg_desc->arg.shift;
for (auto chunk : arg_desc->arg) {
sum += chunk.count;
}
return sum;
}());
}
switch (arg_desc->kind) {
case 'g': {
if (symbolic_registers_enabled) {
res += QString(Rv_regnames[field]);
} else {
res += "x" + QString::number(field);
}
break;
}
case 'p':
case 'a': {
field += (int32_t)inst_addr.get_raw();
res.append(str::asHex(field));
break;
}
case 'o':
case 'n': {
if (arg_desc->min < 0) {
res += QString::number((int32_t)field, 10);
} else {
res.append(str::asHex(uint32_t(field)));
}
break;
}
case 'E': {
if (symbolic_registers_enabled) {
try {
res += CSR::REGISTERS[CSR::REGISTER_MAP.at(CSR::Address(field))].name;
} catch (std::out_of_range &e) { res.append(str::asHex(field)); }
} else {
res.append(str::asHex(field));
}
break;
}
}
return res;
}

QString Instruction::to_str(Address inst_addr) const {
const InstructionMap &im = InstructionMapFind(dt);
// TODO there are exception where some fields are zero and such so we should
Expand All @@ -787,50 +836,7 @@ QString Instruction::to_str(Address inst_addr) const {
continue;
}
auto field = (int32_t)arg_desc->arg.decode(this->dt);
if (arg_desc->min < 0) {
field = extend(field, [&]() {
int sum = (int)arg_desc->arg.shift;
for (auto chunk : arg_desc->arg) {
sum += chunk.count;
}
return sum;
}());
}
switch (arg_desc->kind) {
case 'g': {
if (symbolic_registers_enabled) {
res += QString(Rv_regnames[field]);
} else {
res += "x" + QString::number(field);
}
break;
}
case 'p':
case 'a': {
field += (int32_t)inst_addr.get_raw();
res.append(str::asHex(uint32_t(field)));
break;
}
case 'o':
case 'n': {
if (arg_desc->min < 0) {
res += QString::number((int32_t)field, 10);
} else {
res.append(str::asHex(uint32_t(field)));
}
break;
}
case 'E': {
if (symbolic_registers_enabled) {
try {
res += CSR::REGISTERS[CSR::REGISTER_MAP.at(CSR::Address(field))].name;
} catch (std::out_of_range &e) { res.append(str::asHex(field)); }
} else {
res.append(str::asHex(field));
}
break;
}
}
res += field_to_string(field, arg_desc, inst_addr, symbolic_registers_enabled);
}
}
return res;
Expand Down Expand Up @@ -1375,14 +1381,15 @@ bool parse_immediate_value(

uint16_t parse_csr_address(const QString &field_token, uint &chars_taken) {
if (field_token.at(0).isLetter()) {
size_t index = CSR::REGISTER_MAP_BY_NAME.at(qPrintable(field_token));
if (index < 0) {
try {
size_t index = CSR::REGISTER_MAP_BY_NAME.at(field_token.toStdString());
auto &reg = CSR::REGISTERS[index];
chars_taken = strlen(reg.name);
return reg.address.data;
} catch (std::out_of_range &e) {
chars_taken = 0;
return 0;
}
auto &reg = CSR::REGISTERS[index];
chars_taken = strlen(reg.name);
return reg.address.data;
} else {
char *r;
uint64_t val;
Expand Down Expand Up @@ -1449,9 +1456,6 @@ void Instruction::set_symbolic_registers(bool enable) {
symbolic_registers_enabled = enable;
}

inline int32_t Instruction::extend(uint32_t value, uint32_t used_bits) const {
return value | ~((value & (1 << (used_bits - 1))) - 1);
}

void Instruction::append_recognized_registers(QStringList &list) {
for (auto name : Rv_regnames) {
Expand Down
5 changes: 4 additions & 1 deletion src/machine/instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ struct TokenizedInstruction {
struct RelocExpression;
typedef QVector<RelocExpression *> RelocExpressionList;

inline int32_t extend(uint32_t value, uint32_t used_bits) {
return value | ~((value & (1 << (used_bits - 1))) - 1);
}

class Instruction {
public:
Instruction();
Expand Down Expand Up @@ -208,7 +212,6 @@ class Instruction {
RelocExpressionList *reloc,
Modifier pseudo_mod = Modifier::NONE,
uint64_t initial_immediate_value = 0);
inline int32_t extend(uint32_t value, uint32_t used_bits) const;
static uint32_t parse_field(
QString &field_token,
const QString &arg,
Expand Down
79 changes: 72 additions & 7 deletions src/machine/instruction.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,91 @@ using namespace machine;
// Test that we are correctly encoding instructions in constructor
void TestInstruction::instruction() {
QCOMPARE(Instruction(0x0), Instruction());
// QCOMPARE(Instruction(0x4432146), Instruction(1, 2, 3, 4, 5, 6));
// QCOMPARE(Instruction(0x4430004), Instruction(1, 2, 3, 4));
// QCOMPARE(Instruction(0x4000002), Instruction(1, 2_addr));
}

// Test that we are correctly decoding instruction fields
void TestInstruction::instruction_access() {
Instruction i(0xffffffff);

QCOMPARE(i.data(), (uint32_t)0xffffffff);
QCOMPARE(i.opcode(), (uint8_t)0x3f);
QCOMPARE(i.opcode(), (uint8_t)0x7f);
QCOMPARE(i.rs(), (uint8_t)0x1f);
QCOMPARE(i.rt(), (uint8_t)0x1f);
QCOMPARE(i.rd(), (uint8_t)0x1f);
QCOMPARE(i.shamt(), (uint8_t)0x1f);
QCOMPARE(i.funct(), (uint16_t)0x3f);
QCOMPARE(i.immediate(), (int32_t)0xffff);
QCOMPARE(i.funct(), (uint16_t)0x3ff);
QCOMPARE(i.immediate(), (int32_t)0);
QCOMPARE(i.address().get_raw(), (uint64_t)0x3ffffff);
}

// TODO test to_str
static const struct {
uint32_t code;
QString str;
QString alias_str = nullptr;
} code_to_string[] = {
{ 0xffffffff, "unknown" },
{ 0x0, "unknown" },
{ 0b00000000000000000000000000010011, "nop" },
{ 0b00000000000000001000000010010011, "addi x1, x1, 0" },
{ 0b01111111111111111000111110010011, "addi x31, x31, 2047" },
{ 0b11111111111100001000000010010011, "addi x1, x1, -1" },
{ 0b10000000000000001000000010010011, "addi x1, x1, -2048" },
#include <./instruction.test.data.def>
};

struct Pair {
uint32_t code;
QString str;
};
static const struct {
QString string_data;
std::vector<Pair> pairs;
} pesude_code_to_string[] = {
{ "nop", { { 0b00000000000000000000000000010011, "nop" } } },
{ "la x1, 0xffffefff",
{ { 0xfffff097, "auipc x1, 0xfffff" }, { 0xfff08093, "addi x1, x1, -1" } } },
{ "li x1, 0xffffefff",
{ { 0xfffff0b7, "lui x1, 0xfffff" }, { 0xfff08093, "addi x1, x1, -1" } } },
{ "call 0xfffeffff",
{ { 0xffff0317, "auipc x6, 0xffff0" }, { 0xfff300e7, "jalr x1, -1(x6)" } } },
{ "tail 0xfffeffff",
{ { 0xffff0317, "auipc x6, 0xffff0" }, { 0xfff30067, "jalr x0, -1(x6)" } } },
{ "sext.b x1, x2", { { 0x11093, "slli x1, x2, 0x0" }, { 0x4000d093, "srai x1, x1, 0x0" } } },
{ "sext.h x1, x2", { { 0x11093, "slli x1, x2, 0x0" }, { 0x4000d093, "srai x1, x1, 0x0" } } },
{ "zext.h x1, x2", { { 0x11093, "slli x1, x2, 0x0" }, { 0xd093, "srli x1, x1, 0x0" } } },
{ "zext.w x1, x2", { { 0x11093, "slli x1, x2, 0x0" }, { 0xd093, "srli x1, x1, 0x0" } } },
};

void TestInstruction::instruction_to_str() {
for (size_t i = 0; i < sizeof(code_to_string) / sizeof(code_to_string[0]); i++) {
QCOMPARE(Instruction(code_to_string[i].code).to_str(), code_to_string[i].str);
}

for (size_t i = 0; i < sizeof(pesude_code_to_string) / sizeof(pesude_code_to_string[0]); i++) {
for (size_t j = 0; j < pesude_code_to_string[i].pairs.size(); j++) {
Pair pair = pesude_code_to_string[i].pairs[j];
QCOMPARE(Instruction(pair.code).to_str(), pair.str);
}
}
}

void TestInstruction::instruction_code_from_str() {
for (size_t i = 0; i < sizeof(code_to_string) / sizeof(code_to_string[0]); i++) {
if (code_to_string[i].str == "unknown") { continue; }
QString test_string_data = code_to_string[i].alias_str == nullptr
? code_to_string[i].str
: code_to_string[i].alias_str;
uint32_t code = 0;
try {
Instruction::code_from_string(
&code, code_to_string[i].str.length(), test_string_data, Address(0x0));
QCOMPARE(code, code_to_string[i].code);
} catch (const Instruction::ParseError &e) {
TokenizedInstruction inst
= TokenizedInstruction::from_line(test_string_data, Address(0x0), nullptr, 0);
QFAIL(qPrintable(e.message));
} catch (...) { QFAIL("code_from_string throw unexpected exception"); }
}
}

QTEST_APPLESS_MAIN(TestInstruction)
Loading