com.synflow.cx.internal.validation.ClockDomainComputer.java Source code

Java tutorial

Introduction

Here is the source code for com.synflow.cx.internal.validation.ClockDomainComputer.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Synflow SAS.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Matthieu Wipliez - initial API and implementation and/or initial documentation
 *******************************************************************************/
package com.synflow.cx.internal.validation;

import static com.synflow.core.IProperties.DEFAULT_CLOCK;
import static com.synflow.core.IProperties.PROP_CLOCKS;
import static com.synflow.core.IProperties.PROP_DOMAINS;

import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.synflow.models.dpn.Actor;
import com.synflow.models.dpn.Connection;
import com.synflow.models.dpn.DPN;
import com.synflow.models.dpn.Endpoint;
import com.synflow.models.dpn.Entity;
import com.synflow.models.dpn.Instance;
import com.synflow.models.dpn.Port;
import com.synflow.models.dpn.util.DpnSwitch;

/**
 * This class defines a clock domain computer.
 * 
 * @author Matthieu Wipliez
 * 
 */
public class ClockDomainComputer extends DpnSwitch<JsonObject> {

    private static final String PORT_MAP = "__port_map";

    /**
     * Associates candidates to the given ports in the given domains.
     * 
     * @param domains
     *            target clock domains
     * @param port
     *            a port
     * @param candidates
     *            set of candidates
     */
    private void addCandidates(JsonObject domains, Port port, Set<String> candidates) {
        JsonArray array = new JsonArray();
        for (String candidate : candidates) {
            if (candidate != null) {
                // skips null clocks
                array.add(new JsonPrimitive(candidate));
            }
        }

        int size = array.size();
        if (size == 1) {
            domains.add(port.getName(), array.get(0));
        } else if (size > 1) {
            domains.add(port.getName(), array);
        }
    }

    @Override
    public JsonObject caseActor(Actor actor) {
        JsonObject properties = actor.getProperties();
        JsonObject domains = properties.getAsJsonObject(PROP_DOMAINS);
        if (properties.has(PORT_MAP)) {
            // if the flag is here, domains are available
            return domains;
        }

        if (domains == null) {
            // no domain, creates one
            domains = createPortMap(actor);
        } else {
            // domains exists, but not in port map format, convert them
            domains = convertToPortMap(domains);
        }

        // adds/updates domains to properties, and adds the "port map domains" flag
        properties.add(PROP_DOMAINS, domains);
        properties.add(PORT_MAP, null);

        return domains;
    }

    @Override
    public JsonObject caseDPN(DPN dpn) {
        JsonObject properties = dpn.getProperties();
        JsonObject domains = properties.getAsJsonObject(PROP_DOMAINS);
        if (domains != null) {
            return domains;
        }

        // creates domains and adds them to properties so next time they will just be reused
        domains = new JsonObject();
        properties.add(PROP_DOMAINS, domains);

        // compute domains for input ports
        for (Port port : dpn.getInputs()) {
            Set<String> candidates = new HashSet<>();
            for (Connection connection : dpn.getOutgoing(port)) {
                for (Endpoint endpoint : getClocked(dpn, connection.getTargetEndpoint())) {
                    candidates.add(getClockDomain(endpoint));
                }
            }

            addCandidates(domains, port, candidates);
        }

        // compute domains for output ports
        for (Port port : dpn.getOutputs()) {
            Endpoint incoming = dpn.getIncoming(port);
            if (incoming != null) {
                Set<String> candidates = new HashSet<>();
                for (Endpoint endpoint : getClocked(dpn, incoming)) {
                    candidates.add(getClockDomain(endpoint));
                }
                addCandidates(domains, port, candidates);
            }
        }

        return domains;
    }

    @Override
    public JsonObject caseInstance(Instance instance) {
        Entity entity = instance.getEntity();
        JsonObject domains = doSwitch(entity);

        // translate entity clocks to instance clocks
        JsonObject instDomains = new JsonObject();
        JsonElement properties = instance.getProperties().get(PROP_CLOCKS);
        if (!properties.isJsonObject()) {
            return instDomains;
        }

        JsonObject instClocks = properties.getAsJsonObject();
        for (Entry<String, JsonElement> domain : domains.entrySet()) {
            String port = domain.getKey();
            JsonElement value = domain.getValue();
            if (value.isJsonPrimitive()) {
                String clock = value.getAsString();
                JsonElement instClock = instClocks.get(clock);
                if (instClock != null) {
                    instDomains.add(port, instClock);
                }
            }
        }

        return instDomains;
    }

    /**
     * Returns a port-map version of the given clock domain definitions.
     * 
     * @param domains
     *            a clock to port array object
     * @return a port map
     */
    private JsonObject convertToPortMap(JsonObject domains) {
        // create a port map domain
        JsonObject portMap = new JsonObject();
        for (Entry<String, JsonElement> pair : domains.entrySet()) {
            String clock = pair.getKey();
            JsonArray ports = pair.getValue().getAsJsonArray();
            for (JsonElement port : ports) {
                portMap.addProperty(port.getAsString(), clock);
            }
        }

        return portMap;
    }

    /**
     * Creates the simple port map for the given task.
     * 
     * @param actor
     *            an actor
     * @return a new port map domains
     */
    private JsonObject createPortMap(Actor actor) {
        JsonObject domains = new JsonObject();

        // single-clock and combinational tasks
        JsonArray clocks = actor.getProperties().getAsJsonArray(PROP_CLOCKS);
        String clock;
        if (clocks.size() == 0) {
            clock = DEFAULT_CLOCK.getAsString();
        } else {
            clock = clocks.get(0).getAsString();
        }

        Iterable<Port> ports = Iterables.concat(actor.getInputs(), actor.getOutputs());
        for (Port port : ports) {
            domains.addProperty(port.getName(), clock);
        }

        return domains;
    }

    /**
     * Returns the clock domain of the given endpoint. The endpoint must be of an instance.
     * 
     * @param endpoint
     *            an endpoint
     * @return a port-clock domains
     */
    public String getClockDomain(Endpoint endpoint) {
        if (!endpoint.hasInstance()) {
            throw new IllegalArgumentException("expected endpoint of an instance");
        }

        JsonObject domains = doSwitch(endpoint.getInstance());
        JsonElement value = domains.get(endpoint.getPort().getName());
        if (value == null) {
            return null;
        }
        return value.getAsString();
    }

    /**
     * If the given endpoint is clocked, returns it. Otherwise, iterate over the endpoint's incoming
     * and outgoing edges until it finds a clocked instance.
     * 
     * @param dpn
     *            dpn
     * @param endpoint
     *            an endpoint
     * @return an iterable over clocked endpoints
     */
    private Iterable<Endpoint> getClocked(DPN dpn, Endpoint endpoint) {
        Instance instance = endpoint.getInstance();
        if (!isCombinational(instance)) {
            return ImmutableSet.of(endpoint);
        }

        Set<Endpoint> endpoints = new HashSet<>();
        Set<Instance> visited = new HashSet<>();
        visitEndpoint(visited, endpoints, dpn, endpoint);
        return endpoints;
    }

    /**
     * Returns true if the entity referenced by this instance is combinational (has no clocks).
     * 
     * @param instance
     *            an instance
     * @return a boolean
     */
    public boolean isCombinational(Instance instance) {
        JsonObject properties = instance.getEntity().getProperties();
        JsonElement clocks = properties.get(PROP_CLOCKS);
        return clocks.isJsonArray() && clocks.getAsJsonArray().size() == 0;
    }

    /**
     * Visits the given endpoint. If the given endpoint's instance is clocked, adds it to the
     * endpoints set and returns. Otherwise, recursively visit the endpoint's incoming and outgoing
     * edges.
     * 
     * @param visited
     *            the set of instances already visited
     * @param endpoints
     *            the set of endpoints found so far
     * @param dpn
     *            dpn
     * @param endpoint
     *            an endpoint
     */
    private void visitEndpoint(Set<Instance> visited, Set<Endpoint> endpoints, DPN dpn, Endpoint endpoint) {
        Instance instance = endpoint.getInstance();
        if (visited.contains(instance)) {
            return;
        }

        // remember that we visited this instance
        visited.add(instance);

        // if this instance is clocked, adds the endpoint and returns
        if (!isCombinational(instance)) {
            endpoints.add(endpoint);
            return;
        }

        // visit incoming
        for (Connection connection : dpn.getIncoming(instance)) {
            Endpoint target = connection.getSourceEndpoint();
            if (target.hasInstance()) {
                visitEndpoint(visited, endpoints, dpn, target);
            }
        }

        // visit outgoing
        for (Connection connection : dpn.getOutgoing(instance)) {
            Endpoint target = connection.getTargetEndpoint();
            if (target.hasInstance()) {
                visitEndpoint(visited, endpoints, dpn, target);
            }
        }
    }

}