network_model_emesh_hop_by_hop.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. #include "network_model_emesh_hop_by_hop.h"
  2. #include "core.h"
  3. #include "simulator.h"
  4. #include "config.h"
  5. #include "utils.h"
  6. #include "packet_type.h"
  7. #include "queue_model_history_list.h"
  8. #include "memory_manager_base.h"
  9. #include "dvfs_manager.h"
  10. #include "stats.h"
  11. #include "config.hpp"
  12. #include <math.h>
  13. #include <stdlib.h>
  14. const char* output_direction_names[] = {
  15. "up", "down", "left", "right", "---", "self", "peer", "destination"
  16. };
  17. static_assert(NetworkModelEMeshHopByHop::MAX_OUTPUT_DIRECTIONS == sizeof(output_direction_names) / sizeof(output_direction_names[0]),
  18. "Not enough values in output_direction_names");
  19. const char* OutputDirectionString(NetworkModelEMeshHopByHop::OutputDirection direction)
  20. {
  21. LOG_ASSERT_ERROR(direction < NetworkModelEMeshHopByHop::MAX_OUTPUT_DIRECTIONS, "Invalid output direction %d", direction);
  22. return output_direction_names[direction];
  23. }
  24. NetworkModelEMeshHopByHop::NetworkModelEMeshHopByHop(Network* net, EStaticNetwork net_type):
  25. NetworkModel(net, net_type),
  26. m_enabled(false),
  27. m_total_bytes_sent(0),
  28. m_total_packets_sent(0),
  29. m_total_bytes_received(0),
  30. m_total_packets_received(0),
  31. m_total_contention_delay(SubsecondTime::Zero()),
  32. m_total_packet_latency(SubsecondTime::Zero()),
  33. m_fake_node(false),
  34. m_core_id(getNetwork()->getCore()->getId()),
  35. // Placeholders. These values will be overwritten in a derived class.
  36. m_link_bandwidth(Sim()->getDvfsManager()->getCoreDomain(m_core_id), 0),
  37. m_hop_latency(Sim()->getDvfsManager()->getCoreDomain(m_core_id), 0)
  38. {
  39. // Get the Link Bandwidth, Hop Latency and if it has broadcast tree mechanism
  40. try
  41. {
  42. // Link Bandwidth is specified in bits/clock_cycle
  43. m_link_bandwidth = ComponentBandwidthPerCycle(Sim()->getDvfsManager()->getCoreDomain(m_core_id), Sim()->getCfg()->getInt("network/emesh_hop_by_hop/link_bandwidth"));
  44. // Hop Latency is specified in cycles
  45. m_hop_latency = ComponentLatency(Sim()->getDvfsManager()->getCoreDomain(m_core_id), Sim()->getCfg()->getInt("network/emesh_hop_by_hop/hop_latency"));
  46. UInt32 smt_cores = Sim()->getCfg()->getInt("perf_model/core/logical_cpus");
  47. m_concentration = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/concentration") * smt_cores;
  48. m_dimensions = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/dimensions");
  49. m_wrap_around = Sim()->getCfg()->getBool("network/emesh_hop_by_hop/wrap_around");
  50. // Queue Model enabled? If no, this degrades into a hop counter model
  51. m_queue_model_enabled = Sim()->getCfg()->getBool("network/emesh_hop_by_hop/queue_model/enabled");
  52. m_queue_model_type = Sim()->getCfg()->getString("network/emesh_hop_by_hop/queue_model/type");
  53. m_broadcast_tree_enabled = Sim()->getCfg()->getBool("network/emesh_hop_by_hop/broadcast_tree/enabled");
  54. }
  55. catch(...)
  56. {
  57. LOG_PRINT_ERROR("Could not read parameters from the configuration file");
  58. }
  59. String name = String("network.")+EStaticNetworkStrings[net_type]+".mesh";
  60. registerStatsMetric(name, m_core_id, "bytes-out", &m_total_bytes_sent);
  61. registerStatsMetric(name, m_core_id, "packets-out", &m_total_packets_sent);
  62. registerStatsMetric(name, m_core_id, "bytes-in", &m_total_bytes_received);
  63. registerStatsMetric(name, m_core_id, "packets-in", &m_total_packets_received);
  64. registerStatsMetric(name, m_core_id, "contention-delay", &m_total_contention_delay);
  65. registerStatsMetric(name, m_core_id, "total-delay", &m_total_packet_latency);
  66. computeMeshDimensions(m_mesh_width, m_mesh_height);
  67. if (m_core_id % m_concentration != 0 || m_core_id >= m_concentration * m_mesh_width * m_mesh_height)
  68. {
  69. m_fake_node = true;
  70. return;
  71. }
  72. createQueueModels(name);
  73. }
  74. NetworkModelEMeshHopByHop::~NetworkModelEMeshHopByHop()
  75. {
  76. if (m_fake_node)
  77. return;
  78. for (UInt32 i = 0; i < NUM_OUTPUT_DIRECTIONS; i++)
  79. {
  80. if (m_queue_models[i])
  81. delete m_queue_models[i];
  82. }
  83. delete m_injection_port_queue_model;
  84. delete m_ejection_port_queue_model;
  85. }
  86. void
  87. NetworkModelEMeshHopByHop::createQueueModels(String name)
  88. {
  89. SubsecondTime min_processing_time = m_link_bandwidth.getPeriod();
  90. // Initialize the queue models for all the '4' output directions
  91. m_queue_models[DOWN] = QueueModel::create(name+".link-down", m_core_id, m_queue_model_type, min_processing_time);
  92. m_queue_models[LEFT] = QueueModel::create(name+".link-left", m_core_id, m_queue_model_type, min_processing_time);
  93. m_queue_models[UP] = QueueModel::create(name+".link-up", m_core_id, m_queue_model_type, min_processing_time);
  94. m_queue_models[RIGHT] = QueueModel::create(name+".link-right", m_core_id, m_queue_model_type, min_processing_time);
  95. m_injection_port_queue_model = QueueModel::create(name+".link-in", m_core_id, m_queue_model_type, min_processing_time);
  96. m_ejection_port_queue_model = QueueModel::create(name+".link-out", m_core_id, m_queue_model_type, min_processing_time);
  97. }
  98. void
  99. NetworkModelEMeshHopByHop::routePacket(const NetPacket &pkt, std::vector<Hop> &nextHops)
  100. {
  101. ScopedLock sl(m_lock);
  102. core_id_t requester = INVALID_CORE_ID;
  103. if (pkt.type == SHARED_MEM_1)
  104. requester = getNetwork()->getCore()->getMemoryManager()->getShmemRequester(pkt.data);
  105. else // Other Packet types
  106. requester = pkt.sender;
  107. LOG_ASSERT_ERROR((requester >= 0) && (requester < (core_id_t) Config::getSingleton()->getTotalCores()),
  108. "requester(%i)", requester);
  109. UInt32 pkt_length = getNetwork()->getModeledLength(pkt);
  110. LOG_PRINT("pkt length(%u)", pkt_length);
  111. if (pkt.sender == m_core_id)
  112. {
  113. m_total_packets_sent ++;
  114. m_total_bytes_sent += pkt_length;
  115. }
  116. if (pkt.receiver == NetPacket::BROADCAST)
  117. {
  118. if (m_fake_node)
  119. {
  120. for (core_id_t i = 0; i < (core_id_t) Config::getSingleton()->getTotalCores(); i++)
  121. {
  122. addHop(DESTINATION, i, i, pkt.time, pkt_length, nextHops, requester);
  123. }
  124. }
  125. else if (m_broadcast_tree_enabled)
  126. {
  127. // Injection Port Modeling
  128. SubsecondTime injection_port_queue_delay = SubsecondTime::Zero();
  129. if (pkt.sender == m_core_id)
  130. injection_port_queue_delay = computeInjectionPortQueueDelay(pkt.receiver, pkt.time, pkt_length);
  131. SubsecondTime curr_time = pkt.time + injection_port_queue_delay;
  132. // Broadcast tree is enabled
  133. // Build the broadcast tree
  134. SInt32 sx, sy, cx, cy;
  135. computePosition(pkt.sender, sx, sy);
  136. computePosition(m_core_id, cx, cy);
  137. if (cy >= sy)
  138. addHop(UP, NetPacket::BROADCAST, computeCoreId(cx,cy+1), curr_time, pkt_length, nextHops, requester);
  139. if (cy <= sy)
  140. addHop(DOWN, NetPacket::BROADCAST, computeCoreId(cx,cy-1), curr_time, pkt_length, nextHops, requester);
  141. if (cy == sy)
  142. {
  143. if (cx >= sx)
  144. addHop(RIGHT, NetPacket::BROADCAST, computeCoreId(cx+1,cy), curr_time, pkt_length, nextHops, requester);
  145. if (cx <= sx)
  146. addHop(LEFT, NetPacket::BROADCAST, computeCoreId(cx-1,cy), curr_time, pkt_length, nextHops, requester);
  147. if (cx == sx)
  148. addHop(SELF, m_core_id, m_core_id, curr_time, pkt_length, nextHops, requester);
  149. }
  150. }
  151. else
  152. {
  153. // Broadcast tree is not enabled
  154. // Here, broadcast messages are sent as a collection of unicast messages
  155. LOG_ASSERT_ERROR(pkt.sender == m_core_id,
  156. "BROADCAST message to be sent at (%i), original sender(%i), Tree not enabled",
  157. m_core_id, pkt.sender);
  158. for (core_id_t i = 0; i < (core_id_t) Config::getSingleton()->getTotalCores(); i++)
  159. {
  160. // Injection Port Modeling
  161. SubsecondTime injection_port_queue_delay = computeInjectionPortQueueDelay(i, pkt.time, pkt_length);
  162. SubsecondTime curr_time = pkt.time + injection_port_queue_delay;
  163. // Unicast message to each core
  164. OutputDirection direction;
  165. core_id_t next_dest = getNextDest(i, direction);
  166. addHop(direction, i, next_dest, curr_time, pkt_length, nextHops, requester);
  167. }
  168. }
  169. }
  170. else if (m_fake_node)
  171. {
  172. addHop(DESTINATION, pkt.receiver, pkt.receiver, pkt.time, pkt_length, nextHops, requester);
  173. }
  174. else
  175. {
  176. // Injection Port Modeling
  177. SubsecondTime injection_port_queue_delay = SubsecondTime::Zero();
  178. if (pkt.sender == m_core_id)
  179. {
  180. injection_port_queue_delay = computeInjectionPortQueueDelay(pkt.receiver, pkt.time, pkt_length);
  181. *(subsecond_time_t*)&pkt.queue_delay += injection_port_queue_delay;
  182. }
  183. SubsecondTime curr_time = pkt.time + injection_port_queue_delay;
  184. // A Unicast packet
  185. OutputDirection direction;
  186. core_id_t next_dest = getNextDest(pkt.receiver, direction);
  187. addHop(direction, pkt.receiver, next_dest, curr_time, pkt_length, nextHops, requester, (subsecond_time_t*)&pkt.queue_delay);
  188. }
  189. }
  190. void
  191. NetworkModelEMeshHopByHop::processReceivedPacket(NetPacket& pkt)
  192. {
  193. ScopedLock sl(m_lock);
  194. UInt32 pkt_length = getNetwork()->getModeledLength(pkt);
  195. core_id_t requester = INVALID_CORE_ID;
  196. if (pkt.type == SHARED_MEM_1)
  197. requester = getNetwork()->getCore()->getMemoryManager()->getShmemRequester(pkt.data);
  198. else // Other Packet types
  199. requester = pkt.sender;
  200. LOG_ASSERT_ERROR((requester >= 0) && (requester < (core_id_t) Config::getSingleton()->getTotalCores()),
  201. "requester(%i)", requester);
  202. if ( (!m_enabled) || (requester >= (core_id_t) Config::getSingleton()->getApplicationCores())
  203. || (m_core_id >= (core_id_t) Config::getSingleton()->getApplicationCores()))
  204. return;
  205. SubsecondTime packet_latency = pkt.time - pkt.start_time;
  206. SubsecondTime contention_delay = packet_latency - (computeDistance(pkt.sender, m_core_id) * m_hop_latency.getLatency());
  207. if (pkt.sender != m_core_id && !m_fake_node)
  208. {
  209. SubsecondTime processing_time = computeProcessingTime(pkt_length);
  210. SubsecondTime ejection_port_queue_delay = computeEjectionPortQueueDelay(pkt.time, pkt_length);
  211. packet_latency += (ejection_port_queue_delay + processing_time);
  212. contention_delay += ejection_port_queue_delay;
  213. pkt.time += (ejection_port_queue_delay + processing_time);
  214. pkt.queue_delay += ejection_port_queue_delay;
  215. }
  216. m_total_packets_received ++;
  217. m_total_bytes_received += pkt_length;
  218. m_total_packet_latency += packet_latency;
  219. m_total_contention_delay += contention_delay;
  220. }
  221. void
  222. NetworkModelEMeshHopByHop::addHop(OutputDirection direction,
  223. core_id_t final_dest, core_id_t next_dest,
  224. SubsecondTime pkt_time, UInt32 pkt_length,
  225. std::vector<Hop>& nextHops, core_id_t requester,
  226. subsecond_time_t *queue_delay_stats)
  227. {
  228. Hop h;
  229. h.final_dest = final_dest;
  230. h.next_dest = next_dest;
  231. if (direction > NUM_OUTPUT_DIRECTIONS)
  232. h.time = pkt_time;
  233. else
  234. h.time = pkt_time + computeLatency(direction, pkt_time, pkt_length, requester, queue_delay_stats);
  235. nextHops.push_back(h);
  236. }
  237. SInt32
  238. NetworkModelEMeshHopByHop::computeDistance(core_id_t sender, core_id_t receiver)
  239. {
  240. SInt32 sx, sy, dx, dy;
  241. computePosition(sender, sx, sy);
  242. computePosition(receiver, dx, dy);
  243. if (m_wrap_around)
  244. return std::min(abs(sx - dx), m_mesh_width - abs(sx - dx))
  245. + std::min(abs(sy - dy), m_mesh_height - abs(sy - dy));
  246. else
  247. return abs(sx - dx) + abs(sy - dy);
  248. }
  249. void
  250. NetworkModelEMeshHopByHop::computePosition(core_id_t core_id, SInt32 &x, SInt32 &y)
  251. {
  252. x = (core_id / m_concentration) % m_mesh_width;
  253. y = (core_id / m_concentration) / m_mesh_width;
  254. }
  255. core_id_t
  256. NetworkModelEMeshHopByHop::computeCoreId(SInt32 x, SInt32 y)
  257. {
  258. x = (x + m_mesh_width) % m_mesh_width;
  259. y = (y + m_mesh_height) % m_mesh_height;
  260. return (y * m_mesh_width + x) * m_concentration;
  261. }
  262. SubsecondTime
  263. NetworkModelEMeshHopByHop::computeLatency(OutputDirection direction, SubsecondTime pkt_time, UInt32 pkt_length, core_id_t requester, subsecond_time_t *queue_delay_stats)
  264. {
  265. LOG_ASSERT_ERROR(!m_fake_node, "Cannot computeLatency on a fake network node");
  266. LOG_ASSERT_ERROR((direction >= 0) && (direction < NUM_OUTPUT_DIRECTIONS),
  267. "Invalid Direction(%u)", direction);
  268. if ( (!m_enabled) || (requester >= (core_id_t) Config::getSingleton()->getApplicationCores()) )
  269. return SubsecondTime::Zero();
  270. SubsecondTime processing_time = computeProcessingTime(pkt_length);
  271. SubsecondTime queue_delay = SubsecondTime::Zero();
  272. if (m_queue_model_enabled)
  273. {
  274. queue_delay = m_queue_models[direction]->computeQueueDelay(pkt_time, processing_time);
  275. if (queue_delay_stats)
  276. *queue_delay_stats += queue_delay;
  277. }
  278. LOG_PRINT("Queue Delay(%s), Hop Latency(%s)", itostr(queue_delay).c_str(), itostr(m_hop_latency.getLatency()).c_str());
  279. SubsecondTime packet_latency = m_hop_latency.getLatency() + queue_delay;
  280. return packet_latency;
  281. }
  282. SubsecondTime
  283. NetworkModelEMeshHopByHop::computeInjectionPortQueueDelay(core_id_t pkt_receiver, SubsecondTime pkt_time, UInt32 pkt_length)
  284. {
  285. LOG_ASSERT_ERROR(!m_fake_node, "Cannot computeInjectionPortQueueDelay on a fake network node");
  286. if (!m_queue_model_enabled)
  287. return SubsecondTime::Zero();
  288. if (pkt_receiver == m_core_id)
  289. return SubsecondTime::Zero();
  290. SubsecondTime processing_time = computeProcessingTime(pkt_length);
  291. return m_injection_port_queue_model->computeQueueDelay(pkt_time, processing_time);
  292. }
  293. SubsecondTime
  294. NetworkModelEMeshHopByHop::computeEjectionPortQueueDelay(SubsecondTime pkt_time, UInt32 pkt_length)
  295. {
  296. LOG_ASSERT_ERROR(!m_fake_node, "Cannot computeEjectionPortQueueDelay on a fake network node");
  297. if (!m_queue_model_enabled)
  298. return SubsecondTime::Zero();
  299. SubsecondTime processing_time = computeProcessingTime(pkt_length);
  300. return m_ejection_port_queue_model->computeQueueDelay(pkt_time, processing_time);
  301. }
  302. SubsecondTime
  303. NetworkModelEMeshHopByHop::computeProcessingTime(UInt32 pkt_length)
  304. {
  305. LOG_ASSERT_ERROR(!m_fake_node, "Cannot computeProcessingTime on a fake network node");
  306. // Send: (pkt_length * 8) bits
  307. // Bandwidth: (m_link_bandwidth) bits/cycle
  308. UInt32 num_bits = pkt_length * 8;
  309. return m_link_bandwidth.getRoundedLatency(num_bits);
  310. }
  311. SInt32
  312. NetworkModelEMeshHopByHop::getNextDest(SInt32 final_dest, OutputDirection& direction)
  313. {
  314. // Do dimension-order routing
  315. // Curently, do store-and-forward routing
  316. // FIXME: Should change this to wormhole routing eventually
  317. if (final_dest >= (core_id_t)Config::getSingleton()->getApplicationCores())
  318. {
  319. // We are a fake core (not an application core): warp straight to the destination
  320. direction = DESTINATION;
  321. return final_dest;
  322. }
  323. else if (m_core_id / m_concentration == final_dest / m_concentration)
  324. {
  325. // Destination is self, a peer on our concentrated node
  326. direction = DESTINATION;
  327. return final_dest;
  328. }
  329. else if (m_fake_node)
  330. {
  331. // We are a concentrated node but not the master: first send to master
  332. direction = PEER;
  333. return m_core_id - m_core_id % m_concentration;
  334. }
  335. SInt32 sx, sy, dx, dy;
  336. computePosition(m_core_id, sx, sy);
  337. computePosition(final_dest, dx, dy);
  338. if ((sx > dx) ^ (m_wrap_around && abs(sx - dx) > (m_mesh_width+1) / 2))
  339. {
  340. direction = LEFT;
  341. return computeCoreId(sx-1,sy);
  342. }
  343. else if (sx != dx)
  344. {
  345. direction = RIGHT;
  346. return computeCoreId(sx+1,sy);
  347. }
  348. else if ((sy > dy) ^ (m_wrap_around && abs(sy - dy) > (m_mesh_height+1) / 2))
  349. {
  350. direction = DOWN;
  351. return computeCoreId(sx,sy-1);
  352. }
  353. else if (sy != dy)
  354. {
  355. direction = UP;
  356. return computeCoreId(sx,sy+1);
  357. }
  358. else
  359. {
  360. // A send to itself
  361. direction = SELF;
  362. return m_core_id;
  363. }
  364. }
  365. void
  366. NetworkModelEMeshHopByHop::enable()
  367. {
  368. m_enabled = true;
  369. }
  370. void
  371. NetworkModelEMeshHopByHop::disable()
  372. {
  373. m_enabled = false;
  374. }
  375. bool
  376. NetworkModelEMeshHopByHop::isEnabled()
  377. {
  378. return m_enabled;
  379. }
  380. void
  381. NetworkModelEMeshHopByHop::computeMeshDimensions(SInt32 &mesh_width, SInt32 &mesh_height)
  382. {
  383. SInt32 core_count = Config::getSingleton()->getApplicationCores();
  384. UInt32 smt_cores = Sim()->getCfg()->getInt("perf_model/core/logical_cpus");
  385. SInt32 concentration = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/concentration") * smt_cores;
  386. SInt32 dimensions = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/dimensions");
  387. String size = Sim()->getCfg()->getString("network/emesh_hop_by_hop/size");
  388. if (size == "")
  389. {
  390. switch(dimensions)
  391. {
  392. case 1: // line / ring
  393. mesh_width = core_count / concentration;
  394. mesh_height = 1;
  395. break;
  396. case 2: // 2-d mesh / torus
  397. mesh_width = (SInt32) floor (sqrt(core_count / concentration));
  398. mesh_height = (SInt32) ceil (1.0 * core_count / concentration / mesh_width);
  399. break;
  400. default:
  401. LOG_PRINT_ERROR("Invalid value %d for dimensions, only 1 (line/ring) and 2 (mesh/torus) are currently supported", dimensions);
  402. }
  403. LOG_ASSERT_ERROR(core_count == (concentration * mesh_height * mesh_width), "Cannot build a mesh with %d cores (concentration %d), increase NumApplicationCores to %d for a %d x %d mesh or configure network/emesh_hop_by_hop/size=WIDTH:HEIGHT to manually specify mesh dimensions", core_count, concentration, concentration * mesh_width * mesh_height, mesh_width, mesh_height);
  404. }
  405. else
  406. {
  407. // ":"-separated, "," is for heterogeneity
  408. int res = sscanf(size.c_str(), "%d:%d", &mesh_width, &mesh_height);
  409. LOG_ASSERT_ERROR(res == 2, "Invalid mesh size \"%s\", expected \"width:height\"", size.c_str());
  410. LOG_ASSERT_ERROR(core_count == (concentration * mesh_height * mesh_width), "Invalid mesh size %s for %d cores (concentration %d): %d x %d (x %d) == %d != %d", size.c_str(), core_count, concentration, mesh_width, mesh_height, concentration, concentration * mesh_width * mesh_height, core_count);
  411. }
  412. }
  413. std::pair<bool,SInt32>
  414. NetworkModelEMeshHopByHop::computeCoreCountConstraints(SInt32 core_count)
  415. {
  416. SInt32 mesh_width, mesh_height;
  417. computeMeshDimensions(mesh_width, mesh_height);
  418. assert(core_count <= mesh_width * mesh_height);
  419. assert(core_count > (mesh_width - 1) * mesh_height);
  420. assert(core_count > mesh_width * (mesh_height - 1));
  421. return std::make_pair(true,mesh_height * mesh_width);
  422. }
  423. std::pair<bool, std::vector<core_id_t> >
  424. NetworkModelEMeshHopByHop::computeMemoryControllerPositions(SInt32 num_memory_controllers, SInt32 core_count)
  425. {
  426. UInt32 smt_cores = Sim()->getCfg()->getInt("perf_model/core/logical_cpus");
  427. SInt32 concentration = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/concentration") * smt_cores;
  428. SInt32 dimensions = Sim()->getCfg()->getInt("network/emesh_hop_by_hop/dimensions");
  429. SInt32 mesh_width, mesh_height;
  430. computeMeshDimensions(mesh_width, mesh_height);
  431. // core_id_list_along_perimeter : list of cores along the perimeter of the chip in clockwise order starting from (0,0)
  432. std::vector<core_id_t> core_id_list_along_perimeter;
  433. for (SInt32 i = 0; i < mesh_width; i++)
  434. core_id_list_along_perimeter.push_back(i);
  435. if (dimensions > 1 && mesh_height > 1)
  436. {
  437. for (SInt32 i = 1; i < (mesh_height-1); i++)
  438. core_id_list_along_perimeter.push_back((i * mesh_width) + mesh_width-1);
  439. for (SInt32 i = mesh_width-1; i >= 0; i--)
  440. core_id_list_along_perimeter.push_back(((mesh_height-1) * mesh_width) + i);
  441. for (SInt32 i = mesh_height-2; i >= 1; i--)
  442. core_id_list_along_perimeter.push_back(i * mesh_width);
  443. assert(core_id_list_along_perimeter.size() == (UInt32) (2 * (mesh_width + mesh_height - 2)));
  444. }
  445. LOG_ASSERT_ERROR(core_id_list_along_perimeter.size() >= (UInt32) num_memory_controllers,
  446. "num cores along perimeter(%u), num memory controllers(%i)",
  447. core_id_list_along_perimeter.size(), num_memory_controllers);
  448. SInt32 spacing_between_memory_controllers = core_id_list_along_perimeter.size() / num_memory_controllers;
  449. // core_id_list_with_memory_controllers : list of cores that have memory controllers attached to them
  450. std::vector<core_id_t> core_id_list_with_memory_controllers;
  451. for (SInt32 i = 0; i < num_memory_controllers; i++)
  452. {
  453. SInt32 index = (i * spacing_between_memory_controllers + mesh_width/2) % core_id_list_along_perimeter.size();
  454. core_id_list_with_memory_controllers.push_back(concentration * core_id_list_along_perimeter[index]);
  455. }
  456. return (std::make_pair(true, core_id_list_with_memory_controllers));
  457. }