/** * **************************************************************************** * Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com). * <p> * This file is part of the Archimulator multicore architectural simulator. * <p> * Archimulator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * <p> * Archimulator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * <p> * You should have received a copy of the GNU General Public License * along with Archimulator. If not, see <http://www.gnu.org/licenses/>. * **************************************************************************** */ package archimulator.uncore.noc.routers; import archimulator.uncore.noc.Direction; import archimulator.uncore.noc.Node; import archimulator.uncore.noc.Packet; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; /** * Router. * * @author Min Cai */ public class Router { private Node node; private List<Packet> injectionBuffer; private Map<Direction, InputPort> inputPorts; private Map<Direction, OutputPort> outputPorts; private Map<FlitState, Integer> numInflightHeadFlits; private Map<FlitState, Integer> numInflightNonHeadFlits; /** * Create a router. * * @param node the parent node */ public Router(Node node) { this.node = node; this.injectionBuffer = new ArrayList<>(); this.inputPorts = new TreeMap<>(); for (Direction direction : Direction.values()) { this.inputPorts.put(direction, new InputPort(this, direction)); } this.outputPorts = new TreeMap<>(); for (Direction direction : Direction.values()) { this.outputPorts.put(direction, new OutputPort(this, direction)); } this.numInflightHeadFlits = new TreeMap<>(); for(FlitState flitState : FlitState.values()) { this.numInflightHeadFlits.put(flitState, 0); } this.numInflightNonHeadFlits = new TreeMap<>(); for(FlitState flitState : FlitState.values()) { this.numInflightNonHeadFlits.put(flitState, 0); } this.node.getNetwork().getCycleAccurateEventQueue().getPerCycleEvents().add(this::advanceOneCycle); } /** * Advance one cycle. */ private void advanceOneCycle() { if (this.getNode().getNetwork().getEnvironment().isInDetailedSimulationMode()) { this.stageLinkTraversal(); this.stageSwitchTraversal(); this.stageSwitchAllocation(); this.stageVirtualChannelAllocation(); this.stageRouteComputation(); this.localPacketInjection(); } } /** * The link traversal stage. */ private void stageLinkTraversal() { if(this.numInflightHeadFlits.get(FlitState.SWITCH_TRAVERSAL) == 0 && this.numInflightNonHeadFlits.get(FlitState.SWITCH_TRAVERSAL) == 0) { return; } for(OutputPort outputPort : this.outputPorts.values()) { for(OutputVirtualChannel outputVirtualChannel : outputPort.getVirtualChannels()) { InputVirtualChannel inputVirtualChannel = outputVirtualChannel.getInputVirtualChannel(); if(inputVirtualChannel != null && outputVirtualChannel.getCredits() > 0) { Flit flit = inputVirtualChannel.getInputBuffer().peek(); if(flit != null && flit.getState() == FlitState.SWITCH_TRAVERSAL) { if(outputPort.getDirection() != Direction.LOCAL) { flit.setNodeAndState(this.node, FlitState.LINK_TRAVERSAL); int nextHop = this.node.getNeighbors().get(outputPort.getDirection()); Direction ip = outputPort.getDirection().getReflexDirection(); int ivc = outputVirtualChannel.getId(); this.node.getNetwork().getCycleAccurateEventQueue().schedule(this, () -> { nextHopArrived(flit, nextHop, ip, ivc); }, this.node.getNetwork().getEnvironment().getConfig().getLinkDelay()); } inputVirtualChannel.getInputBuffer().pop(); if(outputPort.getDirection() != Direction.LOCAL) { outputVirtualChannel.setCredits(outputVirtualChannel.getCredits() - 1); } else { flit.setNodeAndState(this.node, FlitState.DESTINATION_ARRIVED); } if(flit.isTail()) { inputVirtualChannel.setOutputVirtualChannel(null); outputVirtualChannel.setInputVirtualChannel(null); if(outputPort.getDirection() == Direction.LOCAL) { this.node.getSelectionAlgorithm().handleDestArrived(flit.getPacket(), inputVirtualChannel); } } } } } } } /** * Handle the event when a flit arrives at the next hop node. * * @param flit the flit * @param nextHop the next hop node ID * @param ip the input port direction * @param ivc the input virtual channel */ private void nextHopArrived(Flit flit, int nextHop, Direction ip, int ivc) { InputBuffer inputBuffer = this.node.getNetwork().getNodes().get(nextHop).getRouter().getInputPorts().get(ip).getVirtualChannels().get(ivc).getInputBuffer(); if(inputBuffer.size() + 1 <= this.node.getNetwork().getEnvironment().getConfig().getMaxInputBufferSize()) { this.node.getNetwork().getNodes().get(nextHop).getRouter().insertFlit(flit, ip, ivc); } else { this.node.getNetwork().getCycleAccurateEventQueue().schedule(this, () -> this.nextHopArrived(flit, nextHop, ip, ivc), 1); } } /** * The switch traversal stage. */ private void stageSwitchTraversal() { if(this.numInflightHeadFlits.get(FlitState.SWITCH_ALLOCATION) == 0 && this.numInflightNonHeadFlits.get(FlitState.SWITCH_ALLOCATION) == 0) { return; } for (OutputPort outputPort: this.outputPorts.values()) { for (InputPort inputPort : this.inputPorts.values()) { if (outputPort.getDirection() == inputPort.getDirection()) { continue; } for(InputVirtualChannel inputVirtualChannel : inputPort.getVirtualChannels()) { if(inputVirtualChannel.getOutputVirtualChannel() != null && inputVirtualChannel.getOutputVirtualChannel().getOutputPort() == outputPort) { Flit flit = inputVirtualChannel.getInputBuffer().peek(); if(flit != null && flit.getState() == FlitState.SWITCH_ALLOCATION) { flit.setNodeAndState(this.node, FlitState.SWITCH_TRAVERSAL); if(inputPort.getDirection() != Direction.LOCAL) { Node parent = this.node.getNetwork().getNodes().get( this.node.getNeighbors().get(inputPort.getDirection()) ); OutputVirtualChannel outputVirtualChannelAtParent = parent.getRouter().getOutputPorts().get(inputPort.getDirection().getReflexDirection()) .getVirtualChannels().get(inputVirtualChannel.getId()); outputVirtualChannelAtParent.setCredits(outputVirtualChannelAtParent.getCredits() + 1); } } } } } } } /** * The switch allocation stage. */ private void stageSwitchAllocation() { if(this.numInflightHeadFlits.get(FlitState.VIRTUAL_CHANNEL_ALLOCATION) == 0 && this.numInflightNonHeadFlits.get(FlitState.INPUT_BUFFER) == 0) { return; } for(OutputPort outputPort : this.outputPorts.values()) { InputVirtualChannel winnerInputVirtualChannel = outputPort.getArbiter().next(); if(winnerInputVirtualChannel != null) { Flit flit = winnerInputVirtualChannel.getInputBuffer().peek(); flit.setNodeAndState(this.node, FlitState.SWITCH_ALLOCATION); } } } /** * The virtual channel allocation stage. */ private void stageVirtualChannelAllocation() { if(this.numInflightHeadFlits.get(FlitState.ROUTE_COMPUTATION) == 0) { return; } for(OutputPort outputPort : this.outputPorts.values()) { for(OutputVirtualChannel outputVirtualChannel : outputPort.getVirtualChannels()) { if(outputVirtualChannel.getInputVirtualChannel() == null) { InputVirtualChannel winnerInputVirtualChannel = outputVirtualChannel.getArbiter().next(); if(winnerInputVirtualChannel != null) { Flit flit = winnerInputVirtualChannel.getInputBuffer().peek(); flit.setNodeAndState(this.node, FlitState.VIRTUAL_CHANNEL_ALLOCATION); winnerInputVirtualChannel.setOutputVirtualChannel(outputVirtualChannel); outputVirtualChannel.setInputVirtualChannel(winnerInputVirtualChannel); } } } } } /** * The route computation stage. */ private void stageRouteComputation() { if(this.numInflightHeadFlits.get(FlitState.INPUT_BUFFER) == 0) { return; } for(InputPort inputPort : this.inputPorts.values()) { for(InputVirtualChannel inputVirtualChannel : inputPort.getVirtualChannels()) { Flit flit = inputVirtualChannel.getInputBuffer().peek(); if(flit != null && flit.isHead() && flit.getState() == FlitState.INPUT_BUFFER) { if(flit.getPacket().getDest() == this.node.getId()) { inputVirtualChannel.setRoute(Direction.LOCAL); } else { inputVirtualChannel.setRoute( this.node.getSelectionAlgorithm().doRouteCalculation( flit.getPacket(), inputVirtualChannel ) ); } flit.setNodeAndState(this.node, FlitState.ROUTE_COMPUTATION); } } } } /** * Handle the local packet injection. */ private void localPacketInjection() { while (true) { boolean requestInserted = false; for(int ivc = 0; ivc < this.node.getNetwork().getEnvironment().getConfig().getNumVirtualChannels(); ivc++) { if(this.injectionBuffer.isEmpty()) { return; } Packet packet = this.injectionBuffer.get(0); int numFlits = (int) Math.ceil((double)(packet.getSize()) / this.node.getNetwork().getEnvironment().getConfig().getLinkWidth()); InputBuffer inputBuffer = this.inputPorts.get(Direction.LOCAL).getVirtualChannels().get(ivc).getInputBuffer(); if(inputBuffer.size() + numFlits <= this.node.getNetwork().getEnvironment().getConfig().getMaxInputBufferSize()) { for(int i = 0; i < numFlits; i++) { Flit flit = new Flit(packet, i, i == 0, i == numFlits - 1); this.insertFlit(flit, Direction.LOCAL, ivc); } this.injectionBuffer.remove(packet); requestInserted = true; break; } } if(!requestInserted) { break; } } } /** * Inject the specified packet into the router. * * @param packet the packet * @return whether the injection succeeds or not */ public boolean injectPacket(Packet packet) { if(this.injectionBuffer.size() < this.node.getNetwork().getEnvironment().getConfig().getMaxInjectionBufferSize()) { this.injectionBuffer.add(packet); return true; } return false; } /** * Insert the flit into the router. * * @param flit the flit * @param ip the input port direction * @param ivc the input virtual channel number */ public void insertFlit(Flit flit, Direction ip, int ivc) { this.inputPorts.get(ip).getVirtualChannels().get(ivc).getInputBuffer().append(flit); flit.setNodeAndState(this.node, FlitState.INPUT_BUFFER); } /** * Get the number of free slots in the specified input virtual channel. * * @param ip the input port direction * @param ivc the input virtual channel number * @return the number of free slots in the specified input virtual channel */ public int getFreeSlots(Direction ip, int ivc) { return this.inputPorts.get(ip).getVirtualChannels().get(ivc).getInputBuffer().getFreeSlots(); } @Override public String toString() { return String.format("Router{node=%s}", node); } /** * Get the node. * * @return the node */ public Node getNode() { return node; } /** * Get the injection buffer. * * @return the injection buffer */ public List<Packet> getInjectionBuffer() { return injectionBuffer; } /** * Get the map of input ports. * * @return the map of input ports */ public Map<Direction, InputPort> getInputPorts() { return inputPorts; } /** * Get the map of output ports. * * @return the map of output ports */ public Map<Direction, OutputPort> getOutputPorts() { return outputPorts; } /** * Get the number of inflight head flits. * * @return the number of inflight head flits */ public Map<FlitState, Integer> getNumInflightHeadFlits() { return numInflightHeadFlits; } /** * Get the number of inflight non-head flits. * * @return the number of inflight non-head flits */ public Map<FlitState, Integer> getNumInflightNonHeadFlits() { return numInflightNonHeadFlits; } }