// Simulator for the RISC-V[ECTOR] mini-ISA
// Copyright (C) 2025 Siddarth Suresh
// Copyright (C) 2025 bdunahu
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
#include "id.h"
#include "instr.h"
#include "instrDTO.h"
#include "logger.h"
#include "response.h"
#include "stage.h"
void ID::split_instr(signed int &raw, unsigned int &type, Mnemonic &m)
{
unsigned int opcode, opcode_size;
type = GET_LS_BITS(raw, TYPE_SIZE);
opcode_size = (type == 0b0) ? R_OPCODE_SIZE : OPCODE_SIZE;
opcode = GET_MID_BITS(raw, TYPE_SIZE, TYPE_SIZE + opcode_size);
try {
m = instr::mnemonic_map.at((opcode << TYPE_SIZE) + type);
} catch (std::out_of_range const &) {
m = NOP;
}
raw = (unsigned int)raw >> (TYPE_SIZE + opcode_size);
}
Response ID::read_guard(signed int &v)
{
Response r;
if (this->is_checked_out(v))
r = STALLED;
else {
r = OK;
v = this->dereference_register(v);
}
return r;
}
void ID::write_guard(signed int &v)
{
// zero register shouldn't be written.
if (v != 0) {
// keep track in the instrDTO for displaying to user and writeback
// keep track in checked_out so we can still access this information!
this->checked_out.push_back(v);
this->curr_instr->checked_out = v;
}
v = this->dereference_register(v);
}
void ID::advance_helper()
{
signed int s1, s2, s3;
Mnemonic m;
Type t;
if (curr_instr->mnemonic == NOP)
this->status = OK;
else {
s1 = curr_instr->slot_A;
get_instr_fields(s1, s2, s3, m, t);
if (this->status == OK) {
curr_instr->operands.integer.slot_one = s1;
curr_instr->operands.integer.slot_two = s2;
curr_instr->operands.integer.slot_three = s3;
curr_instr->mnemonic = m;
curr_instr->type = t;
}
}
}
void ID::get_instr_fields(
signed int &s1, signed int &s2, signed int &s3, Mnemonic &m, Type &t)
{
unsigned int type;
this->split_instr(s1, type, m);
switch (type) {
case 0b00:
t = R;
this->decode_R_type(s1, s2, s3, m);
break;
case 0b01:
t = I;
this->decode_I_type(s1, s2, s3, m);
break;
case 0b10:
t = J;
this->decode_J_type(s1, s2, s3, m);
break;
case 0b11:
t = INV;
this->status = OK;
}
}
void ID::decode_R_type(
signed int &s1, signed int &s2, signed int &s3, Mnemonic &m)
{
unsigned int s0b, s1b, s2b;
Response r1, r2;
s0b = REG_SIZE;
s1b = s0b + REG_SIZE;
s2b = s1b + REG_SIZE;
s3 = GET_MID_BITS(s1, s1b, s2b);
s2 = GET_MID_BITS(s1, s0b, s1b);
s1 = GET_LS_BITS(s1, s0b);
r1 = this->read_guard(s1);
r2 = this->read_guard(s2);
this->status = (r1 == OK && r2 == OK) ? OK : STALLED;
switch (m) {
case CMP:
case CEV:
break;
default:
if (this->status == OK)
this->write_guard(s3);
}
}
void ID::decode_I_type(
signed int &s1, signed int &s2, signed int &s3, Mnemonic &m)
{
unsigned int s0b, s1b, s2b;
Response r1, r2;
s0b = REG_SIZE;
s1b = s0b + REG_SIZE;
s2b = WORD_SPEC - LINE_SPEC - OPCODE_SIZE;
s3 = GET_BITS_SIGN_EXTEND(s1, s1b, s2b);
switch (m) {
case STORE:
case STOREV:
s2 = GET_MID_BITS(s1, s0b, s1b);
s1 = GET_LS_BITS(s1, s0b);
// both operands are read values
r1 = this->read_guard(s1);
r2 = this->read_guard(s2);
this->status = (r1 == OK && r2 == OK) ? OK : STALLED;
return;
case LOAD:
case LOADV:
s2 = GET_LS_BITS(s1, s0b);
s1 = GET_MID_BITS(s1, s0b, s1b);
break;
default:
s2 = GET_MID_BITS(s1, s0b, s1b);
s1 = GET_LS_BITS(s1, s0b);
}
r1 = this->read_guard(s1);
if (r1 == OK)
this->write_guard(s2);
this->status = r1;
}
void ID::decode_J_type(
signed int &s1, signed int &s2, signed int &s3, Mnemonic &m)
{
Response r1, r2;
unsigned int s0b, s1b;
s0b = REG_SIZE;
s1b = WORD_SPEC - LINE_SPEC - OPCODE_SIZE;
s3 = 0;
s2 = GET_BITS_SIGN_EXTEND(s1, s0b, s1b);
s1 = GET_LS_BITS(s1, s0b);
switch (m) {
case PUSH:
s2 = s1; // source
s3 = 2; // stack pointer
s1 = -1; // increment amount
r1 = this->read_guard(s2);
r2 = (this->is_checked_out(s3)) ? STALLED : OK; // we read the stack pointer
if (r1 == OK && r2 == OK) {
this->write_guard(s3); // we write the stack pointer
}
this->status = (r1 == OK && r2 == OK) ? OK : STALLED;
break;
case POP:
s2 = s1; // destination
s3 = 2; // stack pointer
s1 = 1; // increment amount
r1 = (this->is_checked_out(s3)) ? STALLED : OK; // we read the stack pointer
if (r1 == OK) {
this->write_guard(s2);
this->write_guard(s3); // we write the stack pointer
}
this->status = r1;
break;
case RET:
s1 = 1; // link register
[[fallthrough]];
default:
this->status = this->read_guard(s1);
}
}
std::vector ID::stage_info()
{
std::vector info;
if (this->curr_instr) {
info.push_back(this->curr_instr->is_squashed);
info.push_back(this->curr_instr->slot_A);
}
return info;
}