rubbler 0.1.2

Rubbler is a RISC-V assembler written in Rust 🦀. This library was written with the main purpose of embedding a simple RISC-V assembler inside of a RISC-V CPU test bench code written with verilator.
Documentation
use crate::expression::ExprType;
use crate::expression::Expression;
use crate::expression::MemOffset;
use crate::generator::Code;
use crate::generator::ResolveType;
use crate::inst::AsmArgs;
use crate::inst::InstType;
use crate::inst::Instructions;

/// Return byte vector representation of an instruction with opcode `op` and arguments `args`.
///
/// # Arguments
/// * `op` - opcode (see [`crate::generator::inst::Instructions`] for valid opcodes).
/// * `args` - arguments to the operation statement (see
/// [`crate::generator::expression::Expression`].
///
pub fn generate_inst_code(
    op: &String,
    args: &Vec<Expression>,
    code: &mut Code,
) -> Result<(), String> {
    if let Some(mut inst) = Instructions::get_code(op) {
        let inst_type = Instructions::get_type(op).unwrap();
        let expected_args = Instructions::get_args(op).unwrap();
        if expected_args.len() != args.len() {
            return Err(arg_err(op, &expected_args));
        }
        for (ex_arg, arg) in expected_args.iter().zip(args.iter()) {
            match (ex_arg, arg.get_type()) {
                (AsmArgs::Imm, ExprType::NumberLiteral(n)) => {
                    set_imm(&mut inst, *n, inst_type).unwrap()
                }
                (AsmArgs::Mem, ExprType::MemAddrLiteral(r, MemOffset::Number(n))) => {
                    set_mem(&mut inst, *r, *n, inst_type).unwrap()
                }
                (AsmArgs::RegSrc1, ExprType::RegisterLiteral(r)) => {
                    set_reg(&mut inst, *r, AsmArgs::RegSrc1).unwrap()
                }
                (AsmArgs::RegSrc2, ExprType::RegisterLiteral(r)) => {
                    set_reg(&mut inst, *r, AsmArgs::RegSrc2).unwrap()
                }
                (AsmArgs::RegDest, ExprType::RegisterLiteral(r)) => {
                    set_reg(&mut inst, *r, AsmArgs::RegDest).unwrap()
                }
                (AsmArgs::Imm, ExprType::Symbol(sym)) => {
                    let resolve_type = get_resolve_type(inst_type);
                    if let Some(imm) = code.resolve_sym(sym, resolve_type) {
                        set_imm(&mut inst, imm, inst_type).unwrap()
                    } else {
                        return Err(format!("Cannot resolve symbol: `{}`.", sym));
                    }
                }
                _ => return Err(arg_err(op, &expected_args)),
            }
        }
        code.append_bytes(u32_to_vecu8(inst))?;
        Ok(())
    } else {
        Err("".to_string())
    }
}

fn arg_err(op: &str, expected_args: &Vec<AsmArgs>) -> String {
    format!(
        "`{}` instruction expects the following arguments: {:?}.",
        op, expected_args
    )
}

fn get_resolve_type(inst_type: InstType) -> ResolveType {
    match inst_type {
        InstType::I => ResolveType::Abs,
        InstType::J => ResolveType::Rel,
        InstType::B => ResolveType::Rel,
        _ => panic!(
            "Instruction type {:?} should've not entered here.",
            inst_type
        ),
    }
}

fn set_mem(
    inst_bits: &mut u32,
    reg: u32,
    mem_offset: i32,
    inst_type: InstType,
) -> Result<(), &'static str> {
    set_reg(inst_bits, reg, AsmArgs::RegSrc1)?;
    set_imm(inst_bits, mem_offset, inst_type)?;
    Ok(())
}

fn set_imm(inst_bits: &mut u32, imm: i32, inst_type: InstType) -> Result<(), &'static str> {
    let imm = imm as u32;
    match inst_type {
        InstType::I | InstType::L => *inst_bits |= (imm & 0xFFF) << 20,
        InstType::S => {
            let imm_11_5 = (imm >> 5) & 0x7F;
            let imm_4_0 = imm & 0x1F;
            *inst_bits |= (imm_11_5 << 25) + (imm_4_0 << 7);
        }
        InstType::B => {
            let imm_12 = (imm >> 12) & 0x1;
            let imm_10_5 = (imm >> 5) & 0x3F;
            let imm_4_1 = (imm >> 1) & 0xF;
            let imm_11 = (imm >> 11) & 0x1;
            *inst_bits |= (imm_12 << 31) + (imm_10_5 << 25) + (imm_4_1 << 8) + (imm_11 << 7);
        }
        InstType::U => {
            let imm_31_12 = (imm >> 12) & 0xFFFFF;
            *inst_bits |= imm_31_12 << 12;
        }
        InstType::J => {
            let imm_20 = (imm >> 20) & 0x1;
            let imm_10_1 = (imm >> 1) & 0x3FF;
            let imm_11 = (imm >> 11) & 0x1;
            let imm_19_12 = (imm >> 12) & 0xFF;
            *inst_bits |= (imm_20 << 31) + (imm_10_1 << 21) + (imm_11 << 20) + (imm_19_12 << 12);
        }
        InstType::R => panic!("R-type instruction should've not entered here."),
    }
    Ok(())
}

fn set_reg(inst_bits: &mut u32, reg: u32, reg_function: AsmArgs) -> Result<(), &'static str> {
    // Set instruction bits
    match reg_function {
        AsmArgs::RegSrc1 => *inst_bits |= reg << 15,
        AsmArgs::RegSrc2 => *inst_bits |= reg << 20,
        AsmArgs::RegDest => *inst_bits |= reg << 7,
        _ => panic!("Only AsmArgs::Reg* should've entered here."),
    }
    Ok(())
}

fn u32_to_vecu8(value: u32) -> Vec<u8> {
    let mut bytes = vec![];
    for i in 0..4 {
        bytes.push((value >> (8 * i)) as u8)
    }
    bytes
}