| #include "mem/hmc_controller.hh" | |
| #include "base/random.hh" | |
| #include "debug/HMCController.hh" | |
| HMCController::HMCController(const HMCControllerParams* p) : | |
| NoncoherentXBar(p), | |
| n_master_ports(p->port_master_connection_count), | |
| rr_counter(0) | |
| { | |
| assert(p->port_slave_connection_count == 1); | |
| } | |
| HMCController* | |
| HMCControllerParams::create() | |
| { | |
| return new HMCController(this); | |
| } | |
| // Since this module is a load distributor, all its master ports have the same | |
| // range so we should keep only one of the ranges and ignore the others | |
| void HMCController::recvRangeChange(PortID master_port_id) | |
| { | |
| if (master_port_id == 0) | |
| { | |
| gotAllAddrRanges = true; | |
| BaseXBar::recvRangeChange(master_port_id); | |
| } | |
| else | |
| gotAddrRanges[master_port_id] = true; | |
| } | |
| int HMCController::rotate_counter() | |
| { | |
| int current_value = rr_counter; | |
| rr_counter++; | |
| if (rr_counter == n_master_ports) | |
| rr_counter = 0; | |
| return current_value; | |
| } | |
| bool HMCController::recvTimingReq(PacketPtr pkt, PortID slave_port_id) | |
| { | |
| // determine the source port based on the id | |
| SlavePort *src_port = slavePorts[slave_port_id]; | |
| // we should never see express snoops on a non-coherent component | |
| assert(!pkt->isExpressSnoop()); | |
| // For now, this is a simple round robin counter, for distribution the | |
| // load among the serial links | |
| PortID master_port_id = rotate_counter(); | |
| // test if the layer should be considered occupied for the current | |
| // port | |
| if (!reqLayers[master_port_id]->tryTiming(src_port)) { | |
| DPRINTF(HMCController, "recvTimingReq: src %s %s 0x%x BUSY\n", | |
| src_port->name(), pkt->cmdString(), pkt->getAddr()); | |
| return false; | |
| } | |
| DPRINTF(HMCController, "recvTimingReq: src %s %s 0x%x\n", | |
| src_port->name(), pkt->cmdString(), pkt->getAddr()); | |
| // store size and command as they might be modified when | |
| // forwarding the packet | |
| unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0; | |
| unsigned int pkt_cmd = pkt->cmdToIndex(); | |
| // store the old header delay so we can restore it if needed | |
| Tick old_header_delay = pkt->headerDelay; | |
| // a request sees the frontend and forward latency | |
| Tick xbar_delay = (frontendLatency + forwardLatency) * clockPeriod(); | |
| // set the packet header and payload delay | |
| calcPacketTiming(pkt, xbar_delay); | |
| // determine how long to be layer is busy | |
| Tick packetFinishTime = clockEdge(Cycles(1)) + pkt->payloadDelay; | |
| // before forwarding the packet (and possibly altering it), | |
| // remember if we are expecting a response | |
| const bool expect_response = pkt->needsResponse() && | |
| !pkt->cacheResponding(); | |
| // since it is a normal request, attempt to send the packet | |
| bool success = masterPorts[master_port_id]->sendTimingReq(pkt); | |
| if (!success) { | |
| DPRINTF(HMCController, "recvTimingReq: src %s %s 0x%x RETRY\n", | |
| src_port->name(), pkt->cmdString(), pkt->getAddr()); | |
| // restore the header delay as it is additive | |
| pkt->headerDelay = old_header_delay; | |
| // occupy until the header is sent | |
| reqLayers[master_port_id]->failedTiming(src_port, | |
| clockEdge(Cycles(1))); | |
| return false; | |
| } | |
| // remember where to route the response to | |
| if (expect_response) { | |
| assert(routeTo.find(pkt->req) == routeTo.end()); | |
| routeTo[pkt->req] = slave_port_id; | |
| } | |
| reqLayers[master_port_id]->succeededTiming(packetFinishTime); | |
| // stats updates | |
| pktCount[slave_port_id][master_port_id]++; | |
| pktSize[slave_port_id][master_port_id] += pkt_size; | |
| transDist[pkt_cmd]++; | |
| return true; | |
| } |