llhd-sim 0.3.0

The reference simulator for Low Level Hardware Description assembly.
// Copyright (c) 2017 Fabian Schuiki

//! The simulation state.

use llhd::{Block, ConstTime, Entity, Module, ModuleContext, Process, Type, ValueId, ValueRef};
use num::zero;
use std::{
    cmp::Ordering,
    collections::{BinaryHeap, HashMap},
    sync::Mutex,
};

pub struct State<'tm> {
    module: &'tm Module,
    context: ModuleContext<'tm>,
    time: ConstTime,
    signals: Vec<Signal>,
    probes: HashMap<SignalRef, Vec<String>>,
    scope: Scope,
    insts: Vec<Mutex<Instance<'tm>>>,
    events: BinaryHeap<Event>,
    timed: BinaryHeap<TimedInstance>,
}

impl<'tm> State<'tm> {
    /// Create a new simulation state.
    pub fn new(
        module: &'tm Module,
        signals: Vec<Signal>,
        probes: HashMap<SignalRef, Vec<String>>,
        scope: Scope,
        insts: Vec<Mutex<Instance<'tm>>>,
    ) -> State<'tm> {
        State {
            module: module,
            context: ModuleContext::new(module),
            time: ConstTime::new(zero(), zero(), zero()),
            signals,
            probes,
            scope,
            insts,
            events: BinaryHeap::new(),
            timed: BinaryHeap::new(),
        }
    }

    /// Get the module whose state this object holds.
    pub fn module(&self) -> &'tm Module {
        self.module
    }

    /// Get the module context for the module whose state this object holds
    pub fn context(&self) -> &ModuleContext {
        &self.context
    }

    /// Get the current simulation time.
    pub fn time(&self) -> &ConstTime {
        &self.time
    }

    /// Change the current simulation time.
    pub fn set_time(&mut self, time: ConstTime) {
        self.time = time
    }

    /// Get a slice of instances in the state.
    pub fn instances(&self) -> &[Mutex<Instance<'tm>>] {
        &self.insts
    }

    /// Get a mutable slice of instances in the state.
    pub fn instances_mut(&mut self) -> &mut [Mutex<Instance<'tm>>] {
        &mut self.insts
    }

    /// Get a reference to an instance in the state.
    pub fn instance(&self, ir: InstanceRef) -> &Mutex<Instance<'tm>> {
        &self.insts[ir.0]
    }

    /// Obtain a reference to one of the state's signals.
    pub fn signal(&self, sr: SignalRef) -> &Signal {
        &self.signals[sr.0]
    }

    /// Obtain a mutable reference to one of the state's signals.
    pub fn signal_mut(&mut self, sr: SignalRef) -> &mut Signal {
        &mut self.signals[sr.0]
    }

    /// Get a reference to all signals of this state.
    pub fn signals(&self) -> &[Signal] {
        &self.signals
    }

    /// Get a map of all probe signals and the corresponding names.
    pub fn probes(&self) -> &HashMap<SignalRef, Vec<String>> {
        &self.probes
    }

    /// Get the root scope of the design.
    pub fn scope(&self) -> &Scope {
        &self.scope
    }

    /// Add a set of events to the schedule.
    pub fn schedule_events<I>(&mut self, iter: I)
    where
        I: Iterator<Item = Event>,
    {
        let time = self.time.clone();
        self.events.extend(iter.map(|i| {
            assert!(i.time > time);
            i
        }));
    }

    /// Add a set of timed instances to the schedule.
    pub fn schedule_timed<I>(&mut self, iter: I)
    where
        I: Iterator<Item = TimedInstance>,
    {
        let time = self.time.clone();
        self.timed.extend(iter.map(|i| {
            assert!(i.time > time);
            i
        }));
    }

    /// Dequeue all events due at the current time.
    pub fn take_next_events(&mut self) -> Vec<Event> {
        let mut v = Vec::new();
        while self
            .events
            .peek()
            .map(|x| x.time == self.time)
            .unwrap_or(false)
        {
            v.push(self.events.pop().unwrap());
        }
        v
    }

    /// Dequeue all timed instances due at the current time.
    pub fn take_next_timed(&mut self) -> Vec<TimedInstance> {
        let mut v = Vec::new();
        while self
            .timed
            .peek()
            .map(|x| x.time == self.time)
            .unwrap_or(false)
        {
            v.push(self.timed.pop().unwrap());
        }
        v
    }

    /// Determine the time of the next simulation step. This is the lowest time
    /// value of any event or wake up request in the schedule. If both the event
    /// and timed instances queue are empty, None is returned.
    pub fn next_time(&self) -> Option<ConstTime> {
        use std::cmp::min;
        match (
            self.events.peek().map(|e| &e.time),
            self.timed.peek().map(|t| &t.time),
        ) {
            (Some(e), Some(t)) => Some(min(e, t).clone()),
            (Some(e), None) => Some(e.clone()),
            (None, Some(t)) => Some(t.clone()),
            (None, None) => None,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct SignalRef(usize);

impl SignalRef {
    /// Create a new signal reference.
    pub fn new(id: usize) -> SignalRef {
        SignalRef(id)
    }

    /// Return the underlying index of this reference.
    pub fn as_usize(&self) -> usize {
        self.0
    }
}

pub struct Signal {
    ty: Type,
    value: ValueRef,
}

impl Signal {
    /// Create a new signal.
    pub fn new(ty: Type, value: ValueRef) -> Signal {
        Signal {
            ty: ty,
            value: value,
        }
    }

    /// Get the signal's type.
    pub fn ty(&self) -> &Type {
        &self.ty
    }

    /// Get the signal's current value.
    pub fn value(&self) -> &ValueRef {
        &self.value
    }

    /// Change the signal's current value. Returns whether the values were
    /// identical.
    pub fn set_value(&mut self, value: ValueRef) -> bool {
        if self.value != value {
            self.value = value;
            true
        } else {
            false
        }
    }
}

pub struct Instance<'tm> {
    values: HashMap<ValueId, ValueSlot>,
    kind: InstanceKind<'tm>,
    state: InstanceState,
    inputs: Vec<SignalRef>,
    outputs: Vec<SignalRef>,
}

impl<'tm> Instance<'tm> {
    pub fn new(
        values: HashMap<ValueId, ValueSlot>,
        kind: InstanceKind<'tm>,
        inputs: Vec<SignalRef>,
        outputs: Vec<SignalRef>,
    ) -> Instance<'tm> {
        Instance {
            values: values,
            kind: kind,
            state: InstanceState::Ready,
            inputs: inputs,
            outputs: outputs,
        }
    }

    /// Get the instance's current state.
    pub fn state(&self) -> &InstanceState {
        &self.state
    }

    /// Change the instance's current state.
    pub fn set_state(&mut self, state: InstanceState) {
        self.state = state;
    }

    pub fn kind(&self) -> &InstanceKind<'tm> {
        &self.kind
    }

    pub fn kind_mut(&mut self) -> &mut InstanceKind<'tm> {
        &mut self.kind
    }

    /// Get a reference to the value table of this instance.
    pub fn values(&self) -> &HashMap<ValueId, ValueSlot> {
        &self.values
    }

    /// Access an entry in this instance's value table.
    pub fn value(&self, id: ValueId) -> &ValueSlot {
        self.values.get(&id).unwrap()
    }

    /// Change an entry in this instance's value table.
    pub fn set_value(&mut self, id: ValueId, value: ValueSlot) {
        self.values.insert(id, value);
    }

    /// Get a slice of the instance's input signals.
    pub fn inputs(&self) -> &[SignalRef] {
        &self.inputs
    }

    /// Get a slice of the instance's output signals.
    pub fn outputs(&self) -> &[SignalRef] {
        &self.outputs
    }

    /// Get the name of the entity or process.
    pub fn name(&self) -> &str {
        match self.kind {
            InstanceKind::Process { prok, .. } => prok.name(),
            InstanceKind::Entity { entity, .. } => entity.name(),
        }
    }
}

/// A slot that carries a single value.
///
/// Slots are assigned to each entity in the LLHD graph that may carry a value.
/// Execution of instructions change the value slots.
#[derive(Debug, Clone)]
pub enum ValueSlot {
    /// A signal.
    Signal(SignalRef),
    /// A variable with its current value.
    Variable(ValueRef),
    /// A constant value.
    Const(ValueRef),
    /// A pointer to a variable.
    VariablePointer(ValuePointer<ValueId>),
    /// A pointer to a signal.
    SignalPointer(ValuePointer<SignalRef>),
}

/// A pointer to a value.
///
/// A `ValuePointer` represents a variable or signal that is either referenced
/// in its entirety, or by selecting a subset of its elements, bits, or fields.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ValuePointer<T> {
    /// The targeted variable or signal.
    pub target: T,
    /// The associated selection into the target.
    pub select: Vec<ValueSelect>,
    /// The discarded portions to the left and right of the assigned value.
    pub discard: (usize, usize),
}

/// A selection of a part of a value.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ValueSelect {
    /// An individual array element, struct field, or integer bit.
    Element(usize),
    /// A slice of array elements or integer bits, given by `(offset, length)`.
    Slice(usize, usize),
}

pub enum InstanceKind<'tm> {
    Process {
        prok: &'tm Process,
        next_block: Option<&'tm Block>,
    },
    Entity {
        entity: &'tm Entity,
    },
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InstanceState {
    Ready,
    Wait(Option<ConstTime>, Vec<SignalRef>),
    Done,
}

#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct InstanceRef(usize);

impl InstanceRef {
    /// Create a new instance reference.
    pub fn new(id: usize) -> InstanceRef {
        InstanceRef(id)
    }
}

/// An event that can be scheduled in a binary heap, forming an event queue. The
/// largest element, i.e. the one at the top of the heap, is the one with the
/// lowest time value.
#[derive(Debug, Eq, PartialEq)]
pub struct Event {
    pub time: ConstTime,
    pub signal: ValuePointer<SignalRef>,
    pub value: ValueRef,
}

impl Ord for Event {
    fn cmp(&self, rhs: &Event) -> Ordering {
        match self.time.cmp(&rhs.time) {
            Ordering::Equal => self.signal.cmp(&rhs.signal),
            Ordering::Greater => Ordering::Less,
            Ordering::Less => Ordering::Greater,
        }
    }
}

impl PartialOrd for Event {
    fn partial_cmp(&self, rhs: &Event) -> Option<Ordering> {
        Some(self.cmp(rhs))
    }
}

/// A notice that an instance is in a wait state and wants to be resumed once a
/// certain simulation time has been reached. TimedInstance objects can be
/// scheduled in a binary heap, which forms a wake up queue. The largest
/// element, i.e. the one at the top of the heap, is the one with the lowest
/// time value.
#[derive(Debug, Eq, PartialEq)]
pub struct TimedInstance {
    pub time: ConstTime,
    pub inst: InstanceRef,
}

impl Ord for TimedInstance {
    fn cmp(&self, rhs: &TimedInstance) -> Ordering {
        match self.time.cmp(&rhs.time) {
            Ordering::Equal => self.inst.cmp(&rhs.inst),
            Ordering::Greater => Ordering::Less,
            Ordering::Less => Ordering::Greater,
        }
    }
}

impl PartialOrd for TimedInstance {
    fn partial_cmp(&self, rhs: &TimedInstance) -> Option<Ordering> {
        Some(self.cmp(rhs))
    }
}

/// A level of hierarchy level.
///
/// The scope represents the hierarchy of a design. Each instantiation or
/// process creates a new subscope with its own set of probes.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Scope {
    /// The name of the scope.
    pub name: String,
    /// The probes in this scope.
    pub probes: HashMap<SignalRef, Vec<String>>,
    /// The subscopes.
    pub subscopes: Vec<Scope>,
}

impl Scope {
    /// Create a new empty scope.
    pub fn new(name: impl Into<String>) -> Scope {
        Scope {
            name: name.into(),
            probes: Default::default(),
            subscopes: vec![],
        }
    }

    /// Add a subscope.
    pub fn add_subscope(&mut self, scope: Scope) {
        self.subscopes.push(scope);
    }

    /// Add a probe.
    pub fn add_probe(&mut self, signal: SignalRef, name: String) {
        self.probes.entry(signal).or_insert(Vec::new()).push(name);
    }
}