Módulo Intérprete

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.2.0 (rama v1.2.0) Última actualización: 2026-08-03


Tabla de contenido

  1. Descripción general del proyecto
  2. El módulo Intérprete
  3. Arquitectura
  4. Decisiones de diseño críticas
  5. Componentes clave
  6. Cómo usarlo
  7. Pruebas
  8. Problemas comunes y soluciones
  9. Guía del desarrollador
  10. Registro de cambios

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 que suponen 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 basados en 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 Intérprete

Este módulo específico, el Intérprete de VHDL, es el motor de ejecución de la canalización de simulación. Su responsabilidad principal es tomar la representación estructurada en memoria del código VHDL (generada por el Módulo Analizador (Parser)) y simular su comportamiento a lo largo del tiempo.

Estado actual (v1.14.x)

  • Características de VHDL soportadas: Entidades, arquitecturas, procesos, señales, variables, puertos, genéricos, if/elsif/else, case/when, bucles for/while/simples, exit/next/null/return, funciones, procedimientos, funciones estándar IEEE, definiciones de tipos personalizados (enum / array / record / subtype), paquetes, bloques generate, asignaciones concurrentes condicionales y seleccionadas, instanciaciones de componentes.
  • Motor de simulación: Planificador completo de ciclos delta (DeltaCycleEngine + SignalScheduler + ProcessScheduler), registro del historial de señales (SignalHistory) con exportación a VCD / CSV / Texto, soporte de la cláusula after, activación de procesos dirigida por lista de sensibilidad, seguimiento de atributos IEEE ('event / 'last_value / 'stable / 'active).
  • Fase de síntesis (espacio de nombres Synthesis/): El VHDL de comportamiento se reduce a una netlist estructural (Netlist, Net, Cell, Pin, PortPin) mediante sintetizadores por construcción y se dispone mediante LayeredLayout para la vista esquemática en el Editor. Esto es independiente de la canalización de simulación y alimenta el componente Esquemático (Schematic).
  • Fachada del bucle de ejecución (SimulationRunner + SimulationConfig): Punto de entrada único usado por la página del Editor para ejecutar un SimulationConfig (entidad + total de ticks + lista de ClockConfig con conmutación automática + lista de StimulusEvent), con Pause / Resume / Stop, y los eventos OnProgressChanged, OnSimulationEnded y OnError.
  • Tipos personalizados: TypeDefinition + TypeRegistry cubren enumeraciones, arreglos, registros y subtipos. VariableSyntetizer, Operation, FunctionExecutor y Resources consultan a TypeRegistry.
  • Estimación de recursos de FPGA: Resources produce estimaciones de flip-flops, LUT, DSP, BRAM, E/S y dominios de reloj por entidad; consumidas por el chip de ajuste de FPGA (FPGA fit) en la barra de estado del Editor.
  • Progreso / cancelación: ProgressReporter (singleton, basado en eventos) impulsa la interfaz de estado de compilación del Editor; el CancellationToken se propaga a través de la canalización de síntesis y ejecución.
  • Estado de las pruebas: Hoy existen dos superficies de prueba independientes (véase §7):
    1. El corpus histórico de consola impulsado por Program.cs — ahora 54 fixtures test*.vhdl (numerados hasta test41, más variantes con nombre) — de los cuales la antigua cifra del 80 % (16/20) citada en el registro de cambios es una instantánea de la v0.15.0 sobre solo las primeras 20 fixtures y no se ha vuelto a medir desde entonces; vuelva a ejecutar Program.cs contra el corpus para obtener una cifra actual.
    2. Una suite xUnit de primera clase bajo Interpreter/Tests/ (DeltaCycleEngineTests, SynthesisTests, NestedIEEECallTests, IEEEPrimitivesTests, IEEELibraryLoaderTests, IEEELoweringEndToEndTests, LibraryCompilerTests, PortStrictnessTests, PracticeExerciseTests, VcdComparison) que se ejecuta con dotnet test. Esta es la superficie de regresión autoritativa; el corpus de consola es un arnés de humo/exploración.

Responsabilidades principales

  • Evaluación de expresiones: Calcular los resultados de operaciones aritméticas, lógicas y relacionales usando el algoritmo Shunting-yard con la precedencia de operadores adecuada
  • Ejecución de sentencias: Procesar sentencias secuenciales como asignaciones de señales, asignaciones de variables y estructuras de control dentro de los procesos VHDL
  • Gestión del flujo de control: Implementar la ejecución condicional (if-then-else, case-when), los bucles (for, while, loop) y las estructuras de control anidadas
  • Simulación de estado: Gestionar el estado de todas las señales, variables y puertos, actualizando los valores a medida que avanza la simulación
  • Manejo de eventos: Simular los cambios de señales que disparan la ejecución de procesos (modelo de simulación dirigido por eventos)
  • Soporte de funciones: Funciones estándar IEEE (rising_edge, falling_edge, conversiones de tipo, operaciones vectoriales) y funciones/procedimientos definidos por el usuario

3. Arquitectura

Canalización de análisis (Parsing Pipeline)

VHDL Source Code
    ↓
[Parser Module] - Tokenization
    ↓
Token Stream
    ↓
[Entity] - Entity/Port/Generic parsing
    ↓
[Architecture] - Signal declarations, behavior synthesis
    ↓
[Process] - Sequential statement parsing
    ↓
[Control Structures] - If/Case/Loop synthesis
    ↓
[Operation] - Expression evaluation
    ↓
[ShuntingYard] - Infix to postfix conversion
    ↓
[Operator] - Actual computation
    ↓
Result (Updated signal/variable values)

Jerarquía de clases

ISControl (Interface) - Sequential control structures with async execution
├── IfStructure              - if-elsif-else conditional execution
├── CaseStructure            - case-when statement with multiple branches
├── ForLoopStructure         - for loop with ascending/descending ranges
├── WhileLoopStructure       - while loop with condition evaluation
├── SimpleLoopStructure      - infinite loop with exit conditions
├── ExitStatement            - exit statement (throws ExitException)
├── NextStatement            - next statement (throws NextException)
├── NullStatement            - null no-op statement
├── ReturnStatement          - return statement (throws ReturnException)
├── ProcedureCallStatement   - procedure call with argument evaluation
├── ReportStatement          - VHDL `report` statement, renders + emits via ReportBus
├── AssertStatement          - VHDL `assert` statement, emits on false condition via ReportBus
├── WaitForStatement         - `wait for N ns` / `wait` statement handling
├── ConditionalAssignment    - concurrent when...else assignment
├── SelectedAssignment       - concurrent with...select assignment
├── GenerateBlock            - for-generate and if-generate statements
├── Process                  - VHDL process with sensitivity list (also IControl)
├── Architecture             - architecture synthesis and execution (also IControl)
├── Entity                   - entity synthesis and execution (also IControl)
├── VariableSyntetizer       - variable/signal/port declaration parser
└── Executer                 - task list orchestrator with pause/resume (also IDisposable)

IOperation (Interface) - Expression evaluation
├── IArithOperation
│   └── Operator (arithmetic: +, -, *, /, **, mod)
└── ILogicOperation
    └── Operator (logic: and, or, xor, nand, nor, xnor, not, sll, srl, sla, sra, rol, ror)

IFinder (Interface) - Literal identification
└── LiteralsFinder

Utility / Singleton Services
├── Operation               - Expression parsing and RPN evaluation (IOperation)
├── ShuntingYard            - Infix to postfix conversion (ISYard)
├── OperationDetector       - Operation type classification
├── ScheduledOperation      - Delta cycle signal scheduling wrapper
├── StandardFunctions       - IEEE standard library functions (static)
├── FunctionExecutor        - User-defined function/procedure executor (singleton)
├── SignalAttributeHandler  - Signal attribute tracking (singleton)
├── SignalScheduler         - Delta cycle signal update scheduling
├── ProcessScheduler        - Process execution scheduling by sensitivity
├── DeltaCycleEngine        - Delta cycle simulation coordinator (IDisposable)
├── SignalHistory           - Signal value recording for waveforms
├── PackageRegistry         - Package constants/functions/procedures (singleton)
├── EntityRegistry          - Multi-file entity discovery (singleton)
├── FunctionRegistry        - FunctionDefinition stubs for skipped funcs/procs (singleton)
├── PackageSubtypeScanner   - Cross-file `subtype` scanner → PackageRegistry
├── TypeRegistry            - Custom VHDL type definitions
├── SourceElaborator        - Component-instance elaboration + string-safe source splitting
├── ReportBus               - Singleton event sink for `report` / `assert` output
├── ParseTrace              - Shared static skip/render helpers + skip-iteration cap
├── SkipDiagnosticLog       - Records silent-skip events with stable codes
├── IEEELibraryLoader       - Thin façade delegating to the LibraryCompiler module
├── ProgressReporter        - Event-based progress tracking (singleton)
└── Resources               - FPGA resource estimation

Models
├── TypeDefinition          - Custom type metadata (enum, array, record, subtype)
├── FunctionDefinition      - User-defined function signature and body
├── ProcedureDefinition     - User-defined procedure signature and body
├── ParameterDefinition     - Function/procedure parameter metadata
├── DeferredFunction        - Runtime-evaluated IEEE/known function call
├── DeferredUserFunction    - Runtime-evaluated *user-defined* function call
├── DeferredExpression      - Sub-expression whose evaluation is deferred to run time
├── DeferredAttribute       - Signal/array attribute resolved at run time (Tier 4.1)
├── DeferredIndex           - Deferred array/vector index resolution
├── DynamicIndex            - Run-time-computed index into an array/vector
├── IProcessFrame           - Interface for the sequential-execution frame stack
├── BranchFrame             - Frame for an in-flight if/elsif/case branch
├── LoopFrame               - Frame for an in-flight for/while/loop iteration
├── ProcedureFrame          - Frame for an in-flight procedure call
├── ControlExceptions       - ExitException, NextException, ReturnException
├── SimulationConfig        - Entity + ticks + clocks + stimuli (input to SimulationRunner)
├── ClockConfig             - Auto-toggling clock spec (signal + half-period + initial state)
├── StimulusEvent           - "set Signal to Value at Tick" stimulus record
└── Constansts              - Operator precedence enums, operation regex (sic — historical typo)

Synthesis Namespace (Interpreter.Synthesis) - behavioral → structural lowering
├── Synthesizer                  - Static façade: Synthesize(Entity) → Netlist
├── SynthesisContext             - Per-entity build context (nets, cells, ports, diagnostics)
├── ComponentSynthesizer         - Emits BLACKBOX_COMPONENT cells for component instantiations
│                                  registered in EntityRegistry (shallow; honours pin direction)
├── ConcurrentAssignSynthesizer  - Lowers concurrent signal assignments
├── ConditionalAssignSynthesizer - Lowers `when…else` concurrent assignments
├── SelectedAssignSynthesizer    - Lowers `with…select` concurrent assignments
├── ProcessSynthesizer           - Lowers VHDL processes (registered FFs, combinational paths)
├── IEEEPrimitives               - Table-driven IEEE-function → primitive-cell emitter
│                                  (internal IIEEEPrimitive registry; extensible via Register)
├── SelectionFilter              - Filters a Netlist + LaidOutNetlist for export
│                                  (SelectionPicks → FilteredLayout for the Schematic basket)
├── Models.cs                    - Records: Net, Pin, Cell, PortPin, Netlist, Diagnostic
│                                  Enums: CellKind, NetOrigin, NetKind, PortDir, DiagnosticSeverity
└── Layout/LayeredLayout         - Sugiyama-style placement → LaidOutNetlist (rects + wire paths)

Façade
├── ISimulationRunner            - Shared surface of the two interchangeable run drivers
│                                  (Engine, IsRunning/IsPaused, Pause/Resume/Stop, the
│                                  OnProgressChanged/OnSimulationEnded/OnError/OnEngineReady/
│                                  OnReport events). Lets callers (e.g. ProgramBuilder) pick a
│                                  runner at run time without branching on the concrete type.
├── SimulationRunner             - "Visual/Slow mode" IDisposable runner: visits every tick
│                                  0..totalTicks. RunAsync(SimulationConfig), Pause/Resume/Stop.
└── FastSimulationRunner         - "Fast mode" sibling: drives the SAME DeltaCycleEngine but
                                   jumps straight to the next tick where something can happen
                                   (clock edge / stimulus / scheduled update / process wake),
                                   skipping idle ticks. Same public surface as SimulationRunner;
                                   verified byte-identical to it by VCD diff across a corpus.

Placeholder
└── Services/DataStructure       - Reserved for future complex data-structure ops
                                   (currently empty class — operations live in
                                    VariableSyntetizer / TypeRegistry / TypeDefinition)

Resumen de componentes clave

Canalización de ejecución central:

  • Program.cs: Ejecutor de pruebas que procesa archivos test*.vhdl, analiza entidades/arquitecturas
  • Entity.cs: Analiza declaraciones de entidad (puertos, genéricos), coordina la arquitectura y la estimación de recursos
  • Architecture.cs: Analiza declaraciones de arquitectura (señales) y la región de comportamiento (procesos, sentencias concurrentes, bloques generate)
  • Process.cs: Gestiona procesos secuenciales con listas de sensibilidad, soporta la ejecución por ciclos delta a través de SignalScheduler
  • Executer.cs: Orquesta la ejecución de tareas con soporte de pausa/reanudación/cancelación, se integra con TimeMachine

Estructuras de control (implementaciones de ISControl):

  • IfStructure.cs: Maneja sentencias condicionales if-elsif-else con estructuras anidadas
  • CaseStructure.cs: Maneja sentencias case-when con múltiples ramas
  • ForLoopStructure.cs: Maneja bucles for con rangos ascendentes/descendentes y límites basados en expresiones
  • WhileLoopStructure.cs: Maneja bucles while con evaluación de condición
  • SimpleLoopStructure.cs: Maneja bucles infinitos con condiciones de salida
  • ExitStatement.cs: Maneja sentencias exit de VHDL con etiqueta y condición opcionales
  • NextStatement.cs: Maneja sentencias next de VHDL con etiqueta y condición opcionales
  • NullStatement.cs: Maneja sentencias null (sin operación) de VHDL
  • ReturnStatement.cs: Maneja sentencias return de VHDL para funciones y procedimientos
  • ProcedureCallStatement.cs: Maneja llamadas a procedimientos con evaluación de argumentos y reescritura de parámetros de salida
  • ConditionalAssignment.cs: Maneja asignaciones concurrentes condicionales de señales (when...else)
  • SelectedAssignment.cs: Maneja asignaciones concurrentes seleccionadas de señales (with...select)
  • GenerateBlock.cs: Maneja sentencias for-generate e if-generate con desenrollado de bucles

Evaluación de expresiones:

  • Operation.cs: Orquesta el análisis de expresiones, la síntesis a RPN y la evaluación
  • ShuntingYard.cs: Implementa el algoritmo Shunting-yard para la precedencia de operadores
  • Operator.cs: Ejecuta operaciones individuales (aritméticas, lógicas, de comparación, de desplazamiento/rotación)
  • ScheduledOperation.cs: Envuelve a Operation para la semántica de planificación de señales por ciclos delta
  • LiteralsFinder.cs: Identifica y clasifica valores literales (enteros, std_logic, vectores, hexadecimales, booleanos) usando expresiones regulares
  • OperationDetector.cs: Detecta tipos de operación (asignación, etc.) a partir de la entrada de cadena en bruto

Análisis de declaraciones:

  • VariableSyntetizer.cs: Analiza declaraciones de variable/señal/puerto/genérico/constante con soporte de tipos personalizados

Motor de simulación:

  • DeltaCycleEngine.cs: Coordina la simulación de ciclos delta de VHDL con planificación de procesos, actualizaciones de señales y avance del tiempo. Ganchos de reproducción en vivo: OnFrameEmitted(FrameEmittedArgs) (señales cambiadas por delta + sus líneas de origen), Pause()/Resume()/StepOneDelta(), History y — el 2026-05-27 — OnConditionEvaluated(int line) que se dispara cuando se toma una rama if/elsif o coincide un brazo case, de modo que los puntos de interrupción puedan pausar en una línea de condición que no tiene asignación de señal.
  • SignalScheduler.cs: Gestiona la planificación de actualizaciones de señales para el ciclo delta actual y los futuros (soporte de la cláusula after)
  • ProcessScheduler.cs: Gestiona la planificación de la ejecución de procesos con base en las listas de sensibilidad
  • SignalHistory.cs: Registra los cambios de valor de las señales a lo largo del tiempo para la exportación de formas de onda (VCD, CSV, Texto). Las marcas de tiempo son long (tick*1000+delta); ampliadas desde int el 2026-09-13 para que las ejecuciones a escala de segundos ya no desborden a los ~21 ms. Solo almacenamiento — la ruta semántica de SignalAttributeHandler conserva su propia marca de tiempo int, por lo que la salida de ejecuciones cortas es idéntica byte a byte.

Servicios y registros:

  • StandardFunctions.cs: Implementa funciones de la biblioteca estándar IEEE (detección de flancos, conversiones de tipo, operaciones vectoriales)
  • SignalAttributeHandler.cs: Singleton que rastrea atributos de señal ('event, 'last_value, 'stable, 'active)
  • FunctionExecutor.cs: Ejecutor singleton para funciones/procedimientos definidos por el usuario con ámbito local y soporte de recursión
  • PackageRegistry.cs: Registro singleton para constantes, funciones y procedimientos de paquetes VHDL
  • EntityRegistry.cs: Registro singleton para el descubrimiento de entidades multi-archivo y el seguimiento de la jerarquía de componentes
  • TypeRegistry.cs: Registro para definiciones de tipos personalizados de VHDL (enumeración, arreglo, registro, subtipo)
  • Resources.cs: Estima el uso de recursos de hardware de FPGA (flip-flops, LUT, slices DSP, BRAM, E/S, relojes)
  • ProgressReporter.cs: Sistema de reporte de progreso basado en eventos para la integración con la interfaz de usuario, con soporte de cancelación

Modelos:

  • TypeDefinition.cs: Representa declaraciones de tipos personalizados con límites, campos y valores predeterminados
  • FunctionDefinition.cs: Representa firmas de funciones definidas por el usuario con síntesis perezosa del cuerpo
  • ProcedureDefinition.cs: Representa firmas de procedimientos definidos por el usuario con soporte de parámetros out/inout
  • ParameterDefinition.cs: Representa metadatos de parámetros de función/procedimiento con modo (in/out/inout)
  • DeferredFunction.cs: Representa llamadas a funciones diferidas al tiempo de ejecución (por ejemplo, rising_edge)
  • ControlExceptions.cs: ExitException, NextException, ReturnException para el flujo de control
  • Constansts.cs: Enumeraciones de precedencia de operadores y patrones de expresiones regulares de operaciones (el nombre del archivo conserva el error tipográfico histórico)
  • SimulationConfig.cs: Composición de required Entity Entity, ulong TotalTicks, List<ClockConfig> Clocks, List<StimulusEvent> Stimuli. Además de los tipos auxiliares ClockConfig (Signal, HalfPeriodTicks, InitialHigh) y StimulusEvent (Tick, Signal, Value).

Fachada del bucle de ejecución:

  • SimulationRunner.cs (Services/): Fachada IDisposable con RunAsync(SimulationConfig), Pause(), Resume(), Stop(), además de los eventos OnProgressChanged(double), OnSimulationEnded(bool) y OnError(string). Es el punto de entrada usado por Kmila.Shared.Services.SimulationCoordinator (ningún otro sitio de llamada reimplementa la canalización).
  • Services/DataStructure.cs: Clase de marcador de posición reservada — actualmente solo un constructor vacío. Las operaciones reales de tipos residen en VariableSyntetizer, TypeRegistry y TypeDefinition.

Espacio de nombres de síntesis (Interpreter.Synthesis):

  • Synthesizer.cs: Punto de entrada estático — Netlist Synthesize(Entity entity) primero llama a ComponentSynthesizer.SynthesizeAll para las instanciaciones de la entidad, luego recorre el cuerpo de la arquitectura y despacha cada construcción concurrente (Operation / condicional / seleccionada / Process) a un sintetizador por construcción.
  • SynthesisContext.cs: Contexto de construcción por entidad. Posee las listas de nets/cells y expone GetOrCreateNet, NewInternal, Constant, AddCell, AddPort, AddSignal, AddDiagnostic y, finalmente, Build(), que devuelve una Netlist congelada.
  • ComponentSynthesizer.cs: internal static; SynthesizeAll(parentEntityName, ctx) emite una celda CellKind.BLACKBOX_COMPONENT por cada instanciación de componente que el analizador registró contra la entidad en EntityRegistry.Instance. Deliberadamente superficial — no desciende a la propia arquitectura del componente referenciado (eso lo cubre SourceElaborator en tiempo de simulación); solo hace visible la jerarquía de nivel superior en el Esquemático. Respeta la dirección del pin formal cuando la entidad referenciada se resuelve; de lo contrario, emite cada formal como un pin de entrada.
  • ConcurrentAssignSynthesizer.cs: Reduce asignaciones concurrentes de señales, incluidas las sobrecargas Synthesize(Operation, ctx) y SynthesizeInto(Operation, outputNet, ctx) + SynthesizeExpression(expr, scope, ctx, targetWidth) para la reutilización de subexpresiones. Resuelve los argumentos de DeferredFunction (incluidas las llamadas anidadas) y reduce las llamadas IEEE a través de IEEEPrimitives.TryLower.
  • ConditionalAssignSynthesizer.cs: Reduce las cadenas when…else.
  • SelectedAssignSynthesizer.cs: Reduce los bloques with…select.
  • ProcessSynthesizer.cs: Reduce un Process a una mezcla de celdas registradas (con reloj) y combinacionales.
  • IEEEPrimitives.cs: Reducción dirigida por tablas de llamadas a funciones IEEE a celdas reales (EDGE_DETECT, TO_INTEGER, TO_VECTOR, RESIZE, SHL/SHR/ROL/ROR, BUF, …). Los tipos IEEEPrimitiveArgs / IEEEPrimitiveResult / IIEEEPrimitive / IEEEPrimitives son internal (referencian al SynthesisContext interno); los hosts extienden la tabla mediante el estático público IEEEPrimitives.Register. TryLower devuelve la net de salida emitida o un marcador de posición + diagnóstico UnknownIEEEFunction en caso de fallo.
  • SelectionFilter.cs: Filtro público que recorta una Netlist + LaidOutNetlist a un subconjunto seleccionado por el usuario para su exportación — SelectionPicks (ids de celda / net / port-net en cesta + FocusedSourceLine opcional) → FilteredLayout (mismo espacio de coordenadas, con una caja Bounds). Respalda la cesta de exportación del SchematicViewer y el enfoque de la vista Naive.
  • Synthesis/Models.cs: Alberga los tipos record que componen una netlist — Net, Pin, Cell, PortPin, Netlist, Diagnostic — y las enumeraciones CellKind, NetOrigin, NetKind, PortDir, DiagnosticSeverity.
  • Synthesis/Layout/LayeredLayout.cs: Fase de disposición de estilo Sugiyama — Layout(Netlist) → LaidOutNetlist (con cajas Rect y polilíneas WirePath). Consumida por el componente Esquemático del Editor.

4. Decisiones de diseño críticas

4.1. Anclaje de patrones de expresiones regulares (v0.13.0)

Una de las correcciones más críticas de la v0.13.0 fue anclar correctamente los patrones de expresiones regulares de los operadores. Los patrones sin anclar causaban una grave clasificación errónea de tokens:

Ejemplo del problema:

// WRONG - Unanchored pattern
LOGIC_OPERATORS = "(and|or|xor|nand|nor|not)"

// This matches "or" in "memory" -> "mem[or]y"
// This matches "or" in "std_logic_vector" -> "std_logic_vect[or]"
// Result: Identifiers incorrectly classified as operators

Solución:

// CORRECT - Anchored pattern with ^ and $
LOGIC_OPERATORS = @"^(and|or|xor|nand|nor|not|xnor|abs)$"

// Now only exact matches work
// "or" matches, but "memory" and "std_logic_vector" don't

Impacto: Esta corrección por sí sola resolvió 9 fallos de prueba, mejorando la tasa de aprobación del 35 % al 80 %.

4.2. Estrategia de evaluación de expresiones

El intérprete usa un enfoque de dos fases para la evaluación de expresiones:

  1. Fase de síntesis: Convertir la notación infija a postfija usando el algoritmo Shunting-yard
  2. Fase de ejecución: Evaluar la expresión postfija usando un enfoque basado en pila

Beneficios:

  • Manejo correcto de la precedencia de operadores (**, *, /, +, -)
  • Soporte de subexpresiones entre paréntesis
  • Separación clara entre análisis y evaluación
  • Evaluación eficiente con complejidad O(n)

Ejemplo:

result <= (A + B) * C - D / 2;

Procesamiento:

  1. Tokens: (, A, +, B, ), *, C, -, D, /, 2
  2. Postfijo: A B + C * D 2 / -
  3. Evaluación: Basada en pila con la precedencia adecuada

4.3. Manejo de declaraciones de puertos/genéricos

Las declaraciones de puertos y genéricos pueden contener expresiones complejas que usan otros genéricos o funciones:

generic (
    NUM_INPUTS : integer := 8;
    DATA_WIDTH : integer := 16
);
port (
    inputs : in std_logic_vector((NUM_INPUTS * DATA_WIDTH) - 1 downto 0);
    sum : out std_logic_vector(DATA_WIDTH + clog2(NUM_INPUTS) - 1 downto 0)
);

Estrategia (v0.13.0): El intérprete omite la evaluación de estas expresiones durante la declaración porque:

  • Funciones como clog2() pueden definirse más adelante en la arquitectura
  • Los genéricos aún no tienen valores asignados durante el análisis de la entidad
  • La información de tipo es suficiente para la validación de sintaxis
  • Los valores reales se calculan en tiempo de ejecución cuando se necesitan

4.4. Estimación de recursos de hardware (v0.13.0)

El intérprete incluye un estimador automático de recursos de FPGA que analiza las entidades VHDL sintetizadas para predecir el uso de hardware.

Precisión de la estimación de recursos:

Recurso Precisión Método
Flip-Flops ~95 % Cuenta las señales asignadas en procesos con reloj (rising_edge/falling_edge)
Pines de E/S 100 % Conteo exacto a partir de las declaraciones de puertos de la entidad
Dominios de reloj 100 % Detecta llamadas a rising_edge/falling_edge en los procesos
Slices DSP ~80-90 % Cuenta operaciones de multiplicación y aritmética ancha
BRAM ~85 % Detecta tipos de arreglo y patrones de memoria
LUT ~60-80 % Estima a partir de la lógica combinacional (dependiente de la síntesis)

Ejemplo de salida:

Resources: FF=8, LUT=0, DSP=0, BRAM=0.0, IO=18, CLK=1

Cómo funciona:

  1. Conteo de flip-flops:
// Scans all processes with clock sensitivity
// Counts signals assigned within rising_edge(clk) blocks
foreach (var process in clocked_processes)
{
    flipFlops += Sum(assigned_signals.Select(s => s.Size));
}
  1. Conteo de pines de E/S:
// Sums bit widths of all entity ports
ioPins = entity.Ports.Sum(p => p.Size);
  1. Detección de reloj:
// Finds signals used in rising_edge() or falling_edge() calls
// Identifies clock domains by unique clock signal names

Ejemplo:

entity example is
    port (
        clk : in STD_LOGIC;         -- 1 I/O pin
        reset : in STD_LOGIC;       -- 1 I/O pin
        data_in : in STD_LOGIC_VECTOR(7 downto 0);   -- 8 I/O pins
        data_out : out STD_LOGIC_VECTOR(7 downto 0)  -- 8 I/O pins
    );
end example;

architecture behavioral of example is
begin
    process(clk, reset)
    begin
        if reset = '1' then
            data_out <= "00000000";
        elsif rising_edge(clk) then
            data_out <= data_in;    -- 8 flip-flops for data_out
        end if;
    end process;
end behavioral;

Resultado de la estimación de recursos:

  • Flip-Flops: 8 (data_out es un registro de 8 bits)
  • Pines de E/S: 18 (1+1+8+8)
  • Dominios de reloj: 1 (clk)
  • LUT: 0 (sin lógica combinacional)
  • DSP: 0 (sin multiplicaciones)
  • BRAM: 0 (sin arreglos)

4.5. Diseño de la precedencia de operadores

Reto: VHDL tiene dos sistemas de tipos de operadores separados:

  • Operadores aritméticos (+, -, *, /, **, mod)
  • Operadores lógicos (and, or, xor, nand, nor, not)

Solución:

// Precedence stored as 'object' to accommodate both enum types
public object Precedence { get; private set; }

// v0.13.0 Fix: Use Convert.ToInt32() for proper enum unboxing
Convert.ToInt32(operator.Precedence)

Valores de precedencia (menor = mayor precedencia):

  • 0: UNARY (**, abs, not)
  • 1: MULTIPLICATION/DIVISION, AND/NAND
  • 2: ADD/SUBTRACT, OR/NOR
  • 3: XOR/XNOR
  • 4: PARENTHESIS
  • 5: COMPARATION, ASSIGNATION

5. Componentes clave

5.1. ShuntingYard - Evaluador de expresiones

Archivo: Repositories/ShuntingYard.cs

Propósito: Convierte expresiones infijas a notación postfija usando el algoritmo Shunting-yard de Dijkstra.

Algoritmo:

Input: [A, +, B, *, C]

Process:
1. A (operand) → output
2. + (operator) → stack
3. B (operand) → output
4. * (higher precedence than +) → stack
5. C (operand) → output
6. End: pop all operators to output

Output: [A, B, C, *, +]

Corrección crítica de la v0.13.0:

// Changed from direct cast to Convert.ToInt32()
Convert.ToInt32(_operators.Peek().Precedence) <= Convert.ToInt32(operation.Precedence)

Por qué: La precedencia es de tipo object que contiene un enum. La conversión directa (int) falla para los enums.

5.2. Operation - Manejador de expresiones

Archivo: Services/Operation.cs

Propósito: Orquesta el análisis, la síntesis y la evaluación de expresiones.

Métodos clave:

Syntetize() - Procesa la expresión en bruto para convertirla en una forma evaluable:

// Input: "A + B * C"
// Output: List<object> { Variable(A), Operator(+), Variable(B), Operator(*), Variable(C) }

Execute() - Evalúa la expresión y devuelve el resultado:

// Creates ShuntingYard, evaluates postfix, returns Variable with result

Mejoras de la v0.13.0:

  1. Omisión de palabras clave:
// Skip 'downto'/'to' keywords in range expressions
// Example: signal(7 downto 0) → process "signal" and indices, skip "downto"
if (currentItem.ToLower() == "downto" || currentItem.ToLower() == "to")
    continue;

// Skip 'when...else' in conditional assignments
// Example: output <= input1 when sel='0' else input2
if (currentItem.ToLower() == "when")
    /* skip to 'else' */
  1. Verificación de tipo de operador unario:
// v0.13.0 Fix: Prevent ** (power) from being treated as NOT
bool isUnaryNot = op.OperatorType == LOGIC_OPERATION &&
                  op.Precedence is PrecedencesLogicOperators &&
                  (PrecedencesLogicOperators)op.Precedence == NOT;

Por qué: Tanto PrecedencesArithOperators.UNARY como PrecedencesLogicOperators.NOT tienen el valor 0.

5.3. Architecture - Sintetizador de comportamiento

Archivo: Services/Architecture.cs

Propósito: Analiza la región declarativa de la arquitectura y las sentencias de comportamiento.

Responsabilidades:

  1. Analizar las declaraciones de señales
  2. Identificar y analizar los procesos
  3. Manejar las asignaciones concurrentes de señales
  4. Omitir las instanciaciones de componentes (estructurales, no de comportamiento)
  5. Gestionar las sentencias con etiqueta

Características de la v0.13.0:

1. Detección de sentencias con etiqueta:

-- Labeled process
timer_proc : process(clk, reset)
begin
    -- statements
end process;

-- Labeled component instantiation
cpu_inst : CPU port map (clk => clk, data => data);

-- Labeled generate
gen_adders : for i in 0 to 7 generate
    -- structure
end generate;

Implementación:

if (_streamer.Current?.Type == IDENTIFIERS)
{
    // Check for label : keyword/component pattern
    if (nextToken == ":")
    {
        if (followingToken is KEYWORDS)
            // Handle process, for, if generate
        else if (followingToken is IDENTIFIERS)
            // Handle component instantiation
            SkipComponentInstantiation();
    }
}

2. Omisión de instanciaciones de componentes:

private void SkipComponentInstantiation()
{
    // Skip: label : ComponentName [generic map (...)] port map (...);
    int parenDepth = 0;
    while (current != ";\" || parenDepth > 0)
    {
        if (current == "(") parenDepth++;
        else if (current == ")") parenDepth--;
        moveNext();
    }
}

5.4. IfStructure - Manejador condicional

Archivo: Repositories/IfStructure.cs

Propósito: Gestiona la ejecución condicional if-elsif-else.

Sintaxis soportada:

if condition1 then
    statements;
elsif condition2 then
    statements;
else
    statements;
end if;

Diseño: Usa una lista plana de objetos IfBranch (no recursiva) para una mejor mantenibilidad.

2026-05-27 — puntos de interrupción en condiciones (IfBranch.ConditionLine): cada rama registra la línea de origen (base 1) de su palabra clave if / elsif (capturada de _streamer.Current.Line antes de que la palabra clave sea consumida). En tiempo de ejecución, DeltaCycleEngine dispara OnConditionEvaluated(line) cuando una rama se toma (condición verdadera), de modo que la capa de reproducción en vivo pueda pausar en un punto de interrupción fijado en una línea de condición — la ruta de transición de señales no puede capturarlos porque la condición en sí no produce ningún cambio de señal. Véase DeltaCycleEngine.OnConditionEvaluated.

Mejora de la v0.13.0 - Sentencias case anidadas:

case "case":
    // Nested case statement within if/elsif/else branch
    CaseStructure nestedCase = new(_variables, _streamer);
    nestedCase.Syntetize();
    branch.Tasks.Add(nestedCase);
    ConsumeEndKeyword("case");
    break;

Ejemplo de uso:

if button_flag = '1' then
    case selector is
        when "00" => opcode <= ADD;
        when "01" => opcode <= SUB;
        when others => opcode <= NOP;
    end case;
elsif timeout = '1' then
    opcode <= RESET;
end if;

5.5. CaseStructure - Manejador de Case/When

Archivo: Repositories/CaseStructure.cs

Propósito: Gestiona las sentencias case de VHDL con múltiples ramas when.

Sintaxis soportada:

case selector is
    when "0000" => statements;
    when "0001" | "0010" => statements;  -- Multiple choices
    when others => statements;           -- Default branch
end case;

Características:

  • Múltiples valores de elección por rama usando el separador |
  • Rama por defecto/de reserva when others =>
  • Estructuras de control anidadas (if, case, bucles) dentro de las ramas
  • v0.13.0: Puede anidarse dentro de ramas if/elsif/else
  • 2026-05-27: cada CaseWhenBranch registra la línea de origen (base 1) de su palabra clave when (ConditionLine); el motor dispara OnConditionEvaluated(line) cuando un brazo coincide, de modo que un punto de interrupción en un brazo when pause la ejecución en vivo (en paralelo con IfBranch.ConditionLine).

5.6. VariableSyntetizer - Analizador de declaraciones

Archivo: Services/VariableSyntetizer.cs

Propósito: Analiza declaraciones de señales, variables, puertos y genéricos.

Maneja:

  • Tipos simples: signal counter : integer := 0;
  • Vectores: signal data : std_logic_vector(7 downto 0);
  • Puertos: port (clk : in std_logic; data : out std_logic_vector(7 downto 0));
  • Genéricos: generic (WIDTH : integer := 8);

Corrección crítica de la v0.13.0 - Omisión de expresiones de puerto:

// For ports/generics with complex expressions, skip evaluation
if (_isPort || _isGeneric)
{
    // Skip: std_logic_vector((NUM_INPUTS * DATA_WIDTH) - 1 downto 0)
    int parenDepth = 1;
    while (parenDepth > 0) {
        if (current == "(") parenDepth++;
        else if (current == ")") parenDepth--;
        if (parenDepth > 0) moveNext();
    }
    VectorSize = 8; // Default for complex expressions
}

Por qué esto importa:

  • Puerto: inputs : in std_logic_vector((NUM_INPUTS * DATA_WIDTH) - 1 downto 0)
  • La expresión usa los genéricos NUM_INPUTS y DATA_WIDTH
  • También usa la función clog2(NUM_INPUTS), que puede definirse más adelante
  • No puede evaluarse durante el análisis de la entidad - se omite y se usan valores por defecto

5.7. StandardFunctions - Biblioteca IEEE

Archivo: Services/StandardFunctions.cs

Propósito: Implementa las funciones IEEE std_logic_1164 y numeric_std.

Funciones implementadas:

Categoría Funciones
Detección de flancos rising_edge(signal), falling_edge(signal)
Conversión de tipo to_integer(slv), to_unsigned(val, width), to_signed(val, width), conv_integer(slv), std_logic_vector(val, width)
Operaciones vectoriales resize(vec, width), shift_left(vec, amt), shift_right(vec, amt), rotate_left(vec, amt), rotate_right(vec, amt)
Aritmética AddVectors(a, b), SubtractVectors(a, b)
Síntesis clog2(val) - techo del logaritmo base 2

Mejora de ToInteger() en la v0.13.0:

public static int ToInteger(string slv)
{
    string cleaned = slv.Replace("\"", "").Replace("'", "").Trim();

    // v0.13.0: Safety checks for VHDL meta-values
    if (string.IsNullOrEmpty(cleaned)) return 0;
    if (cleaned.Contains('U') || cleaned.Contains('X') || cleaned.Contains('Z')) return 0;

    // Try decimal first (e.g., integer literals)
    if (int.TryParse(cleaned, out int dec)) return dec;

    // Try binary (e.g., std_logic_vector)
    try { return Convert.ToInt32(cleaned, 2); }
    catch { return 0; }
}

Metavalores de VHDL:

  • 'U' - Sin inicializar
  • 'X' - Desconocido/forzado
  • 'Z' - Alta impedancia
  • 'W' - Desconocido débil
  • '-' - No importa (don't care)

Estos se tratan como 0 para efectos de la simulación.

5.8. SignalAttributeHandler - Rastreador de atributos

Archivo: Services/SignalAttributeHandler.cs

Propósito: Servicio singleton que rastrea el estado de las señales para las consultas de atributos.

Atributos soportados:

  • signal'event - Verdadero si la señal cambió en este ciclo
  • signal'last_value - Valor anterior antes del cambio
  • signal'stable - Verdadero si la señal no ha cambiado
  • signal'active - Verdadero si la señal tuvo una transacción

Uso en rising_edge():

public static bool RisingEdge(Variable signal)
{
    // Check if signal had an event
    if (!SignalAttributeHandler.Instance.GetEvent(signal))
        return false;

    // Check if current value is '1'
    string currentValue = signal.Value?.ToString()?.Replace("'", "") ?? "";
    return currentValue == "1";
}

5.9. Resources - Estimador de recursos de FPGA

Archivo: Services/Resources.cs

Propósito: Estima el uso de recursos de hardware para las entidades VHDL sintetizadas.

Características:

  • Adjunto a cada instancia de Entity mediante la propiedad entity.ResourceUsage
  • Calculado automáticamente después de la síntesis de la arquitectura
  • Proporciona conteos exactos de flip-flops y E/S, y buenas estimaciones para DSP/BRAM/LUT

Propiedades:

public class Resources
{
    public int FlipFlops { get; }        // ~95% accuracy
    public int LUTs { get; }             // ~60-80% accuracy
    public int DSPSlices { get; }        // ~80-90% accuracy
    public double BRAMBlocks { get; }    // ~85% accuracy
    public int IOPins { get; }           // 100% exact
    public int ClockDomains { get; }     // 100% exact
    public HashSet<string> ClockSignals { get; }
}

Uso:

// Resources are automatically calculated when architecture is attached
entity.SetArchitecture(architecture);

// Access resource estimates
Console.WriteLine($"Flip-Flops: {entity.ResourceUsage.FlipFlops}");
Console.WriteLine($"I/O Pins: {entity.ResourceUsage.IOPins}");
Console.WriteLine($"Clocks: {string.Join(", ", entity.ResourceUsage.ClockSignals)}");

// Or print formatted report
entity.ResourceUsage.PrintReport("Xilinx 7-Series");

Métodos de cálculo:

  1. CalculateIOPins() - Suma los anchos de bits de los puertos (100 % de precisión)
  2. CalculateClocks() - Detecta las señales de reloj a partir de las listas de sensibilidad de los procesos (100 % de precisión)
  3. CalculateFlipFlops() - Escanea recursivamente los procesos con reloj en busca de señales asignadas (~95 % de precisión)
  4. CalculateDSPSlices() - Cuenta las operaciones de multiplicación (TODO: implementación pendiente)
  5. CalculateBRAM() - Detecta patrones de arreglo/memoria (TODO: implementación pendiente)
  6. CalculateLUTs() - Estima la lógica combinacional (TODO: implementación pendiente)

5.10. ProgressReporter - Sistema de seguimiento de progreso

Archivo: Services/ProgressReporter.cs

Propósito: Reporte de progreso basado en eventos para la integración con la interfaz de usuario y la depuración.

Características:

  • Patrón singleton para acceso global
  • Actualizaciones dirigidas por eventos mediante los eventos ProgressChanged, ErrorOccurred, Completed
  • Progreso como tupla (float, string) para una fácil vinculación con la interfaz
  • Visualización de barra de progreso ASCII
  • Seguimiento de la fase de procesamiento
  • Reportadores con ámbito para suboperaciones

Fases de procesamiento:

public enum ProcessingPhase
{
    Initializing, Tokenizing, ParsingEntity, ParsingPorts, ParsingGenerics,
    ParsingArchitecture, ParsingSignals, ParsingTypes, SynthesizingBehavior,
    ParsingProcess, ParsingIfStructure, ParsingCaseStructure, ParsingLoop,
    EvaluatingExpression, CalculatingResources, Completed, Error
}

Uso - Suscribirse a las actualizaciones de progreso:

// Subscribe to progress events
ProgressReporter.Instance.ProgressChanged += (sender, e) =>
{
    // Get progress as tuple (float, string)
    var (progress, message) = e.Update.ToTuple();

    // Or access individual properties
    float percent = e.Update.Progress;           // 0.0 to 1.0
    string status = e.Update.Message;            // "Parsing entity"
    string detail = e.Update.Detail;             // "AND_Gate"
    ProcessingPhase phase = e.Update.Phase;      // ProcessingPhase.ParsingEntity

    // Update your UI
    progressBar.Value = percent * 100;
    statusLabel.Text = $"{status} {detail}";
};

// Subscribe to errors
ProgressReporter.Instance.ErrorOccurred += (sender, e) =>
{
    ShowError(e.Update.Message, e.Update.Detail);
};

// Subscribe to completion
ProgressReporter.Instance.Completed += (sender, e) =>
{
    ShowSuccess("Parsing completed!");
};

Uso - Reportar progreso (desde el código de análisis):

var progress = ProgressReporter.Instance;

// Report with phase-relative progress (0-1 within phase)
progress.ReportPhase(0.5f, "Parsing ports", ProcessingPhase.ParsingPorts, "clk");

// Report absolute progress
progress.Report(0.75f, "Almost done", ProcessingPhase.Completed);

// Report error
progress.ReportError("Failed to parse entity", "Unexpected token at line 42");

// Report completion
progress.ReportCompleted("Successfully parsed 5 entities");

Soporte de cancelación:

El reportador de progreso se integra con CancellationToken para la cancelación elegante de operaciones:

// Setup cancellation (typically in Program.cs)
var cts = new CancellationTokenSource();
var progress = ProgressReporter.Instance;

// Register CTS with progress reporter
progress.StartCancellableOperation(cts);

// Handle Ctrl+C for console applications
Console.CancelKeyPress += (sender, e) =>
{
    e.Cancel = true; // Prevent immediate termination
    progress.RequestCancellation();
};

// Subscribe to cancellation events
progress.CancellationRequested += (sender, e) =>
{
    Console.WriteLine($"Cancelled: {e.Update.Message}");
};

// Pass token to parsing methods
try
{
    entity.Syntetize(rawData, cts.Token);
    architecture.Syntetize(rawData, cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation was cancelled by user.");
}

Métodos de cancelación:

  • StartCancellableOperation() - Crea un nuevo CancellationTokenSource
  • StartCancellableOperation(CancellationTokenSource) - Usa el CTS proporcionado
  • RequestCancellation() - Dispara la cancelación y genera el evento
  • ThrowIfCancellationRequested() - Lanza una excepción si se canceló (para uso en bucles)
  • CheckCancellation() - Devuelve un bool sin lanzar excepción
  • IsCancellationRequested - Propiedad para verificar el estado de cancelación
  • CancellationToken - Propiedad para obtener el token actual

Componentes integrados: Todos los componentes principales de análisis soportan CancellationToken:

  • Entity.Syntetize(string[], CancellationToken)
  • Architecture.Syntetize(string[], CancellationToken)
  • Process.Syntetize(CancellationToken)
  • VariableSyntetizer.Syntetize(CancellationToken)

La cancelación se verifica en cada iteración de bucle en los métodos de análisis, garantizando una cancelación responsiva incluso para archivos VHDL grandes.

Visualización de la barra de progreso:

[████████████░░░░░░░░]  62.0% | Parsing process variables [counter]

Salida de consola con iconos (modo verbose):

📝 [█░░░░░░░░░░░░░░░░░░░]   5.0% | Tokenizing entity
📦 [████░░░░░░░░░░░░░░░░]  20.0% | Found entity [AND_Gate]
🔌 [█████░░░░░░░░░░░░░░░]  25.0% | Parsed 3 port(s)
🏗️ [██████░░░░░░░░░░░░░░]  33.0% | Found architecture [behavioral of AND_Gate]
📡 [███████░░░░░░░░░░░░░]  38.0% | Parsed 5 signal(s)
⚡ [██████████████░░░░░░]  73.0% | Synthesizing behavior
📊 [█████████████████░░░]  87.5% | Calculating resource usage
✅ [████████████████████] 100.0% | Processing completed

6. Cómo usarlo

Ejecución de la suite de pruebas

cd /mnt/ssd1tb/Codigos/Kmila-9s/Interpreter

# Standard output
dotnet run --framework net9.0

# Verbose mode with progress details
dotnet run --framework net9.0 -- -v
dotnet run --framework net9.0 -- --verbose

# Compact progress bar mode
dotnet run --framework net9.0 -- -p
dotnet run --framework net9.0 -- --progress

Salida estándar:

Found 20 test file(s)
============================================================

[TEST] Processing: test1.vhdl
----------------------------------------
  Entities parsed: 1
    - TestEntity: 5 ports, 2 generics
      Architecture tasks: 3
  [PASS] test1.vhdl - Synthesized successfully!

...

Test Results: 16 passed, 4 failed, 20 total

Salida detallada (-v):

[TEST] Processing: test1.vhdl
----------------------------------------
    📝 [█░░░░░░░░░░░░░░░░░░░]   5.0% | Tokenizing entity
    📦 [████░░░░░░░░░░░░░░░░]  20.0% | Found entity [AND_Gate]
    🔌 [█████░░░░░░░░░░░░░░░]  25.0% | Parsed 3 port(s)
  Entities parsed: 1
    - AND_Gate: 3 ports, 0 generics
  [PASS] test1.vhdl - Synthesized successfully!

Uso programático

using Interpreter.Services;
using Parser.Repositories;

// 1. Parse VHDL source
string vhdlCode = File.ReadAllText("design.vhdl");
var entities = ParseVhdlCode(vhdlCode);

// 2. Access parsed structure
foreach (var entity in entities)
{
    Console.WriteLine($"Entity: {entity.Name}");
    Console.WriteLine($"  Ports: {entity.Ports.Count}");
    Console.WriteLine($"  Generics: {entity.Generics.Count}");

    if (entity.Architecture != null)
    {
        Console.WriteLine($"  Signals: {entity.Architecture.Signals.Count}");
        Console.WriteLine($"  Tasks: {entity.Architecture.Tasks.Count}");
    }
}

// 3. Execute simulation (future implementation)
// await entity.Architecture.ExecuteAsync();

7. Pruebas

Hoy existen dos superficies de prueba:

7.1. Suite de regresión xUnit (Interpreter/Tests/) — autoritativa

Se ejecuta con dotnet test. Esta es la superficie en la que confiar para las regresiones:

Archivo de prueba Enfoque
DeltaCycleEngineTests Planificación de ciclos delta, actualizaciones de señales, avance del tiempo
SynthesisTests Reducción de comportamiento → estructural (corrección de la netlist)
NestedIEEECallTests Llamadas anidadas a funciones IEEE (por ejemplo, to_integer(unsigned(...)))
IEEEPrimitivesTests Tabla de reducción de función IEEE → celda primitiva
IEEELibraryLoaderTests / IEEELoweringEndToEndTests Carga de la biblioteca IEEE + reducción de extremo a extremo
LibraryCompilerTests Resolución/compilación del LibraryCompiler entre módulos
PortStrictnessTests Rigurosidad de la dirección / conectividad de los puertos
PracticeExerciseTests Casos de integración del calificador de prácticas
VcdComparison Comparación con VCD de referencia (equivalencia de formas de onda)

La equivalencia byte a byte entre FastSimulationRunnerSimulationRunner está cubierta por pruebas de diff/equivalencia de VCD (véanse las notas de Fast mode en §2).

7.2. Corpus de consola (test*.vhdl, impulsado por Program.cs) — humo/exploración

El corpus ha crecido hasta 54 fixtures test*.vhdl (numeradas hasta test41, más variantes con nombre). Es un arnés de humo/exploración, no una compuerta de aprobado/reprobado. Cobertura aproximada de las primeras fixtures:

Higiene del repositorio: la raíz del módulo también carga artefactos de trabajo sobrantes que no forman parte de la compilación ni de ninguna de las superficies de pruebaProgram.cs.1 (una copia obsoleta de Program.cs), run_delta_tests.cs y varios VCD de referencia/trabajo sueltos (counter_sim.vcd, reference_*.vcd, test_*.vcd). Pueden ignorarse/eliminarse durante la limpieza; los VCD de referencia autoritativos que usan las pruebas residen bajo Interpreter/Tests/.

Pruebas Cobertura
test1-test7 Entidades básicas, señales, procesos, estructuras de control simples
test8-test10 Flujo de control complejo con sentencias case anidadas
test11 Sentencias generate con instanciación de componentes
test12-test15 Funciones, procedimientos, atributos de señal
test16 Expresiones de puerto con parámetros genéricos
test17-test20 Características avanzadas (componentes, buses, controladores GPIO)
test21-test41 + variantes Fixtures posteriores añadidas junto con el trabajo de motor/síntesis (no catalogadas individualmente aquí)
Instantánea histórica (v0.15.0) — NO vuelta a medir desde entonces

Las cifras a continuación son una instantánea de la v0.15.0 sobre solo las primeras 20 fixtures y no se han vuelto a medir; trátelas como históricas, no actuales. Vuelva a ejecutar Program.cs para obtener una cifra fresca.

✓ Passing: 16/20 tests (80%)  [v0.15.0, first-20 corpus, historical]
✗ Failing: 4/20 tests (20%)

Passing tests: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 17, 18, 19
Failing tests: 11, 13, 16, 20

Análisis de fallos:

  • test11: Sentencias generate complejas con la función clog2() - causa tiempo de espera agotado en el análisis (necesita optimización)
  • test13: Múltiples arquitecturas con sentencias case del algoritmo de Booth - causa tiempo de espera agotado en el análisis
  • test16: Constantes de paquete como MAX_ROWS usadas en expresiones complejas - causa tiempo de espera agotado en el análisis
  • test20: Diseño complejo de SoC con controladores GPIO - causa tiempo de espera agotado en el análisis

Nota: Las pruebas 11, 13, 16 y 20 involucran construcciones VHDL avanzadas que disparan rutas de análisis profundas. El intérprete las maneja con elegancia (sin bloqueos), pero agotan el tiempo de espera durante el análisis de estructuras complejas. El soporte de cancelación (v0.15.0) permite a los usuarios abortar análisis de larga duración mediante Ctrl+C.

Modo de depuración

Habilite la salida detallada de depuración:

# Build in Debug configuration
dotnet build -c Debug
dotnet run -c Debug --framework net9.0

La salida de depuración incluye:

  • Posiciones del flujo de tokens durante el análisis
  • Detalles de omisión de funciones/procedimientos (prefijo [SkipFunction])
  • Estado de la síntesis de la entidad
  • Operaciones del sintetizador de variables

8. Problemas comunes y soluciones

Problema 1: "Expected KEYWORDS, found IDENTIFIERS"

Síntoma: Error durante el análisis de la entidad o la arquitectura

Ejemplo:

Error: Expected type: KEYWORDS, found: IDENTIFIERS -> ComponentName at Line: 1:4

Causas comunes:

  1. La instanciación del componente no se omitió correctamente
  2. La sentencia con etiqueta no se reconoció
  3. El fin de una función/procedimiento se confundió con el fin de la arquitectura

Solución (v0.13.0):

  • Se añadió la detección de componentes con etiqueta: if (label : IDENTIFIER)
  • Se añadió el método SkipComponentInstantiation()
  • Se añadió el seguimiento de funcProcDepth en Program.cs

Problema 2: "Instance not in this scope"

Síntoma: Variable/señal no encontrada durante la evaluación de expresiones

Ejemplo:

Error: Instance not in this scope: 'downto' in expression 'signal 7 downto 0'
Error: Instance not in this scope: 'ELEMENT_WIDTH' in expression 'ELEMENT_WIDTH - 1'

Causas comunes:

  1. Palabras clave (downto, to, when) tratadas como identificadores
  2. Genérico usado en una expresión de puerto antes de que se evalúen los genéricos
  3. Variable local declarada pero la verificación de ámbito falla

Soluciones (v0.13.0):

  • Omitir las palabras clave downto/to/when en Operation.Syntetize()
  • Omitir la evaluación de expresiones de puerto con genéricos en VariableSyntetizer
  • Se eliminó la verificación de ámbito errónea que impedía las declaraciones de variables locales

Problema 3: "Unable to cast object to Int32"

Síntoma: ShuntingYard falla durante la comparación de precedencia

Ejemplo:

Error: Unable to cast object of type 'System.Object' to type 'System.Int32'
at ShuntingYard.OrderPrecedenceList() line 47

Causa: Operator.Precedence es de tipo object, la conversión directa (int) falla para los valores de enum

Solución (v0.13.0):

// WRONG
(int)_operators.Peek().Precedence

// CORRECT
Convert.ToInt32(_operators.Peek().Precedence)

Problema 4: El operador de potencia (**) tratado como NOT

Síntoma: Una expresión como 2 ** 8 se evalúa incorrectamente

Ejemplo:

Expected: 256 (2^8)
Actual: Logic NOT applied to 2

Causa:

  • PrecedencesArithOperators.UNARY = 0 (para el operador **)
  • PrecedencesLogicOperators.NOT = 0 (para el operador not)
  • Sin verificación de tipo, ambos coinciden al convertirse a int

Solución (v0.13.0):

// Added type check before applying unary NOT logic
bool isUnaryNot = op.OperatorType == LOGIC_OPERATION &&
                  op.Precedence is PrecedencesLogicOperators &&
                  (PrecedencesLogicOperators)op.Precedence == NOT;

Problema 5: Identificadores que coinciden con los operadores "or"/"and"

Síntoma: Variables como "memory" o "factor" tratadas como operadores

Ejemplo:

Error: "memoryDirection1" classified as operator (contains "or")
Error: "std_logic_vector" classified as operator (contains "or")

Causa: El patrón de expresión regular sin anclar coincide con subcadenas

Solución (v0.13.0):

// WRONG
LOGIC_OPERATORS = "(and|or|xor|nand|nor|not)"

// CORRECT - Anchored with ^ and $
LOGIC_OPERATORS = @"^(and|or|xor|nand|nor|not|xnor|abs)$"

9. Guía del desarrollador

9.1. Agregar una nueva función estándar

Paso 1: Agregar al registro de funciones

public static bool IsStandardFunction(string name)
{
    return name.ToLower() switch
    {
        "my_new_function" => true,
        // ... existing functions
        _ => false
    };
}

Paso 2: Implementar la función

/// <summary>
/// Description of what the function does.
/// </summary>
/// <param name="arg1">First argument</param>
/// <param name="arg2">Second argument</param>
/// <returns>Result value</returns>
public static int MyNewFunction(int arg1, string arg2)
{
    // Implementation
    return result;
}

Paso 3: Agregar al despachador

public static object EvaluateFunction(string functionName, object[] arguments, HashSet<Variable> variables)
{
    switch (functionName.ToLower())
    {
        case "my_new_function":
            if (arguments.Length >= 2)
            {
                int val1 = GetIntValue(arguments[0], variables);
                string val2 = GetStringValue(arguments[1], variables);
                return MyNewFunction(val1, val2);
            }
            return 0;
        // ... existing cases
    }
}

9.2. Agregar una nueva estructura de control

Paso 1: Crear la implementación de ISControl

public class MyControlStructure : ISControl
{
    private readonly HashSet<Variable> _variables;
    private readonly TokenStream _streamer;

    public List<object> Tasks { get; private set; } = new();

    public MyControlStructure(HashSet<Variable> variables, TokenStream streamer)
    {
        _variables = variables;
        _streamer = streamer;
    }

    public void Syntetize()
    {
        _streamer.Expect(KEYWORDS); // consume structure keyword
        // Parse structure-specific syntax
    }

    public async Task ExecuteAsync()
    {
        // Execute the structure's tasks
        Executer executer = new(Tasks);
        await executer.ExecuteAsync();
    }

    public void Syntetize(string[] data) => throw new NotImplementedException();
}

Paso 2: Agregar a Architecture y/o Process

// In Architecture.SyntetizeBehaviour() or Process.SyntetizeBehaviour()
case "mystructure":
    MyControlStructure myStruct = new(comparators.ToHashSet(), _streamer);
    myStruct.Syntetize();
    Tasks.Add(myStruct);
    // Consume 'end mystructure;' if applicable
    break;

9.3. Técnicas de depuración

1. Depuración del flujo de tokens:

#if DEBUG
Console.WriteLine($"[ComponentName] Current: '{_streamer.Current?.Value}' " +
                  $"Type: {_streamer.Current?.Type} " +
                  $"Line: {_streamer.Current?.Line}");
#endif

2. Depuración de expresiones:

// In Operation.cs, enable DEBUG_OPERATION
#define DEBUG_OPERATION

// Shows:
// - Raw expression string
// - Tokenized data array
// - Available variables
// - Syntetized items before ShuntingYard

3. Seguimiento de la profundidad de paréntesis:

int depth = 0;
while (_streamer.Current != null)
{
    if (current == "(") {
        depth++;
        Console.WriteLine($"[DEBUG] Open paren, depth now {depth}");
    }
    else if (current == ")") {
        depth--;
        Console.WriteLine($"[DEBUG] Close paren, depth now {depth}");
    }
}

4. Puntos de control comunes de depuración:

  • Después de las llamadas a Expect(): Verificar que el token Current avanzó correctamente
  • Antes de los bucles while: Verificar que la condición de salida se cumplirá eventualmente
  • Después de los métodos de síntesis: Verificar que el flujo de tokens esté en la posición esperada
  • En las sentencias switch: Agregar un caso por defecto con salida de depuración

9.4. Mejores prácticas

1. Patrones de expresiones regulares:

  • Anclar siempre: Usar ^ y $ para coincidencias exactas
  • Usar cadenas literales (raw): Anteponer @ para cadenas verbatim
  • Probar casos límite: Cadenas vacías, caracteres individuales, palabras similares

2. Uso del flujo de tokens:

  • Usar Expect(): Para tokens que deben estar presentes
  • Usar Match(): Para tokens opcionales (devuelve bool, avanza si coincide)
  • Establecer puntos de retroceso: Antes del análisis especulativo con SetRollBack()
  • Seguir la profundidad: Para estructuras anidadas (paréntesis, pares begin/end)

3. Manejo de errores:

  • Fallar rápido: Lanzar excepciones significativas de forma temprana
  • Proporcionar contexto: Incluir el valor del token y el número de línea en los mensajes de error
  • Agregar límites de seguridad: Máximo de iteraciones para evitar bucles infinitos

4. Pruebas:

  • Casos de prueba mínimos: Comenzar con el código más simple que reproduzca el problema
  • Correcciones incrementales: Corregir un problema a la vez, verificar que las pruebas pasen
  • Pruebas de regresión: Asegurar que las correcciones no rompan pruebas que antes pasaban

10. Registro de cambios

Resincronización de la documentación (2026-08-03, rama v1.2.0)

  • Se actualizó el sello del encabezado a la rama (v1.2.0) y fecha actuales. Se volvieron a cotejar las listas de estructura de §3 contra el árbol en disco y se cerró la desviación que había abierto el trabajo de síntesis: se añadieron ComponentSynthesizer, IEEEPrimitives y SelectionFilter al listado del espacio de nombres Interpreter.Synthesis; se añadieron los archivos de sentencias de control ReportStatement / AssertStatement / WaitForStatement (todos ISControl) a la jerarquía de clases; y se añadieron las entradas más recientes de Services/ (FunctionRegistry, PackageSubtypeScanner, SourceElaborator, ReportBus, ParseTrace, SkipDiagnosticLog, IEEELibraryLoader) al listado de servicios. Sin cambios de código fuente — solo documentación; las narrativas detalladas de características a continuación (report/assert del Ring 5, reducción IEEE de v1.15–v1.19, endurecimiento de prácticas) ya estaban al día.

v1.14.1 - Realineación de la documentación (2026-05-01)

Sincronización de la documentación

  • Bloque de Estado Actual de §2: Se reemplazó la instantánea de la v0.15.0 (que aún afirmaba una tasa de aprobación del 80 % / 16 de 20 como si estuviera vigente) con un inventario de características que refleja la superficie real de hoy: planificador completo de ciclos delta, espacio de nombres Synthesis/, fachada SimulationRunner, infraestructura de tipos personalizados, estimación de recursos de FPGA, canalización de progreso + cancelación. La cifra histórica de la tasa de aprobación se conserva en este registro de cambios, pero ya no se presenta como el estado actual.
  • Diagrama de la Jerarquía de Clases de §3: Se añadió todo el espacio de nombres Interpreter.Synthesis (Synthesizer, SynthesisContext, ConcurrentAssignSynthesizer, ConditionalAssignSynthesizer, SelectedAssignSynthesizer, ProcessSynthesizer, records / enums de Models.cs, Layout/LayeredLayout), además de la nueva fachada SimulationRunner y el trío de modelos SimulationConfig / ClockConfig / StimulusEvent. Se marcó Services/DataStructure.cs explícitamente como marcador de posición para que su presencia en el árbol de archivos no induzca a error.
  • Resumen de Componentes Clave de §3: Se añadieron descripciones de una línea correspondientes para SimulationRunner, SimulationConfig (+ tipos auxiliares), los seis sintetizadores, SynthesisContext, Synthesis/Models.cs y LayeredLayout. Se anotó que SimulationRunner es el punto de entrada usado por Kmila.Shared.Services.SimulationCoordinator.
  • Constansts.cs: Se documentó el error tipográfico histórico del nombre de archivo para que los futuros lectores no intenten "corregirlo" y rompan las referencias.
  • Sin cambios a nivel de código fuente en esta versión; la superficie pública del Intérprete de la v1.14.0 no ha cambiado.

v0.15.1 - Documentación exhaustiva del código (2026-02-21)

Mejoras de la documentación

  • Interfaces: Se añadió documentación XML a los 7 archivos de interfaz (IArithOperation, IControl, IFinder, ILogicOperation, IOperation, ISControl, ISYard)
  • Modelos: Se añadió documentación XML a los 8 archivos de modelo (TypeDefinition, Constansts, ControlExceptions, ParameterDefinition, FunctionDefinition, ProcedureDefinition, SimulationConfig, DeferredFunction)
  • Repositorios: Se añadió documentación XML a los 17 archivos de repositorio, incluidos Executer, ShuntingYard, todas las estructuras de control y los manejadores de sentencias
  • Servicios: Se añadió documentación XML a los 21 archivos de servicio, incluidos DeltaCycleEngine, SimulationRunner, Architecture, Entity, Process y todos los registros/manejadores
  • Pruebas: Se añadió documentación XML a VcdComparison, DeltaCycleEngineTests y run_delta_tests

v0.15.0 - Versión de documentación y estabilidad (2026-01-17)

Mejoras de la documentación

  • Actualizaciones del README:

    • Se actualizó la versión a 0.15.0 y la fecha al 17 de enero de 2026
    • Se revisó la sección de Estado Actual para reflejar una tasa de aprobación de pruebas del 80 %
    • Se actualizó la sección de resultados de pruebas con las pruebas actuales aprobadas/reprobadas
    • Se añadió una nota sobre el soporte de cancelación para análisis de larga duración
  • Mejoras de la documentación del código:

    • Se mejoró la documentación XML en las clases de Repository
    • Se añadieron comentarios exhaustivos a nivel de clase con ejemplos de sintaxis VHDL
    • Se documentaron las mejoras de v0.13.0+ en las clases de estructuras de control

Estabilidad y mantenimiento

  • Alineación de versión: La versión de la rama v0.15.0 ahora coincide con la documentación
  • Consistencia de la documentación: Todas las entradas del registro de cambios correctamente fechadas
  • Documentación de la cobertura de pruebas: Actualizada para reflejar la tasa de aprobación actual de 16/20 (80 %)

v0.16.1 - Actualizaciones de diagramas (2025-12-13)

Actualizaciones de diagramas y documentación

  • Jerarquía de clases: Actualizada para incluir todas las clases de Repository:

    • Se añadió Executer bajo las implementaciones de la interfaz ISControl
    • Se añadió la interfaz IFinder con la implementación LiteralsFinder
    • Se añadió la sección de Clases de Utilidad con OperationDetector y ShuntingYard
  • Resumen de componentes clave: Se añadieron las descripciones de componentes faltantes:

    • Executer.cs - Orquestación de tareas con soporte de pausa/reanudación/cancelación
    • LiteralsFinder.cs - Identificación y clasificación de valores literales
    • OperationDetector.cs - Detección del tipo de operación a partir de la entrada en bruto

v0.16.0 - Versión de mejora de documentación (2025-12-13)

Mejoras de la documentación

  • DataStructure.cs: Se añadió documentación XML exhaustiva que explica:

    • El papel de la clase como marcador de posición para futuras operaciones de estructuras de datos VHDL
    • Casos de uso potenciales (tipos record, agregados de arreglos, operaciones de tipos personalizados)
    • Estado actual y relaciones con otras clases
    • Referencias cruzadas a clases relacionadas (TypeDefinition, TypeRegistry, VariableSyntetizer)
  • Actualizaciones del README:

    • Se actualizó el número de versión y la fecha de última actualización
    • Se añadió esta entrada del registro de cambios que documenta las mejoras de la documentación
    • Historial de versiones debidamente mantenido

Documentación de la interacción de componentes

Las siguientes interacciones ahora están mejor documentadas en la base de código:

Componente Interactúa con Propósito
DataStructure TypeDefinition, TypeRegistry Reservado para operaciones complejas de estructuras de datos
TypeRegistry TypeDefinition, VariableSyntetizer Almacena y recupera definiciones de tipos personalizados de VHDL
TypeDefinition TypeRegistry Representa definiciones de enumeración, arreglo, registro y subtipo
VariableSyntetizer Todas las clases relacionadas con tipos Analiza declaraciones de señal/variable con tipos personalizados

Notas de diseño

Esta versión se centra en mejorar la mantenibilidad del código mediante una mejor documentación. La clase DataStructure se ha documentado explícitamente como marcador de posición para evitar confusión sobre su implementación vacía. Las versiones futuras podrían implementar esta clase cuando se necesiten operaciones avanzadas de estructuras de datos (acceso a campos de record, manipulación de arreglos) más allá de lo que proporcionan las clases existentes.


v0.13.0 - Correcciones de regex, estimación de recursos, tipos personalizados, cancelación y correcciones de memoria (2025-12-09)

Nuevas características

  • Estimación de recursos de hardware (Services/Resources.cs): NUEVO módulo para la estimación del uso de recursos de FPGA

    • Conteo de flip-flops a partir de procesos con reloj (~95 % de precisión)
    • Conteo de pines de E/S a partir de los puertos de la entidad (100 % exacto)
    • Detección de dominios de reloj a partir del uso de rising_edge/falling_edge (100 % exacto)
    • Marco de trabajo para la estimación de DSP, BRAM, LUT (implementación en progreso)
    • Cálculo automático cuando la arquitectura se adjunta a la entidad
    • Ejemplo de salida: Resources: FF=8, LUT=0, DSP=0, BRAM=0.0, IO=18, CLK=1
  • Soporte de tipos personalizados (Models/TypeDefinition.cs, Services/TypeRegistry.cs): NUEVA infraestructura para las declaraciones de tipos personalizados de VHDL

    • Modelo TypeDefinition que soporta los tipos Enumeration, Array, Record y Subtype
    • TypeRegistry para almacenar y consultar tipos personalizados dentro del ámbito de la arquitectura
    • Análisis completo de declaraciones de tipo en VariableSyntetizer.ParseTypeDeclaration()
    • Sintaxis soportada:
      • Enumeración: type state_t is (IDLE, RUN, STOP);
      • Arreglo: type memory_t is array (0 to 255) of std_logic;
      • Record: type packet_t is record valid: std_logic; end record;
    • Cálculo automático de tamaño y generación de valores por defecto
    • Las señales que usan tipos personalizados ahora se dimensionan e inicializan correctamente
  • Sistema de reporte de progreso (Services/ProgressReporter.cs): NUEVO seguimiento de progreso basado en eventos para la integración con la interfaz de usuario

    • Singleton ProgressReporter.Instance para acceso global
    • Eventos: ProgressChanged, ErrorOccurred, Completed, CancellationRequested
    • Salida de tupla de progreso: (float Progress, string Message) mediante e.Update.ToTuple()
    • Visualización de barra de progreso ASCII con iconos emoji
    • Fases de procesamiento: Tokenizing, ParsingEntity, ParsingPorts, ParsingArchitecture, etc.
    • Reportadores con ámbito para suboperaciones (CreateScope(), CreateItemScope())
    • Opciones de línea de comandos: -v/--verbose para progreso detallado, -p/--progress para barra compacta
    • Integrado en el análisis de Entity, Architecture y Process
  • Soporte de CancellationToken: Cancelación elegante de operaciones en toda la canalización de análisis

    • Métodos de ProgressReporter: StartCancellableOperation(), RequestCancellation(), ThrowIfCancellationRequested(), CheckCancellation()
    • Evento CancellationRequested para notificación cuando se dispara la cancelación
    • Manejo de Ctrl+C en Program.cs para la cancelación de la aplicación de consola
    • Parámetro CancellationToken añadido a todos los métodos principales de análisis:
      • Entity.Syntetize(string[], CancellationToken)
      • Architecture.Syntetize(string[], CancellationToken)
      • Process.Syntetize(CancellationToken)
      • VariableSyntetizer.Syntetize(CancellationToken)
    • Verificaciones de cancelación en cada iteración de bucle para una cancelación responsiva
    • El manejo elegante de OperationCanceledException evita los bloqueos
    • Útil para detener el procesamiento de archivos VHDL grandes (por ejemplo, FPGA_KILLER)
  • Soporte de operadores de desplazamiento/rotación (Services/Operator.cs): Implementación completa de los operadores de desplazamiento y rotación de VHDL

    • sll - Desplazamiento lógico a la izquierda (rellena con '0')
    • srl - Desplazamiento lógico a la derecha (rellena con '0')
    • sla - Desplazamiento aritmético a la izquierda (rellena con el bit más a la derecha)
    • sra - Desplazamiento aritmético a la derecha (rellena con el bit de signo)
    • rol - Rotación a la izquierda
    • ror - Rotación a la derecha
    • Añadidos al enum PrecedencesLogicOperators en Models/Constansts.cs
    • Añadidas definiciones de precedencia en Operator.DefinePrecedence()
    • Implementado SolveShiftOperation() para operaciones vectoriales
  • Manejo de declaraciones de constantes (Services/VariableSyntetizer.cs): Análisis mejorado de constantes

    • HasFunctionCallInConstant() - detección por anticipación (lookahead) de llamadas a funciones en la inicialización de constantes
    • SkipConstantDeclaration() - omite de forma segura las constantes con inicializaciones que contienen llamadas a funciones
    • Las constantes simples (por ejemplo, constant X := "0101";) se siguen analizando normalmente
    • Las constantes complejas con llamadas a funciones (por ejemplo, constant Y := clog2(N);) se omiten para evitar errores de análisis
    • Test19 ahora pasa (antes fallaba con una constante con inicialización de agregado)

Correcciones de fugas de memoria

  • Se corrigió un bucle infinito en SkipTypeDeclaration() (Services/VariableSyntetizer.cs): Las declaraciones de tipo de arreglo como type big_mem_t is array (0 to 199999) of STD_LOGIC; causaban bucles infinitos y agotamiento de memoria. Se añadió:

    • Seguimiento de parenDepth para los límites del arreglo
    • Límite de seguridad MAX_ITERATIONS = 10000
    • Romper solo con ; cuando parenDepth == 0
  • Se corrigió una fuga de memoria de LoggerFactory (Parser/Repositories/TokenStream.cs): Cada instancia de TokenStream creaba un nuevo LoggerFactory. Se cambió a una instancia estática compartida.

  • Se corrigió la asignación de Regex en la clase Operation (Services/Operation.cs): Cada instancia de Operation creaba 4 nuevos objetos Regex. Se cambió a instancias estáticas compiladas y compartidas.

  • Se corrigió la asignación de lista en un bucle (Services/Architecture.cs): La List<Variable> comparators se asignaba en cada iteración del bucle de SyntetizeBehaviour(). Se movió fuera del bucle y se cambió a HashSet<Variable>.

Correcciones de errores críticos

  • Se corrigió el setter de Variable.Type (Parser/Models/Variable.cs): Se eliminó la inicialización prematura _value = "U" para STD_LOGIC_VECTOR que hacía que el setter de Size calculara mal los anchos de los vectores (Size se establecía en 1 en lugar del ancho de bits real, como 8)

  • Se corrigió el setter de Variable.Size (Parser/Models/Variable.cs): Se cambió if (Value == null) por if (_value == null) para evitar que el getter de Value devolviera new() durante la inicialización del objeto

  • Se corrigió el dimensionamiento de puertos STD_LOGIC (Services/VariableSyntetizer.cs): Se añadió VectorSize = 1 por defecto para los tipos STD_LOGIC y BIT (las señales de un solo bit mostraban Size=0)

  • Se corrigió la detección del fin de arquitectura (Program.cs): Ahora verifica tanto el nombre de la arquitectura COMO el nombre de la entidad en las sentencias end (soporta tanto end behavioral; como end example;)

  • Se corrigió el orden de inicialización de propiedades (Services/VariableSyntetizer.cs): Se estableció Size antes que Value en el inicializador del objeto Variable para evitar que el setter de Value interfiriera con el cálculo de Size

Correcciones de errores del analizador (Parser)

  • Se corrigieron los patrones de regex de los operadores (Parser/Models/Constants.cs):

    • ASSIGNATION_OPERATORS: Se cambió de @"^[(\<\=)|\:\=]" a @"^((\<\=)|\:\=)$" - Se corrigió el corchete que creaba una clase de caracteres que incluía (
    • COMPARATION_OPERATORS: Se cambió de @"^[=|<|>|!=]" a @"^(=|!=|/=|<|>|<=|>=)$" - Se corrigió la clase de caracteres y se completó el conjunto de operadores
    • LOGIC_OPERATORS: Se cambió de "(and|or|xor|nand|nor|not)" a @"^(and|or|xor|nand|nor|not|xnor|abs)$" - Se añadieron anclas para evitar coincidencias parciales
    • ARITH_OPERATORS: Se cambió de @"(\+|\-|\*\*|\*|\/|mod)" a @"^(\+|\-|\*\*|\*|\/|mod)$" - Se añadieron anclas
  • Se corrigió la detección del operador NOT unario (Services/Operation.cs): Se añadió una verificación de tipo adecuada para asegurar que el operador ** (potencia) con PrecedencesArithOperators.UNARY no se trate como PrecedencesLogicOperators.NOT

  • Se corrigió la conversión de precedencia en ShuntingYard (Repositories/ShuntingYard.cs:47): Se cambió de la conversión directa (int)operation.Precedence a Convert.ToInt32() para manejar la conversión de enum a int cuando Precedence se almacena como object

  • Se añadió la detección de procesos con etiqueta (Services/Architecture.cs): Se añadió soporte para la sintaxis label : process(...) donde la etiqueta va antes de la palabra clave process

  • Se corrigió la verificación de ámbito de variables (Services/VariableSyntetizer.cs): Se eliminó la verificación errónea que impedía las declaraciones de variables locales en los procesos

  • Se añadió el manejo de palabras clave en expresiones (Services/Operation.cs): Se añadió el manejo explícito para omitir las palabras clave downto, to, when y else durante la síntesis de expresiones

  • Se corrigió el análisis de funciones/procedimientos (Program.cs): Se añadió el seguimiento de funcProcDepth para evitar que end FunctionName; se confunda con el fin de la arquitectura

  • Se mejoró la seguridad de ToInteger (Services/StandardFunctions.cs): Se añadieron verificaciones para valores vacíos, 'U', 'X', 'Z' y un manejo de errores adecuado

  • Se corrigió el manejo de la palabra clave bus (Services/VariableSyntetizer.cs): Se añadió soporte para declaraciones de señales protegidas (guarded) con la palabra clave bus (por ejemplo, signal x : std_logic_vector bus := ...;)

  • Se corrigieron las declaraciones de señales de tipo personalizado (Services/VariableSyntetizer.cs): Los tipos personalizados (por ejemplo, reg_array_t) ahora se reconocen correctamente como IDENTIFIERS en lugar de KEYWORDS durante el análisis de la declaración de señales

  • Se corrigió el manejo de la sentencia null de VHDL (Repositories/CaseStructure.cs): Se añadió el manejo explícito para las sentencias sin operación null; en las ramas case

  • Se corrigió el manejo de identificadores no declarados (Services/Operation.cs): Los identificadores no declarados ahora crean variables stub en lugar de lanzar excepciones, permitiendo que el análisis continúe con elegancia

  • Se corrigió el manejo de discordancia de tipos (Services/Operator.cs): Las operaciones que involucran variables stub (identificadores no declarados) ahora omiten la verificación estricta de tipos, devolviendo valores por defecto

  • Se corrigió el análisis del rango del bucle for (Repositories/ForLoopStructure.cs): ParseRangeValue() ahora maneja expresiones como TREE_DEPTH - 1, no solo enteros simples

  • Se corrigió la seguridad ante nulos de Variable (Parser/Models/Variable.cs): Se añadieron verificaciones de nulos a todas las sobrecargas de operadores para evitar NullReferenceException

  • Se añadieron guardas de iteración: Se añadieron límites máximos de iteración para evitar bucles infinitos en:

    • Process.SyntetizeBehaviour() - 10000 iteraciones
    • Operation.Syntetize() - 5000 iteraciones
    • IfStructure.SyntetizeBranchBody() - 5000 iteraciones
    • ForLoopStructure.SyntetizeLoopBody() - 5000 iteraciones
    • Varios métodos de análisis de asignaciones - 1000 iteraciones

Nuevas características

  • Soporte de sentencias case en ramas If (Repositories/IfStructure.cs): Se añadió el manejo de case en SyntetizeBranchBody() - las sentencias case ahora se pueden anidar dentro de ramas if/elsif/else

  • Omisión de instanciaciones de componentes (Services/Architecture.cs): Se añadió el método SkipComponentInstantiation() para manejar la sintaxis label : ComponentName [generic map (...)] port map (...);

  • Omisión de expresiones de puerto/genérico (Services/VariableSyntetizer.cs): Se añadió un manejo especial para puertos y genéricos con expresiones complejas (por ejemplo, (NUM_INPUTS * DATA_WIDTH) - 1) - omite la evaluación y usa el tamaño por defecto

Resultados de las pruebas

  • Pruebas aprobadas: 13/20 pruebas base (65 %) + demo de estimación de recursos
  • Recién aprobadas: test8, test9, test10, test12, test99_example (demo de recursos)
  • Regresiones: test13, test14, test16, test19 (nuevos errores por los cambios en el análisis de expresiones - requiere correcciones adicionales)
  • Fallos restantes: test11, test17, test18, test20 (problemas de posicionamiento de tokens de entidad)

Nota: El conteo de pruebas disminuyó de 16→13 debido a un análisis más estricto de expresiones vectoriales que reveló casos límite con operadores de desplazamiento y expresiones complejas que antes se analizaban incorrectamente.

Documentación

  • Documentación de código en línea exhaustiva con anotaciones de v0.13.0
  • README mejorado con decisiones de diseño, guía de pruebas y resolución de problemas
  • Toda la documentación consolidada en un solo README para una mejor mantenibilidad
  • Se añadió la sección de Limitaciones Conocidas con explicaciones específicas de los mensajes de error
  • Se documentaron las construcciones VHDL no soportadas: tipos personalizados, agregados, bloques generate, atributos de rango de señal

Versiones anteriores

v0.12.0 - Implementación de la estructura If (2025-11-26)

Características:

  • Soporte completo de if-elsif-else con múltiples ramas
  • Clase auxiliar IfBranch para la representación de ramas
  • Sentencias if anidadas dentro de cualquier rama

Correcciones de errores:

  • Se corrigió la condición de terminación de Architecture.SyntetizeAssingations()
  • Se corrigió la detección del fin de arquitectura en Program.cs
  • Se añadió el método ConsumeEndIf() para el consumo adecuado de tokens

Resultados de las pruebas: 7/20 aprobadas (35 %)


Dependencias

Módulos requeridos

  • Módulo Analizador (Parser) (/mnt/ssd1tb/Codigos/Kmila-9s/Parser/) - Proporciona la tokenización, los modelos de entidad/arquitectura y las constantes
  • .NET 9.0 - Framework de destino con las características del lenguaje C# 13

Dependencias opcionales

  • TimeMachine.dll - Para la simulación basada en el tiempo y la gestión de ciclos de reloj

Limitaciones conocidas

Las siguientes características de VHDL requieren desarrollo adicional:

A nivel del analizador (Parser):

  • Atributos de señal con 'range - for i in inputs'range loop
  • Segmentación (slicing) de arreglos con expresiones complejas

Totalmente soportado (Parser + Intérprete):

  • Agregados de VHDL - (others => '0'), (others => '1') ✓ Soportado
  • Declaraciones de tipos personalizados - Tipos de enumeración, arreglo, record y subtipo ✓ Soportado mediante TypeDefinition, TypeRegistry
  • Soporte de literales de tiempo - fs, ps, ns, us, ms, sec, min, hr ✓ Soportado por el analizador

A nivel del intérprete (módulo actual):

  • Ejecución de sentencias generate - for-generate e if-generate se analizan pero pueden agotar el tiempo de espera en diseños complejos
  • Simulación completa de jerarquía estructural - la instanciación de componentes se detecta pero no se simula por completo
  • Funciones de resolución de bus - afecta a test18
  • Estimación de DSP/BRAM/LUT - el marco de trabajo existe en Resources.cs pero la implementación está pendiente

Mensajes de error

"Expected type: KEYWORDS, found: -> at Line:"

  • Este error ocurre típicamente con construcciones VHDL no soportadas:
    • Declaraciones de type personalizado (type memory_t is array...)
    • Bloques generate (for i in 0 to N generate)
    • Agregados ((others => '0'))
    • Atributos de rango de señal (signal'range)
  • Solución alternativa: Simplificar el código VHDL para usar construcciones soportadas o esperar futuras actualizaciones del analizador

Hoja de ruta futura

Próxima versión (planificada)

  • Corregir los 4 fallos de prueba restantes (test11, test13, test16, test20)
  • Optimizar las rutas de análisis profundas que causan tiempos de espera agotados
  • Mejorar los mensajes de error con mejor contexto
  • Soporte completo de la ejecución de sentencias generate

v1.0.0 (a largo plazo)

  • Cobertura completa de VHDL-93
  • Soporte parcial de VHDL-2008
  • Integración de un depurador interactivo
  • Simulación completa de jerarquía estructural
  • Estimación completa de recursos de FPGA (implementaciones de DSP, BRAM, LUT)

Diagramas de auditoría (2026-05-09)

El README del Intérprete antes no tenía diagramas Mermaid (solo árboles ASCII). La auditoría identificó que varios comportamientos del motor de simulación estaban mal representados en TT1 (fig_5_5_10, fig_4_5) o completamente sin documentar. Las fuentes también residen en Documentacion/TT1/diagrams/.

fig_5_5_10_clases_motor_simulacion — Diagrama de clases del motor (reescrito)

classDiagram
    direction TB

    class SimulationRunner {
        +DeltaCycleEngine Engine
        +bool IsRunning
        +bool IsPaused
        +RunAsync(SimulationConfig) Task
        +Pause() void
        +Resume() void
        +Stop() void
        +OnProgressChanged EventHandler
        +OnSimulationEnded EventHandler
        +OnError EventHandler
    }

    class DeltaCycleEngine {
        -int _currentTick
        -int _currentDelta
        -int MAX_DELTA_CYCLES
        +SignalHistory History
        +Initialize(signals, tasks) void
        +RunTimeStepAsync() Task~bool~
        +RunDeltaCycleAsync() Task~bool~
        +AdvanceTime(tick) void
        +ConnectToTimeMachine(timer) void
    }

    class ProcessScheduler {
        -Dictionary _sensitivityMap
        -List _allProcesses
        -Queue _pending
        +RegisterProcess(p) void
        +InitializeAllProcesses() void
        +NotifySignalChanges(changed) void
        +GetPendingProcesses() IEnumerable
    }

    class SignalScheduler {
        -Dictionary _currentDeltaUpdates
        -Dictionary _futureUpdates
        +Schedule(signal, value) void
        +ScheduleAfter(signal, value, ticks) void
        +ApplyCurrentUpdates() ISet~Variable~
        +AdvanceTime(tick) void
    }

    class SignalHistory {
        +RegisterSignal(signal, time, record) void
        +RecordChanges(signals, time) void
        +ExportToVcd(module, ns) string
        +SaveToVcd(path, module, ns) void
        +ExportToCsv() string
    }

    class SignalAttributeHandler {
        <<singleton>>
        +RegisterSignal(signal) void
        +UpdateAllPreviousValues() void
        +HasEvent(signal) bool
        +LastValue(signal) object
    }

    class ProcessSynthesizer {
        +Synthesize(entity) Netlist
    }

    class SimulationConfig {
        +Entity Entity
        +int TotalTicks
        +List Clocks
        +List Stimuli
    }

    SimulationRunner "1" *-- "1" DeltaCycleEngine : compone
    DeltaCycleEngine "1" *-- "1" SignalHistory : registra en
    DeltaCycleEngine "1" *-- "1" ProcessScheduler : delega procesos
    DeltaCycleEngine "1" *-- "1" SignalScheduler : delega senales
    DeltaCycleEngine ..> SignalAttributeHandler : consulta atributos
    SimulationRunner ..> SimulationConfig : recibe
    ProcessSynthesizer ..> SignalHistory : independiente

fig_5_5_11_clases_planificadores — Detalle de planificadores (nuevo)

classDiagram
    direction TB

    class ProcessScheduler {
        -Dictionary~Variable, List~Process~~ _sensitivityMap
        -List~Process~ _allProcesses
        -HashSet~Process~ _pending
        +RegisterProcess(p: Process) void
        +InitializeAllProcesses() void
        +NotifySignalChanges(changed: ISet) void
        +GetPendingProcesses() IEnumerable~Process~
        +HasPendingProcesses bool
    }

    class SignalScheduler {
        -Dictionary~Variable, object~ _currentDeltaUpdates
        -SortedDictionary~int, Dictionary~ _futureUpdates
        +Schedule(signal, value) void
        +ScheduleAfter(signal, value, ticks) void
        +ApplyCurrentUpdates() ISet~Variable~
        +AdvanceTime(tick) void
        +HasPendingUpdates bool
    }

    class ScheduledOperation {
        +IOperation Source
        +Execute() void
    }

    class Process {
        +List~Variable~ SensitiveList
        +List~Variable~ SignalsPorts
        +List~ITask~ Tasks
        +SyntentizeSensitiveList(tokens) void
    }

    class Variable {
        +string Name
        +DATATYPES Type
        +object Value
        +event OnValueChange
    }

    ProcessScheduler "1" o-- "*" Process : registra
    ProcessScheduler "1" --> "*" Variable : indexa por sensibilidad
    SignalScheduler "1" --> "*" Variable : programa updates
    SignalScheduler "1" *-- "*" ScheduledOperation : cachea
    Process "1" o-- "*" Variable : SensitiveList

fig_5_8_4_seq_resolucion_sensitivity — Resolución de la lista de sensibilidad (nuevo)

sequenceDiagram
    participant DCE as DeltaCycleEngine
    participant P as Process
    participant SP as SignalsPorts (scope)
    participant PS as ProcessScheduler
    participant SAH as SignalAttributeHandler

    Note over DCE: Elaboracion (Initialize)
    DCE->>P: VariableSyntetizer (declaraciones)
    DCE->>P: SyntentizeSensitiveList(rawTokens)
    loop Por cada nombre en lista de sensibilidad
        P->>SP: Buscar Variable por nombre
        SP-->>P: Variable referencia (o stub si no existe)
        P->>P: Agregar a SensitiveList
    end
    DCE->>SAH: RegisterSignal por cada senal
    DCE->>PS: RegisterProcess(P)
    PS->>PS: Por cada signal en P.SensitiveList:<br/>_sensitivityMap[signal].Add(P)

    Note over DCE: Tiempo t=0
    DCE->>PS: InitializeAllProcesses
    PS-->>DCE: Todos los Process en pendientes
    DCE->>P: ExecuteProcessWithSchedulingAsync
    P-->>DCE: Cambios de senal (en SignalScheduler)

    Note over DCE: Cambio de senal mas tarde
    DCE->>PS: NotifySignalChanges(changedSignals)
    PS->>PS: Para cada signal:<br/>marcar Process en _sensitivityMap[signal]<br/>como pending para siguiente delta

fig_5_11_10_state_delta_cycle — Máquina de estados del ciclo delta (nuevo)

stateDiagram-v2
    [*] --> Idle
    Idle --> Initializing : RunAsync(config)
    Initializing --> TimeStep : Initialize completo
    TimeStep --> DeltaCycle : InitializeAllProcesses (t=0)<br/>or procesos pendientes

    state DeltaCycle {
        [*] --> ExecPending
        ExecPending --> ExecConcurrent : pending procesados
        ExecConcurrent --> ApplyUpdates : asignaciones encoladas
        ApplyUpdates --> RecordHistory : SignalScheduler.ApplyCurrentUpdates
        RecordHistory --> NotifyScheduler : SignalHistory.RecordChanges
        NotifyScheduler --> [*] : ProcessScheduler.NotifySignalChanges
    }

    DeltaCycle --> DeltaCycle : HasPendingProcesses<br/>or HasPendingUpdates<br/>(_currentDelta < MAX)
    DeltaCycle --> Aborted : _currentDelta > MAX_DELTA_CYCLES<br/>(bucle combinacional)
    DeltaCycle --> AdvanceTick : Estable (sin pendientes)
    AdvanceTick --> TimeStep : tick < TotalTicks
    AdvanceTick --> Done : tick = TotalTicks
    Done --> [*] : OnSimulationEnded
    Aborted --> [*] : OnError

    Idle --> Paused : OnPaused(true) (TimeMachine)
    Paused --> Idle : OnPaused(false)

fig_5_10_1_seq_vcd_id_alloc — Asignación de identificadores VCD (nuevo)

sequenceDiagram
    participant SR as SimulationRunner
    participant SH as SignalHistory
    participant Map as signalIds (Dict)
    participant FS as Filesystem

    SR->>SH: ExportToVcd(moduleName, timescaleNs)
    SH->>SH: Escribir $date / $version / $timescale
    SH->>Map: idCounter = 0
    loop Por cada Senal ordenada por nombre
        SH->>SH: GenerateVcdId(idCounter)
        Note right of SH: ASCII 33..126 con<br/>wraparound: !, ", #, ...,<br/>~, !!, "!, ...
        SH->>Map: signalIds[signal] = id
        SH->>SH: Emitir $var wire/reg width id name $end
        SH->>SH: idCounter++
    end
    SH->>SH: Escribir $dumpvars (valor inicial por senal)
    loop Por cada tiempo unico en el historial
        SH->>SH: Emitir #tiempo (en timescaleNs)
        loop Senales con cambio en este tiempo
            SH->>Map: Lookup id por senal
            Map-->>SH: id
            SH->>SH: Emitir nuevoValor + id
        end
    end
    SH-->>SR: string VCD
    SR->>FS: SaveToVcd(path) escribe string

fig_4_5_dominio_simulacion — Dominio de simulación (reescrito)

classDiagram
    direction TB

    class ConfigSimulacion {
        +Int totalTicks
        +Int timescaleNs
        +List clocks
        +List stimuli
    }
    class DispositivoFPGA {
        +String familia
        +String fabricante
        +Int celdasLogicas
    }
    class Senal {
        +String nombre
        +Tipo tipo
        +Int ancho
    }
    class HistorialDeSenales {
        +Map~Senal, List~ transiciones
        +Int tickActual
    }
    class FormaDeOnda {
        +List valoresTemporales
    }
    class VisualizadorDeOndas {
        +List senalesMostradas
        +Int zoom
    }
    class ArchivoVCD {
        +String contenido
        +Int timescaleNs
    }

    ConfigSimulacion "1" --> "0..1" DispositivoFPGA : se calcula sobre
    ConfigSimulacion "1" --> "0..*" Senal : estimula
    HistorialDeSenales "1" --> "0..*" Senal : muestrea
    Senal "1" --> "1" FormaDeOnda : produce
    VisualizadorDeOndas "1" ..> "0..*" FormaDeOnda : presenta
    HistorialDeSenales "1" --> "1" ArchivoVCD : exporta a

Registro de cambios

Barrido de observabilidad del Ring 5 — report / procedure / to_string (2026-07-18)

Sesión de seguimiento que hizo que los reportes de los testbench de VHDL realmente se ejecutaran y que su barrido de marcadores case fuera legible mediante el log[] de la respuesta de /api/v1/simulate. Cada corrección es aditiva: rutas por defecto sin cambios, 121/121 de SimulatorBench idénticos byte a byte.

  • F-G-11 — Las sentencias report de VHDL se ejecutan + afloran mediante el log de la API. Nuevo Interpreter/Services/ReportBus.cs (sumidero de eventos singleton); Interpreter/Repositories/ReportStatement.cs implementa ISControl con captura de expresión en tiempo de análisis (rastreando insideString para que un ; dentro de un literal de cadena no termine prematuramente) y renderizado en tiempo de ejecución mediante ReportBus.Emit. El case "report" de Process.SyntetizeBehaviour ahora crea una tarea ReportStatement en lugar de saltar al ;. SimulationRunner.RunAsync conecta ReportBus.OnReport a un evento OnReport con ámbito de instancia durante la vida de la ejecución, desmontándolo en finally para que el bus estático no acumule oyentes obsoletos. HeadlessSimulator.RunAsync se suscribe y añade [report] <msg> al log de la respuesta. Antes de F-G-11, cada report de VHDL era descartado silenciosamente por SkipToSemicolonInclusive.

  • F-G-11b — ejecución de report anidado. Los cinco despachadores de cuerpo de subestructura (WhileLoop / ForLoop / SimpleLoop / If / Case) también crean tareas ReportStatement ahora en lugar de saltarlas. assert aún se salta en los seis (candidato a F-G-16).

  • F-G-12 — MVP de inserción (inlining) de llamadas a procedimientos. Interpreter/Services/VariableSyntetizer.cs: se reescribió SkipProcedureDeclaration para capturar la lista de parámetros (mediante el TryParseParameterList existente) y los tokens del cuerpo entre begin y una coincidencia de dos tokens end procedure / end <name>. La coincidencia de dos tokens es naturalmente segura ante cadenas — un end desnudo dentro de un fragmento report "…" es seguido por más fragmentos de cadena, no por procedure/<name>. Registra una FunctionDefinition completa (Parameters + BodyRaw, IsPure=false) en FunctionRegistry. Interpreter/Repositories/ProcedureCallStatement.cs (andamiaje preexistente, ahora conectado): Syntetize captura los tokens de argumentos posicionales (respetando el estado de cadena para que , y ) dentro de un literal no disparen la lógica de límite de argumentos). ExecuteAsync recorre BodyRaw, sustituye los nombres de parámetros con los tokens de argumento por cada report … ;, y emite mediante ReportBus. Las asignaciones de señales, wait, las llamadas anidadas y la evaluación de funciones dentro de los reportes NO se modelan — la inserción completa es territorio de F-G-14 + F-G-13. La rama else de Process.SyntetizeBehaviour despacha mediante ProcedureCallStatement.IsProcedureCall antes de caer a SyntetizeAssignment. También se corrigió SourceElaborator.StripLineComments (era ciego a los literales de cadena — report "-------- " & name & … veía su contenido truncado en el primer -- dentro del mensaje).

  • F-G-12a — tres errores fuertemente acoplados de tokenizador / recorrido. Aflorados al finalmente ejecutar de verdad los cuerpos de los procedimientos.

    1. TokenizeSpaces de Parser/Repositories/Tokenizer.cs era ciego a las cadenas: if (line.Contains("--")) line = line.Split("--")[0]; truncaba report "-------- " en el primer -- dentro de la cadena. El nuevo helper FindTrailingLineCommentStart recorre la línea rastreando una bandera insideString conmutada por " y devuelve el índice de byte de un -- fuera de cualquier literal.
    2. El TokenizeSpecialCharacters del mismo tokenizador tenía una red de seguridad por token if (line.StartsWith("--")) return new(); que se disparaba durante la división recursiva por carácter de "--------", descartando la subcadena intermedia de 7 guiones. Ahora se eliminó, dado que el despojado a nivel de línea es consciente de las comillas.
    3. El break del recorrido interno de ProcedureCallStatement.ExecuteAsync era i++; break; mientras que el for exterior también hacía i++, saltando un token más allá de cada ;. Todos los report alternos en un cuerpo con múltiples reportes se perdían. Se corrigió dejando i EN el ;.
  • F-G-15 — evaluación de to_string(sig) / to_string(sig(hi downto|to lo)) en las expresiones de report. Nuevo estático compartido Interpreter/Services/ParseTrace.RenderReportTokens(tokens, variables) consolida la lógica de renderizado previamente duplicada entre ReportStatement.ExecuteAsync y ProcedureCallStatement. El comparador de patrones en línea TryEvalToString reconoce las formas de 4 tokens (to_string ( name )) y de 9 tokens (to_string ( name ( hi downto lo ) )); resuelve la señal mediante el ámbito de variables; extrae el segmento (slice) como raw.Substring(size-1-high, high-low+1) (Kmila almacena los valores de std_logic_vector como cadenas con el índice 0 = carácter más a la izquierda/MSB). Recurre a la emisión del texto fuente ante cualquier señal desconocida / segmento fuera de límites / dirección no reconocida. Los reportes de LCD end_case del Ring 5 ahora renderizan los valores de bits reales en lugar del texto de la expresión fuente.

Referencia: ejecución de la sentencia report de extremo a extremo

Source .vhd:                        report "TB: reset released";
      │
      ▼
Parser.ParseFile                 (line-based state machine)
      │
      ▼
Architecture.Syntetize           (per-arch)
      │
      ▼
VariableSyntetizer.Syntetize     (declarative region)
      │
      ▼
Architecture.SyntetizeBehaviour  (body)
      │
      ├─ case "process" ──► Process.Syntetize
      │                          │
      │                          ▼
      │                     Process.SyntetizeBehaviour
      │                          │
      │                          ├─ case "report" ──► new ReportStatement + Syntetize + Tasks.Add
      │                          ├─ case "procedure_call" ──► new ProcedureCallStatement + Syntetize + Tasks.Add
      │                          └─ …
      │
      ▼
DeltaCycleEngine.RunTimeStep     (loops until stable)
      │
      ▼
ExecuteProcessWithSchedulingAsync
      │
      ▼
foreach task in process.Tasks:
   task.ExecuteAsync()  ──► ReportStatement / ProcedureCallStatement
                                 │
                                 ▼
                            ParseTrace.RenderReportTokens()
                                 │
                                 ▼
                            ReportBus.Instance.Emit(msg)
                                 │
                                 ▼
                            SimulationRunner.OnReport (event, per-run bridge)
                                 │
                                 ▼
                            HeadlessSimulator: log.Add($"[report] {msg}")
                                 │
                                 ▼
                            /api/v1/simulate response `log[]`

Limitaciones conocidas rastreadas como seguimientos:

  • F-G-13 — planificación de wait for N ns. Kmila no modela la suspensión/reanudación de procesos a través del tiempo de simulación. Todas las sentencias en el cuerpo de un proceso se disparan actualmente en el delta 0 en orden de código.
  • F-G-14 — asignaciones de señales dentro de los cuerpos de procedimientos. El cuerpo del procedimiento press_key(KEY_1) del Ring 5 tiene escrituras cols_in <= …; el MVP de F-G-12 las ignora, por lo que el DUT nunca ve las entradas.

También lanzado el 2026-07-18 — F-G-16 (ejecución de assert) + pulido del renderizado de report:

  • F-G-16 — ejecución de assert de VHDL. El nuevo Interpreter/Repositories/AssertStatement.cs refleja la forma de ReportStatement. Syntetize captura tres segmentos opcionales: tokens de condición (hasta report / severity / ; respetando insideString), tokens de la expresión de report (hasta severity / ;) y el nivel de severidad (por defecto error). ExecuteAsync evalúa la condición mediante Operation (el mismo idioma que la cláusula when de ExitStatement); cuando la condición es FALSA — o cuando la evaluación lanza una excepción — emite [assert-<sev>] <msg> mediante ReportBus. Mensaje por defecto cuando la fuente omite report: "Assertion violation". Sesgado hacia el fallo ante una excepción de evaluación, para que los estudiantes que diagnostican diseños rotos obtengan ruido en lugar de silencio. Conectado en los seis despachadores (cuerpos de Process + While / For / SimpleLoop / If / Case).
  • Colapso de secuencias de caracteres iguales consecutivos en el renderizado de report. La ruta de cadena fragmentada en ParseTrace.RenderReportTokens rastrea el carácter emitido previamente; cuando el token actual es un solo carácter igual al anterior, se une SIN espacio. ======== (fuente) se renderizaba como = = = = = = = = porque el tokenizador divide el contenido de la cadena en caracteres especiales — ahora se renderiza como ========. Lo mismo para --------. Los tokens de varios caracteres (lcd, line0) mantienen el separador de espacio para que los límites de palabra sobrevivan. Las secuencias heterogéneas de un solo carácter también mantienen el espacio.

Desbloqueo del Ring 5 — parser + elaborador (2026-07-17 → 2026-07-18)

La sesión 22 lanzó cuatro correcciones relacionadas más el andamiaje de diagnóstico que las encontró. Las cuatro son aditivas: rutas por defecto sin cambios, VCD de SimulatorBench idénticos byte a byte en 121 casos.

  • F-G-08 (cuelgue del parser) — los despachadores de cuerpo de subestructura ahora manejan wait / report / assert. WhileLoopStructure, ForLoopStructure, IfStructure, CaseStructure, SimpleLoopStructure — ninguna de sus sentencias switch de cuerpo tenía casos para palabras clave exclusivas de testbench, por lo que wait for N ns; dentro de un while ... loop anidado caía por default: _streamer.MoveNext(); el parser entonces despachaba el for trailing como un encabezado de bucle for, ForLoopStructure.ParseRangeValue engullía tokens hasta el siguiente loop, y new Operation(...) sobre la gigantesca cadena de rango malformada se colgaba indefinidamente. La corrección añade case "wait" / case "report" / case "assert" a los cinco despachadores, delegando a los nuevos helpers estáticos compartidos Interpreter.Services.ParseTrace.SkipWaitStatement y .SkipToSemicolonInclusive. Process.SkipWaitStatement / Process.SkipToSemicolonInclusive ahora delegan a los mismos helpers. Corrección adicional latente en el helper compartido: el tokenizador divide "TB: end of stimulus" en espacios en blanco, por lo que la antigua red de seguridad de salida temprana por end se disparaba en la palabra end dentro del literal fragmentado — el helper compartido conmuta una bandera insideString mediante el conteo de comillas sin emparejar, de modo que permanece opaco al recorrido.

  • F-G-09 (ceguera del elaborador ante literales de cadena) — SourceElaborator.SplitArchitecture enmascara el contenido de los literales de cadena antes de su regex de palabras clave. Misma forma que el error de cadena fragmentada de F-G-08, un nivel más arriba. El regex \b(architecture|process|...|end|...)\b solía coincidir falsamente con el end dentro de report "TB: end of stimulus";, decrementando innerDepth de 1 (dentro de stim_proc) a 0 y terminando prematuramente bodyText en el end process; real. El emisor entonces escribía end architecture; justo después de wait; sin un end process; de cierre para el último proceso. Corrección: el nuevo helper MaskStringLiterals devuelve una copia de la fuente que preserva la longitud con el contenido de "..." reemplazado por . (las comillas se preservan); SplitArchitecture usa la copia enmascarada para el regex + IndexOf(';') y el original para los retornos de subcadena, de modo que las cadenas de report sobreviven intactas en la salida elaborada.

  • F-G-10 (sombreado de artefactos del elaborador) — los escáneres omiten _elaborated_*.vhd. SourceElaborator.ScanProject y el bucle de escaneo de HeadlessSimulator enumeran cada archivo .vhd* en el sandbox. Un _elaborated_<entry>.vhd sobrante de una ejecución previa del mismo proyecto también se escanearía, y su registro posterior a la elaboración (con todas las instancias de componentes insertadas en señales) sobrescribiría el registro de la fuente en el diccionario de entidades. Aguas abajo, HasComponentInstance(top.ArchitectureBody) devolvía false, ElaborateOrNull devolvía null, HeadlessSimulator recurría a la fuente original, y el parser veía solo el cascarón externo del testbench (11 señales para el trabajo cumbre del Ring 5, faltando los 174 internos del DUT). Corrección: ambos escáneres ahora omiten los nombres de archivo que comienzan con _elaborated_. El reproductor era invisible para SimulatorBench porque el bench no establece retain: true en sus proyectos de sandbox, por lo que cada caso comienza vacío.

  • Superficie de cancelación a través del descenso de análisis + síntesis. Se añadió la sobrecarga Debugger.Repositories.Parser.ParseFile(CancellationToken); SimulationRunner.RunAsync registra _cts.Token.Register(() => _engine?.Stop()) después de Initialize para que la cancelación del lado del runner realmente rompa el bucle delta del motor. HeadlessSimulator.RunAsync construye un CTS con ámbito de solicitud en Phase("start") (antes del análisis) en lugar de dentro de SimulationRunner (después del análisis), de modo que KMILA_SIM_WALL_TIMEOUT_MS=<n> ahora cubre la canalización completa — análisis incluido — no solo el bucle de simulación.

  • Redes de seguridad en tiempo de análisis en cada bucle Skip* no acotado. Se añadió el límite ParseTrace.MAX_SKIP_ITER = 100_000 + lanzamiento con contexto mediante la nueva Parser.Models.ParserSkipOverflowException en: VariableSyntetizer.Skip{Function,Component,Subtype,Alias,Procedure,Attribute,Disconnect,ToSemicolon}Declaration más TryParseParameterList; históricamente estos no tenían límite y podían colgar el parser indefinidamente ante entradas patológicas. Un disparo del límite ahora aflora mediante la respuesta como [engine] ParserSkipOverflowException in <site> at line <N>, tokens=[...] — en menos de 100 ms en lugar de "curl agota el tiempo de espera tras 10 min".

  • Diagnósticos en tiempo de análisis controlados por variables de entorno.

    • KMILA_PARSER_STEP_TRACE=1 — stderr [kmila-parse t=...ms line=...] {caller}: {tag} en las entradas de Skip* + por declaración en el bucle externo de VariableSyntetizer
      • a la entrada de cada Syntetize de subestructura.
    • KMILA_SIM_PHASE_TRACE=1 — stderr [kmila-phase t=...ms] {tag} en cada límite de la canalización (start / registries-cleared / scan / subscan / elaborate / parse / stimulus / runner).
    • KMILA_SIM_PROGRESS_EVERY_N_DELTAS=<n> — stderr [kmila-diag t=...ms] tick=... delta=... cumDeltas=... cada N deltas.
    • KMILA_SIM_TRUNCATE_DELTA_CAP=1 — convierte el lanzamiento del límite delta en un break-and-continue para que una ejecución más allá del primer límite sea observable de extremo a extremo.
    • KMILA_SIM_NO_HISTORY=1 (legado de la fase 1) — omite el registro del historial VCD; el VCD de la respuesta queda vacío pero los barridos basados en report aún funcionan. Siempre activo: el log[] de la respuesta obtiene una línea [diag] al final con los contadores de tick / delta / cap / elaboración inicial.

Barrido de endurecimiento de prácticas (2026-06-22 → 2026-06-23)

Impulsado por las 5 prácticas de Arquitectura de Computadoras de la ESCOM del usuario (P1–P4). Cada error a continuación se fijó como un caso de SimulatorBench (120–132) antes de ser corregido; la narrativa completa por caso reside en Documentacion/HANDOFF_practicas_2026-06-23.md. La regla en todo momento: Kmila se adapta para manejar los códigos, nunca al revés. No se modificó ninguna fuente de práctica por compatibilidad exclusiva de simulación.

  • **Acceso a elementos de arreglos constantes (error de Bench #19) — Parser/Models/Variable.cs

    • Interpreter/Models/{Deferred,Dynamic}Index.cs + Interpreter/Services/VariableSyntetizer.cs + Interpreter/Services/Operation.cs.** Los agregados posicionales (("10101010", "10111011", …)) ahora se analizan en una cadena de bits concatenada y limpia de ElementSize × ArrayLength bits; Variable lleva los nuevos campos ElementSize / ArrayLength; DeferredIndex / DynamicIndex segmentan elemento por elemento cuando la fuente tiene ElementSize > 1. El nuevo DynamicIndex en modo expresión reevalúa los índices con forma de rom_init(to_integer(unsigned(addr))) en cada delta. ParseIndexedAccessDeferred es consciente de los paréntesis. Trampa: Variable sobrecarga operator== para comparar nombres — use is null / is not null para las verificaciones de referencia.
  • Agregados de asociación nombrada + cuerpos de funciones definidas por el usuario (seguimiento del error de Bench #19) — VariableSyntetizer.cs, Operation.cs, FunctionExecutor.cs, FunctionDefinition.cs. TryParseNamedAggregate maneja (0 => pack(170), …, others => …) colocando cada valor en el desplazamiento key × ElementSize. SkipFunctionDeclaration ahora analiza la firma + cuerpo y registra una FunctionDefinition real en PackageRegistry. ParseUserDefinedFunctionCall invoca a FunctionExecutor cuando la función tiene un cuerpo. Nuevo CachedScope en FunctionDefinition — las llamadas subsiguientes reutilizan las mismas instancias de Variable de parámetro y solo refrescan .Value (ganancia de rendimiento de 20× en la inicialización pesada de ROM).

  • Identificador de sentencia case + selector de segmento (errores de Bench #20, #21) — DeltaCycleEngine.cs. ExecuteCaseStructureWithSchedulingAsync ahora extrae el segmento cuando HasSelectorSlice es true; ResolveChoiceForCase resuelve cada elección when — literales hexadecimales, cadenas de bits entrecomilladas, bits con comilla simple, y búsquedas de identificadores contra _allSignals para que when OP_LOAD => coincida con el valor binario de OP_LOAD.

  • Canalización LOAD de P2A + detección de HALT (Fase E). Operation.ParseFunctionCall::TryConsumeSliceArg emite un DeferredIndex cuando un argumento parece <var>(N) / <var>(H downto L); StandardFunctions.GetStringValue/GetIntValue aprendieron a evaluarlo; la rama INTEGER de Operator.SolveComparation recurre a la igualdad ordinal de cadenas cuando ambos lados no son numéricos (los literales enum viven como sus cadenas de nombre); el operador * se ensanchó a leftW + rightW según la multiplicación sin signo de numeric_std. P2A de extremo a extremo: acc_reg = 56, halted = 1 — coincide con GHDL bit a bit y con la FPGA MachXO2.

  • Captura del cuerpo de función para cualquier cuerpo anidado en if/case/loop (error de Bench #24) — VariableSyntetizer.SkipFunctionDeclaration. El contador de profundidad heredado trataba end if como decremento + reincremento en la siguiente palabra clave, truncando los cuerpos en el primer end interno. Ahora consume end <closer> como una sola coincidencia; el prefijo de apertura de bucle for X in Y loop / while X loop salta más allá de la palabra clave loop trailing. La función key_index de P4 fue el disparador.

  • Atributos de arreglo ('length, 'high, 'low, 'left, 'right) — Operation.TryParseSignalAttribute. P4 necesitaba to_unsigned(N, slot_cnt'length), que de otro modo corrompería la lista de argumentos de la llamada a función. Los atributos booleanos de flanco (event, last_value, stable, active) quedan intactos.

  • Sustitución de valores por defecto de genéricos en SourceElaboratorSourceElaborator.cs. El nuevo ExtractGenericDefaults lee los valores por defecto := de la cláusula generic de una entidad hija; orden: primero los valores por defecto, luego las anulaciones explícitas de generic map(...). Los textos de tipo de puerto también se sustituyen (para que los alias STD_LOGIC_VECTOR(WIDTH-1 downto 0) lleguen al padre como STD_LOGIC_VECTOR(16-1 downto 0)).

  • Rendimiento del motor: condicionamiento por sensibilidad de tareas concurrentes (Fase H.3) — DeltaCycleEngine.cs. El nuevo caché por Operation _operationInputs registra el conjunto estático de señales de entrada (Variable, DeferredIndex.Source, DynamicIndex.Source + IndexSource; null = "ejecutar siempre" para DeferredFunction / DynamicIndex en modo expresión). _lastDeltaChangedSignals toma una instantánea de los cambios confirmados por delta; el bucle de tareas concurrentes omite las Operations cuyas entradas no se solapan. Se reinicia / se une en cada límite de paso de tiempo para que los cambios de estímulo externo aún se propaguen. P22 12× más rápido en diseños con muchas señales.

Continuación 2026-06-24 — seguimiento de la §6 del handoff

Se cerraron los ítems 1, 3, 5, 6 de la §6 de HANDOFF_practicas_2026-06-23.md. Los ítems 2 (reescritura de la ROM de P22) y 4 (contenido de la ROM de P3/P22) se reconfirmaron como diferidos — ambos son trabajo de edición de prácticas y permanecen pausados hasta que el usuario reautorice. No se modificó ningún VHDL de práctica.

  • Expansión temprana del valor inicial (others => 'X') (ítem 1 de la §6) — VariableSyntetizer.cs, sitio de construcción de señales. La rama (others=>X) del setter de Size en Parser/Models/Variable.cs:135 solo se dispara cuando _value ya no es nulo. El patrón de inicializador de objeto en el sitio de construcción de señales establece Size primero (aún sin valor) y Value después, por lo que el marcador quedaba en Value literalmente — los escritores de VCD luego emitían la cadena literal de 11 caracteres (others=>0) (los caracteres no de bit se mapean a 'x') por cada señal inicializada de este modo, hasta la primera escritura del usuario. Ahora: se detecta el marcador en el valor del inicializador, se expande a una cadena de bits del ancho de VectorSize antes de la construcción, condicionado a VectorSize > 0 (de modo que unsigned / signed / bit_vector / std_logic_vector queden todos cubiertos).

  • Expansión en tiempo de escritura del agregado RHS (others => 'X') (ítem 3 de la §6, causa raíz de P3) — Operation.cs::ParseOthersAggregate + SignalScheduler.cs::ExpandAggregateMarker. Cuando (others => '0') aparece en el RHS de una rama de reset con reloj (pc_reg <= (others => '0') etc.), el motor creaba una Variable Aggregate_others_X mediante el constructor de 4 argumentos. Pasar Size = 0 a ese constructor disparaba la propia ruta de expansión de marcador de Variable.Size, que producía una cadena de longitud cero — el Value de la Variable Aggregate colapsaba a "". El planificador entonces escribía "" en la señal de destino, y el escritor de VCD lo renderizaba de vuelta como el marcador original de 11 caracteres. La corrección tiene dos partes: (a) ParseOthersAggregate ahora usa la construcción por inicializador de objeto para que el setter de Size nunca se llame, preservando la cadena marcador en Value; (b) el nuevo helper SignalScheduler.ExpandAggregateMarker(newValue, target) se ejecuta en cada escritura con cola drenada en ApplyCurrentUpdatesWithLines — si la carga útil es un marcador (others=>X) y target.Size > 0, se expande a una cadena de bits del ancho de target.Size usando el carácter de relleno del marcador. **Resultado de P3: todos los registros internos de la CPU ahora se inicializan a ceros limpios en t = 0; el PC avanza normalmente; mbr_reg

    • rom_data_w obtienen valores reales.** El estancamiento restante del ACC en P3 es un error de contenido de la práctica (el mux de dirección de ROM eq_sel aplica OR-mask a las lecturas de datos — mismo patrón que la Fase G.1 de P22) y está fuera del alcance según la regla.
  • Detector (tripwire) del ancho de * (ítem 5 de la §6) — sin cambios de código, fijado mediante el caso de bench 135. La auditoría mostró que ningún caso de bench existente ejercitaba directamente el ensanchamiento leftW + rightW de la Fase E; P1 era el único consumidor, y solo indirectamente. Se añadió un detector focalizado para que cualquier deriva de vuelta a la multiplicación estrecha (max(leftW, rightW)) falle el barrido de inmediato: unsigned(4..0) * unsigned(4..0)unsigned(9..0)std_logic_vector(8..0); 12 * 11 = 132 = "010000100" pierde el MSB bajo la multiplicación estrecha.

  • Reenlace de resolución de símbolos del cuerpo de función (ítem 6 de la §6) — sin cambios de código, fijado mediante el caso de bench 136. Se construyó un detector donde la misma entidad sub se inserta dos veces con distintos valores de señal de ámbito de arquitectura (k_sig impulsada desde el puerto de entrada kin = 3 y kin = 7), y una función add_off declarada en la arquitectura de sub lee k_sig directamente. La hipótesis del handoff era que la reutilización de instancias de Variable de parámetro por parte de CachedScope enlazaría las Operations del cuerpo de la función a la k_sig de la primera instancia y produciría lecturas obsoletas para la segunda instancia. El caso pasa hoy (o1 = 5 + 3 = 8, o2 = 10 + 7 = 17) — el renombrado de señales por instancia del elaborador crea entradas FunctionDefinition distintas en PackageRegistry, por lo que el cuerpo de la función de cada instancia se analiza contra su propio ámbito y no hay colisión de caché. La solución alternativa se demostró correcta; el detector permanece en el bench como una red de regresión permanente.

Estado al final de la continuación:

  • SimulatorBench: 119 / 119 PASS (115 de base + 4 nuevas fijaciones 133–136). ~26 errores acumulados de simulador/bench corregidos.
  • P1: 5 / 5 PASS.
  • P2A: ACC = 56, halted = 1 — coincidencia bit a bit con GHDL, estado verificado en FPGA preservado.
  • P3 / P22 / P4: analizan + simulan limpiamente. La fuga del marcador de registro interno de P3 se corrigió; los estancamientos de ACC restantes son problemas de contenido de práctica, no errores de Kmila.

Archivos modificados en esta continuación (sin confirmar, el usuario gestiona git):

Interpreter/Services/VariableSyntetizer.cs            §6 item 1 eager-expansion at construction
Interpreter/Services/Operation.cs                     §6 item 3 no-Size construction of Aggregate_others_X
Interpreter/Services/SignalScheduler.cs               §6 item 3 ExpandAggregateMarker write-time helper
SimulatorBench/Cases/133-others-init-readback/        §6 item 1 lock
SimulatorBench/Cases/134-others-rhs-clocked-reset/    §6 item 3 lock
SimulatorBench/Cases/135-mul-width-tripwire/          §6 item 5 lock
SimulatorBench/Cases/136-fn-multi-caller-scope/       §6 item 6 lock (no Kmila change)

Versión 1.14.0 (2026-04-19)

  • Services/Operator.cs — xnor corregido. El evaluador caía a 'U' porque xnor no estaba en la lista de casos. Ahora a xnor b = '1' when a = b else '0'. Coincide con la forma derivada a mano not (a xor b), que había sido la solución alternativa del usuario.
  • Services/Resources.csCalculateLUTs implementado. Recorre el árbol de sentencias (Tasks / Then / Else / Body / Cases / Branches) mediante reflexión y suma los pesos de los operadores: operaciones lógicas 1 × bits, aritmética +/− a 3 × bits, comparadores a max(1, bits/2). abs, not, *, /, mod, ** permanecen en 0 porque se mapean a DSP / BRAM y se contabilizan en sus propios calculadores. Elimina el stub de TODO.
  • Services/SimulationRunner.cs — estímulo en tick 0 + historial. Dos correcciones relacionadas: (1) se eliminó el filtro .Where(s => s.Tick > 0) para que el estímulo en el tick 0 llegue al motor; esto elimina la solución alternativa que StimulusBuilder usaba para presembrar el tick 0. (2) Se propagó un HashSet directlyChanged a través del bucle principal y ahora se llama a _engine.History.RecordChanges(directlyChanged, tick * 1000), de modo que las escrituras directas .Value = en el intérprete se registren — antes solo los cambios impulsados por ciclos delta llegaban a la forma de onda.

Contribuir

Estilo de código

  • Usar comentarios de documentación XML para todos los métodos públicos
  • Añadir comentarios // v0.x.x Fix: para las correcciones de errores con explicación
  • Usar guardas #if DEBUG para la salida de depuración
  • Seguir las convenciones de nomenclatura existentes (PascalCase para lo público, _camelCase para lo privado)

Pruebas

  • Añadir casos de prueba para las nuevas características
  • Asegurar que las pruebas existentes sigan pasando
  • Documentar cualquier archivo de prueba nuevo en este README

Documentación

  • Actualizar la sección de CHANGELOG para todos los cambios
  • Añadir comentarios en línea que expliquen la lógica no obvia
  • Incluir ejemplos de código en la documentación de los métodos

Novedades en la v1.15 (2 de mayo de 2026)

Nuevos servicios en Interpreter.Services:

  • FunctionRegistry (#18) — singleton a nivel de proceso que contiene stubs de FunctionDefinition para cada función y procedimiento que el parser ha omitido. Se limpia entre fixtures junto con EntityRegistry / PackageRegistry.
  • SkipDiagnosticLog (#19) — cada evento de omisión silenciosa (cuerpo de función, cuerpo de procedimiento, alias, atributo, subtipo, especificación de disconnect, constante con llamada a función) se registra con un código estable (VHD-FUNC-BLACKBOX, VHD-ALIAS-PLACEHOLDER, …). El ejecutor de pruebas lo drena e imprime después de cada fixture para que los usuarios vean qué falta en la netlist.

Mejoras del análisis:

  • JoinContinuationLines (#11) colapsa las sentencias VHDL de múltiples líneas físicas antes de que se ejecute la máquina de estados línea por línea en ProcessVhdlCode.
  • Selectores case <slice> (#10) — Process.SyntetizeBehaviour ahora reconoce case op(3 downto 0) is; los índices de bits se almacenan en CaseStructure.SelectorSliceHigh / Low para la síntesis aguas abajo.
  • <sig>'range y <sig>'reverse_range en los bucles for (#14) se resuelven al rango de bits declarado de la señal.
  • Las declaraciones de alias (#17) ahora registran el nombre del alias + tamaño (a partir del subtipo explícito → segmento de destino → por defecto) en lugar de ser descartadas silenciosamente.
  • Valores por defecto computados de genéricos (#16) — ParseInitializationValue pliega como constantes las cadenas + - * / de modo que TOTAL : integer := BASE * TIMES se resuelve correctamente.
  • Error de detección del fin de arquitectura corregido (#15/#18): funcProcDepth ya no decrementa en los fines de flujo de control (end if, end loop, end case); la coincidencia del fin de arquitectura es solo por nombre exacto.

Reporte de errores (#21):

  • Entity.Syntetize y Architecture.Syntetize aceptan IReadOnlyList<(int sourceLine, string text)> para que los diagnósticos hagan referencia a los números de línea fuente del usuario.
  • La falta de ; entre declaraciones de puertos ahora produce un Missing ';' before '<name>' at Line N:M explícito en lugar del mensaje genérico "expected ( or )".

Memoria (#7): result = default después de cada fixture, GC de Gen-2 no bloqueante cada 8 fixtures, cadena fuente anulada inmediatamente después del análisis. GC de estación de trabajo (Workstation) habilitado a nivel de proyecto. RSS máximo durante el barrido de 54 fixtures: ~101 MB.

Notas de versión completas: ../Documentacion/CHANGELOG_v1.15.md.


Novedades en la v1.16 — módulo LibraryCompiler (2 de mayo de 2026)

La compilación de bibliotecas se movió fuera del Intérprete a un módulo hermano dedicado: ../LibraryCompiler/. Es dueño de los builtins de IEEE (std_logic_1164.vhd, numeric_std.vhd), expone una API portátil impulsada por el host (OpenLibrarySource + IBlobStore) y produce artefactos compilados que alimentan al resto de la canalización.

El antiguo Interpreter.Services.IEEELibraryLoader ahora delega al nuevo módulo — su API pública no ha cambiado, por lo que los llamadores existentes siguen funcionando.

Lo que cambió dentro de Interpreter/:

  • Services/IEEELibraryLoader.cs — reescrito como una fachada delgada que construye un LibraryCompiler por defecto (sin callbacks de host, con recurso embebido de respaldo) y reenvía EnsureLoaded().
  • Tests/LibraryCompilerTests.cs — 17 casos de prueba nuevos que cubren toda la superficie del módulo (resolver / builder / compiler / orchestrator / 3 modos de política / almacenes de blobs / compilación de proyectos).
  • Interpreter.csproj+ProjectReference LibraryCompiler.

Contrato del host: LibraryCompiler no toma ninguna decisión sobre el sistema de archivos. Los hosts pasan Func<string, CancellationToken, Task<Stream?>> OpenLibrarySource y un IBlobStore? para la persistencia. La biblioteca solo incluye InMemoryBlobStore y NullBlobStore — los FileSystemBlobStore / MauiAppDataBlobStore concretos residen en el host (Kmila.Shared/Services/).

Notas de versión completas: ../Documentacion/CHANGELOG_v1.16.md.


Novedades en la v1.17 — reducción de primitivas IEEE en tiempo de síntesis

El sintetizador solía emitir una única net de marcador de posición Constant("@FunctionName", 1) por cada llamada a función IEEE. Ahora emite celdas reales:

VHDL del usuario Celda emitida
q <= rising_edge(clk) EDGE_DETECT (Edge=rising)
idx <= to_integer(unsigned(addr)) BUF (cast) → TO_INTEGER
widen <= resize(small, 16) RESIZE (FromWidth=4, ToWidth=16)
shifted <= shift_left(v, n) SHL
rotated <= rotate_right(v, n) ROR

Archivos clave

  • Synthesis/Models.cs — se añadieron CellKind.TO_INTEGER, TO_VECTOR, RESIZE, EDGE_DETECT. Los SHL / SHR / ROL / ROR / BUF existentes se reutilizan para desplazamientos / rotaciones / casts.
  • Synthesis/IEEEPrimitives.cs — búsqueda dirigida por tablas que mapea cada nombre de función IEEE a un emisor de primitivas (IIEEEPrimitive). 13 emisores sembrados por defecto; las bibliotecas de proveedores pueden añadir más mediante IEEEPrimitives.Register.
  • Synthesis/ConcurrentAssignSynthesizer.cs — cuando el recorredor de Shunting-yard encuentra una DeferredFunction, ahora resuelve los argumentos de la llamada a nets y llama a IEEEPrimitives.TryLower. En un acierto, la net de salida del emisor se une a la pila de operandos; en un fallo, una constante de marcador de posición + diagnóstico UnknownIEEEFunction.
  • Tests/IEEEPrimitivesTests.cs — 18 casos que cubren la búsqueda por nombre canónico, la búsqueda por alias, la búsqueda insensible a mayúsculas/minúsculas, el respaldo ante nombres desconocidos y las aserciones de forma de celda por emisor.

Los tipos IEEEPrimitiveArgs, IEEEPrimitiveResult, IIEEEPrimitive e IEEEPrimitives son todos internal porque referencian al SynthesisContext interno. Las pruebas en el mismo ensamblado los impulsan directamente; los packs de proveedores que quieran registrarse desde fuera necesitan InternalsVisibleTo (o residir como un sub-espacio de nombres dentro de Interpreter).

Notas de versión completas: ../Documentacion/CHANGELOG_v1.17.md.


Novedades en la v1.18 — la reducción de primitivas llega al código del usuario

La v1.17 conectó la tabla; la v1.18 hizo que realmente se disparara para las expresiones típicas del usuario.

Models/DeferredFunction.cs::ShouldDefer se extendió de {rising_edge, falling_edge} a {rising_edge, falling_edge, to_integer, conv_integer, to_unsigned, to_signed, resize, shift_left, shift_right, rotate_left, rotate_right}. Las sobrecargas por nombre de tipo (unsigned, signed, std_logic_vector) se difirieron por separado en la v1.19.

La ruta de tiempo de ejecución sigue funcionando a través de DeferredFunction.Evaluate() (que internamente aún llama a StandardFunctions.EvaluateFunction), por lo que se preserva la semántica de simulación. La ruta de síntesis ahora ve llamadas estructuradas en lugar de literales evaluados en línea.

Tests/IEEELoweringEndToEndTests.cs — 6 casos que impulsan una lista de ítems Operation hecha a mano a través del sintetizador y aseveran que la netlist resultante contiene el CellKind esperado. Demuestra que el cableado desde DeferredFunction a través de IEEEPrimitives.TryLower hasta una Cell real realmente se dispara.

Notas de versión completas: ../Documentacion/CHANGELOG_v1.18.md.


Novedades en la v1.19 — llamadas IEEE anidadas en los argumentos

Un error preexistente del parser: to_integer(unsigned(addr)) producía args == ["unsigned"] y el operando addr se descartaba silenciosamente. Cada llamada IEEE anidada se simulaba como si su operando interno fuera 0. Oculto por las pruebas porque las fixtures exclusivas de síntesis solo validan "¿lanzó una excepción?", no "¿coincidieron los valores?".

Corrección — cuatro piezas

  1. Services/Operation.cs::ParseFunctionCall — al recorrer los tokens de argumentos de profundidad 1, si el token actual es un nombre de función conocido (mediante StandardFunctions.IsStandardFunction o FunctionRegistry.IsKnown) y el siguiente token es (, se llama recursivamente a ParseFunctionCall para consumir la llamada anidada. La llamada recursiva usa su propio parenDepth local, por lo que el seguimiento de paréntesis se mantiene correcto.
  2. Synthesis/ConcurrentAssignSynthesizer.cs::ResolveDeferredArgs — se añadió case DeferredFunction nested que resuelve recursivamente los argumentos de la llamada anidada y la reduce mediante IEEEPrimitives.TryLower. Resultado: grafos de celdas correctamente cableados.
  3. Models/DeferredFunction.cs::Evaluate — evalúa recursivamente las DeferredFunctions anidadas a sus resultados Variable antes de pasarlas a StandardFunctions.EvaluateFunction.
  4. Models/DeferredFunction.cs::ShouldDefer — se añadieron las sobrecargas por nombre de tipo (unsigned, signed, std_logic_vector, conv_std_logic_vector). Diferir estas era el bloqueador original para la v1.18; la recursión del lado del parser finalmente lo hizo seguro.

Error adicional eliminado

StandardFunctions.ToInteger("01010101") devolvía 1,010,101 decimal en lugar de 85 binario porque int.TryParse se intentaba antes del análisis binario. Reordenado: cuando la entrada es de múltiples caracteres, todos 0/1, se analiza primero como binario; la interpretación decimal cae para los literales enteros reales.

Este era un error preexistente separado — pero habría ocultado la corrección del parser, así que se lanzó en conjunto. El código antiguo calculaba to_integer(unsigned("01010101")) como 1,010,101 en lugar de 85 incluso después de corregir el parser.

Impacto en el mundo real

Los diseños que indexan memoria mediante to_integer(unsigned(addr)) (que es el idioma canónico — todos los diseños de UART / SPI / cache / banco de registros en la suite de pruebas lo usan) ahora computan la dirección correcta. Antes de la v1.19, esos diseños se simulaban como si cada acceso cayera en la ubicación 0.

Tests/NestedIEEECallTests.cs — 7 casos que cubren el anidamiento del parser, la evaluación en tiempo de ejecución (to_integer(unsigned("01010101")) == 85) y la topología de cableado de síntesis (BUF.outputId == TO_INTEGER.inputPinId).

Notas de versión completas: ../Documentacion/CHANGELOG_v1.19.md.


Conteos de pruebas después de la v1.19

Interpreter sweep (test*.vhdl) ............... 54 / 54
Interpreter --test-delta:
  DeltaCycleEngine ........................... 12 / 12
  Synthesis .................................. 16 / 16
  IEEELibraryLoader ...........................  7 /  7
  LibraryCompiler ............................ 21 / 21
  IEEEPrimitives ............................. 19 / 19
  IEEELoweringEndToEnd ........................  7 /  7
  NestedIEEECall ..............................  8 /  8
  PortStrictness ..............................  5 /  5
  PracticeExercise ............................  1 /  1
─────────────────────────────────────────────────
                                              150 / 150

Más 40 (Parser) + 31 (Debugger) + 23 (TimeMachine) para un gran total de 230 / 230 en toda la pila del simulador.


Puntero rápido de arquitectura

Si está conectando el Intérprete a un host, la guía de integración está en ../Documentacion/IEEE_PIPELINE_GUIDE.md — recorre el flujo completo desde una línea de usuario q <= to_integer(unsigned(addr)) hasta un grafo de celdas BUF + TO_INTEGER, con ejemplos de código en cada capa.


Consumidor aguas abajo de step-replay (2026-05-17, pase de pulido de la App)

SignalHistory y la fábrica WaveformData.FromSignalHistory ahora alimentan una nueva canalización de step-replay del lado de la App. Después de que una simulación se completa, App/Kmila/Kmila.Shared/Services/ReplayBuilder.cs recorre la unión de los tiempos de transición de todas las señales en ≤200 frames y emite un ExecutionTimelineDoc consumido por el componente ExecutionPlayer de la pista de Concepts. El Editor de la App lo renderiza como una nueva pestaña de dock "Replay" — transporte de cinta + tabla de señales en vivo por frame, aún sin resaltado de líneas fuente (eso es la Fase B).

Implicaciones para este módulo:

  • SignalHistory.RawHistory y WaveformData.FromSignalHistory(history, timescaleNs) ahora son de carga (load-bearing) para la reproducción pedagógica de la App, no solo para el calificador de Prácticas / exportación de VCD / persistencia de Runs. Cualquier cambio futuro que descarte transiciones, las reordene o cambie la semántica de WaveformTransition.Label degradará visiblemente la reproducción.
  • La Fase B (metadatos de resaltado de línea por sentencia ejecutada) requerirá instrumentar DeltaCycleEngine para emitir un log de frames junto con SignalHistory. El mismo consumidor ExecutionTimelineDoc; el contrato está fijado en App/Kmila/Kmila.Shared/Models/ExecutionTimeline.cs (la forma de TimelineStep con T, HighlightLines, Signals, Note). No se necesitan cambios del lado de la App cuando llegue la Fase B — solo instrumentación del lado del Intérprete.
  • Véase Documentacion/CHANGELOG_concepts_module_2026-05-17.md (sección Task 5) para el diagrama completo de la canalización y la tabla de decisiones de diseño.

Licencia

Este proyecto es parte de la investigación académica en el Instituto Politécnico Nacional (IPN).

Contacto

Autor del proyecto: Ulrich Tamayo Daniel Correo: [email protected] Correo institucional: [email protected]