/* Create contents of routing table */ void net_routing_table_initiate(struct net_routing_table_t *routing_table) { int i, j; struct net_t *net = routing_table->net; struct net_node_t *src_node, *dst_node; struct net_buffer_t *buffer; struct net_link_t *link; struct net_routing_table_entry_t *entry; /* Allocate routing table entries */ if (routing_table->entries) panic("%s: network \"%s\": routing table already allocated", __FUNCTION__, net->name); routing_table->dim = list_count(net->node_list); routing_table->entries = calloc(routing_table->dim * routing_table->dim, sizeof(struct net_routing_table_entry_t)); if (!routing_table->entries) fatal("%s: out of memory", __FUNCTION__); /* Initialize table with infinite costs */ for (i = 0; i < net->node_count; i++) { for (j = 0; j < net->node_count; j++) { src_node = list_get(net->node_list, i); dst_node = list_get(net->node_list, j); entry = net_routing_table_lookup(routing_table, src_node, dst_node); entry->cost = i == j ? 0 : routing_table->dim; /* Infinity or 0 */ entry->next_node = NULL; entry->output_buffer = NULL; } } /* Set 1-hop connections */ for (i = 0; i < net->node_count; i++) { src_node = list_get(net->node_list, i); for (j = 0; j < list_count(src_node->output_buffer_list); j++) { buffer = list_get(src_node->output_buffer_list, j); link = buffer->link; assert(link); entry = net_routing_table_lookup(routing_table, src_node, link->dst_node); entry->cost = 1; entry->next_node = link->dst_node; entry->output_buffer = buffer; } } }
/* Return TRUE if a message can be sent through the network. Return FALSE * otherwise, whether the reason is temporary of permanent. This function is * being used just in stand alone simulator. So in here if a buffer is busy * for any reason, the node simply discards the message and sends another * one. */ int net_can_send(struct net_t *net, struct net_node_t *src_node, struct net_node_t *dst_node, int size) { struct net_routing_table_t *routing_table = net->routing_table; struct net_routing_table_entry_t *entry; struct net_buffer_t *output_buffer; long long cycle; /* Get current cycle */ cycle = esim_domain_cycle(net_domain_index); /* Get output buffer */ entry = net_routing_table_lookup(routing_table, src_node, dst_node); output_buffer = entry->output_buffer; /* No route to destination */ if (!output_buffer) return 0; /* Output buffer is busy */ if (output_buffer->write_busy >= cycle) return 0; /* Message does not fit in output buffer */ if (output_buffer->count + size > output_buffer->size) return 0; /* All conditions satisfied, can send */ return 1; }
/* Updating the entries in the routing table based on the routes existing in configuration file*/ void net_routing_table_route_update(struct net_routing_table_t *routing_table, struct net_node_t *src_node, struct net_node_t *dst_node, struct net_node_t *next_node, int vc_num) { int k; int route_check = 0 ; struct net_buffer_t *buffer; struct net_link_t *link; struct net_routing_table_entry_t *entry; entry = net_routing_table_lookup(routing_table, src_node, dst_node); entry->next_node = next_node; entry->output_buffer = NULL ; /* Look for output buffer */ buffer = NULL; assert(list_count(src_node->output_buffer_list)); for (k = 0; (k < list_count(src_node->output_buffer_list) && route_check != 1); k++) { buffer = list_get(src_node->output_buffer_list, k); link = buffer->link; assert(link); if ((link->dst_node == next_node)) { if (vc_num == 0) { entry->output_buffer = buffer; route_check = 1; } else { if (link->virtual_channel <= vc_num) fatal("Network %s: %s.to.%s: wrong virtual channel number is used in route \n %s", routing_table->net->name, src_node->name, dst_node->name, err_net_config); struct net_buffer_t *vc_buffer; vc_buffer = list_get(src_node->output_buffer_list, (buffer->index)+vc_num); assert(vc_buffer->link == buffer->link); entry->output_buffer = vc_buffer; route_check = 1; } } } /*If there is not a route between the source node and next node , error */ if (route_check == 0) fatal("Network %s : route %s.to.%s = %s : Missing Link \n%s ", routing_table->net->name, src_node->name, dst_node->name, next_node->name, err_net_routing); /* Find cycle in routing table */ net_routing_table_cycle_detection(routing_table); }
/* Return TRUE if a message can be sent to the network. If it cannot be sent, * return FALSE, and schedule 'retry_event' for the cycle when the check * should be performed again. This function should not be called if the * reason why a message cannot be sent is permanent (e.g., no route to * destination). */ int net_can_send_ev(struct net_t *net, struct net_node_t *src_node, struct net_node_t *dst_node, int size, int retry_event, void *retry_stack) { struct net_routing_table_t *routing_table = net->routing_table; struct net_routing_table_entry_t *entry; struct net_buffer_t *output_buffer; long long cycle; /* Get current cycle */ cycle = esim_domain_cycle(net_domain_index); /* Get output buffer */ entry = net_routing_table_lookup(routing_table, src_node, dst_node); output_buffer = entry->output_buffer; /* No route to destination */ if (!output_buffer) fatal("%s: no route between %s and %s.\n%s", net->name, src_node->name, dst_node->name, net_err_no_route); /* Message is too long */ if (size > output_buffer->size) fatal("%s: message too long.\n%s", net->name, net_err_large_message); /* Output buffer is busy */ if (output_buffer->write_busy >= cycle) { esim_schedule_event(retry_event, retry_stack, output_buffer->write_busy - cycle + 1); return 0; } /* Message does not fit in output buffer */ if (output_buffer->count + size > output_buffer->size) { net_buffer_wait(output_buffer, retry_event, retry_stack); return 0; } /* All conditions satisfied, can send */ return 1; }
/* Round-robin scheduler for network switch, choosing between several * candidate messages at the head of all input buffers that have a given * output buffer as an immediate target. */ struct net_buffer_t *net_node_schedule(struct net_node_t *node, struct net_buffer_t *output_buffer) { struct net_t *net = node->net; struct net_routing_table_t *routing_table = net->routing_table; struct net_routing_table_entry_t *entry; struct net_buffer_t *input_buffer; struct net_packet_t * pkt; long long cycle; int last_input_buffer_index; int input_buffer_index; int input_buffer_count; int i; /* Checks */ assert(output_buffer->node == node); assert(list_get(node->output_buffer_list, output_buffer->index) == output_buffer); /* Get current cycle */ cycle = esim_domain_cycle(net_domain_index); /* If last scheduling decision was done in current cycle, return the * same value. */ if (output_buffer->sched_when == cycle) return output_buffer->sched_buffer; /* Make a new decision */ output_buffer->sched_when = cycle; input_buffer_count = list_count(node->input_buffer_list); last_input_buffer_index = output_buffer->sched_buffer ? output_buffer->sched_buffer->index : 0; /* Output buffer must be ready to be written */ if (output_buffer->write_busy >= cycle) { output_buffer->sched_buffer = NULL; return NULL; } /* Find input buffer to fetch from */ for (i = 0; i < input_buffer_count; i++) { input_buffer_index = (last_input_buffer_index + i + 1) % input_buffer_count; input_buffer = list_get(node->input_buffer_list, input_buffer_index); /* There must be a message at the head */ pkt = list_get(input_buffer->msg_list, 0); if (!pkt) continue; /* Message must be ready */ if (pkt->busy >= cycle) continue; /* Input buffer must be ready to be read */ if (input_buffer->read_busy >= cycle) continue; /* Message must target this output buffer */ entry = net_routing_table_lookup(routing_table, node, pkt->msg->dst_node); assert(entry->output_buffer); if (entry->output_buffer != output_buffer) continue; /* Message must fit in this output buffer */ if (output_buffer->count + pkt->size > output_buffer->size) continue; /* All conditions satisfied - schedule */ output_buffer->sched_buffer = input_buffer; return input_buffer; } /* No input buffer ready */ output_buffer->sched_buffer = NULL; return NULL; }
void net_event_handler(int event, void *data) { struct net_stack_t *stack = data; struct net_t *net = stack->net; struct net_routing_table_t *routing_table = net->routing_table; struct net_packet_t *pkt= stack->packet; struct net_node_t *src_node = pkt->msg->src_node; struct net_node_t *dst_node = pkt->msg->dst_node; struct net_node_t *node = pkt->node; struct net_buffer_t *buffer = pkt->buffer; long long cycle; /* Get current cycle */ cycle = esim_domain_cycle(net_domain_index); if ((net_snap_period != 0) && (net->last_recorded_cycle < (cycle/net_snap_period ))) net_bandwidth_snapshot(net, cycle); if (event == EV_NET_SEND) { struct net_routing_table_entry_t *entry; struct net_buffer_t *output_buffer; if (net->magicNet) { /* Magic Net work-around */ src_node->bytes_sent += pkt->size; src_node->msgs_sent++; dst_node->bytes_received += pkt->size; dst_node->msgs_received++; pkt->node = dst_node; esim_schedule_event(EV_NET_RECEIVE, stack, net->fixed_delay); } /* Get output buffer */ entry = net_routing_table_lookup(routing_table, src_node, dst_node); output_buffer = entry->output_buffer; if (!output_buffer) fatal("%s: no route from %s to %s.\n%s", net->name, src_node->name, dst_node->name, net_err_no_route); if (pkt->msg->size > output_buffer->size) panic("%s: message does not fit in buffer.\n%s", __FUNCTION__, net_err_can_send); if (output_buffer->count + pkt->size > output_buffer->size) panic("%s: output buffer full.\n%s", __FUNCTION__, net_err_can_send); /* Insert in output buffer (1 cycle latency) */ net_buffer_insert(output_buffer, pkt); output_buffer->write_busy = cycle; pkt->node = src_node; pkt->buffer = output_buffer; pkt->busy = cycle; /* Schedule next event */ esim_schedule_event(EV_NET_OUTPUT_BUFFER, stack, 1); } else if (event == EV_NET_OUTPUT_BUFFER) { struct net_buffer_t *input_buffer; int lat; /* Debug */ net_debug("msg " "a=\"obuf\" " "net=\"%s\" " "msg-->pkt=%lld-->%d " "node=\"%s\" " "buf=\"%s\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); /* If message is not at buffer head, process later */ assert(list_count(buffer->msg_list)); if (list_get(buffer->msg_list, 0) != pkt) { net_buffer_wait(buffer, event, stack); net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->packet=%lld:%d " "why=\"not output buffer head\"\n", net->name, pkt->msg->id, pkt->session_id); return; } if (buffer->read_busy >= cycle) { esim_schedule_event(event, stack, buffer->read_busy - cycle + 1); net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"output buffer busy\" \n", net->name, pkt->msg->id, pkt->session_id); return; } /* If link is busy, wait */ if (buffer->kind == net_buffer_link) { struct net_link_t *link; assert(buffer->link); link = buffer->link; if (link->busy >= cycle) { esim_schedule_event(event, stack, link->busy - cycle + 1); net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"link busy\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:link_busy\" " "stg=\"LB\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); return; } /* If buffer contain the message but doesn't have the * shared link in control, wait */ if (link->virtual_channel > 1) { struct net_buffer_t *temp_buffer; temp_buffer = net_link_arbitrator_vc(link, node); if (temp_buffer != buffer) { net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"arbitrator sched\"\n", net->name, pkt->msg->id, pkt->session_id); esim_schedule_event(event, stack, 1); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:VC_arbitration_fail\" " "stg=\"VCA\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); return; } } /* If destination input buffer is busy, wait */ assert(buffer == link->src_buffer); input_buffer = link->dst_buffer; if (input_buffer->write_busy >= cycle) { net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"input buffer busy\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:Dest_buffer_busy\" " "stg=\"DBB\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); esim_schedule_event(event, stack, input_buffer->write_busy - cycle + 1); return; } /* If destination input buffer is full, wait */ if (pkt->size > input_buffer->size) fatal("%s: packet does not fit in buffer.\n%s", net->name, net_err_large_message); if (input_buffer->count + pkt->size > input_buffer->size) { net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"input buffer full\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:Dest_buffer_full\" " "stg=\"DBF\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); net_buffer_wait(input_buffer, event, stack); return; } /* Calculate latency and occupy resources */ lat = (pkt->size - 1) / link->bandwidth + 1; assert(lat > 0); buffer->read_busy = cycle + lat - 1; link->busy = cycle + lat - 1; input_buffer->write_busy = cycle + lat - 1; /* Transfer message to next input buffer */ assert(pkt->busy < cycle); net_buffer_extract(buffer, pkt); net_buffer_insert(input_buffer, pkt); pkt->node = input_buffer->node; pkt->buffer = input_buffer; pkt->busy = cycle + lat - 1; /* Stats */ link->busy_cycles += lat; link->transferred_bytes += pkt->size; link->transferred_msgs++; net->topology_util_bw += pkt->size; node->bytes_sent += pkt->size; node->msgs_sent++; input_buffer->node->bytes_received += pkt->size; input_buffer->node->msgs_received++; net_trace("net.link_transfer net=\"%s\" link=\"%s\" " "transB=%lld last_size=%d busy=%lld\n", net->name, link->name, link->transferred_bytes, pkt->size, link->busy); } else if (buffer->kind == net_buffer_bus) { struct net_bus_t *bus, *updated_bus; struct net_node_t *bus_node; assert(!buffer->link); assert(buffer->bus); bus = buffer->bus; bus_node = bus->node; /* before initiating bus transfer we have to figure out what is the * next input buffer since it is not clear from the * output buffer */ int input_buffer_detection = 0; struct net_routing_table_entry_t *entry; entry = net_routing_table_lookup(routing_table, pkt->node, pkt->msg->dst_node); for (int i = 0; i < list_count(bus_node->dst_buffer_list); i++) { input_buffer = list_get(bus_node->dst_buffer_list, i); if (entry->next_node == input_buffer->node) { input_buffer_detection = 1; break; } } if (input_buffer_detection == 0) fatal("%s: Something went wrong so there is no appropriate input" "buffer for the route between %s and %s \n", net->name, pkt->node->name,entry->next_node->name); /* 1. Check the destination buffer is busy or not */ if (input_buffer->write_busy >= cycle) { esim_schedule_event(event, stack, input_buffer->write_busy - cycle + 1); net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"input busy\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:Dest_buffer_busy\" " "stg=\"DBB\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); return; } /* 2. Check the destination buffer is full or not */ if (pkt->size > input_buffer->size) fatal("%s: packet does not fit in buffer.\n%s", net->name, net_err_large_message); if (input_buffer->count + pkt->size > input_buffer->size) { net_buffer_wait(input_buffer, event, stack); net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"input full\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:Dest_buffer_full\" " "stg=\"DBF\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); return; } /* 3. Make sure if any bus is available; return one * that is available the fastest */ updated_bus = net_bus_arbitration(bus_node, buffer); if (updated_bus == NULL) { esim_schedule_event(event, stack, 1); net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"bus arbiter\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:BUS_arbit_fail\" " "stg=\"BA\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); return; } /* 4. assign the bus to the buffer. update the * necessary data ; before here, the bus is not * assign to anything and is not updated so it can be * assign to other buffers as well. If this certain * buffer wins that specific bus_lane the appropriate * fields will be updated. Contains: bus_lane * cin_buffer and cout_buffer and busy time as well as * buffer data itself */ assert(updated_bus); buffer->bus = updated_bus; input_buffer->bus = updated_bus; bus = buffer->bus; assert(bus); /* Calculate latency and occupy resources */ /* Wire delay is introduced when the packet is on transit */ lat = bus->fix_delay + ((pkt->size - 1) / bus->bandwidth + 1) ; assert(lat > 0); buffer->read_busy = cycle + lat - 1; bus->busy = cycle + lat - 1; input_buffer->write_busy = cycle + lat - 1 ; /* Transfer message to next input buffer */ assert(pkt->busy < cycle); net_buffer_extract(buffer, pkt); net_buffer_insert(input_buffer, pkt); pkt->node = input_buffer->node; pkt->buffer = input_buffer; pkt->busy = cycle + lat - 1; /* Stats */ bus->busy_cycles += lat; bus->transferred_bytes += pkt->size; bus->transferred_msgs++; net->topology_util_bw += pkt->size; node->bytes_sent += pkt->size; node->msgs_sent++; input_buffer->node->bytes_received += pkt->size; input_buffer->node->msgs_received++; net_trace("net.bus_transfer net=\"%s\" node=\"%s\" " "lane_index=%d transB=%lld last_size=%d busy=%lld\n", net->name, bus->node->name, bus->index, bus->transferred_bytes, pkt->size, bus->busy); } else if (buffer->kind == net_buffer_photonic) { struct net_bus_t *bus, *updated_bus; struct net_node_t *bus_node; assert(!buffer->link); assert(buffer->bus); bus = buffer->bus; bus_node = bus->node; /* before 1 and 2 we have to figure out what is the * next input buffer since it is not clear from the * output buffer */ int input_buffer_detection = 0; struct net_routing_table_entry_t *entry; entry = net_routing_table_lookup(routing_table, pkt->node, pkt->msg->dst_node); for (int i = 0; i < list_count(bus_node->dst_buffer_list); i++) { input_buffer = list_get(bus_node->dst_buffer_list, i); if (entry->next_node == input_buffer->node) { input_buffer_detection = 1; break; } } if (input_buffer_detection == 0) fatal("%s: Something went wrong so there is no appropriate input" "buffer for the route between %s and %s \n", net->name, pkt->node->name,entry->next_node->name); /* 1. Check the destination buffer is busy or not */ if (input_buffer->write_busy > cycle) { esim_schedule_event(event, stack, input_buffer->write_busy - cycle + 1); net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"input busy\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:Dest_buffer_busy\" " "stg=\"DBB\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); return; } /* 2. Check the destination buffer is full or not */ if (pkt->size > input_buffer->size) fatal("%s: message does not fit in buffer.\n%s", net->name, net_err_large_message); if (input_buffer->count + pkt->size > input_buffer->size) { net_buffer_wait(input_buffer, event, stack); net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"input full\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:Dest_buffer_full\" " "stg=\"DBF\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); return; } /* 3. Make sure if any bus is available; return one * that is available the fastest */ updated_bus = net_photo_link_arbitration(bus_node, buffer); if (updated_bus == NULL) { esim_schedule_event(event, stack, 1); net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"bus arbiter\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:photonic_arbitration\" " "stg=\"BA\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); return; } /* 4. assign the bus to the buffer. update the * necessary data ; before here, the bus is not * assign to anything and is not updated so it can be * assign to other buffers as well. If this certain * buffer wins that specific bus_lane the appropriate * fields will be updated. Contains: bus_lane * cin_buffer and cout_buffer and busy time as well as * buffer data itself */ assert(updated_bus); buffer->bus = updated_bus; input_buffer->bus = updated_bus; bus = buffer->bus; assert(bus); /* Calculate latency and occupy resources */ lat = (pkt->size - 1) / bus->bandwidth + 1; assert(lat > 0); buffer->read_busy = cycle + lat - 1; bus->busy = cycle + lat - 1; input_buffer->write_busy = cycle + lat - 1; /* Transfer message to next input buffer */ assert(pkt->busy < cycle); net_buffer_extract(buffer, pkt); net_buffer_insert(input_buffer, pkt); pkt->node = input_buffer->node; pkt->buffer = input_buffer; pkt->busy = cycle + lat - 1; /* Stats */ bus->busy_cycles += lat; bus->transferred_bytes += pkt->size; bus->transferred_msgs++; net->topology_util_bw += pkt->size; node->bytes_sent += pkt->size; node->msgs_sent++; input_buffer->node->bytes_received += pkt->size; input_buffer->node->msgs_received++; net_trace("net.photonic_transfer net=\"%s\" node=\"%s\" " "lane_index=%d transB=%lld last_size=%d busy=%lld\n", net->name, bus->node->name, bus->index, bus->transferred_bytes,pkt->size, bus->busy); net_debug("msg " "a=\"success photonic transmission\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "through = \" %d\"\n", net->name, pkt->msg->id, pkt->session_id, updated_bus->index); } /* Schedule next event */ esim_schedule_event(EV_NET_INPUT_BUFFER, stack, lat); } else if (event == EV_NET_INPUT_BUFFER) { struct net_routing_table_entry_t *entry; struct net_buffer_t *output_buffer; int lat; /* Debug */ net_debug("msg " "a=\"ibuf\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "node=\"%s\" " "buf=\"%s\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); /* If message is not at buffer head, process later */ assert(list_count(buffer->msg_list)); if (list_get(buffer->msg_list, 0) != pkt) { net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d" "why=\"not-head\"\n", net->name, pkt->msg->id, pkt->session_id); net_buffer_wait(buffer, event, stack); return; } /* If this is the destination node, finish */ if (node == pkt->msg->dst_node) { esim_schedule_event(EV_NET_RECEIVE, stack, 0); return; } /* If source input buffer is busy, wait */ if (buffer->read_busy >= cycle) { net_debug("pkt" "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"src-busy\"\n", net->name, pkt->msg->id, pkt->session_id); esim_schedule_event(event, stack, buffer->read_busy - cycle + 1); return; } /* Get output buffer */ entry = net_routing_table_lookup(routing_table, node, dst_node); output_buffer = entry->output_buffer; if (!output_buffer) fatal("%s: no route from %s to %s.\n%s", net->name, node->name, dst_node->name, net_err_no_route); /* If destination output buffer is busy, wait */ if (output_buffer->write_busy >= cycle) { net_debug("pkt " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"dst-busy\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:Dest_buffer_busy\" " "stg=\"DBB\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); esim_schedule_event(event, stack, output_buffer->write_busy - cycle + 1); return; } /* If destination output buffer is full, wait */ if (pkt->size > output_buffer->size) fatal("%s: packet does not fit in buffer.\n%s", net->name, net_err_large_message); if (output_buffer->count + pkt->size > output_buffer->size) { net_debug("pkt " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"dst-full\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:Dest_buffer_full\" " "stg=\"DBF\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); net_buffer_wait(output_buffer, event, stack); return; } /* If scheduler says that it is not our turn, try later */ if (net_node_schedule(node, output_buffer) != buffer) { net_debug("pkt " "a=\"stall\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "why=\"sched\"\n", net->name, pkt->msg->id, pkt->session_id); net_trace("net.packet " "net=\"%s\" " "name=\"P-%lld:%d\" " "state=\"%s:%s:switch_arbit_fail\" " "stg=\"SA\"\n", net->name, pkt->msg->id, pkt->session_id, node->name, buffer->name); esim_schedule_event(event, stack, 1); return; } /* Calculate latency and occupy resources */ assert(node->kind != net_node_end); assert(node->bandwidth > 0); lat = (pkt->size - 1) / node->bandwidth + 1; assert(lat > 0); buffer->read_busy = cycle + lat - 1; output_buffer->write_busy = cycle + lat - 1; /* Transfer message to next output buffer */ assert(pkt->busy < cycle); net_buffer_extract(buffer, pkt); net_buffer_insert(output_buffer, pkt); pkt->buffer = output_buffer; pkt->busy = cycle + lat - 1; /* Schedule next event */ esim_schedule_event(EV_NET_OUTPUT_BUFFER, stack, lat); } else if (event == EV_NET_RECEIVE) { assert (pkt); struct net_msg_t *msg = pkt->msg; /* Debug */ net_debug("pkt " "a=\"receive\" " "net=\"%s\" " "msg-->pkt=%lld:%d " "node=\"%s\"\n", net->name, pkt->msg->id, pkt->session_id, dst_node->name); if (net_depacketizer(net, node, pkt) == 1) { if (pkt->msg->packet_list_count > 1) net_trace("net.msg net=\"%s\" name=\"M-%lld\" " "state=\"%s:depacketize\"\n", net->name, msg->id, node->name); if (stack->ret_event == ESIM_EV_NONE) { assert (msg); net_receive(net, node, msg); } /* Finish */ net_stack_return(stack); } else /* Freeing packet stack, not the message */ free(stack); } else { panic("%s: unknown event", __FUNCTION__); } }
/* This algorithm will be recursively called to do the backtracking of DFS algorithm. */ static void routing_table_cycle_detection_dfs_visit(struct net_routing_table_t *routing_table, struct list_t *buffer_list, struct list_t *color_list, struct list_t *parent_list, int list_elem, int buffer_count) { int j; struct net_t *net = routing_table->net; struct net_buffer_t *parent_index; struct net_node_t *buffer_color; struct net_node_t *node_elem; struct net_buffer_t *buffer_elem; list_set(color_list, list_elem, NET_NODE_COLOR_GRAY); buffer_elem = list_get(buffer_list, list_elem); node_elem = buffer_elem->node; for (j = 0; j < routing_table->dim && !routing_table->has_cycle; j++) { struct net_node_t *node_adj; struct net_routing_table_entry_t *entry; node_adj = list_get(net->node_list, j); entry = net_routing_table_lookup(routing_table, node_elem, node_adj); if (entry->output_buffer == buffer_elem) { struct net_routing_table_entry_t *entry_adj; entry_adj = net_routing_table_lookup(routing_table, entry->next_node, node_adj); for (int i = 0; i < buffer_count; i++) { struct net_buffer_t *buffer_adj; buffer_adj = list_get(buffer_list, i); if (buffer_adj == entry_adj->output_buffer) { buffer_color = list_get(color_list, i); if(buffer_color == NET_NODE_COLOR_WHITE) { list_set(parent_list, i, buffer_elem); routing_table_cycle_detection_dfs_visit(routing_table, buffer_list, color_list, parent_list, i, buffer_count); } buffer_color = list_get(color_list, i); parent_index = list_get(parent_list, i); if (buffer_color == NET_NODE_COLOR_GRAY && parent_index != buffer_elem) { warning("network %s: cycle found in routing table.\n%s", net->name, err_net_cycle); routing_table->has_cycle = 1; } } } } } list_set(color_list, list_elem, NET_NODE_COLOR_BLACK); }
void net_routing_table_dump(struct net_routing_table_t *routing_table, FILE *f) { int i, j, k; struct net_t *net = routing_table->net; struct net_node_t *next_node; /* Routing table */ fprintf(f, " "); for (i = 0; i < net->node_count; i++) fprintf(f, "%2d ", i); fprintf(f, "\n"); for (i = 0; i < net->node_count; i++) { fprintf(f, "node %2d: ", i); for (j = 0; j < net->node_count; j++) { struct net_node_t *node_i; struct net_node_t *node_j; struct net_routing_table_entry_t *entry_i_j; node_i = list_get(net->node_list, i); node_j = list_get(net->node_list, j); entry_i_j = net_routing_table_lookup(routing_table, node_i, node_j); next_node = entry_i_j->next_node; if (next_node) fprintf(f, "%2d ", next_node->index); else fprintf(f, "-- "); } fprintf(f, "\n"); } fprintf(f, "\n"); /* Node combinations */ for (i = 0; i < net->node_count; i++) { for (j = 0; j < net->node_count; j++) { struct net_node_t *node_i; struct net_node_t *node_j; node_i = list_get(net->node_list, i); node_j = list_get(net->node_list, j); fprintf(f, "from %s to %s: ", node_i->name, node_j->name); k = i; while (k != j) { struct net_node_t *node_k; struct net_routing_table_entry_t *entry_k_j; node_k = list_get(net->node_list, k); entry_k_j = net_routing_table_lookup(routing_table, node_k, node_j); next_node = entry_k_j->next_node; if (!next_node) { fprintf(f, "x "); break; } fprintf(f, "%s ", next_node->name); k = next_node->index; } fprintf(f, "\n"); } fprintf(f, "\n"); } }
/* Calculate shortest paths Floyd-Warshall algorithm.*/ void net_routing_table_floyd_warshall(struct net_routing_table_t *routing_table) { int i, j, k ; struct net_t *net = routing_table->net; struct net_node_t *next_node; struct net_buffer_t *buffer; struct net_link_t *link; struct net_routing_table_entry_t *entry; /* The 'routing_table_entry->next_node' values do * not necessarily point to the immediate next hop after this. */ for (k = 0; k < net->node_count; k++) { for (i = 0; i < net->node_count; i++) { for (j = 0; j < net->node_count; j++) { struct net_node_t *node_i; struct net_node_t *node_j; struct net_node_t *node_k; struct net_routing_table_entry_t *entry_i_k; struct net_routing_table_entry_t *entry_k_j; struct net_routing_table_entry_t *entry_i_j; node_i = list_get(net->node_list, i); node_j = list_get(net->node_list, j); node_k = list_get(net->node_list, k); entry_i_k = net_routing_table_lookup(routing_table, node_i, node_k); entry_k_j = net_routing_table_lookup(routing_table, node_k, node_j); entry_i_j = net_routing_table_lookup(routing_table, node_i, node_j); if (entry_i_k->cost + entry_k_j->cost < entry_i_j->cost) { entry_i_j->cost = entry_i_k->cost + entry_k_j->cost; entry_i_j->next_node = node_k; } } } } /* Calculate output buffers */ for (i = 0; i < net->node_count; i++) { for (j = 0; j < net->node_count; j++) { struct net_node_t *node_i; struct net_node_t *node_j; struct net_routing_table_entry_t *entry_i_j; node_i = list_get(net->node_list, i); node_j = list_get(net->node_list, j); entry_i_j = net_routing_table_lookup(routing_table, node_i, node_j); next_node = entry_i_j->next_node; /* No route to node */ if (!next_node) { entry_i_j->output_buffer = NULL; continue; } /* Follow path */ for (;;) { entry = net_routing_table_lookup(routing_table, node_i, next_node); if (entry->cost <= 1) break; next_node = entry->next_node; } /* Look for output buffer */ buffer = NULL; assert(list_count(node_i->output_buffer_list)); for (k = 0; k < list_count(node_i->output_buffer_list); k++) { buffer = list_get(node_i->output_buffer_list, k); link = buffer->link; assert(link); if (link->dst_node == next_node) break; } assert(k < list_count(node_i->output_buffer_list)); entry_i_j->output_buffer = buffer; } } /* Update routing table entries to point to the next hop */ for (i = 0; i < net->node_count; i++) { for (j = 0; j < net->node_count; j++) { struct net_node_t *node_i; struct net_node_t *node_j; struct net_routing_table_entry_t *entry_i_j; node_i = list_get(net->node_list, i); node_j = list_get(net->node_list, j); entry_i_j = net_routing_table_lookup(routing_table, node_i, node_j); buffer = entry_i_j->output_buffer; if (buffer) { link = buffer->link; assert(link); entry_i_j->next_node = link->dst_node; } } } /* Find cycle in routing table */ net_routing_table_cycle_detection(routing_table); }
/* Updating the entries in the routing table based on the routes existing in * configuration file */ void net_routing_table_route_update(struct net_routing_table_t *routing_table, struct net_node_t *src_node, struct net_node_t *dst_node, struct net_node_t *next_node, int vc_num) { int route_check = 0; int k; struct net_buffer_t *buffer; struct net_link_t *link; struct net_bus_t *bus; struct net_routing_table_entry_t *entry; entry = net_routing_table_lookup(routing_table, src_node, dst_node); entry->next_node = next_node; entry->output_buffer = NULL; /* Look for output buffer */ buffer = NULL; assert(list_count(src_node->output_buffer_list)); for (k = 0; (k < list_count(src_node->output_buffer_list) && route_check != 1); k++) { buffer = list_get(src_node->output_buffer_list, k); if (buffer->kind == net_buffer_link) { link = buffer->link; assert(link); assert(!buffer->bus); if ((link->dst_node == next_node)) { if (vc_num == 0) { entry->output_buffer = buffer; route_check = 1; } else { if (link->virtual_channel <= vc_num) fatal("Network %s: %s.to.%s: wrong virtual channel " "number is used in route \n %s", routing_table->net->name, src_node->name, dst_node->name, net_err_config); struct net_buffer_t *vc_buffer; vc_buffer = list_get(src_node->output_buffer_list, (buffer->index)+vc_num); assert(vc_buffer->link == buffer->link); entry->output_buffer = vc_buffer; route_check = 1; } } } else if (buffer->kind == net_buffer_bus || buffer->kind == net_buffer_photonic) { assert(!buffer->link); bus = buffer->bus; assert(bus); struct net_node_t * bus_node; bus_node = bus->node; for (int i = 0; i < list_count(bus_node->dst_buffer_list); i++) { struct net_buffer_t *dst_buffer; dst_buffer = list_get(bus_node->dst_buffer_list, i); if (dst_buffer->node == next_node) { entry->output_buffer = buffer; route_check = 1; break; } } if (vc_num != 0) fatal("Network %s: %s.to.%s: BUS does not contain virtual channel \n %s", routing_table->net->name, src_node->name, dst_node->name, net_err_config); } } /*If there is not a route between the source node and next node , error */ if (route_check == 0) fatal("Network %s : route %s.to.%s = %s : Missing connection \n%s ", routing_table->net->name, src_node->name, dst_node->name, next_node->name, net_err_route_step); /* Find cycle in routing table */ net_routing_table_cycle_detection(routing_table); }
void net_routing_table_dump(struct net_routing_table_t *routing_table, FILE *f) { int i, j; struct net_t *net = routing_table->net; struct net_node_t *next_node; struct net_node_t *node_l; /* Routing table */ fprintf(f, " "); for (i = 0; i < net->node_count; i++) { node_l = list_get(net->node_list, i); fprintf(f, "\t%s \t\t", node_l->name); } fprintf(f, "\n"); for (i = 0; i < net->node_count; i++) { node_l = list_get(net->node_list, i); fprintf(f, "%s\t\t", node_l->name); for (j = 0; j < net->node_count; j++) { struct net_node_t *node_i; struct net_node_t *node_j; struct net_routing_table_entry_t *entry_i_j; struct net_buffer_t *buffer; node_i = list_get(net->node_list, i); node_j = list_get(net->node_list, j); entry_i_j = net_routing_table_lookup(routing_table, node_i, node_j); next_node = entry_i_j->next_node; buffer = entry_i_j->output_buffer; if (next_node) { fprintf(f, "%s:\t", next_node->name); if (buffer) { fprintf(f,"%s \t", buffer->name); } else fprintf(f,"-------- \t"); } else fprintf(f, "--\t\t\t"); } fprintf(f, "\n"); } fprintf(f, "\n"); /* Node combinations */ /* int k; for (i = 0; i < net->node_count; i++) { for (j = 0; j < net->node_count; j++) { struct net_node_t *node_i; struct net_node_t *node_j; node_i = list_get(net->node_list, i); node_j = list_get(net->node_list, j); fprintf(f, "from %s to %s: ", node_i->name, node_j->name); k = i; while (k != j) { struct net_node_t *node_k; struct net_routing_table_entry_t *entry_k_j; node_k = list_get(net->node_list, k); entry_k_j = net_routing_table_lookup(routing_table, node_k, node_j); next_node = entry_k_j->next_node; if (!next_node) { fprintf(f, "x "); break; } fprintf(f, "%s ", next_node->name); k = next_node->index; } fprintf(f, "\n"); } fprintf(f, "\n"); } */ }
/* Create contents of routing table */ void net_routing_table_initiate(struct net_routing_table_t *routing_table) { struct net_t *net = routing_table->net; int i, j; struct net_node_t *src_node, *dst_node; struct net_buffer_t *buffer; struct net_routing_table_entry_t *entry; /* Allocate routing table entries */ if (routing_table->entries) panic("%s: network \"%s\": routing table already allocated", __FUNCTION__, net->name); routing_table->dim = list_count(net->node_list); routing_table->entries = xcalloc(routing_table->dim * routing_table->dim, sizeof(struct net_routing_table_entry_t)); /* Initialize table with infinite costs */ for (i = 0; i < net->node_count; i++) { for (j = 0; j < net->node_count; j++) { src_node = list_get(net->node_list, i); dst_node = list_get(net->node_list, j); entry = net_routing_table_lookup(routing_table, src_node, dst_node); entry->cost = i == j ? 0 : routing_table->dim; /* Infinity or 0 */ entry->next_node = NULL; entry->output_buffer = NULL; } } /* Set 1-hop connections */ for (i = 0; i < net->node_count; i++) { src_node = list_get(net->node_list, i); for (j = 0; j < list_count(src_node->output_buffer_list); j++) { buffer = list_get(src_node->output_buffer_list, j); /* For each buffer of each node we check to see if it * is connected to a link or a BUS. If it is * connected to the link we update the table by * getting the destination node of the Link */ if (buffer->kind == net_buffer_link) { struct net_link_t *link; assert(!buffer->bus); link = buffer->link; assert(link); entry = net_routing_table_lookup (routing_table, src_node, link->dst_node); entry->cost = 1; entry->next_node = link->dst_node; entry->output_buffer = buffer; } /* If it is connected to a BUS we create a connection * between this node and all the nodes that are * connected to the BUS */ else if (buffer->kind == net_buffer_bus || buffer->kind == net_buffer_photonic) { int k; struct net_node_t *bus_node; struct net_bus_t *bus; assert(!buffer->link); assert(buffer->bus); bus = buffer->bus; bus_node = bus->node; for (k = 0; k < list_count(bus_node->dst_buffer_list); k++) { struct net_buffer_t *dst_buffer; dst_buffer = list_get(bus_node->dst_buffer_list, k); if (src_node != dst_buffer->node) { entry = net_routing_table_lookup(routing_table, src_node, dst_buffer->node); entry->cost = 1; entry->next_node = dst_buffer->node; entry->output_buffer = buffer; } /*For BUS. Even though we never get to BUS in the routing table (no routes path * through BUS node. They all pass through BUS node's buffer) we still provide a * Path from BUS nodes to all the nodes. There is no path from nodes that ends in * BUS. Next buffer is still NULL*/ entry = net_routing_table_lookup(routing_table, bus_node, dst_buffer->node); entry->cost = 1; entry->next_node = dst_buffer->node; } } } } }
void net_event_handler(int event, void *data) { struct net_stack_t *stack = data; struct net_t *net = stack->net; struct net_routing_table_t *routing_table = net->routing_table; struct net_msg_t *msg = stack->msg; struct net_node_t *src_node = msg->src_node; struct net_node_t *dst_node = msg->dst_node; struct net_node_t *node = msg->node; struct net_buffer_t *buffer = msg->buffer; if (event == EV_NET_SEND) { struct net_routing_table_entry_t *entry; struct net_buffer_t *output_buffer; /* Debug */ net_debug("msg " "a=\"send\" " "net=\"%s\" " "msg=%lld " "size=%d " "src=\"%s\" " "dst=\"%s\"\n", net->name, msg->id, msg->size, src_node->name, dst_node->name); /* Get output buffer */ entry = net_routing_table_lookup(routing_table, src_node, dst_node); output_buffer = entry->output_buffer; if (!output_buffer) fatal("%s: no route from %s to %s.\n%s", net->name, src_node->name, dst_node->name, net_err_no_route); if (output_buffer->write_busy >= esim_cycle) panic("%s: output buffer busy.\n%s", __FUNCTION__, net_err_can_send); if (msg->size > output_buffer->size) panic("%s: message does not fit in buffer.\n%s", __FUNCTION__, net_err_can_send); if (output_buffer->count + msg->size > output_buffer->size) panic("%s: output buffer full.\n%s", __FUNCTION__, net_err_can_send); /* Insert in output buffer (1 cycle latency) */ net_buffer_insert(output_buffer, msg); output_buffer->write_busy = esim_cycle; msg->node = src_node; msg->buffer = output_buffer; msg->busy = esim_cycle; /* Schedule next event */ esim_schedule_event(EV_NET_OUTPUT_BUFFER, stack, 1); } else if (event == EV_NET_OUTPUT_BUFFER) { struct net_link_t *link; struct net_buffer_t *input_buffer; int lat; /* Debug */ net_debug("msg " "a=\"obuf\" " "net=\"%s\" " "msg=%lld " "node=\"%s\" " "buf=\"%s\"\n", net->name, msg->id, node->name, buffer->name); /* If message is not at buffer head, process later */ assert(list_count(buffer->msg_list)); if (list_get(buffer->msg_list, 0) != msg) { net_buffer_wait(buffer, event, stack); return; } /* If source output buffer is busy, wait */ if (buffer->read_busy >= esim_cycle) { esim_schedule_event(event, stack, buffer->read_busy - esim_cycle + 1); return; } /* If link is busy, wait */ link = buffer->link; if (link->busy >= esim_cycle) { esim_schedule_event(event, stack, link->busy - esim_cycle + 1); return; } /* If buffer contain the message but doesn't have the shared link in control, wait*/ if (link->virtual_channel > 1) { struct net_buffer_t *temp_buffer; temp_buffer = net_link_arbitrator_vc(link, node); if (temp_buffer != buffer) { net_debug("msg " "a=\"arbitrator stall\" " "net=\"%s\" " "msg=%lld " "why=\"sched\"\n", net->name, msg->id); esim_schedule_event(event, stack, 1); return; } } /* If destination input buffer is busy, wait */ assert(buffer == link->src_buffer); input_buffer = link->dst_buffer; if (input_buffer->write_busy >= esim_cycle) { esim_schedule_event(event, stack, input_buffer->write_busy - esim_cycle + 1); return; } /* If destination input buffer is full, wait */ if (msg->size > input_buffer->size) fatal("%s: message does not fit in buffer.\n%s", net->name, net_err_large_message); if (input_buffer->count + msg->size > input_buffer->size) { net_buffer_wait(input_buffer, event, stack); return; } /* Calculate latency and occupy resources */ lat = (msg->size - 1) / link->bandwidth + 1; assert(lat > 0); buffer->read_busy = esim_cycle + lat - 1; link->busy = esim_cycle + lat - 1; input_buffer->write_busy = esim_cycle + lat - 1; /* Transfer message to next input buffer */ assert(msg->busy < esim_cycle); net_buffer_extract(buffer, msg); net_buffer_insert(input_buffer, msg); msg->node = input_buffer->node; msg->buffer = input_buffer; msg->busy = esim_cycle + lat - 1; /* Stats */ link->busy_cycles += lat; link->transferred_bytes += msg->size; link->transferred_msgs++; node->bytes_sent += msg->size; node->msgs_sent++; input_buffer->node->bytes_received += msg->size; input_buffer->node->msgs_received++; /* Schedule next event */ esim_schedule_event(EV_NET_INPUT_BUFFER, stack, lat); } else if (event == EV_NET_INPUT_BUFFER) { struct net_routing_table_entry_t *entry; struct net_buffer_t *output_buffer; int lat; /* Debug */ net_debug("msg " "a=\"ibuf\" " "net=\"%s\" " "msg=%lld " "node=\"%s\" " "buf=\"%s\"\n", net->name, msg->id, node->name, buffer->name); /* If message is not at buffer head, process later */ assert(list_count(buffer->msg_list)); if (list_get(buffer->msg_list, 0) != msg) { net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg=%lld " "why=\"not-head\"\n", net->name, msg->id); net_buffer_wait(buffer, event, stack); return; } /* If this is the destination node, finish */ if (node == msg->dst_node) { esim_schedule_event(EV_NET_RECEIVE, stack, 0); return; } /* If source input buffer is busy, wait */ if (buffer->read_busy >= esim_cycle) { net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg=%lld " "why=\"src-busy\"\n", net->name, msg->id); esim_schedule_event(event, stack, buffer->read_busy - esim_cycle + 1); return; } /* Get output buffer */ entry = net_routing_table_lookup(routing_table, node, dst_node); output_buffer = entry->output_buffer; if (!output_buffer) fatal("%s: no route from %s to %s.\n%s", net->name, node->name, dst_node->name, net_err_no_route); /* If destination output buffer is busy, wait */ if (output_buffer->write_busy >= esim_cycle) { net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg=%lld " "why=\"dst-busy\"\n", net->name, msg->id); esim_schedule_event(event, stack, output_buffer->write_busy - esim_cycle + 1); return; } /* If destination output buffer is full, wait */ if (msg->size > output_buffer->size) fatal("%s: message does not fit in buffer.\n%s", net->name, net_err_large_message); if (output_buffer->count + msg->size > output_buffer->size) { net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg=%lld " "why=\"dst-full\"\n", net->name, msg->id); net_buffer_wait(output_buffer, event, stack); return; } /* If scheduler says that it is not our turn, try later */ if (net_node_schedule(node, output_buffer) != buffer) { net_debug("msg " "a=\"stall\" " "net=\"%s\" " "msg=%lld " "why=\"sched\"\n", net->name, msg->id); esim_schedule_event(event, stack, 1); return; } /* Calculate latency and occupy resources */ assert(node->kind != net_node_end); assert(node->bandwidth > 0); lat = (msg->size - 1) / node->bandwidth + 1; assert(lat > 0); buffer->read_busy = esim_cycle + lat - 1; output_buffer->write_busy = esim_cycle + lat - 1; /* Transfer message to next output buffer */ assert(msg->busy < esim_cycle); net_buffer_extract(buffer, msg); net_buffer_insert(output_buffer, msg); msg->buffer = output_buffer; msg->busy = esim_cycle + lat - 1; /* Schedule next event */ esim_schedule_event(EV_NET_OUTPUT_BUFFER, stack, lat); } else if (event == EV_NET_RECEIVE) { /* Debug */ net_debug("msg " "a=\"receive\" " "net=\"%s\" " "msg=%lld " "node=\"%s\"\n", net->name, msg->id, dst_node->name); /* Stats */ net->transfers++; net->lat_acc += esim_cycle - msg->send_cycle; net->msg_size_acc += msg->size; /* If not return event was specified, free message here */ if (stack->ret_event == ESIM_EV_NONE) net_receive(net, node, msg); /* Finish */ net_stack_return(stack); } else { panic("%s: unknown event", __FUNCTION__); } }