mcpat-sys 0.8.0

The package provides bindings to McPAT.
Documentation
#![allow(non_snake_case)]

extern crate mcpat_sys;

use mcpat_sys::*;
use std::collections::HashMap;

enum Action {
    CompareWith(f64),
    CompareWithProcessed(f64, Box<Fn(f64) -> f64>),
}

macro_rules! ok(
    ($result:expr) => ($result.unwrap());
);

macro_rules! c_str(
    ($string:expr) => (ok!(::std::ffi::CString::new($string)));
);

macro_rules! hash_map(
    ($($key:expr => $value:expr,)*) => ({
        let mut map = HashMap::new();
        $(map.insert($key, $value);)*
        map
    });
);

// https://github.com/copies/mcpat/blob/master/main.cc
// https://github.com/copies/mcpat/blob/master/ProcessorDescriptionFiles/Xeon.xml
// https://github.com/copies/mcpat/blob/master/ExampleResults/Xeon
#[test]
fn main() {
    let filename = c_str!("build/source/ProcessorDescriptionFiles/Xeon.xml");

    unsafe {
        initialize();

        let parsexml = new_ParseXML();
        ParseXML_parse(parsexml, filename.as_ptr() as *mut _);

        let processor = new_Processor(parsexml);
        Processor_displayEnergy(processor, parsexml);

        delete_Processor(processor);
        delete_ParseXML(parsexml);

        deinitialize();
    }
}

#[cfg(not(feature = "caching"))]
unsafe fn initialize() {
    opt_for_clk_set(1);
}

#[cfg(feature = "caching")]
unsafe fn initialize() {
    opt_for_clk_set(1);
    assert_eq!(cache_activate(c_str!("127.0.0.1").as_ptr(), 6379), 0);
}

#[cfg(not(feature = "caching"))]
unsafe fn deinitialize() {
}

#[cfg(feature = "caching")]
unsafe fn deinitialize() {
    cache_deactivate();
}

unsafe fn Processor_displayEnergy(processor: *mut Processor, parsexml: *mut ParseXML) {
    use Action::*;

    let system = &*ParseXML_sys(parsexml);

    check(system, processor as *mut _, hash_map!(
        "Area" => CompareWith(410.507),
        "Peak Power" => CompareWith(134.938),
        "Total Leakage" => CompareWith(36.8319),
        "Peak Dynamic" => CompareWith(98.1063),
        "Subthreshold Leakage" => CompareWith(35.1632),
        "Subthreshold Leakage with power gating" => CompareWith(16.3977),
        "Gate Leakage" => CompareWith(1.66871),
    ));

    check_runtime(system, processor as *mut _, hash_map!(
        "Runtime Dynamic" => CompareWith(72.9199),
    ));

    let numCore = Processor_numCore(processor);
    assert_eq!(numCore, 1);

    let numL2 = Processor_numL2(processor);
    assert_eq!(numL2, 1);

    let numL3 = Processor_numL3(processor);
    assert_eq!(numL3, 1);

    let core = Processor_cores(processor, 0);

    let clockRate = Core_clockRate(core);
    check(system, core as *mut _, hash_map!(
        "Area" => CompareWith(55.8565),
        "Peak Dynamic" => CompareWithProcessed(39.2989, Box::new(move |v| v * clockRate)),
        "Subthreshold Leakage" => CompareWith(12.0565),
        "Subthreshold Leakage with power gating" => CompareWith(5.15028),
        "Gate Leakage" => CompareWith(0.74513),
    ));

    let executionTime = Core_executionTime(core);
    check_runtime(system, core as *mut _, hash_map!(
        "Runtime Dynamic" => CompareWithProcessed(55.7891, Box::new(move |v| v / executionTime)),
    ));

    assert!(system.Private_L2 > 0);
    let l2cache = Core_l2cache(core);

    let clockRate = CacheDynParam_clockRate(SharedCache_cachep(l2cache));
    check(system, l2cache as *mut _, hash_map!(
        "Area" => CompareWith(16.0033),
        "Peak Dynamic" => CompareWithProcessed(3.16559, Box::new(move |v| v * clockRate)),
        "Subthreshold Leakage" => CompareWith(2.73387),
        "Subthreshold Leakage with power gating" => CompareWith(1.3859),
        "Gate Leakage" => CompareWith(0.0221925),
    ));

    let executionTime = CacheDynParam_executionTime(SharedCache_cachep(l2cache));
    check_runtime(system, l2cache as *mut _, hash_map!(
        "Runtime Dynamic" => CompareWithProcessed(7.23071, Box::new(move |v| v / executionTime)),
    ));

    let l3 = Processor_l3(processor);

    check(system, l3, hash_map!(
        "Area" => CompareWith(293.281),
        "Peak Dynamic" => CompareWith(6.70159),
        "Subthreshold Leakage" => CompareWith(10.9824),
        "Subthreshold Leakage with power gating" => CompareWith(6.06659),
        "Gate Leakage" => CompareWith(0.165767),
    ));

    check_runtime(system, l3, hash_map!(
        "Runtime Dynamic" => CompareWith(4.32382),
    ));
}

unsafe fn check(system: &root_system, component: *mut Component, map: HashMap<&str, Action>) {
    use Action::*;

    let long_channel = system.longer_channel_device != 0;
    let power_gating = system.power_gating != 0;

    let area = Component_area(component);
    let readOp = powerDef_readOp(Component_power(component));

    let area = Area_get_area(area) * 1e-6;
    let dynamic = powerComponents_dynamic(readOp);
    let leakage = powerComponents_leakage(readOp);
    let gate_leakage = powerComponents_gate_leakage(readOp);
    let longer_channel_leakage = powerComponents_longer_channel_leakage(readOp);
    let power_gated_leakage = powerComponents_power_gated_leakage(readOp);
    let power_gated_with_long_channel_leakage = powerComponents_power_gated_with_long_channel_leakage(readOp);

    let subthreshold_leakage = if long_channel { longer_channel_leakage } else { leakage };
    let total_leakage = subthreshold_leakage + gate_leakage;

    for (key, action) in map {
        let actual = match key {
            "Area" => area,
            "Peak Power" => dynamic + total_leakage,
            "Total Leakage" => total_leakage,
            "Peak Dynamic" => dynamic,
            "Subthreshold Leakage" => subthreshold_leakage,
            "Subthreshold Leakage with power gating" => {
                assert!(power_gating);
                if long_channel {
                    power_gated_with_long_channel_leakage
                } else {
                    power_gated_leakage
                }
            },
            "Gate Leakage" => gate_leakage,
            _ => panic!("encountered an unknown quantity"),
        };
        match action {
            CompareWith(expected) => equal(actual, expected),
            CompareWithProcessed(expected, process) => equal(process(actual), expected),
        }
    }
}

unsafe fn check_runtime(_: &root_system, component: *mut Component, map: HashMap<&str, Action>) {
    use Action::*;

    let readOp = powerDef_readOp(Component_rt_power(component));

    let dynamic = powerComponents_dynamic(readOp);

    for (key, action) in map {
        let actual = match key {
            "Runtime Dynamic" => dynamic,
            _ => panic!("encountered an unknown quantity"),
        };
        match action {
            CompareWith(expected) => equal(actual, expected),
            CompareWithProcessed(expected, process) => equal(process(actual), expected),
        }
    }
}

fn equal(actual: f64, expected: f64) {
    let (mut precision, mut scale) = (1i32, 1f64);
    loop {
        if (expected * scale).round() / scale == expected {
            break;
        }
        precision += 1;
        scale = 10f64.powi(precision);
        assert!(precision < 16);
    }
    assert_eq!((actual * scale).round() / scale, expected);
}