five32-instruction-set 0.1.0

Definitions for an implementation of the RISC-V instruction set.
Documentation
use paste::paste;
use strum_macros::EnumString;

use crate::layout;
use crate::rv32i::instr_masks::*;

/// This macro is used to parse an Instruction if its corresponding bitmask matches the input word.
macro_rules! define_parse_single_instruction {
    ( $source:ident, $m_sym:ident, $m_layout:ty ) => {
        {
            paste!{
                if ($source & [<MASK_ $m_sym>]) == [<MATCH_ $m_sym>] {
                    return Some(Instruction::$m_sym($m_layout::from($source)));
                }
            }
        }
    };
}

/// This macro is used to define an enum which organizes instructions and byte layouts.
macro_rules! define_instruction_set_enum {
    ($(($name:ident, $layout:ty)),*) => {
        #[derive(Debug, PartialEq, EnumString)]
        pub enum Instruction {
            $(
                $name($layout),
            )*
        }
    }
}

/// This macro is used to define a function which parses (decodes) an Instruction from a given word.
macro_rules! define_instruction_set_parser {
    ($(($name:ident, $layout:ty)),*) => {
        // This will generate a function 'parse_from_word' for the given set of instructions.
        impl Instruction {
            /// Returns the Instruction name (ex: "ADD")
            pub fn get_name(&self) -> String {
                match self {
                    $(
                        Self::$name(_) => String::from(stringify!($name)),
                    )*
                }
            }

            /// Parses (decodes) the Instruction from a 32-bit word.
            pub fn parse_from_word(word: u32) -> Option<Instruction>{
                $(
                    define_parse_single_instruction!(word, $name, $layout);
                )*
                None
            }
        }
    }
}

/// This macro is used to define an instruction set. It takes a collection of instruction names and
/// corresponding binary layouts. It then defines an enum to encapsulate these instructions and
/// writes associated helper functions.
///
/// note:   The binary layout type must be fully-specified. This shortcoming is documented in
///         https://rust-lang.github.io/api-guidelines/macros.html#type-fragments-are-flexible-c-macro-ty
///
/// ```rust
///define_instruction_set!((ADD, layout::R));
///
/// // produces...
/// pub enum Instruction {
///     ADD(five32_instruction_set::layout::R)
/// }
///
///impl Instruction {
///   pub fn parse_from_word(word: u32) -> Option<Instruction> {
///       if (word & MASK_AND) == MATCH_AND {
///           return Some(Instruction::ADD(five32_instruction_set::R::from(word)));
///       }
///       None
///   }
///
///   pub fn get_name(&self) -> String {
///       match self {
///           Instruction::ADD(_) => String::from("ADD")
///       }
///   }
///}
///
/// ```
macro_rules! define_instruction_set {
    {$($x:tt)*} => {
        define_instruction_set_enum!($($x)*);
        define_instruction_set_parser!($($x)*);
    }
}

define_instruction_set!{
    // Arithmetic
    (ADD,   layout::R),
    (SUB,   layout::R),
    (ADDI,  layout::I),
    (SLT,   layout::R),
    (SLTI,  layout::I),
    (SLTU,  layout::R),
    (SLTIU, layout::I),
    (LUI,   layout::U),
    (AUIPC, layout::U),
    // Logical
    (AND,   layout::R),
    (OR,    layout::R),
    (XOR,   layout::R),
    (ANDI,  layout::I),
    (ORI,   layout::I),
    (XORI,  layout::I),
    (SLL,   layout::R),
    (SRL,   layout::R),
    (SRA,   layout::R),
    (SLLI,  layout::I),
    (SRLI,  layout::I),
    (SRAI,  layout::I),
    // IO
    (LW,    layout::I),
    (LH,    layout::I),
    (LB,    layout::I),
    (LHU,   layout::I),
    (LBU,   layout::S),
    (SW,    layout::S),
    (SH,    layout::S),
    (SB,    layout::S),
    // Branching
    (BEQ,   layout::B),
    (BNE,   layout::B),
    (BGE,   layout::B),
    (BGEU,  layout::B),
    (BLT,   layout::B),
    (BLTU,  layout::B),
    (JAL,   layout::I),
    (JALR,  layout::I),
    // Special Flags
    (ECALL,     layout::Raw),
    (EBREAK,    layout::Raw),
    (FENCE,     layout::Raw)
}