Módulo TimeMachine
Proyecto: Kmila-9s
Este módulo es un componente central de Kmila-9s, un proyecto titulado oficialmente "Aplicación Multiplataforma para el Aprendizaje y Depuración de Código VHDL sin Requerimiento de Hardware Físico" (Multiplatform Application for Learning and Debugging VHDL Code without Requiring Physical Hardware).
Autor: Ulrich Tamayo Daniel Correo: [email protected] Correo 2: [email protected] Versión: 1.14.1 Última actualización: 3 de agosto de 2026
1. Descripción general del proyecto
El objetivo principal de este proyecto es desarrollar una aplicación de escritorio libre, de código abierto y multiplataforma para la depuración interactiva de código VHDL. La aplicación busca facilitar el aprendizaje del diseño digital al proporcionar un entorno unificado para la simulación y la visualización en tiempo real de señales digitales, ciclos de reloj y estados de puertos, eliminando por completo la necesidad de hardware físico como las FPGA.
Motivaciones clave
- Accesibilidad: Superar la barrera económica de las costosas tarjetas FPGA y las licencias de software propietario.
- Mejora pedagógica: Ofrecer una interfaz gráfica intuitiva e integrada que simplifica el proceso de depuración, a diferencia de los flujos de trabajo fragmentados de línea de comandos de herramientas como GHDL y GTKWave.
- Flexibilidad: Proporcionar una aplicación autónoma que se ejecuta localmente en múltiples sistemas operativos (Windows, macOS, Linux, Android, iOS) sin requerir conexión a internet.
Este proyecto está construido con .NET y MAUI, lo que garantiza una base de código moderna y mantenible con amplio soporte de plataformas.
2. El módulo TimeMachine
Este módulo en particular, el TimeMachine, funciona como el corazón del motor de simulación. Su responsabilidad principal es proporcionar una fuente de tiempo discreta y controlable que impulsa toda la simulación de VHDL. Simula el paso del tiempo en "ticks" discretos, lo que permite al motor de simulación ejecutar procesos de VHDL y actualizar los valores de las señales en una secuencia determinista, imitando el reloj de un circuito digital real.
El módulo está diseñado para ser altamente flexible, ofreciendo un control preciso sobre la ejecución de la simulación (iniciar, pausar, detener) y proporcionando retroalimentación a través de eventos.
Responsabilidades principales
- Simulación de tiempo discreto: Genera un flujo continuo de ticks de tiempo que representan la unidad de tiempo más pequeña de la simulación.
- Control del ciclo de vida de la simulación: Proporciona una API sencilla para
Start,Pause,ContinueyStopdel flujo de tiempo, habilitando la depuración interactiva. - Comunicación basada en eventos: Notifica al motor de simulación principal y a la interfaz de usuario sobre eventos importantes relacionados con el tiempo, como las actualizaciones de progreso (
OnProgressAdvance) y la finalización de la simulación (OnSimulationEnded). - Desacoplamiento: Separa la lógica de la gestión del tiempo de la lógica de la simulación de VHDL, promoviendo una arquitectura más limpia y modular.
3. Arquitectura del módulo TimeMachine
El módulo está construido en torno a una clase central Timer que gestiona el estado de la simulación y el bucle de temporización. Está diseñado para ejecutarse en un hilo en segundo plano con el fin de garantizar que la interfaz de usuario de la aplicación permanezca receptiva.
Componentes clave
Timer.cs: La clase central del módulo. Orquesta la simulación de tiempo, ejecutando un bucle en unTasken segundo plano. Gestiona el estado de la simulación (por ejemplo,IsRunning,IsPaused) y expone métodos (Start,Pause, etc.) para controlar su ciclo de vida. Utiliza unCancellationTokenSourcepara pausar y detener de manera eficiente.TimeClock.cs: El reloj de simulación global que proporciona el "tick" fundamental que impulsa todos los eventos basados en el tiempo. Lleva el conteo del tick actual, maneja el desbordamiento de ticks para simulaciones largas y define la constante de resolución de tiempo (NS_PER_TICK = 10nanosegundos por tick). Dispara los eventos estáticosOnTickChangedyResetCounter.Clock.cs: Representa una única señal de reloj dentro de la simulación. Se suscribe a los eventosTimeClock.OnTickChangedy alterna su estado en función de su frecuencia. Admite cualquier frecuencia especificada en MHz.Input.cs: Una clase de marcador de posición para la futura implementación del manejo de entradas de la simulación. Actualmente solo contiene el nombre de la entrada.
Modelos de datos principales
- Eventos (
Action<T>): La claseTimerutiliza varios delegadosActioncomo eventos para comunicarse con otras partes de la aplicación:OnPaused(estático): Indica que la simulación se ha pausado o reanudado.OnSimulationEnded: Señala que una simulación de duración fija ha finalizado.OnProgressAdvance: Informa el progreso de la simulación como un porcentaje.OnSimulationTick(estático): Se dispara en cada tick de la simulación para el procesamiento de ciclos delta.
4. Cómo usar este módulo
Para usar el TimeMachine, se instancia la clase Timer y se suscribe a sus eventos. El motor de simulación o la interfaz de usuario pueden luego llamar a sus métodos de control en respuesta a las acciones del usuario.
using TimeMachine.Repositories;
using System;
using System.Threading.Tasks;
public class SimulationHost
{
private readonly Timer _timeMachine;
public SimulationHost()
{
// 1. Instantiate the TimeMachine.
_timeMachine = new Timer();
// 2. Subscribe to its events to receive updates.
_timeMachine.OnProgressAdvance += (percent) => Console.WriteLine($"Progress: {percent}%");
_timeMachine.OnSimulationEnded += (finished) => Console.WriteLine("Simulation Finished.");
// Note: OnPaused and OnSimulationTick are static events on the Timer class
Timer.OnPaused += (isPaused) => Console.WriteLine(isPaused ? "Simulation Paused." : "Simulation Resumed.");
}
public void RunSimulation()
{
// 3. Start the simulation for a defined duration (e.g., 1 microsecond).
Console.WriteLine("Starting simulation...");
_timeMachine.Start(TimeSpan.FromMicroseconds(1));
// The simulation now runs on a background thread.
}
public void Pause() => _timeMachine.Pause();
public void Continue() => _timeMachine.Continue();
public void Stop() => _timeMachine.Stop();
}
5. Dependencias
Este módulo es autónomo y no tiene dependencias de bibliotecas externas más allá del entorno de ejecución estándar de .NET 9.
6. Flujo del TimeMachine y relaciones entre clases
Para comprender mejor el funcionamiento interno del módulo, los siguientes diagramas ilustran el flujo de datos y las relaciones entre clases.
6.1. Flujo de simulación de alto nivel
Este diagrama muestra cómo el TimeMachine interactúa con el motor de simulación en su conjunto.
graph TD
A["Simulation Engine / UI"] -->|Instantiates & Controls| B(TimeMachine);
B -->|Runs on a background thread| C{Time Simulation Loop};
C -->|Reads| D[TimeClock Constants];
C -->|Fires Events| A;
subgraph "TimeMachine Module"
B; C; D;
end
style B fill:#ccf,stroke:#333,stroke-width:2px
style A fill:#bbf,stroke:#333,stroke-width:2px
6.2. Diagrama de clases detallado
Este diagrama detalla las clases clave dentro del módulo TimeMachine.
classDiagram
direction LR
class Timer {
-TimeClock _clock
-CancellationTokenSource _cts
-CancellationTokenSource _pauseCts
+TimeElapsed : TimeSpan
+CurrentTick : double
+Paused : volatile bool
+OnPaused : Action~bool~$
+OnSimulationEnded : Action~bool~
+OnProgressAdvance : Action~double~
+OnSimulationTick : Action~ulong~$
+Start(TimeSpan? duration)
+Pause()
+Continue()
+Stop()
+Restart(TimeSpan? duration)
+Dispose()
+GetSimulationTicks(TimeSpan, int)$ ulong
+GetTimeFromTicks(ulong, int)$ TimeSpan
}
class TimeClock {
+Tick : ulong
+Multiplier : ulong
+Ns : int
+NS_PER_TICK : int$
+OnTickChanged : Action~ulong~$
+ResetCounter : EventHandler~bool~$
+Next()
+Reset()
+Dispose()
}
class Clock {
+Name : string
+State : bool
+Period : ulong
+OnStatusChanged : EventHandler~bool~
+Clock(bool, double, string)
+Dispose()
}
class Input {
+Name : string
}
Timer o-- TimeClock : uses internally
Clock ..> TimeClock : subscribes to OnTickChanged
Timer ..|> IDisposable : implements
Clock ..|> IDisposable : implements
TimeClock ..|> IDisposable : implements
6.3. Diagrama de secuencia: Inicio y pausa
Este diagrama de secuencia muestra las interacciones en tiempo de ejecución cuando un usuario inicia y luego pausa la simulación.
sequenceDiagram
participant User
participant SimulationEngine
participant Timer
participant BackgroundThread
User->>SimulationEngine: ClickStartButton()
SimulationEngine->>Timer: Start(duration)
activate Timer
Timer->>BackgroundThread: Task.Run(SimulationLoop)
deactivate Timer
activate BackgroundThread
loop Simulation Loop
BackgroundThread->>BackgroundThread: Increment CurrentTick
BackgroundThread->>Timer: OnProgressAdvance(percent)
Timer-->>SimulationEngine: Fires event
end
User->>SimulationEngine: ClickPauseButton()
SimulationEngine->>Timer: Pause()
activate Timer
Timer->>Timer: Paused = true
Timer-->>SimulationEngine: OnPaused(true)
deactivate Timer
BackgroundThread-->>BackgroundThread: Enters Task.Delay(Infinite, _pauseCts)
User->>SimulationEngine: ClickResumeButton()
SimulationEngine->>Timer: Continue()
activate Timer
Timer->>Timer: Paused = false, _pauseCts.Cancel()
Timer-->>SimulationEngine: OnPaused(false)
deactivate Timer
BackgroundThread-->>BackgroundThread: Resumes loop
6.4. Diagrama de módulos
graph TD
user["User<br>[External]"]
subgraph kmila9s_boundary["Kmila-9s Application<br>[External]"]
subgraph userInterface_boundary["User Interface<br>[External]"]
codeEditor["Code Editor<br>[External]"]
waveformViewer["Waveform Viewer<br>[External]"]
controlPanel["Control Panel<br>[External]"]
end
subgraph simulationEngine_boundary["Simulation Engine<br>[External]"]
vhdlParser["VHDL Parser<br>[External]"]
simulatorCore["Simulator Core<br>[External]"]
signalManager["Signal Manager<br>[External]"]
%% Edges at this level (grouped by source)
simulatorCore["Simulator Core<br>[External]"] -->|"Updates | Manages signal values during simulation"| signalManager["Signal Manager<br>[External]"]
vhdlParser["VHDL Parser<br>[External]"] -->|"Provides | Parsed VHDL logic"| simulatorCore["Simulator Core<br>[External]"]
end
subgraph timeMachineModule_boundary["TimeMachine Module<br>[External]"]
timerClass["Timer<br>/Repositories/Timer.cs"]
timeClockClass["TimeClock<br>/Models/TimeClock.cs"]
clockModel["Clock Model<br>/Models/Clock.cs"]
inputModel["Input Model<br>/Models/Input.cs"]
%% Edges at this level (grouped by source)
timerClass["Timer<br>/Repositories/Timer.cs"] -->|"Uses | Tick advancement and time resolution"| timeClockClass["TimeClock<br>/Models/TimeClock.cs"]
clockModel["Clock Model<br>/Models/Clock.cs"] -->|"Subscribes to | OnTickChanged events"| timeClockClass["TimeClock<br>/Models/TimeClock.cs"]
timerClass["Timer<br>/Repositories/Timer.cs"] -->|"Uses | To manage simulation inputs"| inputModel["Input Model<br>/Models/Input.cs"]
end
%% Edges at this level (grouped by source)
controlPanel["Control Panel<br>[External]"] -->|"Controls | Starts, pauses, continues, and stops simulation time"| timerClass["Timer<br>/Repositories/Timer.cs"]
timerClass["Timer<br>/Repositories/Timer.cs"] -->|"Drives | Generates time ticks for simulation execution"| simulatorCore["Simulator Core<br>[External]"]
signalManager["Signal Manager<br>[External]"] -->|"Provides | Signal data for visualization"| waveformViewer["Waveform Viewer<br>[External]"]
end
%% Edges at this level (grouped by source)
user["User<br>[External]"] -->|"Uses | Interacts with"| userInterface_boundary["User Interface<br>[External]"]
7. Limitaciones actuales
Las siguientes limitaciones existen en la implementación actual:
- Marcador de posición de la clase Input: La clase
Input.cses un marcador de posición con una implementación mínima. Las versiones futuras agregarán propiedades para el valor, el tipo y los cambios de valor basados en el tiempo. - Retardo de tick fijo: El bucle de simulación utiliza un retardo fijo de 10 ms entre ticks, lo cual puede no ser adecuado para todos los escenarios de simulación.
- Seguridad entre hilos: Solo la propiedad
Pausedestá marcada comovolatile. Otros campos pueden necesitar sincronización para el acceso multihilo.
8. Detalles técnicos
Resolución de tiempo
El TimeMachine opera con una resolución de 10 nanosegundos por tick (NS_PER_TICK = 10). Este valor se eligió para proporcionar suficiente precisión para los circuitos digitales típicos, manteniendo al mismo tiempo un rendimiento de simulación razonable.
Conversión de frecuencia de reloj
Las frecuencias de reloj especificadas en MHz se convierten a ticks de simulación mediante la fórmula:
Period (in ticks) = 1,000,000,000 ns / (frequency_MHz * 1,000,000) / NS_PER_TICK
= 1000 / frequency_MHz / NS_PER_TICK
Por ejemplo:
- Reloj de 50 MHz: Period = 1000 / 50 / 10 = 2 ticks por semiperiodo
- Reloj de 100 MHz: Period = 1000 / 100 / 10 = 1 tick por semiperiodo
Mecanismo de pausa/reanudación
El mecanismo de pausa utiliza Task.Delay(Timeout.Infinite) con un token de cancelación, lo que permite que el bucle de simulación espere de manera eficiente sin consumir ciclos de CPU.
8.bis. Diagramas de auditoría (2026-05-09)
La auditoría identificó que la máquina de estados del temporizador y la ruta de
ejecución dual rápida/interactiva no estaban representadas en los diagramas de la tesis. El bloque de abajo es la
vista canónica; el mismo origen se encuentra en
Documentacion/TT1/diagrams/fig_5_11_11_state_timer.mmd.
fig_5_11_11_state_timer — Máquina de estados del temporizador
stateDiagram-v2
[*] --> Idle
Idle --> Running : Start(Duration?)
Running --> Paused : Pause()<br/>OnPaused(true)
Paused --> Running : Continue()<br/>OnPaused(false), cancel _pauseCts
Running --> Stopped : Stop()<br/>cancel _cts
Paused --> Stopped : Stop()
Running --> Done : Duration alcanzada<br/>OnSimulationEnded
Stopped --> Idle : Reset interno
Done --> Idle : Reset interno
state Running {
[*] --> Tick
Tick --> EmitProgress : OnSimulationTick<br/>(consumido por DeltaCycleEngine)
EmitProgress --> Wait : Task.Delay(10ms)
Wait --> Tick : !Paused y !_cts cancelado
}
state Paused {
[*] --> AwaitResume : Task.Delay(Infinite, _pauseCts.Token)
AwaitResume --> [*] : token cancelado
}
note right of Running
DeltaCycleEngine.ConnectToTimeMachine()
subscribe a OnPaused (estatico)
para sincronizar pausa/resume.
end note
note left of Idle
Path dual:
- Fast: SimulationRunner sin TimeMachine.
- Interactivo: TimeMachine + DeltaCycleEngine.
end note
9. Registro de cambios
Versión 1.14.1 (2026-05-01)
Verificación de la documentación
- README.md: Se verificaron todas las firmas de clases, eventos y comportamientos contra la base de código actual. Se confirmó:
TimerexponeStart,Pause,Continue,Stop,Restart,Dispose, además deTimeElapsed,CurrentTick,Paused(volatile) y los cuatro eventos (OnPausedyOnSimulationTicksonstatic;OnSimulationEndedyOnProgressAdvanceson eventos de instancia).TimeClock.Next()incrementaTick, disparaOnTickChangedy avanza aMultiplierconResetCounteren caso de desbordamiento.NS_PER_TICK = 10ns permanece sin cambios.Clockderiva su semiperiodo a partir defrequencyMHzrespecto aTimeClock.NS_PER_TICK, se suscribe aOnTickChangedyResetCounter, y se cancela la suscripción enDispose.Inputsigue siendo un marcador de posición que solo contieneName.
- Diagrama de clases: Se marcó
Pausedcomovolatile boolpara coincidir con la declaración del campo.
Versión 0.5.2 (2026-02-21)
Correcciones de la documentación
- Clock.cs: Se agregó documentación XML para los miembros privados (
_halfPeriod,_currentTick,OnResetGlobalClock) - README.md: Se corrigió el diagrama de secuencia para reflejar con precisión el mecanismo de pausa/reanudación (utiliza la bandera
Paused+ el token_pauseCts, no_cts.Cancel()) - README.md: Se corrigió el ejemplo de uso para referenciar correctamente
Timer.OnPausedcomo un evento estático en lugar de un evento de instancia
Versión 0.5.1 (2025-12-13)
Actualizaciones de diagramas
- Diagrama de clases (6.2): Actualizado para incluir todas las clases del módulo:
- Se agregó la clase
Clockcon propiedades (Name, State, Period, OnStatusChanged) y métodos (Tick, Dispose) - Se agregó la clase
Input(marcador de posición para las entradas de simulación) - Se actualizó
TimeClockpara mostrar los miembros estáticos reales (eventos NS_PER_TICK, OnTickChanged, ResetCounter) - Se agregó la relación que muestra que Clock se suscribe a TimeClock.OnTickChanged
- Se agregó el indicador de implementación de IDisposable para Clock
- Se agregó la clase
Versión 0.5.0 (2025-12-13)
Mejoras de la documentación
Models/Input.cs: Se agregó documentación XML exhaustiva que incluye:
- Documentación a nivel de clase que explica el estado de marcador de posición
- Casos de uso futuros para el manejo de señales de entrada
- Documentación de propiedades con ejemplos
- Referencias cruzadas a clases relacionadas
Program.cs: Se agregó documentación XML completa que incluye:
- Documentación a nivel de clase que explica la configuración de prueba
- Documentación de métodos con una tabla de comandos disponibles
- Comentarios en línea que explican la elección de las frecuencias de reloj
- Notas sobre el comportamiento del bucle infinito
README.md: Se agregó información de versión, la sección de limitaciones actuales, la sección de detalles técnicos y este registro de cambios
Versión 1.14.0 (2026-04-19)
- Higiene de compilación — se agregó
<NoWarn>para el ruido restante de documentación XML / referencias anulables, de modo que el proyecto compila sin advertencias junto con el resto de la solución Kmila. - No hay cambios de comportamiento en tiempo de ejecución en esta versión; el motor de
ticks del reloj del TimeMachine es estable y se consume sin cambios por el sistema de reloj híbrido
predeterminado para FPGA / con anulación por proyecto agregado en
Kmila.Shared.Services.SimulationParameters.
Novedades en la v1.15 (2 de mayo de 2026)
- Ejecutor de pruebas automatizado (#6).
Program.csera anteriormente un REPL interactivo que se bloqueaba enConsole.ReadLine()y hacía imposible validar el módulo de forma no interactiva. Se reemplazó por un ejecutor de 23 pruebas que finaliza con el código 0/1 según el resultado aprobado/fallido. Casos:- 8 pruebas de la semántica de Clock (cálculo del periodo a 50 / 100 MHz, estado inicial, alternancia, relación de frecuencias, dispose cancela la suscripción a
OnTickChanged). - 4 pruebas unitarias de TimeClock (Next, OnTickChanged, Reset, constante NS_PER_TICK).
- 8 pruebas del ciclo de vida de Timer (Start con duración finita, Pause detiene el avance, Continue reanuda, Stop reinicia, Restart, OnProgressAdvance monótono).
- 3 pruebas de esfuerzo (100 relojes concurrentes, ejecución de 50k ticks, 20 ciclos rápidos de Start/Stop).
- 8 pruebas de la semántica de Clock (cálculo del periodo a 50 / 100 MHz, estado inicial, alternancia, relación de frecuencias, dispose cancela la suscripción a
- GC de estación de trabajo habilitado a nivel de proyecto (#7); el RSS máximo durante la ejecución de las 23 pruebas es de ~37 MB.
Notas de versión completas: ../Documentacion/CHANGELOG_v1.15.md.
Última actualización: 2026-08-03