diff --git a/util/MPSoCConfig.py b/MPSoCConfig.py similarity index 100% rename from util/MPSoCConfig.py rename to MPSoCConfig.py diff --git a/Roteador.py b/Roteador.py deleted file mode 100644 index dc0716e..0000000 --- a/Roteador.py +++ /dev/null @@ -1,151 +0,0 @@ -# -*- coding: utf-8 -*- - -################################################################################ -## Form generated from reading UI file 'RoteadorjxHyuV.ui' -## -## Created by: Qt User Interface Compiler version 6.11.2 -## -## WARNING! All changes made in this file will be lost when recompiling UI file! -################################################################################ - -from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, - QMetaObject, QObject, QPoint, QRect, - QSize, QTime, QUrl, Qt) -from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, - QFont, QFontDatabase, QGradient, QIcon, - QImage, QKeySequence, QLinearGradient, QPainter, - QPalette, QPixmap, QRadialGradient, QTransform) -from PySide6.QtWidgets import (QApplication, QLabel, QSizePolicy, QWidget, QMainWindow) - -class Ui_Form(object): - def setupUi(self, Form): - if not Form.objectName(): - Form.setObjectName(u"Form") - Form.resize(170, 170) - Form.setMinimumSize(QSize(170, 170)) - Form.setMaximumSize(QSize(170, 170)) - self.router = QWidget(Form) - self.router.setObjectName(u"router") - self.router.setGeometry(QRect(30, 30, 130, 130)) - self.routerLabel = QLabel(self.router) - self.routerLabel.setObjectName(u"routerLabel") - self.routerLabel.setGeometry(QRect(40, 50, 50, 30)) - font = QFont() - font.setFamilies([u"Arial"]) - font.setPointSize(14) - font.setBold(True) - self.routerLabel.setFont(font) - self.routerLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.north_HIGH_Label = QLabel(self.router) - self.north_HIGH_Label.setObjectName(u"north_HIGH_Label") - self.north_HIGH_Label.setGeometry(QRect(90, 0, 40, 20)) - font1 = QFont() - font1.setFamilies([u"Andale Mono"]) - font1.setPointSize(10) - self.north_HIGH_Label.setFont(font1) - self.south_HIGH_Label = QLabel(self.router) - self.south_HIGH_Label.setObjectName(u"south_HIGH_Label") - self.south_HIGH_Label.setGeometry(QRect(82, 110, 40, 20)) - self.south_HIGH_Label.setFont(font1) - self.south_LOW_Label = QLabel(self.router) - self.south_LOW_Label.setObjectName(u"south_LOW_Label") - self.south_LOW_Label.setGeometry(QRect(20, 110, 40, 20)) - self.south_LOW_Label.setFont(font1) - self.weast_LOW_Label = QLabel(self.router) - self.weast_LOW_Label.setObjectName(u"weast_LOW_Label") - self.weast_LOW_Label.setGeometry(QRect(1, 70, 40, 20)) - self.weast_LOW_Label.setFont(font1) - self.weast_HIGH_Label = QLabel(self.router) - self.weast_HIGH_Label.setObjectName(u"weast_HIGH_Label") - self.weast_HIGH_Label.setGeometry(QRect(1, 20, 40, 20)) - self.weast_HIGH_Label.setFont(font1) - self.north_LOW_Label = QLabel(self.router) - self.north_LOW_Label.setObjectName(u"north_LOW_Label") - self.north_LOW_Label.setGeometry(QRect(40, 0, 40, 20)) - self.north_LOW_Label.setFont(font1) - self.east_HIGH_Label = QLabel(self.router) - self.east_HIGH_Label.setObjectName(u"east_HIGH_Label") - self.east_HIGH_Label.setGeometry(QRect(90, 40, 40, 20)) - self.east_HIGH_Label.setFont(font1) - self.east_LOW_Label = QLabel(self.router) - self.east_LOW_Label.setObjectName(u"east_LOW_Label") - self.east_LOW_Label.setGeometry(QRect(90, 90, 40, 20)) - self.east_LOW_Label.setFont(font1) - self.out_north_HIGH = QWidget(Form) - self.out_north_HIGH.setObjectName(u"out_north_HIGH") - self.out_north_HIGH.setGeometry(QRect(115, 0, 6, 30)) - self.in_north_HIGH = QWidget(Form) - self.in_north_HIGH.setObjectName(u"in_north_HIGH") - self.in_north_HIGH.setGeometry(QRect(124, 0, 20, 30)) - self.out_weast_HIGH = QWidget(Form) - self.out_weast_HIGH.setObjectName(u"out_weast_HIGH") - self.out_weast_HIGH.setGeometry(QRect(0, 77, 30, 6)) - self.in_weast_HIGH = QWidget(Form) - self.in_weast_HIGH.setObjectName(u"in_weast_HIGH") - self.in_weast_HIGH.setGeometry(QRect(0, 53, 30, 20)) - self.in_east_LOW = QWidget(Form) - self.in_east_LOW.setObjectName(u"in_east_LOW") - self.in_east_LOW.setGeometry(QRect(160, 120, 33, 20)) - self.out_east_LOW = QWidget(Form) - self.out_east_LOW.setObjectName(u"out_east_LOW") - self.out_east_LOW.setGeometry(QRect(160, 110, 33, 6)) - self.in_south_LOW = QWidget(Form) - self.in_south_LOW.setObjectName(u"in_south_LOW") - self.in_south_LOW.setGeometry(QRect(50, 160, 20, 30)) - self.out_south_LOW = QWidget(Form) - self.out_south_LOW.setObjectName(u"out_south_LOW") - self.out_south_LOW.setGeometry(QRect(72, 160, 6, 30)) - self.in_south_HIGH = QWidget(Form) - self.in_south_HIGH.setObjectName(u"in_south_HIGH") - self.in_south_HIGH.setGeometry(QRect(108, 160, 20, 30)) - self.out_south_HIGH = QWidget(Form) - self.out_south_HIGH.setObjectName(u"out_south_HIGH") - self.out_south_HIGH.setGeometry(QRect(131, 160, 6, 30)) - self.out_weast_LOW = QWidget(Form) - self.out_weast_LOW.setObjectName(u"out_weast_LOW") - self.out_weast_LOW.setGeometry(QRect(0, 127, 30, 6)) - self.in_weast_LOW = QWidget(Form) - self.in_weast_LOW.setObjectName(u"in_weast_LOW") - self.in_weast_LOW.setGeometry(QRect(0, 103, 30, 20)) - self.in_east_HIGH = QWidget(Form) - self.in_east_HIGH.setObjectName(u"in_east_HIGH") - self.in_east_HIGH.setGeometry(QRect(160, 70, 20, 20)) - self.out_east_HIGH = QWidget(Form) - self.out_east_HIGH.setObjectName(u"out_east_HIGH") - self.out_east_HIGH.setGeometry(QRect(160, 60, 33, 6)) - self.out_north_LOW = QWidget(Form) - self.out_north_LOW.setObjectName(u"out_north_LOW") - self.out_north_LOW.setGeometry(QRect(57, 0, 6, 30)) - self.in_north_LOW = QWidget(Form) - self.in_north_LOW.setObjectName(u"in_north_LOW") - self.in_north_LOW.setGeometry(QRect(65, 0, 20, 30)) - self.local_out = QWidget(Form) - self.local_out.setObjectName(u"local_out") - self.local_out.setGeometry(QRect(0, 20, 20, 20)) - self.local_Label = QLabel(Form) - self.local_Label.setObjectName(u"local_Label") - self.local_Label.setGeometry(QRect(20, 18, 40, 20)) - self.local_Label.setFont(font1) - self.local_in = QWidget(Form) - self.local_in.setObjectName(u"local_in") - self.local_in.setGeometry(QRect(0, 0, 20, 20)) - - self.retranslateUi(Form) - - QMetaObject.connectSlotsByName(Form) - # setupUi - - def retranslateUi(self, Form): - Form.setStyleSheet(QCoreApplication.translate("Form", u"background-color: rgb(255, 255, 255);", None)) - self.routerLabel.setText("") - self.north_HIGH_Label.setText(QCoreApplication.translate("Form", u"00.00%", None)) - self.south_HIGH_Label.setText(QCoreApplication.translate("Form", u"00.00%", None)) - self.south_LOW_Label.setText(QCoreApplication.translate("Form", u"00.00%", None)) - self.weast_LOW_Label.setText(QCoreApplication.translate("Form", u"00.00%", None)) - self.weast_HIGH_Label.setText(QCoreApplication.translate("Form", u"00.00%", None)) - self.north_LOW_Label.setText(QCoreApplication.translate("Form", u"00.00%", None)) - self.east_HIGH_Label.setText(QCoreApplication.translate("Form", u"00.00%", None)) - self.east_LOW_Label.setText(QCoreApplication.translate("Form", u"00.00%", None)) - self.local_Label.setText(QCoreApplication.translate("Form", u"00.00%", None)) - # retranslateUi - diff --git a/analysis.py b/analysis.py index 69da3d9..82020bc 100644 --- a/analysis.py +++ b/analysis.py @@ -15,7 +15,7 @@ from collections import Counter, defaultdict from dataclasses import dataclass, field -from util.MPSoCConfig import MPSoCConfig +from MPSoCConfig import MPSoCConfig # ID usado para "tarefa" do kernel/injetor nos campos de tarefa do traffic_router.txt KERNEL_TASK_ID = 0xFFFF @@ -345,8 +345,7 @@ def pending_count(self): def read_all_packets(mpsoc_config): """Todos os pacotes do traffic_router.txt, com um leitor próprio (não mexe na simulação).""" - from information.read_traffic_data import ReadTrafficData - from information.router_neighbors import RouterNeighbors + from information import ReadTrafficData, RouterNeighbors reader = ReadTrafficData(mpsoc_config, RouterNeighbors.from_config(mpsoc_config)) packets = [] diff --git a/help_dialogs.py b/dialogs.py similarity index 50% rename from help_dialogs.py rename to dialogs.py index c038269..fe8f544 100644 --- a/help_dialogs.py +++ b/dialogs.py @@ -1,17 +1,26 @@ """ -Janelas do menu Help: "About" (util/AboutFrame.java) e "Packet Format" -(MainFrame.packetFormatMenuItemActionPerformed). +Diálogos dos menus Edit e Help. + +- PlatformSetupDialog (Edit -> Platform Setup): refatoração de + PlatformSetupFrame.java do GraphicalDebugger original; permite trocar o + tamanho do flit, o período de clock e a largura da janela de checkpoint + (intervalo em que a % de uso dos enlaces é recalculada) sem editar o + platform.cfg. As mudanças valem até o próximo "Reset Simulation" ou + "New Debugging", que releem os arquivos. +- AboutDialog (util/AboutFrame.java) e show_packet_format + (MainFrame.packetFormatMenuItemActionPerformed), do menu Help. """ import os -from PySide6.QtCore import Qt -from PySide6.QtGui import QFont, QPixmap -from PySide6.QtWidgets import QDialog, QDialogButtonBox, QLabel, QMessageBox, QVBoxLayout +from PySide6.QtCore import QLocale, Qt +from PySide6.QtGui import QDoubleValidator, QFont, QIntValidator, QPixmap +from PySide6.QtWidgets import (QDialog, QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QMessageBox, + QVBoxLayout) ICON_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon") -# Formato de cada linha do traffic_router.txt, como lido em information/read_traffic_data.py +# Formato de cada linha do traffic_router.txt, como lido em information.py (ReadTrafficData) PACKET_FORMAT = """\
Each line of traffic_router.txt is a packet header captured by a router, with tab-separated fields:
Time | Current Router Address | Service | Payload size | Packet bandwidth | Input port |
@@ -82,3 +91,63 @@ def __init__(self, parent=None):
layout.addWidget(label)
layout.addWidget(buttons)
layout.setSizeConstraint(QVBoxLayout.SizeConstraint.SetFixedSize)
+
+
+class PlatformSetupDialog(QDialog):
+ def __init__(self, mpsoc_config, checkpoint, parent=None):
+ super().__init__(parent)
+ self.mpsoc_config = mpsoc_config
+ self.checkpoint = checkpoint
+ self.setWindowTitle("Platform Setup")
+
+ self.flit_size_edit = QLineEdit(str(mpsoc_config.flit_size))
+ self.flit_size_edit.setValidator(QIntValidator(1, 1024, self))
+
+ self.clock_edit = QLineEdit(str(mpsoc_config.clock_period_in_ns))
+ self.clock_edit.setValidator(QIntValidator(1, 1_000_000, self))
+
+ # Largura da janela em us, como no Java (guardada em ms no controlador)
+ self.window_width_edit = QLineEdit(f"{checkpoint.window_size_ms * 1000:g}")
+ window_validator = QDoubleValidator(0.001, 1e9, 3, self)
+ window_validator.setNotation(QDoubleValidator.Notation.StandardNotation)
+ window_validator.setLocale(QLocale.c()) # aceita ponto decimal em qualquer idioma
+ self.window_width_edit.setValidator(window_validator)
+
+ form = QGridLayout()
+ rows = (("Flit Size", self.flit_size_edit, "bits"),
+ ("Clock Period", self.clock_edit, "ns"),
+ ("Window Width", self.window_width_edit, "us"))
+ for row, (caption, edit, unit) in enumerate(rows):
+ form.addWidget(QLabel(caption), row, 0)
+ form.addWidget(edit, row, 1)
+ form.addWidget(QLabel(unit), row, 2)
+
+ buttons = QDialogButtonBox()
+ buttons.addButton("Confirm", QDialogButtonBox.ButtonRole.AcceptRole)
+ buttons.addButton("Cancel", QDialogButtonBox.ButtonRole.RejectRole)
+ buttons.accepted.connect(self.accept)
+ buttons.rejected.connect(self.reject)
+
+ layout = QVBoxLayout(self)
+ layout.addLayout(form)
+ layout.addWidget(buttons)
+ layout.setSizeConstraint(QVBoxLayout.SizeConstraint.SetFixedSize) # não redimensionável, como no Java
+
+ def accept(self):
+ edits = (self.flit_size_edit, self.clock_edit, self.window_width_edit)
+ invalid = next((edit for edit in edits if not edit.hasAcceptableInput()), None)
+ if invalid is not None:
+ QMessageBox.warning(self, "Platform Setup", "Enter only valid positive numbers.")
+ invalid.setFocus()
+ invalid.selectAll()
+ return
+
+ clock_period = int(self.clock_edit.text())
+ self.mpsoc_config.flit_size = int(self.flit_size_edit.text())
+ self.mpsoc_config.clock_period_in_ns = clock_period
+ # O Java esquecia de repassar o clock ao CheckpointController
+ self.checkpoint.clock_period_ns = clock_period
+ self.checkpoint.window_size_ms = float(self.window_width_edit.text()) / 1000.0
+
+ QMessageBox.information(self, "Platform Setup", "Settings applied")
+ super().accept()
diff --git a/documentation/implementacoes.md b/documentation/implementacoes.md
index cb83114..b23e390 100644
--- a/documentation/implementacoes.md
+++ b/documentation/implementacoes.md
@@ -7,9 +7,9 @@ Este documento descreve as funcionalidades portadas do [GraphicalDebugger origin
1. [Visão geral](#1-visão-geral)
2. [Estrutura de arquivos](#2-estrutura-de-arquivos)
3. [Abrindo um debug](#3-abrindo-um-debug)
-4. [Configuração da plataforma — `util/MPSoCConfig.py`](#4-configuração-da-plataforma--utilmpsocconfigpy)
+4. [Configuração da plataforma — `MPSoCConfig.py`](#4-configuração-da-plataforma--mpsocconfigpy)
5. [Task List e Services List](#5-task-list-e-services-list)
-6. [Modelo de tráfego — pacote `information/`](#6-modelo-de-tráfego--pacote-information)
+6. [Modelo de tráfego — `information.py`](#6-modelo-de-tráfego--informationpy)
7. [Matriz de roteadores — `router_matrix.py`](#7-matriz-de-roteadores--router_matrixpy)
8. [Simulação — `simulation.py`](#8-simulação--simulationpy)
9. [Interface da simulação — `main.py`](#9-interface-da-simulação--mainpy)
@@ -43,12 +43,11 @@ diretório debug/
| `main.py` | Janela principal: menus, botões, tabelas, ligação com a simulação | `MainFrame.java` |
| `simulation.py` | Controle da simulação e janelas de checkpoint | `MainFrame.java` (parte de simulação), `CheckpointController.java` |
| `overview_windows.py` | Janelas Communication Overview, Task Mapping Overview e Message Log, atualizadas durante a simulação | `CommunicationOverview.java`, `TaskMappingFrame.java`, `MainFrame.showMessageList` |
-| `slave_matrix.py` / `pe_matrix.py` | Células das grades das duas janelas acima | — |
+| `overview_matrices.py` | Células das grades das duas janelas acima (`SlaveMatrixWidget`, `PEMatrixWidget`) | — |
| `router_info_window.py` | Janela "Router Information" (clique num roteador): abas Log, Applications, Scheduling e Traffic, janela Task Information e gráfico de escalonamento | `RouterInfoFrame.java`, `PETextLog.java`, `SchedulingTab.java`, `scheduling/SchedulingPanel.java`, `TaskInfoFrame.java` |
-| `platform_setup.py` | Janela Platform Setup (menu Edit): flit size, período de clock e largura da janela de checkpoint | `PlatformSetupFrame.java` |
| `filter_window.py` | Janela Service and PE Filter (menu Filters, Ctrl+F): escolhe quais pacotes a simulação processa, por serviço (Only/Except) e por PE | `FilterForm.java` |
| `projects.py` | Projetos salvos (`.hdf`): File → Save / Open / Delete Project | `MainFrame.java` (save/open/deleteMenuItem) |
-| `help_dialogs.py` | Help → About e Help → Packet Format | `util/AboutFrame.java`, `MainFrame.packetFormatMenuItemActionPerformed` |
+| `dialogs.py` | Janela Platform Setup (menu Edit: flit size, período de clock e largura da janela de checkpoint), Help → About e Help → Packet Format | `PlatformSetupFrame.java`, `util/AboutFrame.java`, `MainFrame.packetFormatMenuItemActionPerformed` |
| `analysis.py` | Análises sem interface: aplicação de cada pacote, latência (casamento injeção → entrega), vazão em Mbps e linha do tempo da admissão de aplicações | — (métricas do artigo do Memphis, DAES 2019) |
| `statistics_windows.py` | Janelas Traffic Statistics (Links, Applications, Messages, Routing, Blocked) e Application Timeline (menu Tools) | — |
| `path_view.py` | Liga as janelas de análise à malha principal: duplo clique num pacote pinta o caminho dele e destaca o roteador | — |
@@ -58,15 +57,9 @@ diretório debug/
| `deloream.py` | Deloream: leitor das mensagens `$$$` das tarefas nos logs dos processadores | `deloream/DeloreamMainFrame.java`, `deloream/TaskMessage.java` |
| `theme.py` | Paletas dos temas claro/escuro, cores da grade e preferência salva | — |
| `router_matrix.py` | Desenho da grade e de cada roteador (imagens, setas, %) | `Roteador.java`, `UJPanelImagem.java`, `MainFrame.createNoCPanel` |
-| `util/MPSoCConfig.py` | Leitura de `platform.cfg` e `services.cfg`, constantes e endereçamento | `util/MPSoCConfig.java` |
-| `information/packet_information.py` | Um pacote do `traffic_router.txt` | `PacketInformation.java` |
-| `information/task_information.py` | Um evento de tarefa em um PE | `TaskInformation.java` |
-| `information/port_information.py` | Volume/banda acumulados por porta | `PortInformation.java` |
-| `information/router_information.py` | Pacotes, portas e tarefas de um roteador | `RouterInformation.java` |
-| `information/router_neighbors.py` | Vizinhos e conversão XY ↔ hamiltoniano | `RouterNeighbors.java` |
-| `information/read_traffic_data.py` | Leitura sob demanda do `traffic_router.txt` | `ReadTrafficData.java` |
-| `information/mpsoc_information.py` | Estado de todos os roteadores do MPSoC | `MPSoCInformation.java` |
-| `Roteador.py` / `ui_mainwindow.py` | Interfaces geradas a partir dos `.ui` (não editar à mão) | — |
+| `MPSoCConfig.py` | Leitura de `platform.cfg` e `services.cfg`, constantes e endereçamento | `util/MPSoCConfig.java` |
+| `information.py` | Modelo de tráfego: `PacketInformation` (um pacote), `TaskInformation` (evento de tarefa), `PortInformation` (volume/banda por porta), `RouterInformation` (pacotes, portas e tarefas de um roteador), `RouterNeighbors` (vizinhos e conversão XY ↔ hamiltoniano), `ReadTrafficData` (leitura sob demanda do `traffic_router.txt`) e `MPSoCInformation` (estado de todos os roteadores) | `PacketInformation.java`, `TaskInformation.java`, `PortInformation.java`, `RouterInformation.java`, `RouterNeighbors.java`, `ReadTrafficData.java`, `MPSoCInformation.java` |
+| `ui_mainwindow.py` / `commsUi.py` / `taskmap.py` | Interfaces geradas a partir dos `.ui` (não editar à mão) | — |
---
@@ -90,7 +83,7 @@ diretório debug/
---
-## 4. Configuração da plataforma — `util/MPSoCConfig.py`
+## 4. Configuração da plataforma — `MPSoCConfig.py`
### 4.1 Leitura do `platform.cfg`
@@ -172,7 +165,7 @@ As linhas `$TASK_…` do `services.cfg` não aparecem na Services List (não sã
---
-## 6. Modelo de tráfego — pacote `information/`
+## 6. Modelo de tráfego — `information.py`
### 6.1 Formato do `traffic_router.txt`
@@ -240,7 +233,7 @@ Com `router_addressing XY`, os endereços das colunas 1 e 6 são convertidos par
### 7.2 `RouterWidget`
-Reaproveita o layout do `Roteador.py` (gerado de `Roteador.ui`) e acrescenta:
+Reproduz o layout do `Roteador.ui` e acrescenta:
| Recurso | Descrição |
|---|---|
diff --git a/filter_window.py b/filter_window.py
index 787a868..b477113 100644
--- a/filter_window.py
+++ b/filter_window.py
@@ -19,7 +19,7 @@
QMessageBox, QPushButton, QRadioButton, QTabWidget, QVBoxLayout, QWidget)
import analysis
-from util.MPSoCConfig import MPSoCConfig
+from MPSoCConfig import MPSoCConfig
# Serviço sempre descartado pelo filtro do Java
IGNORED_SERVICE = "SLACK_TIME_REPORT"
diff --git a/information.py b/information.py
new file mode 100644
index 0000000..cbbcd4e
--- /dev/null
+++ b/information.py
@@ -0,0 +1,443 @@
+"""
+Modelo de dados do tráfego da NoC lido de traffic_router.txt.
+
+Refatoração em Python das classes do pacote "source" do GraphicalDebugger
+original em Java (MPSoCInformation, RouterInformation, PacketInformation,
+TaskInformation, PortInformation, RouterNeighbors e ReadTrafficData).
+"""
+
+import os
+from bisect import bisect_right
+from collections import defaultdict
+from dataclasses import dataclass, field
+
+from MPSoCConfig import MPSoCConfig
+
+
+class RouterNeighbors:
+ """
+ Calcula os vizinhos de um roteador a partir do seu endereço hamiltoniano.
+ Os métodos retornam -1 quando o vizinho não existe (borda da malha).
+ """
+
+ def __init__(self, x_dimension, y_dimension):
+ self.x_dimension = x_dimension
+ self.y_dimension = y_dimension
+
+ @classmethod
+ def from_config(cls, mpsoc_config):
+ return cls(mpsoc_config.mpsoc_x, mpsoc_config.mpsoc_y)
+
+ def xy_to_ham_addr(self, xy_addr):
+ xy_addr &= 0xFFFF # limpa o header
+ y = xy_addr & 0xFF
+ x = xy_addr >> 8
+
+ if y % 2 == 1:
+ return (y * self.x_dimension) + (self.x_dimension - x) - 1
+ return (y * self.x_dimension) + x
+
+ def ham_to_xy_addr(self, ham_addr):
+ ham_addr &= 0xFF # limpa o header
+ y, addr = divmod(ham_addr, self.x_dimension)
+
+ x = self.x_dimension - addr - 1 if y % 2 == 1 else addr
+ return (x << 8) | y
+
+ def xy_address_to_xy_label(self, ham_addr):
+ xy_addr = self.ham_to_xy_addr(ham_addr)
+ return f"{xy_addr >> 8}{xy_addr & 0xFF}"
+
+ def get_x_coordinate(self, ham_addr):
+ return self.ham_to_xy_addr(ham_addr) >> 8
+
+ def get_y_coordinate(self, ham_addr):
+ return self.ham_to_xy_addr(ham_addr) & 0xFF
+
+ def _neighbor(self, ham_addr, dx, dy):
+ x = self.get_x_coordinate(ham_addr) + dx
+ y = self.get_y_coordinate(ham_addr) + dy
+
+ if 0 <= x < self.x_dimension and 0 <= y < self.y_dimension:
+ return self.xy_to_ham_addr((x << 8) | y)
+ return -1
+
+ def get_vizinho_cima(self, ham_addr):
+ return self._neighbor(ham_addr, 0, 1)
+
+ def get_vizinho_baixo(self, ham_addr):
+ return self._neighbor(ham_addr, 0, -1)
+
+ def get_vizinho_esquerda(self, ham_addr):
+ return self._neighbor(ham_addr, -1, 0)
+
+ def get_vizinho_direita(self, ham_addr):
+ return self._neighbor(ham_addr, 1, 0)
+
+
+@dataclass
+class PacketInformation:
+ """Um pacote registrado em traffic_router.txt ao passar por um roteador."""
+
+ router_address: int
+ time: int
+ service: int
+ size: int
+ bandwidth_cycles: int
+ input_port: int
+ target_router: int
+ task_source: int = -1
+ task_target: int = -1
+ channel: int = field(init=False)
+
+ def __post_init__(self):
+ # O canal é definido pela porta de entrada (portas pares = HIGH)
+ self.channel = MPSoCConfig.get_channel(self.input_port)
+
+ def get_input_port_string(self):
+ return MPSoCConfig.get_port_string(self.input_port)
+
+ def get_channel_string(self):
+ return "HIGH" if self.channel == MPSoCConfig.HIGH else "LOW"
+
+ def print_packet(self):
+ print("*********PACKET************")
+ print(f"router_address: {self.router_address}")
+ print(f"time: {self.time}")
+ print(f"service: {self.service}")
+ print(f"size: {self.size}")
+ print(f"bandwidth cycles: {self.bandwidth_cycles}")
+ print(f"channel: {self.get_channel_string()}")
+ print(f"input_port: {self.get_input_port_string()}")
+ print(f"target_router: {self.target_router}")
+ print(f"task_source: {self.task_source}")
+ print(f"task_target: {self.task_target}")
+
+
+@dataclass
+class TaskInformation:
+ """Um evento de tarefa (alocação, término, mensagem) ocorrido em um PE."""
+
+ id: int
+ service: str
+ time: int
+ remote_task_id: int = -1
+
+ @property
+ def task_id(self):
+ # Os 8 bits menos significativos identificam a tarefa dentro da aplicação
+ return self.id & 0xFF
+
+ @property
+ def app_id(self):
+ return self.id >> 8
+
+
+class PortInformation:
+ """Volume e largura de banda acumulados em uma porta de um roteador."""
+
+ def __init__(self, mpsoc_config, channel):
+ self.channel = channel
+ self.total_volume = 0
+ self.total_bandwidth = 0
+ self.throughput = 0
+ self.bandwidth_throughput = 0
+
+ # Serviços desconhecidos começam em zero em vez de gerar erro
+ self.service_volume = defaultdict(int, dict.fromkeys(mpsoc_config.service_reference, 0))
+ self.service_bandwidth = defaultdict(int, dict.fromkeys(mpsoc_config.service_reference, 0))
+
+ def add_new_information(self, packet):
+ volume = packet.size + 1 # +1 por causa do header
+
+ self.total_volume += volume
+ self.throughput += volume
+
+ self.total_bandwidth += packet.bandwidth_cycles
+ self.bandwidth_throughput += packet.bandwidth_cycles
+
+ self.service_volume[packet.service] += volume
+ self.service_bandwidth[packet.service] += packet.bandwidth_cycles
+
+ def get_service_volume_in_flits(self, service):
+ return self.service_volume.get(service, 0)
+
+ def get_service_bandwidth_in_cycles(self, service):
+ return self.service_bandwidth.get(service, 0)
+
+ def reset_port_throughput(self):
+ self.throughput = 0
+
+ def reset_port_bandwidth_throughput(self):
+ self.bandwidth_throughput = 0
+
+
+class RouterInformation:
+ """Pacotes, portas e eventos de tarefa acumulados em um roteador (PE)."""
+
+ def __init__(self, mpsoc_config, router_address):
+ self.router_address = router_address
+ self.mpsoc_config = mpsoc_config
+ self.task_counter = 0
+ self.packets = []
+ self.tasks = []
+ self.port_informations = [
+ PortInformation(mpsoc_config, MPSoCConfig.get_channel(port))
+ for port in range(MPSoCConfig.NPORT)
+ ]
+
+ def add_packet(self, packet):
+ self.packets.append(packet)
+ self.port_informations[packet.input_port].add_new_information(packet)
+ self._update_task_information(packet)
+
+ def _update_task_information(self, packet):
+ config = self.mpsoc_config
+
+ # Pacote está entrando no PE
+ if packet.target_router == self.router_address:
+ if packet.service in config.task_allocation_services:
+ self.task_counter += 1
+ self.tasks.append(TaskInformation(packet.task_source, "ALLOCATED", packet.time))
+ elif packet.service == config.get_service_value("MESSAGE_DELIVERY"):
+ self.tasks.append(TaskInformation(packet.task_target, "MESSAGE_DELIVERY",
+ packet.time, packet.task_source))
+
+ # Pacote está saindo do PE
+ elif packet.input_port in (MPSoCConfig.LOCAL0, MPSoCConfig.LOCAL1):
+ if packet.service in config.task_terminated_services:
+ # No Memphis-V a tarefa terminada vem no último campo (task_source = -1);
+ # no formato do HeMPS, usado pelo Java, vinha no penúltimo
+ task_id = packet.task_source if packet.task_source >= 0 else packet.task_target
+ self.tasks.append(TaskInformation(task_id, "TERMINATED", packet.time))
+ elif packet.service == config.get_service_value("MESSAGE_REQUEST"):
+ self.tasks.append(TaskInformation(packet.task_target, "MESSAGE_REQUEST",
+ packet.time, packet.task_source))
+
+ def get_tasks_information(self):
+ return self.tasks
+
+ def get_task_number(self):
+ return self.task_counter
+
+ # ==========================================
+ # TOTAIS DO ROTEADOR (SOMA DE TODAS AS PORTAS)
+ # ==========================================
+ def get_router_total_throughput_in_flits(self):
+ return sum(port.throughput for port in self.port_informations)
+
+ def get_router_total_bandwidth_throughput_in_cycles(self):
+ return sum(port.bandwidth_throughput for port in self.port_informations)
+
+ def get_router_total_volume_in_flits(self):
+ return sum(port.total_volume for port in self.port_informations)
+
+ def get_router_total_bandwidth_in_cycles(self):
+ return sum(port.total_bandwidth for port in self.port_informations)
+
+ def get_router_total_services_volume_in_flits(self, services):
+ return sum(self.get_port_volume_in_flits(port, services) for port in range(MPSoCConfig.NPORT))
+
+ def get_router_total_services_bandwidth_in_cycles(self, services):
+ return sum(self.get_port_bandwidth_in_cycles(port, services) for port in range(MPSoCConfig.NPORT))
+
+ # ==========================================
+ # INFORMAÇÕES POR PORTA
+ # ==========================================
+ def get_port_total_volume_in_flits(self, port):
+ return self.port_informations[port].total_volume
+
+ def get_port_total_bandwidth_in_cycles(self, port):
+ return self.port_informations[port].total_bandwidth
+
+ def get_port_volume_in_flits(self, port, services):
+ port_info = self.port_informations[port]
+ return sum(port_info.get_service_volume_in_flits(service) for service in services)
+
+ def get_port_bandwidth_in_cycles(self, port, services):
+ port_info = self.port_informations[port]
+ return sum(port_info.get_service_bandwidth_in_cycles(service) for service in services)
+
+ def get_port_throughput_in_flits(self, port):
+ return self.port_informations[port].throughput
+
+ def get_port_bandwidth_throughput_in_cycles(self, port):
+ return self.port_informations[port].bandwidth_throughput
+
+ def reset_throughput(self):
+ for port in self.port_informations:
+ port.reset_port_throughput()
+
+ def reset_bandwidth_throughput(self):
+ for port in self.port_informations:
+ port.reset_port_bandwidth_throughput()
+
+
+class ReadTrafficData:
+ """
+ Lê os pacotes de traffic_router.txt sob demanda.
+
+ Os pacotes já lidos ficam em all_packets, assim é possível voltar no tempo
+ (reset_packet_counter) sem precisar reler o arquivo.
+ """
+
+ def __init__(self, mpsoc_config, neighbors):
+ self.mpsoc_config = mpsoc_config
+ self.neighbors = neighbors
+ self.packet_read_control = 0
+ self.all_packets = []
+ # Arquivo ainda sendo gravado pelo simulador: uma linha sem "\n" no
+ # fim está incompleta e só é lida quando terminar de ser gravada
+ self.follow = False
+
+ traffic_path = os.path.join(mpsoc_config.debug_file_path, "traffic_router.txt")
+ # Gera FileNotFoundError caso o arquivo não exista
+ self.traffic = open(traffic_path, "r", encoding="utf-8")
+
+ def close(self):
+ self.traffic.close()
+
+ def reset_packet_counter(self):
+ self.packet_read_control = 0
+
+ def get_packet_counter_by_time(self, time):
+ """Retorna quantos pacotes já lidos ocorreram até o tempo informado (-1 se fora do intervalo)."""
+ if not self.all_packets or time < 0 or self.all_packets[-1].time < time:
+ return -1
+
+ return bisect_right(self.all_packets, time, key=lambda packet: packet.time)
+
+ def get_next_packet(self):
+ # Ainda não lido: busca a próxima linha válida do arquivo
+ if self.packet_read_control == len(self.all_packets):
+ while True:
+ # tell() em arquivo texto é lento: só é preciso para desfazer uma linha incompleta
+ position = self.traffic.tell() if self.follow else None
+ line = self.traffic.readline()
+ if not line:
+ return None # fim do arquivo (por enquanto, no modo follow)
+ if self.follow and not line.endswith("\n"):
+ self.traffic.seek(position)
+ return None
+ packet = self._parse_packet(line)
+ if packet is not None:
+ self.all_packets.append(packet)
+ self.packet_read_control += 1
+ return packet
+
+ packet = self.all_packets[self.packet_read_control]
+ self.packet_read_control += 1
+ return packet
+
+ def _parse_packet(self, line):
+ fields = line.strip().split("\t")
+ if fields == [""]:
+ return None
+
+ try:
+ time = int(fields[0])
+ router_address = self._extract_router_address(fields[1])
+ service = int(fields[2])
+ size = int(fields[3])
+ bandwidth_cycles = int(fields[4])
+
+ if self.mpsoc_config.channel_number == 1:
+ # Suporte a canal físico duplicado
+ input_port = int(fields[5]) * 2 + 1
+ else:
+ input_port = int(fields[5])
+
+ target_router = self._extract_router_address(fields[6])
+ task_source = int(fields[7]) if len(fields) > 7 else -1
+ task_target = int(fields[8]) if len(fields) > 8 else -1
+ except (ValueError, IndexError):
+ print(f"WARNING: Wrong packet format in traffic_router.txt: {line.strip()}")
+ return None
+
+ return PacketInformation(router_address, time, service, size, bandwidth_cycles,
+ input_port, target_router, task_source, task_target)
+
+ def _extract_router_address(self, value):
+ address = int(value)
+ if self.mpsoc_config.router_addressing == MPSoCConfig.XY:
+ return self.neighbors.xy_to_ham_addr(address)
+ return address
+
+
+class MPSoCInformation:
+ """Estado de todos os roteadores do MPSoC, alimentado pelos pacotes de traffic_router.txt."""
+
+ def __init__(self, mpsoc_config):
+ self.mpsoc_config = mpsoc_config
+ self.initialize_pe_information()
+ self.neighbors = RouterNeighbors.from_config(mpsoc_config)
+ self.read_traffic = ReadTrafficData(mpsoc_config, self.neighbors)
+
+ def initialize_pe_information(self):
+ self.pe_information = {
+ address: RouterInformation(self.mpsoc_config, address)
+ for address in range(self.mpsoc_config.get_pe_number())
+ }
+
+ def close(self):
+ self.read_traffic.close()
+
+ def get_packet_counter_by_time(self, time):
+ """Retorna o número de pacotes até chegar no tempo desejado."""
+ counter = self.read_traffic.get_packet_counter_by_time(time)
+
+ if counter != -1:
+ self.read_traffic.reset_packet_counter()
+
+ return counter
+
+ def get_router_information(self, router_address):
+ return self.pe_information.get(router_address)
+
+ def get_router_information_xy(self, x, y):
+ return self.get_router_information(self.neighbors.xy_to_ham_addr((x << 8) | y))
+
+ # ==========================================
+ # TOTAIS DA NOC
+ # ==========================================
+ def get_total_noc_volume(self):
+ return sum(router.get_router_total_volume_in_flits() for router in self.pe_information.values())
+
+ def get_total_noc_bandwidth(self):
+ return sum(router.get_router_total_bandwidth_in_cycles() for router in self.pe_information.values())
+
+ def get_total_noc_service_volume(self, services):
+ return sum(router.get_router_total_services_volume_in_flits(services)
+ for router in self.pe_information.values())
+
+ def get_total_noc_service_bandwidth(self, services):
+ return sum(router.get_router_total_services_bandwidth_in_cycles(services)
+ for router in self.pe_information.values())
+
+ def get_next_packet(self, packet_filter=None, limit_time=0):
+ """
+ Lê o próximo pacote e o registra no roteador em que foi capturado.
+
+ packet_filter é uma função (packet -> bool); pacotes rejeitados são
+ pulados enquanto estiverem antes de limit_time (0 = sem limite).
+ Retorna None quando não há mais pacotes.
+ """
+ while True:
+ packet = self.read_traffic.get_next_packet()
+
+ if packet is None:
+ return None
+
+ # Serviços desconhecidos são repassados sem atualizar os roteadores
+ if packet.service not in self.mpsoc_config.services_hash:
+ return packet
+
+ if packet_filter is not None and not packet_filter(packet):
+ if limit_time == 0 or limit_time > packet.time:
+ continue
+
+ router = self.pe_information.get(packet.router_address)
+ if router is not None:
+ router.add_packet(packet)
+
+ return packet
diff --git a/information/__init__.py b/information/__init__.py
deleted file mode 100644
index 6f5585f..0000000
--- a/information/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-"""
-Modelo de dados do tráfego da NoC lido de traffic_router.txt.
-
-Refatoração em Python das classes do pacote "source" do GraphicalDebugger
-original em Java (MPSoCInformation, RouterInformation, PacketInformation,
-TaskInformation, PortInformation, RouterNeighbors e ReadTrafficData).
-"""
-
-from information.packet_information import PacketInformation
-from information.task_information import TaskInformation
-from information.port_information import PortInformation
-from information.router_neighbors import RouterNeighbors
-from information.router_information import RouterInformation
-from information.read_traffic_data import ReadTrafficData
-from information.mpsoc_information import MPSoCInformation
-
-__all__ = [
- "PacketInformation",
- "TaskInformation",
- "PortInformation",
- "RouterNeighbors",
- "RouterInformation",
- "ReadTrafficData",
- "MPSoCInformation",
-]
diff --git a/information/mpsoc_information.py b/information/mpsoc_information.py
deleted file mode 100644
index acb8b0e..0000000
--- a/information/mpsoc_information.py
+++ /dev/null
@@ -1,82 +0,0 @@
-from information.read_traffic_data import ReadTrafficData
-from information.router_information import RouterInformation
-from information.router_neighbors import RouterNeighbors
-
-
-class MPSoCInformation:
- """Estado de todos os roteadores do MPSoC, alimentado pelos pacotes de traffic_router.txt."""
-
- def __init__(self, mpsoc_config):
- self.mpsoc_config = mpsoc_config
- self.initialize_pe_information()
- self.neighbors = RouterNeighbors.from_config(mpsoc_config)
- self.read_traffic = ReadTrafficData(mpsoc_config, self.neighbors)
-
- def initialize_pe_information(self):
- self.pe_information = {
- address: RouterInformation(self.mpsoc_config, address)
- for address in range(self.mpsoc_config.get_pe_number())
- }
-
- def close(self):
- self.read_traffic.close()
-
- def get_packet_counter_by_time(self, time):
- """Retorna o número de pacotes até chegar no tempo desejado."""
- counter = self.read_traffic.get_packet_counter_by_time(time)
-
- if counter != -1:
- self.read_traffic.reset_packet_counter()
-
- return counter
-
- def get_router_information(self, router_address):
- return self.pe_information.get(router_address)
-
- def get_router_information_xy(self, x, y):
- return self.get_router_information(self.neighbors.xy_to_ham_addr((x << 8) | y))
-
- # ==========================================
- # TOTAIS DA NOC
- # ==========================================
- def get_total_noc_volume(self):
- return sum(router.get_router_total_volume_in_flits() for router in self.pe_information.values())
-
- def get_total_noc_bandwidth(self):
- return sum(router.get_router_total_bandwidth_in_cycles() for router in self.pe_information.values())
-
- def get_total_noc_service_volume(self, services):
- return sum(router.get_router_total_services_volume_in_flits(services)
- for router in self.pe_information.values())
-
- def get_total_noc_service_bandwidth(self, services):
- return sum(router.get_router_total_services_bandwidth_in_cycles(services)
- for router in self.pe_information.values())
-
- def get_next_packet(self, packet_filter=None, limit_time=0):
- """
- Lê o próximo pacote e o registra no roteador em que foi capturado.
-
- packet_filter é uma função (packet -> bool); pacotes rejeitados são
- pulados enquanto estiverem antes de limit_time (0 = sem limite).
- Retorna None quando não há mais pacotes.
- """
- while True:
- packet = self.read_traffic.get_next_packet()
-
- if packet is None:
- return None
-
- # Serviços desconhecidos são repassados sem atualizar os roteadores
- if packet.service not in self.mpsoc_config.services_hash:
- return packet
-
- if packet_filter is not None and not packet_filter(packet):
- if limit_time == 0 or limit_time > packet.time:
- continue
-
- router = self.pe_information.get(packet.router_address)
- if router is not None:
- router.add_packet(packet)
-
- return packet
diff --git a/information/packet_information.py b/information/packet_information.py
deleted file mode 100644
index ff5ae6c..0000000
--- a/information/packet_information.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from dataclasses import dataclass, field
-
-from util.MPSoCConfig import MPSoCConfig
-
-
-@dataclass
-class PacketInformation:
- """Um pacote registrado em traffic_router.txt ao passar por um roteador."""
-
- router_address: int
- time: int
- service: int
- size: int
- bandwidth_cycles: int
- input_port: int
- target_router: int
- task_source: int = -1
- task_target: int = -1
- channel: int = field(init=False)
-
- def __post_init__(self):
- # O canal é definido pela porta de entrada (portas pares = HIGH)
- self.channel = MPSoCConfig.get_channel(self.input_port)
-
- def get_input_port_string(self):
- return MPSoCConfig.get_port_string(self.input_port)
-
- def get_channel_string(self):
- return "HIGH" if self.channel == MPSoCConfig.HIGH else "LOW"
-
- def print_packet(self):
- print("*********PACKET************")
- print(f"router_address: {self.router_address}")
- print(f"time: {self.time}")
- print(f"service: {self.service}")
- print(f"size: {self.size}")
- print(f"bandwidth cycles: {self.bandwidth_cycles}")
- print(f"channel: {self.get_channel_string()}")
- print(f"input_port: {self.get_input_port_string()}")
- print(f"target_router: {self.target_router}")
- print(f"task_source: {self.task_source}")
- print(f"task_target: {self.task_target}")
diff --git a/information/port_information.py b/information/port_information.py
deleted file mode 100644
index a80465f..0000000
--- a/information/port_information.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from collections import defaultdict
-
-
-class PortInformation:
- """Volume e largura de banda acumulados em uma porta de um roteador."""
-
- def __init__(self, mpsoc_config, channel):
- self.channel = channel
- self.total_volume = 0
- self.total_bandwidth = 0
- self.throughput = 0
- self.bandwidth_throughput = 0
-
- # Serviços desconhecidos começam em zero em vez de gerar erro
- self.service_volume = defaultdict(int, dict.fromkeys(mpsoc_config.service_reference, 0))
- self.service_bandwidth = defaultdict(int, dict.fromkeys(mpsoc_config.service_reference, 0))
-
- def add_new_information(self, packet):
- volume = packet.size + 1 # +1 por causa do header
-
- self.total_volume += volume
- self.throughput += volume
-
- self.total_bandwidth += packet.bandwidth_cycles
- self.bandwidth_throughput += packet.bandwidth_cycles
-
- self.service_volume[packet.service] += volume
- self.service_bandwidth[packet.service] += packet.bandwidth_cycles
-
- def get_service_volume_in_flits(self, service):
- return self.service_volume.get(service, 0)
-
- def get_service_bandwidth_in_cycles(self, service):
- return self.service_bandwidth.get(service, 0)
-
- def reset_port_throughput(self):
- self.throughput = 0
-
- def reset_port_bandwidth_throughput(self):
- self.bandwidth_throughput = 0
diff --git a/information/read_traffic_data.py b/information/read_traffic_data.py
deleted file mode 100644
index 00a8666..0000000
--- a/information/read_traffic_data.py
+++ /dev/null
@@ -1,96 +0,0 @@
-import os
-from bisect import bisect_right
-
-from util.MPSoCConfig import MPSoCConfig
-from information.packet_information import PacketInformation
-
-
-class ReadTrafficData:
- """
- Lê os pacotes de traffic_router.txt sob demanda.
-
- Os pacotes já lidos ficam em all_packets, assim é possível voltar no tempo
- (reset_packet_counter) sem precisar reler o arquivo.
- """
-
- def __init__(self, mpsoc_config, neighbors):
- self.mpsoc_config = mpsoc_config
- self.neighbors = neighbors
- self.packet_read_control = 0
- self.all_packets = []
- # Arquivo ainda sendo gravado pelo simulador: uma linha sem "\n" no
- # fim está incompleta e só é lida quando terminar de ser gravada
- self.follow = False
-
- traffic_path = os.path.join(mpsoc_config.debug_file_path, "traffic_router.txt")
- # Gera FileNotFoundError caso o arquivo não exista
- self.traffic = open(traffic_path, "r", encoding="utf-8")
-
- def close(self):
- self.traffic.close()
-
- def reset_packet_counter(self):
- self.packet_read_control = 0
-
- def get_packet_counter_by_time(self, time):
- """Retorna quantos pacotes já lidos ocorreram até o tempo informado (-1 se fora do intervalo)."""
- if not self.all_packets or time < 0 or self.all_packets[-1].time < time:
- return -1
-
- return bisect_right(self.all_packets, time, key=lambda packet: packet.time)
-
- def get_next_packet(self):
- # Ainda não lido: busca a próxima linha válida do arquivo
- if self.packet_read_control == len(self.all_packets):
- while True:
- # tell() em arquivo texto é lento: só é preciso para desfazer uma linha incompleta
- position = self.traffic.tell() if self.follow else None
- line = self.traffic.readline()
- if not line:
- return None # fim do arquivo (por enquanto, no modo follow)
- if self.follow and not line.endswith("\n"):
- self.traffic.seek(position)
- return None
- packet = self._parse_packet(line)
- if packet is not None:
- self.all_packets.append(packet)
- self.packet_read_control += 1
- return packet
-
- packet = self.all_packets[self.packet_read_control]
- self.packet_read_control += 1
- return packet
-
- def _parse_packet(self, line):
- fields = line.strip().split("\t")
- if fields == [""]:
- return None
-
- try:
- time = int(fields[0])
- router_address = self._extract_router_address(fields[1])
- service = int(fields[2])
- size = int(fields[3])
- bandwidth_cycles = int(fields[4])
-
- if self.mpsoc_config.channel_number == 1:
- # Suporte a canal físico duplicado
- input_port = int(fields[5]) * 2 + 1
- else:
- input_port = int(fields[5])
-
- target_router = self._extract_router_address(fields[6])
- task_source = int(fields[7]) if len(fields) > 7 else -1
- task_target = int(fields[8]) if len(fields) > 8 else -1
- except (ValueError, IndexError):
- print(f"WARNING: Wrong packet format in traffic_router.txt: {line.strip()}")
- return None
-
- return PacketInformation(router_address, time, service, size, bandwidth_cycles,
- input_port, target_router, task_source, task_target)
-
- def _extract_router_address(self, value):
- address = int(value)
- if self.mpsoc_config.router_addressing == MPSoCConfig.XY:
- return self.neighbors.xy_to_ham_addr(address)
- return address
diff --git a/information/router_information.py b/information/router_information.py
deleted file mode 100644
index 62f5675..0000000
--- a/information/router_information.py
+++ /dev/null
@@ -1,104 +0,0 @@
-from util.MPSoCConfig import MPSoCConfig
-from information.port_information import PortInformation
-from information.task_information import TaskInformation
-
-
-class RouterInformation:
- """Pacotes, portas e eventos de tarefa acumulados em um roteador (PE)."""
-
- def __init__(self, mpsoc_config, router_address):
- self.router_address = router_address
- self.mpsoc_config = mpsoc_config
- self.task_counter = 0
- self.packets = []
- self.tasks = []
- self.port_informations = [
- PortInformation(mpsoc_config, MPSoCConfig.get_channel(port))
- for port in range(MPSoCConfig.NPORT)
- ]
-
- def add_packet(self, packet):
- self.packets.append(packet)
- self.port_informations[packet.input_port].add_new_information(packet)
- self._update_task_information(packet)
-
- def _update_task_information(self, packet):
- config = self.mpsoc_config
-
- # Pacote está entrando no PE
- if packet.target_router == self.router_address:
- if packet.service in config.task_allocation_services:
- self.task_counter += 1
- self.tasks.append(TaskInformation(packet.task_source, "ALLOCATED", packet.time))
- elif packet.service == config.get_service_value("MESSAGE_DELIVERY"):
- self.tasks.append(TaskInformation(packet.task_target, "MESSAGE_DELIVERY",
- packet.time, packet.task_source))
-
- # Pacote está saindo do PE
- elif packet.input_port in (MPSoCConfig.LOCAL0, MPSoCConfig.LOCAL1):
- if packet.service in config.task_terminated_services:
- # No Memphis-V a tarefa terminada vem no último campo (task_source = -1);
- # no formato do HeMPS, usado pelo Java, vinha no penúltimo
- task_id = packet.task_source if packet.task_source >= 0 else packet.task_target
- self.tasks.append(TaskInformation(task_id, "TERMINATED", packet.time))
- elif packet.service == config.get_service_value("MESSAGE_REQUEST"):
- self.tasks.append(TaskInformation(packet.task_target, "MESSAGE_REQUEST",
- packet.time, packet.task_source))
-
- def get_tasks_information(self):
- return self.tasks
-
- def get_task_number(self):
- return self.task_counter
-
- # ==========================================
- # TOTAIS DO ROTEADOR (SOMA DE TODAS AS PORTAS)
- # ==========================================
- def get_router_total_throughput_in_flits(self):
- return sum(port.throughput for port in self.port_informations)
-
- def get_router_total_bandwidth_throughput_in_cycles(self):
- return sum(port.bandwidth_throughput for port in self.port_informations)
-
- def get_router_total_volume_in_flits(self):
- return sum(port.total_volume for port in self.port_informations)
-
- def get_router_total_bandwidth_in_cycles(self):
- return sum(port.total_bandwidth for port in self.port_informations)
-
- def get_router_total_services_volume_in_flits(self, services):
- return sum(self.get_port_volume_in_flits(port, services) for port in range(MPSoCConfig.NPORT))
-
- def get_router_total_services_bandwidth_in_cycles(self, services):
- return sum(self.get_port_bandwidth_in_cycles(port, services) for port in range(MPSoCConfig.NPORT))
-
- # ==========================================
- # INFORMAÇÕES POR PORTA
- # ==========================================
- def get_port_total_volume_in_flits(self, port):
- return self.port_informations[port].total_volume
-
- def get_port_total_bandwidth_in_cycles(self, port):
- return self.port_informations[port].total_bandwidth
-
- def get_port_volume_in_flits(self, port, services):
- port_info = self.port_informations[port]
- return sum(port_info.get_service_volume_in_flits(service) for service in services)
-
- def get_port_bandwidth_in_cycles(self, port, services):
- port_info = self.port_informations[port]
- return sum(port_info.get_service_bandwidth_in_cycles(service) for service in services)
-
- def get_port_throughput_in_flits(self, port):
- return self.port_informations[port].throughput
-
- def get_port_bandwidth_throughput_in_cycles(self, port):
- return self.port_informations[port].bandwidth_throughput
-
- def reset_throughput(self):
- for port in self.port_informations:
- port.reset_port_throughput()
-
- def reset_bandwidth_throughput(self):
- for port in self.port_informations:
- port.reset_port_bandwidth_throughput()
diff --git a/information/router_neighbors.py b/information/router_neighbors.py
deleted file mode 100644
index fdd67e7..0000000
--- a/information/router_neighbors.py
+++ /dev/null
@@ -1,59 +0,0 @@
-class RouterNeighbors:
- """
- Calcula os vizinhos de um roteador a partir do seu endereço hamiltoniano.
- Os métodos retornam -1 quando o vizinho não existe (borda da malha).
- """
-
- def __init__(self, x_dimension, y_dimension):
- self.x_dimension = x_dimension
- self.y_dimension = y_dimension
-
- @classmethod
- def from_config(cls, mpsoc_config):
- return cls(mpsoc_config.mpsoc_x, mpsoc_config.mpsoc_y)
-
- def xy_to_ham_addr(self, xy_addr):
- xy_addr &= 0xFFFF # limpa o header
- y = xy_addr & 0xFF
- x = xy_addr >> 8
-
- if y % 2 == 1:
- return (y * self.x_dimension) + (self.x_dimension - x) - 1
- return (y * self.x_dimension) + x
-
- def ham_to_xy_addr(self, ham_addr):
- ham_addr &= 0xFF # limpa o header
- y, addr = divmod(ham_addr, self.x_dimension)
-
- x = self.x_dimension - addr - 1 if y % 2 == 1 else addr
- return (x << 8) | y
-
- def xy_address_to_xy_label(self, ham_addr):
- xy_addr = self.ham_to_xy_addr(ham_addr)
- return f"{xy_addr >> 8}{xy_addr & 0xFF}"
-
- def get_x_coordinate(self, ham_addr):
- return self.ham_to_xy_addr(ham_addr) >> 8
-
- def get_y_coordinate(self, ham_addr):
- return self.ham_to_xy_addr(ham_addr) & 0xFF
-
- def _neighbor(self, ham_addr, dx, dy):
- x = self.get_x_coordinate(ham_addr) + dx
- y = self.get_y_coordinate(ham_addr) + dy
-
- if 0 <= x < self.x_dimension and 0 <= y < self.y_dimension:
- return self.xy_to_ham_addr((x << 8) | y)
- return -1
-
- def get_vizinho_cima(self, ham_addr):
- return self._neighbor(ham_addr, 0, 1)
-
- def get_vizinho_baixo(self, ham_addr):
- return self._neighbor(ham_addr, 0, -1)
-
- def get_vizinho_esquerda(self, ham_addr):
- return self._neighbor(ham_addr, -1, 0)
-
- def get_vizinho_direita(self, ham_addr):
- return self._neighbor(ham_addr, 1, 0)
diff --git a/information/task_information.py b/information/task_information.py
deleted file mode 100644
index 9c88457..0000000
--- a/information/task_information.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from dataclasses import dataclass
-
-
-@dataclass
-class TaskInformation:
- """Um evento de tarefa (alocação, término, mensagem) ocorrido em um PE."""
-
- id: int
- service: str
- time: int
- remote_task_id: int = -1
-
- @property
- def task_id(self):
- # Os 8 bits menos significativos identificam a tarefa dentro da aplicação
- return self.id & 0xFF
-
- @property
- def app_id(self):
- return self.id >> 8
diff --git a/main.py b/main.py
index bb48294..3e72655 100644
--- a/main.py
+++ b/main.py
@@ -8,7 +8,7 @@
import theme
from ui_mainwindow import MainWindow
-from util import MPSoCConfig
+import MPSoCConfig
from information import MPSoCInformation
from router_matrix import RouterMatrixWidget
from simulation import SimulationController, MAX_REPAINT_SPEED, ticks_to_ms
@@ -16,9 +16,8 @@
from deloream import DeloreamWindow
from statistics_windows import ApplicationTimelineWindow, TrafficStatisticsWindow
from router_info_window import RouterInfoWindow
-from platform_setup import PlatformSetupDialog
+from dialogs import AboutDialog, PlatformSetupDialog, show_packet_format
from filter_window import FilterWindow
-from help_dialogs import AboutDialog, show_packet_format
import projects
import export
import path_view
diff --git a/overview_matrices.py b/overview_matrices.py
new file mode 100644
index 0000000..44053c6
--- /dev/null
+++ b/overview_matrices.py
@@ -0,0 +1,317 @@
+"""
+Matrizes de células das janelas de visão geral (overview_windows.py).
+
+- SlaveMatrixWidget: grade "Slave XxY" do Communication Overview (commsUi.py),
+ com fundo de mapa de calor, percentual de tráfego e unidade ("flits").
+- PEMatrixWidget: grade "PEXxY" do Task Mapping Overview (taskmap.py), com
+ uma linha por tarefa na cor da aplicação.
+
+Cada célula é um widget simples e expansível (cresce para ocupar toda a
+largura/altura do scrollArea do formulário). Usam apenas mpsoc_x / mpsoc_y de
+MPSoCConfig.py, que representam a dimensão TOTAL da malha (mesma convenção de
+router_matrix.py): a célula "0x0" fica no canto inferior esquerdo e o eixo Y
+cresce para cima.
+"""
+
+from PySide6.QtCore import Qt
+from PySide6.QtGui import QColor, QPainter, QPen
+from PySide6.QtWidgets import QGridLayout, QLabel, QSizePolicy, QVBoxLayout, QWidget
+
+
+# Cores fixas, independentes do tema (claro/escuro) do MainWindow: sem
+# isso, o toggle Dark/Light recoloriria essas células via
+# QApplication.setPalette().
+# O fundo (cor do mapa de calor) é pintado em paintEvent, pois muda a
+# cada atualização da simulação.
+_SLAVE_STYLESHEET = """
+QLabel {
+ background-color: transparent;
+ color: #ffffff;
+}
+QLabel#slaveTitle {
+ font-weight: bold;
+ font-size: 11pt;
+}
+"""
+
+
+class SlaveWidget(QWidget):
+ """
+ Uma célula "Slave XxY" da visão de comunicação: título, percentual de
+ tráfego e a unidade ("flits"), com fundo azul e borda branca fina —
+ igual ao print de referência do Communication Overview.
+ """
+
+ def __init__(self, pos_x: int, pos_y: int, parent=None):
+ super().__init__(parent)
+ self.setObjectName("slaveWidget")
+
+ self.pos_x = pos_x
+ self.pos_y = pos_y
+
+ # Isola a célula do tema global do app (ver comentário de
+ # _SLAVE_STYLESHEET acima).
+ self.setStyleSheet(_SLAVE_STYLESHEET)
+ self._background = QColor("#0000cc")
+
+ # Cresce para preencher toda a área do scrollArea, como no print
+ # (diferente do RouterWidget, que tem tamanho fixo 170x170).
+ self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ self.setMinimumSize(110, 110)
+
+ layout = QVBoxLayout(self)
+ layout.setContentsMargins(8, 10, 8, 10)
+ layout.setSpacing(0)
+
+ self.title_label = QLabel(f"Slave {pos_x}x{pos_y}")
+ self.title_label.setObjectName("slaveTitle")
+ self.title_label.setAlignment(
+ Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop
+ )
+ layout.addWidget(self.title_label)
+
+ layout.addStretch(1)
+
+ self.percent_label = QLabel("0%")
+ self.percent_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ layout.addWidget(self.percent_label)
+
+ layout.addStretch(1)
+
+ self.flits_label = QLabel("flits")
+ self.flits_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ layout.addWidget(self.flits_label)
+
+ self.setLayout(layout)
+
+ def _set_background(self, color: QColor) -> None:
+ if color != self._background:
+ self._background = QColor(color)
+ self.update()
+
+ def paintEvent(self, event):
+ # Fundo com a cor do mapa de calor e borda preta de 1 px
+ painter = QPainter(self)
+ painter.fillRect(self.rect(), self._background)
+ painter.setPen(QPen(QColor("#000000"), 1))
+ painter.drawRect(self.rect().adjusted(0, 0, -1, -1))
+ painter.end()
+
+ def set_values(self, title: str, value: str, unit: str, background: QColor) -> None:
+ """Atualiza a célula com os dados do mapa de calor do Communication Overview."""
+ self.title_label.setText(title)
+ self.percent_label.setText(value)
+ self.flits_label.setText(unit)
+ self._set_background(background)
+
+ def set_traffic(self, percent: float, flits: int | None = None) -> None:
+ """
+ Atualiza o percentual mostrado (e, opcionalmente, a contagem de
+ flits), útil para quando os dados reais de tráfego estiverem
+ disponíveis.
+ """
+ self.percent_label.setText(f"{percent:.0f}%")
+ if flits is not None:
+ self.flits_label.setText(f"{flits} flits")
+
+
+class SlaveMatrixWidget(QWidget):
+ """
+ Organiza mpsoc_x * mpsoc_y SlaveWidget em uma única grade que ocupa
+ toda a área disponível (linhas e colunas com stretch igual), igual ao
+ print do Communication Overview.
+ """
+
+ def __init__(self, mpsoc_x: int, mpsoc_y: int, parent=None):
+ super().__init__(parent)
+
+ # mpsoc_x/mpsoc_y é o tamanho TOTAL da malha (mesma convenção de
+ # router_matrix.py) — aqui usado direto, sem noção de cluster,
+ # pois o Communication Overview mostra a malha inteira de uma vez.
+ self.total_cols = max(1, mpsoc_x)
+ self.total_rows = max(1, mpsoc_y)
+
+ self.slaves: dict[tuple[int, int], SlaveWidget] = {}
+
+ self._build_matrix()
+
+ def _build_matrix(self) -> None:
+ layout = QGridLayout(self)
+ layout.setSpacing(0)
+ layout.setContentsMargins(0, 0, 0, 0)
+
+ # Stretch igual em todas as linhas/colunas para as células
+ # dividirem uniformemente o espaço disponível, esticando para
+ # preencher a largura/altura do scrollArea como no print.
+ for col in range(self.total_cols):
+ layout.setColumnStretch(col, 1)
+ for row in range(self.total_rows):
+ layout.setRowStretch(row, 1)
+
+ for y in range(self.total_rows):
+ for x in range(self.total_cols):
+ slave = SlaveWidget(x, y, self)
+ self.slaves[(x, y)] = slave
+
+ # Linha 0 do grid = topo; invertemos Y para 0x0 ficar embaixo.
+ grid_row = self.total_rows - 1 - y
+ layout.addWidget(slave, grid_row, x)
+
+ self.setLayout(layout)
+
+ def get_slave(self, x: int, y: int) -> SlaveWidget | None:
+ """Retorna o SlaveWidget na posição global (x, y), se existir."""
+ return self.slaves.get((x, y))
+
+
+# Cores fixas, independentes do tema (claro/escuro) do MainWindow — mesmo
+# motivo do _SLAVE_STYLESHEET acima: sem isso, o toggle
+# Dark/Light recoloriria essas células via QApplication.setPalette().
+_PE_STYLESHEET = """
+QWidget#peWidget {
+ background-color: #ffffff;
+ border: 1px solid #808080;
+}
+QLabel {
+ background-color: transparent;
+ color: #000000;
+}
+QLabel#peTitle {
+ font-weight: bold;
+}
+"""
+
+# Uma linha de tarefa: fundo com a cor da aplicação
+_TASK_STYLESHEET = "QLabel {{ background-color: {background}; color: {text}; font-weight: bold; padding: 2px; }}"
+
+
+def _text_color_for(background: QColor) -> str:
+ """Texto preto em fundos claros e branco em fundos escuros."""
+ luminance = 0.299 * background.red() + 0.587 * background.green() + 0.114 * background.blue()
+ return "#000000" if luminance > 140 else "#ffffff"
+
+
+class PEWidget(QWidget):
+ """
+ Uma célula "PEXxY" da visão de mapeamento de tarefas: fundo branco,
+ borda cinza fina e um rótulo central, igual ao print de referência do
+ Task Mapping Overview. Pensado para, futuramente, também mostrar a
+ tarefa/status alocado no PE (ver set_task).
+ """
+
+ def __init__(self, pos_x: int, pos_y: int, parent=None):
+ super().__init__(parent)
+ self.setObjectName("peWidget")
+
+ self.pos_x = pos_x
+ self.pos_y = pos_y
+
+ # Isola a célula do tema global do app (ver comentário de
+ # _PE_STYLESHEET acima).
+ self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
+ self.setStyleSheet(_PE_STYLESHEET)
+
+ # Cresce para preencher toda a área do scrollArea, como no print
+ # (diferente do RouterWidget, que tem tamanho fixo 170x170).
+ self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ self.setMinimumSize(90, 90)
+
+ layout = QVBoxLayout(self)
+ layout.setContentsMargins(4, 4, 4, 4)
+ layout.setSpacing(2)
+
+ self.pe_label = QLabel(f"PE{pos_x}x{pos_y}")
+ self.pe_label.setObjectName("peTitle")
+ self.pe_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ layout.addWidget(self.pe_label)
+
+ # Linhas de tarefas (preenchidas por set_tasks), centralizadas na célula
+ layout.addStretch(1)
+ self.tasks_layout = QVBoxLayout()
+ self.tasks_layout.setSpacing(2)
+ layout.addLayout(self.tasks_layout)
+ layout.addStretch(1)
+
+ self.task_labels: list[QLabel] = []
+
+ self.setLayout(layout)
+
+ def set_tasks(self, title: str, tasks: list[tuple[str, QColor]]) -> None:
+ """
+ Mostra o título do PE e uma linha por tarefa, cada uma com o texto
+ e a cor da aplicação a que pertence.
+ """
+ self.pe_label.setText(title)
+
+ # Reaproveita os rótulos existentes e cria/remove só a diferença
+ while len(self.task_labels) < len(tasks):
+ label = QLabel(self)
+ label.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.tasks_layout.addWidget(label)
+ self.task_labels.append(label)
+ while len(self.task_labels) > len(tasks):
+ self.task_labels.pop().deleteLater()
+
+ for label, (text, color) in zip(self.task_labels, tasks):
+ label.setText(text)
+ style = _TASK_STYLESHEET.format(background=color.name(), text=_text_color_for(color))
+ if label.styleSheet() != style:
+ label.setStyleSheet(style)
+
+ def set_task(self, task_name: str | None) -> None:
+ """
+ Atualiza o rótulo para mostrar a tarefa alocada neste PE, além da
+ posição — útil para quando o mapeamento real de tarefas estiver
+ disponível (ex.: filtros "Only running" / "Without Task ID").
+ """
+ base = f"PE{self.pos_x}x{self.pos_y}"
+ self.pe_label.setText(f"{base}\n{task_name}" if task_name else base)
+
+
+class PEMatrixWidget(QWidget):
+ """
+ Organiza mpsoc_x * mpsoc_y PEWidget em uma única grade que ocupa toda
+ a área disponível (linhas e colunas com stretch igual), igual ao
+ print do Task Mapping Overview.
+ """
+
+ def __init__(self, mpsoc_x: int, mpsoc_y: int, parent=None):
+ super().__init__(parent)
+
+ # mpsoc_x/mpsoc_y é o tamanho TOTAL da malha (mesma convenção de
+ # router_matrix.py / SlaveMatrixWidget) — usado direto, sem noção de
+ # cluster, pois o Task Mapping Overview mostra a malha inteira.
+ self.total_cols = max(1, mpsoc_x)
+ self.total_rows = max(1, mpsoc_y)
+
+ self.pes: dict[tuple[int, int], PEWidget] = {}
+
+ self._build_matrix()
+
+ def _build_matrix(self) -> None:
+ layout = QGridLayout(self)
+ layout.setSpacing(0)
+ layout.setContentsMargins(0, 0, 0, 0)
+
+ # Stretch igual em todas as linhas/colunas para as células
+ # dividirem uniformemente o espaço disponível, esticando para
+ # preencher a largura/altura do scrollArea como no print.
+ for col in range(self.total_cols):
+ layout.setColumnStretch(col, 1)
+ for row in range(self.total_rows):
+ layout.setRowStretch(row, 1)
+
+ for y in range(self.total_rows):
+ for x in range(self.total_cols):
+ pe = PEWidget(x, y, self)
+ self.pes[(x, y)] = pe
+
+ # Linha 0 do grid = topo; invertemos Y para 0x0 ficar embaixo.
+ grid_row = self.total_rows - 1 - y
+ layout.addWidget(pe, grid_row, x)
+
+ self.setLayout(layout)
+
+ def get_pe(self, x: int, y: int) -> PEWidget | None:
+ """Retorna o PEWidget na posição global (x, y), se existir."""
+ return self.pes.get((x, y))
diff --git a/overview_windows.py b/overview_windows.py
index c0ae0e0..20fa799 100644
--- a/overview_windows.py
+++ b/overview_windows.py
@@ -19,10 +19,9 @@
import analysis
import export
from commsUi import Ui_Form as Ui_CommunicationOverview
-from pe_matrix import PEMatrixWidget
-from slave_matrix import SlaveMatrixWidget
+from overview_matrices import PEMatrixWidget, SlaveMatrixWidget
from taskmap import Ui_TaskMap
-from util.MPSoCConfig import MPSoCConfig
+from MPSoCConfig import MPSoCConfig
IMAGES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "images")
diff --git a/pe_matrix.py b/pe_matrix.py
deleted file mode 100644
index c42993a..0000000
--- a/pe_matrix.py
+++ /dev/null
@@ -1,174 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-pe_matrix.py
-
-Monta a matriz de PEs (Processing Elements) exibida na tela de Task
-Mapping Overview (taskmap.py), dentro do QScrollArea já existente naquele
-formulário (self.scrollArea / self.scrollAreaWidgetContents).
-
-Segue o mesmo padrão de slave_matrix.py: cada célula é um widget simples e
-expansível (cresce para ocupar toda a largura/altura disponível no
-scrollArea), com fundo branco, borda cinza e um rótulo central "PEXxY",
-igual ao print de referência do Task Mapping Overview.
-
-Usa apenas mpsoc_x / mpsoc_y de MPSoCConfig.py, que representam a dimensão
-TOTAL da malha (mesma convenção de router_matrix.py e slave_matrix.py): o
-PE "0x0" fica no canto inferior esquerdo e o eixo Y cresce para cima.
-"""
-
-from PySide6.QtCore import Qt
-from PySide6.QtGui import QColor
-from PySide6.QtWidgets import QGridLayout, QLabel, QSizePolicy, QVBoxLayout, QWidget
-
-
-# Cores fixas, independentes do tema (claro/escuro) do MainWindow — mesmo
-# motivo do _SLAVE_STYLESHEET em slave_matrix.py: sem isso, o toggle
-# Dark/Light recoloriria essas células via QApplication.setPalette().
-_PE_STYLESHEET = """
-QWidget#peWidget {
- background-color: #ffffff;
- border: 1px solid #808080;
-}
-QLabel {
- background-color: transparent;
- color: #000000;
-}
-QLabel#peTitle {
- font-weight: bold;
-}
-"""
-
-# Uma linha de tarefa: fundo com a cor da aplicação
-_TASK_STYLESHEET = "QLabel {{ background-color: {background}; color: {text}; font-weight: bold; padding: 2px; }}"
-
-
-def _text_color_for(background: QColor) -> str:
- """Texto preto em fundos claros e branco em fundos escuros."""
- luminance = 0.299 * background.red() + 0.587 * background.green() + 0.114 * background.blue()
- return "#000000" if luminance > 140 else "#ffffff"
-
-
-class PEWidget(QWidget):
- """
- Uma célula "PEXxY" da visão de mapeamento de tarefas: fundo branco,
- borda cinza fina e um rótulo central, igual ao print de referência do
- Task Mapping Overview. Pensado para, futuramente, também mostrar a
- tarefa/status alocado no PE (ver set_task).
- """
-
- def __init__(self, pos_x: int, pos_y: int, parent=None):
- super().__init__(parent)
- self.setObjectName("peWidget")
-
- self.pos_x = pos_x
- self.pos_y = pos_y
-
- # Isola a célula do tema global do app (ver comentário de
- # _PE_STYLESHEET acima).
- self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
- self.setStyleSheet(_PE_STYLESHEET)
-
- # Cresce para preencher toda a área do scrollArea, como no print
- # (diferente do RouterWidget, que tem tamanho fixo 170x170).
- self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
- self.setMinimumSize(90, 90)
-
- layout = QVBoxLayout(self)
- layout.setContentsMargins(4, 4, 4, 4)
- layout.setSpacing(2)
-
- self.pe_label = QLabel(f"PE{pos_x}x{pos_y}")
- self.pe_label.setObjectName("peTitle")
- self.pe_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- layout.addWidget(self.pe_label)
-
- # Linhas de tarefas (preenchidas por set_tasks), centralizadas na célula
- layout.addStretch(1)
- self.tasks_layout = QVBoxLayout()
- self.tasks_layout.setSpacing(2)
- layout.addLayout(self.tasks_layout)
- layout.addStretch(1)
-
- self.task_labels: list[QLabel] = []
-
- self.setLayout(layout)
-
- def set_tasks(self, title: str, tasks: list[tuple[str, QColor]]) -> None:
- """
- Mostra o título do PE e uma linha por tarefa, cada uma com o texto
- e a cor da aplicação a que pertence.
- """
- self.pe_label.setText(title)
-
- # Reaproveita os rótulos existentes e cria/remove só a diferença
- while len(self.task_labels) < len(tasks):
- label = QLabel(self)
- label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.tasks_layout.addWidget(label)
- self.task_labels.append(label)
- while len(self.task_labels) > len(tasks):
- self.task_labels.pop().deleteLater()
-
- for label, (text, color) in zip(self.task_labels, tasks):
- label.setText(text)
- style = _TASK_STYLESHEET.format(background=color.name(), text=_text_color_for(color))
- if label.styleSheet() != style:
- label.setStyleSheet(style)
-
- def set_task(self, task_name: str | None) -> None:
- """
- Atualiza o rótulo para mostrar a tarefa alocada neste PE, além da
- posição — útil para quando o mapeamento real de tarefas estiver
- disponível (ex.: filtros "Only running" / "Without Task ID").
- """
- base = f"PE{self.pos_x}x{self.pos_y}"
- self.pe_label.setText(f"{base}\n{task_name}" if task_name else base)
-
-
-class PEMatrixWidget(QWidget):
- """
- Organiza mpsoc_x * mpsoc_y PEWidget em uma única grade que ocupa toda
- a área disponível (linhas e colunas com stretch igual), igual ao
- print do Task Mapping Overview.
- """
-
- def __init__(self, mpsoc_x: int, mpsoc_y: int, parent=None):
- super().__init__(parent)
-
- # mpsoc_x/mpsoc_y é o tamanho TOTAL da malha (mesma convenção de
- # router_matrix.py / slave_matrix.py) — usado direto, sem noção de
- # cluster, pois o Task Mapping Overview mostra a malha inteira.
- self.total_cols = max(1, mpsoc_x)
- self.total_rows = max(1, mpsoc_y)
-
- self.pes: dict[tuple[int, int], PEWidget] = {}
-
- self._build_matrix()
-
- def _build_matrix(self) -> None:
- layout = QGridLayout(self)
- layout.setSpacing(0)
- layout.setContentsMargins(0, 0, 0, 0)
-
- # Stretch igual em todas as linhas/colunas para as células
- # dividirem uniformemente o espaço disponível, esticando para
- # preencher a largura/altura do scrollArea como no print.
- for col in range(self.total_cols):
- layout.setColumnStretch(col, 1)
- for row in range(self.total_rows):
- layout.setRowStretch(row, 1)
-
- for y in range(self.total_rows):
- for x in range(self.total_cols):
- pe = PEWidget(x, y, self)
- self.pes[(x, y)] = pe
-
- # Linha 0 do grid = topo; invertemos Y para 0x0 ficar embaixo.
- grid_row = self.total_rows - 1 - y
- layout.addWidget(pe, grid_row, x)
-
- self.setLayout(layout)
-
- def get_pe(self, x: int, y: int) -> PEWidget | None:
- """Retorna o PEWidget na posição global (x, y), se existir."""
- return self.pes.get((x, y))
diff --git a/platform_setup.py b/platform_setup.py
deleted file mode 100644
index bb96e7d..0000000
--- a/platform_setup.py
+++ /dev/null
@@ -1,73 +0,0 @@
-"""
-Janela "Platform Setup" (menu Edit).
-
-Refatoração de PlatformSetupFrame.java do GraphicalDebugger original: permite
-trocar o tamanho do flit, o período de clock e a largura da janela de
-checkpoint (intervalo em que a % de uso dos enlaces é recalculada) sem
-editar o platform.cfg. As mudanças valem até o próximo "Reset Simulation"
-ou "New Debugging", que releem os arquivos.
-"""
-
-from PySide6.QtCore import QLocale
-from PySide6.QtGui import QDoubleValidator, QIntValidator
-from PySide6.QtWidgets import QDialog, QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QMessageBox, QVBoxLayout
-
-
-class PlatformSetupDialog(QDialog):
- def __init__(self, mpsoc_config, checkpoint, parent=None):
- super().__init__(parent)
- self.mpsoc_config = mpsoc_config
- self.checkpoint = checkpoint
- self.setWindowTitle("Platform Setup")
-
- self.flit_size_edit = QLineEdit(str(mpsoc_config.flit_size))
- self.flit_size_edit.setValidator(QIntValidator(1, 1024, self))
-
- self.clock_edit = QLineEdit(str(mpsoc_config.clock_period_in_ns))
- self.clock_edit.setValidator(QIntValidator(1, 1_000_000, self))
-
- # Largura da janela em us, como no Java (guardada em ms no controlador)
- self.window_width_edit = QLineEdit(f"{checkpoint.window_size_ms * 1000:g}")
- window_validator = QDoubleValidator(0.001, 1e9, 3, self)
- window_validator.setNotation(QDoubleValidator.Notation.StandardNotation)
- window_validator.setLocale(QLocale.c()) # aceita ponto decimal em qualquer idioma
- self.window_width_edit.setValidator(window_validator)
-
- form = QGridLayout()
- rows = (("Flit Size", self.flit_size_edit, "bits"),
- ("Clock Period", self.clock_edit, "ns"),
- ("Window Width", self.window_width_edit, "us"))
- for row, (caption, edit, unit) in enumerate(rows):
- form.addWidget(QLabel(caption), row, 0)
- form.addWidget(edit, row, 1)
- form.addWidget(QLabel(unit), row, 2)
-
- buttons = QDialogButtonBox()
- buttons.addButton("Confirm", QDialogButtonBox.ButtonRole.AcceptRole)
- buttons.addButton("Cancel", QDialogButtonBox.ButtonRole.RejectRole)
- buttons.accepted.connect(self.accept)
- buttons.rejected.connect(self.reject)
-
- layout = QVBoxLayout(self)
- layout.addLayout(form)
- layout.addWidget(buttons)
- layout.setSizeConstraint(QVBoxLayout.SizeConstraint.SetFixedSize) # não redimensionável, como no Java
-
- def accept(self):
- edits = (self.flit_size_edit, self.clock_edit, self.window_width_edit)
- invalid = next((edit for edit in edits if not edit.hasAcceptableInput()), None)
- if invalid is not None:
- QMessageBox.warning(self, "Platform Setup", "Enter only valid positive numbers.")
- invalid.setFocus()
- invalid.selectAll()
- return
-
- clock_period = int(self.clock_edit.text())
- self.mpsoc_config.flit_size = int(self.flit_size_edit.text())
- self.mpsoc_config.clock_period_in_ns = clock_period
- # O Java esquecia de repassar o clock ao CheckpointController
- self.checkpoint.clock_period_ns = clock_period
- self.checkpoint.window_size_ms = float(self.window_width_edit.text()) / 1000.0
-
- QMessageBox.information(self, "Platform Setup", "Settings applied")
- super().accept()
diff --git a/router_info_window.py b/router_info_window.py
index 0c2c876..ba9e131 100644
--- a/router_info_window.py
+++ b/router_info_window.py
@@ -28,7 +28,7 @@
import export
import path_view
from overview_windows import _LiveOverview, _router_label
-from util.MPSoCConfig import MPSoCConfig
+from MPSoCConfig import MPSoCConfig
ICON_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon")
diff --git a/router_matrix.py b/router_matrix.py
index f4a0b95..723742d 100644
--- a/router_matrix.py
+++ b/router_matrix.py
@@ -43,7 +43,7 @@
QGraphicsView, QHBoxLayout, QLabel, QToolButton)
import theme
-from util.MPSoCConfig import MPSoCConfig
+from MPSoCConfig import MPSoCConfig
# ==========================================
diff --git a/simulation.py b/simulation.py
index 0426772..6ecbd97 100644
--- a/simulation.py
+++ b/simulation.py
@@ -8,7 +8,7 @@
from PySide6.QtCore import QObject, QTimer, Signal
-from util.MPSoCConfig import MPSoCConfig
+from MPSoCConfig import MPSoCConfig
# Velocidade (slider 0-100) acima da qual os roteadores não são redesenhados,
# para avançar o mais rápido possível
diff --git a/slave_matrix.py b/slave_matrix.py
deleted file mode 100644
index 2680159..0000000
--- a/slave_matrix.py
+++ /dev/null
@@ -1,168 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-slave_matrix.py
-
-Monta a matriz de "slaves" exibida na tela de Communication Overview
-(commsUi.py), dentro do QScrollArea já existente naquele formulário
-(self.scrollArea / self.scrollAreaWidgetContents).
-
-Diferente de router_matrix.py, este módulo NÃO reaproveita o Ui_Form de
-Roteador.py — cada célula é um widget simples e expansível (cresce para
-ocupar toda a largura/altura disponível no scrollArea), com fundo azul e
-três rótulos: nome do slave ("Slave XxY"), percentual de tráfego e a
-unidade ("flits"), igual ao print de referência do Communication Overview.
-
-Usa apenas mpsoc_x / mpsoc_y de MPSoCConfig.py, que já representam a
-dimensão TOTAL da malha (mesma convenção usada em router_matrix.py): o
-slave "0x0" fica no canto inferior esquerdo e o eixo Y cresce para cima.
-"""
-
-from PySide6.QtCore import Qt
-from PySide6.QtGui import QColor, QPainter, QPen
-from PySide6.QtWidgets import QGridLayout, QLabel, QSizePolicy, QVBoxLayout, QWidget
-
-
-# Cores fixas, independentes do tema (claro/escuro) do MainWindow: sem
-# isso, o toggle Dark/Light recoloriria essas células via
-# QApplication.setPalette().
-# O fundo (cor do mapa de calor) é pintado em paintEvent, pois muda a
-# cada atualização da simulação.
-_SLAVE_STYLESHEET = """
-QLabel {
- background-color: transparent;
- color: #ffffff;
-}
-QLabel#slaveTitle {
- font-weight: bold;
- font-size: 11pt;
-}
-"""
-
-
-class SlaveWidget(QWidget):
- """
- Uma célula "Slave XxY" da visão de comunicação: título, percentual de
- tráfego e a unidade ("flits"), com fundo azul e borda branca fina —
- igual ao print de referência do Communication Overview.
- """
-
- def __init__(self, pos_x: int, pos_y: int, parent=None):
- super().__init__(parent)
- self.setObjectName("slaveWidget")
-
- self.pos_x = pos_x
- self.pos_y = pos_y
-
- # Isola a célula do tema global do app (ver comentário de
- # _SLAVE_STYLESHEET acima).
- self.setStyleSheet(_SLAVE_STYLESHEET)
- self._background = QColor("#0000cc")
-
- # Cresce para preencher toda a área do scrollArea, como no print
- # (diferente do RouterWidget, que tem tamanho fixo 170x170).
- self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
- self.setMinimumSize(110, 110)
-
- layout = QVBoxLayout(self)
- layout.setContentsMargins(8, 10, 8, 10)
- layout.setSpacing(0)
-
- self.title_label = QLabel(f"Slave {pos_x}x{pos_y}")
- self.title_label.setObjectName("slaveTitle")
- self.title_label.setAlignment(
- Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop
- )
- layout.addWidget(self.title_label)
-
- layout.addStretch(1)
-
- self.percent_label = QLabel("0%")
- self.percent_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- layout.addWidget(self.percent_label)
-
- layout.addStretch(1)
-
- self.flits_label = QLabel("flits")
- self.flits_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- layout.addWidget(self.flits_label)
-
- self.setLayout(layout)
-
- def _set_background(self, color: QColor) -> None:
- if color != self._background:
- self._background = QColor(color)
- self.update()
-
- def paintEvent(self, event):
- # Fundo com a cor do mapa de calor e borda preta de 1 px
- painter = QPainter(self)
- painter.fillRect(self.rect(), self._background)
- painter.setPen(QPen(QColor("#000000"), 1))
- painter.drawRect(self.rect().adjusted(0, 0, -1, -1))
- painter.end()
-
- def set_values(self, title: str, value: str, unit: str, background: QColor) -> None:
- """Atualiza a célula com os dados do mapa de calor do Communication Overview."""
- self.title_label.setText(title)
- self.percent_label.setText(value)
- self.flits_label.setText(unit)
- self._set_background(background)
-
- def set_traffic(self, percent: float, flits: int | None = None) -> None:
- """
- Atualiza o percentual mostrado (e, opcionalmente, a contagem de
- flits), útil para quando os dados reais de tráfego estiverem
- disponíveis.
- """
- self.percent_label.setText(f"{percent:.0f}%")
- if flits is not None:
- self.flits_label.setText(f"{flits} flits")
-
-
-class SlaveMatrixWidget(QWidget):
- """
- Organiza mpsoc_x * mpsoc_y SlaveWidget em uma única grade que ocupa
- toda a área disponível (linhas e colunas com stretch igual), igual ao
- print do Communication Overview.
- """
-
- def __init__(self, mpsoc_x: int, mpsoc_y: int, parent=None):
- super().__init__(parent)
-
- # mpsoc_x/mpsoc_y é o tamanho TOTAL da malha (mesma convenção de
- # router_matrix.py) — aqui usado direto, sem noção de cluster,
- # pois o Communication Overview mostra a malha inteira de uma vez.
- self.total_cols = max(1, mpsoc_x)
- self.total_rows = max(1, mpsoc_y)
-
- self.slaves: dict[tuple[int, int], SlaveWidget] = {}
-
- self._build_matrix()
-
- def _build_matrix(self) -> None:
- layout = QGridLayout(self)
- layout.setSpacing(0)
- layout.setContentsMargins(0, 0, 0, 0)
-
- # Stretch igual em todas as linhas/colunas para as células
- # dividirem uniformemente o espaço disponível, esticando para
- # preencher a largura/altura do scrollArea como no print.
- for col in range(self.total_cols):
- layout.setColumnStretch(col, 1)
- for row in range(self.total_rows):
- layout.setRowStretch(row, 1)
-
- for y in range(self.total_rows):
- for x in range(self.total_cols):
- slave = SlaveWidget(x, y, self)
- self.slaves[(x, y)] = slave
-
- # Linha 0 do grid = topo; invertemos Y para 0x0 ficar embaixo.
- grid_row = self.total_rows - 1 - y
- layout.addWidget(slave, grid_row, x)
-
- self.setLayout(layout)
-
- def get_slave(self, x: int, y: int) -> SlaveWidget | None:
- """Retorna o SlaveWidget na posição global (x, y), se existir."""
- return self.slaves.get((x, y))
diff --git a/statistics_windows.py b/statistics_windows.py
index 278dab9..f760401 100644
--- a/statistics_windows.py
+++ b/statistics_windows.py
@@ -19,7 +19,7 @@
import export
import path_view
from overview_windows import _LiveOverview, _router_label
-from util.MPSoCConfig import MPSoCConfig
+from MPSoCConfig import MPSoCConfig
def _ticks_to_us(mpsoc_config, ticks):
diff --git a/tests/conftest.py b/tests/conftest.py
index 46626ab..841261a 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -45,15 +45,14 @@ def debug_dir(scenario_dir):
@pytest.fixture(scope="session")
def config(debug_dir):
- from util.MPSoCConfig import MPSoCConfig
+ from MPSoCConfig import MPSoCConfig
return MPSoCConfig(debug_dir)
@pytest.fixture(scope="session")
def packets(config):
"""Todos os pacotes do traffic_router.txt do exemplo."""
- from information.read_traffic_data import ReadTrafficData
- from information.router_neighbors import RouterNeighbors
+ from information import ReadTrafficData, RouterNeighbors
reader = ReadTrafficData(config, RouterNeighbors.from_config(config))
try:
result = []
diff --git a/tests/test_analysis.py b/tests/test_analysis.py
index 89d94be..fc1d8d4 100644
--- a/tests/test_analysis.py
+++ b/tests/test_analysis.py
@@ -3,8 +3,8 @@
import pytest
import analysis
-from information.packet_information import PacketInformation
-from util.MPSoCConfig import MPSoCConfig
+from information import PacketInformation
+from MPSoCConfig import MPSoCConfig
REQUEST = 41
BROADCAST = 0
diff --git a/tests/test_config.py b/tests/test_config.py
index 50991f6..1fb0950 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -2,7 +2,7 @@
import pytest
-from util.MPSoCConfig import MPSoCConfig
+from MPSoCConfig import MPSoCConfig
def test_platform(config):
diff --git a/tests/test_gui.py b/tests/test_gui.py
index 36b672e..56f8046 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -121,7 +121,7 @@ def test_exports(window, tmp_path, monkeypatch):
def test_platform_setup(window):
- from platform_setup import PlatformSetupDialog
+ from dialogs import PlatformSetupDialog
dialog = PlatformSetupDialog(window.mpconfig, window.simulation.checkpoint, window)
dialog.clock_edit.setText("20")
dialog.window_width_edit.setText("250")
diff --git a/tests/test_information.py b/tests/test_information.py
index aa23008..ccbf311 100644
--- a/tests/test_information.py
+++ b/tests/test_information.py
@@ -1,7 +1,7 @@
"""Leitura do traffic_router.txt e estado dos roteadores."""
from information import MPSoCInformation
-from util.MPSoCConfig import MPSoCConfig
+from MPSoCConfig import MPSoCConfig
def test_all_packets_read(packets):
@@ -64,9 +64,8 @@ def test_port_names():
def test_follow_reads_lines_as_they_are_written(tmp_path, debug_dir):
import shutil
- from information.read_traffic_data import ReadTrafficData
- from information.router_neighbors import RouterNeighbors
- from util.MPSoCConfig import MPSoCConfig
+ from information import ReadTrafficData, RouterNeighbors
+ from MPSoCConfig import MPSoCConfig
debug = tmp_path / "debug"
shutil.copytree(debug_dir, debug)
diff --git a/tests/test_testcase_files.py b/tests/test_testcase_files.py
index 3d8a44c..def5251 100644
--- a/tests/test_testcase_files.py
+++ b/tests/test_testcase_files.py
@@ -34,7 +34,7 @@ def test_example_testcase(config):
def test_missing_yaml(tmp_path):
- from util.MPSoCConfig import MPSoCConfig
+ from MPSoCConfig import MPSoCConfig
debug = tmp_path / "scenario" / "debug"
debug.mkdir(parents=True)
(debug / "platform.cfg").write_text("mpsoc_x 2\nmpsoc_y 2\n")