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
- Descripción general del proyecto
- El módulo Intérprete
- Arquitectura
- Decisiones de diseño críticas
- Componentes clave
- Cómo usarlo
- Pruebas
- Problemas comunes y soluciones
- Guía del desarrollador
- 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áusulaafter, 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 medianteLayeredLayoutpara 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 unSimulationConfig(entidad + total de ticks + lista deClockConfigcon conmutación automática + lista deStimulusEvent), conPause/Resume/Stop, y los eventosOnProgressChanged,OnSimulationEndedyOnError. - Tipos personalizados:
TypeDefinition+TypeRegistrycubren enumeraciones, arreglos, registros y subtipos.VariableSyntetizer,Operation,FunctionExecutoryResourcesconsultan aTypeRegistry. - Estimación de recursos de FPGA:
Resourcesproduce 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; elCancellationTokense 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):
- El corpus histórico de consola impulsado por
Program.cs— ahora 54 fixturestest*.vhdl(numerados hastatest41, 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 ejecutarProgram.cscontra el corpus para obtener una cifra actual. - Una suite xUnit de primera clase bajo
Interpreter/Tests/(DeltaCycleEngineTests,SynthesisTests,NestedIEEECallTests,IEEEPrimitivesTests,IEEELibraryLoaderTests,IEEELoweringEndToEndTests,LibraryCompilerTests,PortStrictnessTests,PracticeExerciseTests,VcdComparison) que se ejecuta condotnet test. Esta es la superficie de regresión autoritativa; el corpus de consola es un arnés de humo/exploración.
- El corpus histórico de consola impulsado por
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/arquitecturasEntity.cs: Analiza declaraciones de entidad (puertos, genéricos), coordina la arquitectura y la estimación de recursosArchitecture.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 SignalSchedulerExecuter.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 anidadasCaseStructure.cs: Maneja sentencias case-when con múltiples ramasForLoopStructure.cs: Maneja bucles for con rangos ascendentes/descendentes y límites basados en expresionesWhileLoopStructure.cs: Maneja bucles while con evaluación de condiciónSimpleLoopStructure.cs: Maneja bucles infinitos con condiciones de salidaExitStatement.cs: Maneja sentenciasexitde VHDL con etiqueta y condición opcionalesNextStatement.cs: Maneja sentenciasnextde VHDL con etiqueta y condición opcionalesNullStatement.cs: Maneja sentenciasnull(sin operación) de VHDLReturnStatement.cs: Maneja sentenciasreturnde VHDL para funciones y procedimientosProcedureCallStatement.cs: Maneja llamadas a procedimientos con evaluación de argumentos y reescritura de parámetros de salidaConditionalAssignment.cs: Maneja asignaciones concurrentes condicionales de señales (when...else)SelectedAssignment.cs: Maneja asignaciones concurrentes seleccionadas de señales (with...select)GenerateBlock.cs: Maneja sentenciasfor-generateeif-generatecon desenrollado de bucles
Evaluación de expresiones:
Operation.cs: Orquesta el análisis de expresiones, la síntesis a RPN y la evaluaciónShuntingYard.cs: Implementa el algoritmo Shunting-yard para la precedencia de operadoresOperator.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 deltaLiteralsFinder.cs: Identifica y clasifica valores literales (enteros, std_logic, vectores, hexadecimales, booleanos) usando expresiones regularesOperationDetector.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(),Historyy — el 2026-05-27 —OnConditionEvaluated(int line)que se dispara cuando se toma una ramaif/elsifo coincide un brazocase, 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áusulaafter)ProcessScheduler.cs: Gestiona la planificación de la ejecución de procesos con base en las listas de sensibilidadSignalHistory.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 sonlong(tick*1000+delta); ampliadas desdeintel 2026-09-13 para que las ejecuciones a escala de segundos ya no desborden a los ~21 ms. Solo almacenamiento — la ruta semántica deSignalAttributeHandlerconserva su propia marca de tiempoint, 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ónPackageRegistry.cs: Registro singleton para constantes, funciones y procedimientos de paquetes VHDLEntityRegistry.cs: Registro singleton para el descubrimiento de entidades multi-archivo y el seguimiento de la jerarquía de componentesTypeRegistry.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 predeterminadosFunctionDefinition.cs: Representa firmas de funciones definidas por el usuario con síntesis perezosa del cuerpoProcedureDefinition.cs: Representa firmas de procedimientos definidos por el usuario con soporte de parámetros out/inoutParameterDefinition.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 controlConstansts.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 derequired Entity Entity,ulong TotalTicks,List<ClockConfig> Clocks,List<StimulusEvent> Stimuli. Además de los tipos auxiliaresClockConfig(Signal,HalfPeriodTicks,InitialHigh) yStimulusEvent(Tick,Signal,Value).
Fachada del bucle de ejecución:
SimulationRunner.cs(Services/): FachadaIDisposableconRunAsync(SimulationConfig),Pause(),Resume(),Stop(), además de los eventosOnProgressChanged(double),OnSimulationEnded(bool)yOnError(string). Es el punto de entrada usado porKmila.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 enVariableSyntetizer,TypeRegistryyTypeDefinition.
Espacio de nombres de síntesis (Interpreter.Synthesis):
Synthesizer.cs: Punto de entrada estático —Netlist Synthesize(Entity entity)primero llama aComponentSynthesizer.SynthesizeAllpara 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 exponeGetOrCreateNet,NewInternal,Constant,AddCell,AddPort,AddSignal,AddDiagnosticy, finalmente,Build(), que devuelve unaNetlistcongelada.ComponentSynthesizer.cs:internal static;SynthesizeAll(parentEntityName, ctx)emite una celdaCellKind.BLACKBOX_COMPONENTpor cada instanciación de componente que el analizador registró contra la entidad enEntityRegistry.Instance. Deliberadamente superficial — no desciende a la propia arquitectura del componente referenciado (eso lo cubreSourceElaboratoren 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 sobrecargasSynthesize(Operation, ctx)ySynthesizeInto(Operation, outputNet, ctx)+SynthesizeExpression(expr, scope, ctx, targetWidth)para la reutilización de subexpresiones. Resuelve los argumentos deDeferredFunction(incluidas las llamadas anidadas) y reduce las llamadas IEEE a través deIEEEPrimitives.TryLower.ConditionalAssignSynthesizer.cs: Reduce las cadenaswhen…else.SelectedAssignSynthesizer.cs: Reduce los bloqueswith…select.ProcessSynthesizer.cs: Reduce unProcessa 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 tiposIEEEPrimitiveArgs/IEEEPrimitiveResult/IIEEEPrimitive/IEEEPrimitivessoninternal(referencian alSynthesisContextinterno); los hosts extienden la tabla mediante el estático públicoIEEEPrimitives.Register.TryLowerdevuelve la net de salida emitida o un marcador de posición + diagnósticoUnknownIEEEFunctionen caso de fallo.SelectionFilter.cs: Filtro público que recorta unaNetlist+LaidOutNetlista un subconjunto seleccionado por el usuario para su exportación —SelectionPicks(ids de celda / net / port-net en cesta +FocusedSourceLineopcional) →FilteredLayout(mismo espacio de coordenadas, con una cajaBounds). 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 enumeracionesCellKind,NetOrigin,NetKind,PortDir,DiagnosticSeverity.Synthesis/Layout/LayeredLayout.cs: Fase de disposición de estilo Sugiyama —Layout(Netlist) → LaidOutNetlist(con cajasRecty polilíneasWirePath). 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:
- Fase de síntesis: Convertir la notación infija a postfija usando el algoritmo Shunting-yard
- 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:
- Tokens:
(,A,+,B,),*,C,-,D,/,2 - Postfijo:
A B + C * D 2 / - - 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:
- 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));
}
- Conteo de pines de E/S:
// Sums bit widths of all entity ports
ioPins = entity.Ports.Sum(p => p.Size);
- 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:
- 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' */
- 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:
- Analizar las declaraciones de señales
- Identificar y analizar los procesos
- Manejar las asignaciones concurrentes de señales
- Omitir las instanciaciones de componentes (estructurales, no de comportamiento)
- 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
CaseWhenBranchregistra la línea de origen (base 1) de su palabra clavewhen(ConditionLine); el motor disparaOnConditionEvaluated(line)cuando un brazo coincide, de modo que un punto de interrupción en un brazowhenpause la ejecución en vivo (en paralelo conIfBranch.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 ciclosignal'last_value- Valor anterior antes del cambiosignal'stable- Verdadero si la señal no ha cambiadosignal'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:
- CalculateIOPins() - Suma los anchos de bits de los puertos (100 % de precisión)
- CalculateClocks() - Detecta las señales de reloj a partir de las listas de sensibilidad de los procesos (100 % de precisión)
- CalculateFlipFlops() - Escanea recursivamente los procesos con reloj en busca de señales asignadas (~95 % de precisión)
- CalculateDSPSlices() - Cuenta las operaciones de multiplicación (TODO: implementación pendiente)
- CalculateBRAM() - Detecta patrones de arreglo/memoria (TODO: implementación pendiente)
- 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 CancellationTokenSourceStartCancellableOperation(CancellationTokenSource)- Usa el CTS proporcionadoRequestCancellation()- Dispara la cancelación y genera el eventoThrowIfCancellationRequested()- Lanza una excepción si se canceló (para uso en bucles)CheckCancellation()- Devuelve un bool sin lanzar excepciónIsCancellationRequested- Propiedad para verificar el estado de cancelaciónCancellationToken- 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
FastSimulationRunner↔SimulationRunnerestá cubierta por pruebas de diff/equivalencia de VCD (véanse las notas deFast modeen §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 prueba —
Program.cs.1(una copia obsoleta deProgram.cs),run_delta_tests.csy 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 bajoInterpreter/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_ROWSusadas 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:
- La instanciación del componente no se omitió correctamente
- La sentencia con etiqueta no se reconoció
- 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
funcProcDepthen 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:
- Palabras clave (downto, to, when) tratadas como identificadores
- Genérico usado en una expresión de puerto antes de que se evalúen los genéricos
- 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ñadieronComponentSynthesizer,IEEEPrimitivesySelectionFilteral listado del espacio de nombresInterpreter.Synthesis; se añadieron los archivos de sentencias de controlReportStatement/AssertStatement/WaitForStatement(todosISControl) a la jerarquía de clases; y se añadieron las entradas más recientes deServices/(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/, fachadaSimulationRunner, 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 deModels.cs,Layout/LayeredLayout), además de la nueva fachadaSimulationRunnery el trío de modelosSimulationConfig/ClockConfig/StimulusEvent. Se marcóServices/DataStructure.csexplí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.csyLayeredLayout. Se anotó queSimulationRunneres el punto de entrada usado porKmila.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ó
Executerbajo las implementaciones de la interfaz ISControl - Se añadió la interfaz
IFindercon la implementaciónLiteralsFinder - Se añadió la sección de Clases de Utilidad con
OperationDetectoryShuntingYard
- Se añadió
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ónLiteralsFinder.cs- Identificación y clasificación de valores literalesOperationDetector.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
TypeDefinitionque soporta los tipos Enumeration, Array, Record y Subtype TypeRegistrypara 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;
- Enumeración:
- 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
- Modelo
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.Instancepara acceso global - Eventos:
ProgressChanged,ErrorOccurred,Completed,CancellationRequested - Salida de tupla de progreso:
(float Progress, string Message)mediantee.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/--verbosepara progreso detallado,-p/--progresspara barra compacta - Integrado en el análisis de Entity, Architecture y Process
- Singleton
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
CancellationRequestedpara notificación cuando se dispara la cancelación - Manejo de Ctrl+C en
Program.cspara la cancelación de la aplicación de consola - Parámetro
CancellationTokenañ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
OperationCanceledExceptionevita los bloqueos - Útil para detener el procesamiento de archivos VHDL grandes (por ejemplo, FPGA_KILLER)
- Métodos de
Soporte de operadores de desplazamiento/rotación (
Services/Operator.cs): Implementación completa de los operadores de desplazamiento y rotación de VHDLsll- 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 izquierdaror- Rotación a la derecha- Añadidos al enum
PrecedencesLogicOperatorsenModels/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 constantesHasFunctionCallInConstant()- detección por anticipación (lookahead) de llamadas a funciones en la inicialización de constantesSkipConstantDeclaration()- 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 comotype big_mem_t is array (0 to 199999) of STD_LOGIC;causaban bucles infinitos y agotamiento de memoria. Se añadió:- Seguimiento de
parenDepthpara los límites del arreglo - Límite de seguridad
MAX_ITERATIONS = 10000 - Romper solo con
;cuandoparenDepth == 0
- Seguimiento de
Se corrigió una fuga de memoria de LoggerFactory (Parser/Repositories/TokenStream.cs): Cada instancia de
TokenStreamcreaba un nuevoLoggerFactory. 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
Operationcreaba 4 nuevos objetosRegex. 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> comparatorsse asignaba en cada iteración del bucle deSyntetizeBehaviour(). Se movió fuera del bucle y se cambió aHashSet<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)porif (_value == null)para evitar que el getter de Value devolvieranew()durante la inicialización del objetoSe corrigió el dimensionamiento de puertos STD_LOGIC (Services/VariableSyntetizer.cs): Se añadió
VectorSize = 1por 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 tantoend behavioral;comoend 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 operadoresLOGIC_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 parcialesARITH_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) conPrecedencesArithOperators.UNARYno se trate comoPrecedencesLogicOperators.NOTSe corrigió la conversión de precedencia en ShuntingYard (Repositories/ShuntingYard.cs:47): Se cambió de la conversión directa
(int)operation.PrecedenceaConvert.ToInt32()para manejar la conversión de enum a int cuando Precedence se almacena comoobjectSe 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 processSe 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,whenyelsedurante la síntesis de expresionesSe corrigió el análisis de funciones/procedimientos (Program.cs): Se añadió el seguimiento de
funcProcDepthpara evitar queend FunctionName;se confunda con el fin de la arquitecturaSe 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 clavebus(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ñalesSe corrigió el manejo de la sentencia
nullde VHDL (Repositories/CaseStructure.cs): Se añadió el manejo explícito para las sentencias sin operaciónnull;en las ramas caseSe 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 comoTREE_DEPTH - 1, no solo enteros simplesSe 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 iteracionesOperation.Syntetize()- 5000 iteracionesIfStructure.SyntetizeBranchBody()- 5000 iteracionesForLoopStructure.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/elseOmisión de instanciaciones de componentes (Services/Architecture.cs): Se añadió el método
SkipComponentInstantiation()para manejar la sintaxislabel : 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-elsecon múltiples ramas - Clase auxiliar
IfBranchpara 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.cspero 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
typepersonalizado (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)
- Declaraciones de
- 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
reportde VHDL se ejecutan + afloran mediante el log de la API. NuevoInterpreter/Services/ReportBus.cs(sumidero de eventos singleton);Interpreter/Repositories/ReportStatement.csimplementaISControlcon captura de expresión en tiempo de análisis (rastreandoinsideStringpara que un;dentro de un literal de cadena no termine prematuramente) y renderizado en tiempo de ejecución medianteReportBus.Emit. Elcase "report"deProcess.SyntetizeBehaviourahora crea una tareaReportStatementen lugar de saltar al;.SimulationRunner.RunAsyncconectaReportBus.OnReporta un eventoOnReportcon ámbito de instancia durante la vida de la ejecución, desmontándolo enfinallypara que el bus estático no acumule oyentes obsoletos.HeadlessSimulator.RunAsyncse suscribe y añade[report] <msg>al log de la respuesta. Antes de F-G-11, cadareportde VHDL era descartado silenciosamente porSkipToSemicolonInclusive.F-G-11b — ejecución de
reportanidado. Los cinco despachadores de cuerpo de subestructura (WhileLoop/ForLoop/SimpleLoop/If/Case) también crean tareasReportStatementahora en lugar de saltarlas.assertaú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óSkipProcedureDeclarationpara capturar la lista de parámetros (mediante elTryParseParameterListexistente) y los tokens del cuerpo entrebeginy una coincidencia de dos tokensend procedure/end <name>. La coincidencia de dos tokens es naturalmente segura ante cadenas — unenddesnudo dentro de un fragmentoreport "…"es seguido por más fragmentos de cadena, no porprocedure/<name>. Registra unaFunctionDefinitioncompleta (Parameters + BodyRaw,IsPure=false) enFunctionRegistry.Interpreter/Repositories/ProcedureCallStatement.cs(andamiaje preexistente, ahora conectado):Syntetizecaptura 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).ExecuteAsyncrecorreBodyRaw, sustituye los nombres de parámetros con los tokens de argumento por cadareport … ;, y emite medianteReportBus. 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 ramaelsedeProcess.SyntetizeBehaviourdespacha medianteProcedureCallStatement.IsProcedureCallantes de caer aSyntetizeAssignment. 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.
TokenizeSpacesdeParser/Repositories/Tokenizer.csera ciego a las cadenas:if (line.Contains("--")) line = line.Split("--")[0];truncabareport "-------- "en el primer--dentro de la cadena. El nuevo helperFindTrailingLineCommentStartrecorre la línea rastreando una banderainsideStringconmutada por"y devuelve el índice de byte de un--fuera de cualquier literal.- El
TokenizeSpecialCharactersdel mismo tokenizador tenía una red de seguridad por tokenif (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. - El break del recorrido interno de
ProcedureCallStatement.ExecuteAsyncerai++; break;mientras que elforexterior también hacíai++, saltando un token más allá de cada;. Todos losreportalternos en un cuerpo con múltiples reportes se perdían. Se corrigió dejandoiEN 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 compartidoInterpreter/Services/ParseTrace.RenderReportTokens(tokens, variables)consolida la lógica de renderizado previamente duplicada entreReportStatement.ExecuteAsyncyProcedureCallStatement. El comparador de patrones en líneaTryEvalToStringreconoce 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) comoraw.Substring(size-1-high, high-low+1)(Kmila almacena los valores destd_logic_vectorcomo 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 LCDend_casedel 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 escriturascols_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
assertde VHDL. El nuevoInterpreter/Repositories/AssertStatement.csrefleja la forma deReportStatement.Syntetizecaptura tres segmentos opcionales: tokens de condición (hastareport/severity/;respetandoinsideString), tokens de la expresión de report (hastaseverity/;) y el nivel de severidad (por defectoerror).ExecuteAsyncevalúa la condición medianteOperation(el mismo idioma que la cláusulawhendeExitStatement); cuando la condición es FALSA — o cuando la evaluación lanza una excepción — emite[assert-<sev>] <msg>medianteReportBus. Mensaje por defecto cuando la fuente omitereport:"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 deProcess+While/For/SimpleLoop/If/Case). - Colapso de secuencias de caracteres iguales consecutivos en el renderizado de report. La
ruta de cadena fragmentada en
ParseTrace.RenderReportTokensrastrea 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 quewait for N ns;dentro de unwhile ... loopanidado caía pordefault: _streamer.MoveNext(); el parser entonces despachaba elfortrailing como un encabezado de bucle for,ForLoopStructure.ParseRangeValueengullía tokens hasta el siguienteloop, ynew Operation(...)sobre la gigantesca cadena de rango malformada se colgaba indefinidamente. La corrección añadecase "wait"/case "report"/case "assert"a los cinco despachadores, delegando a los nuevos helpers estáticos compartidosInterpreter.Services.ParseTrace.SkipWaitStatementy.SkipToSemicolonInclusive.Process.SkipWaitStatement/Process.SkipToSemicolonInclusiveahora 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 porendse disparaba en la palabraenddentro del literal fragmentado — el helper compartido conmuta una banderainsideStringmediante el conteo de comillas sin emparejar, de modo que permanece opaco al recorrido.F-G-09 (ceguera del elaborador ante literales de cadena) —
SourceElaborator.SplitArchitectureenmascara 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|...)\bsolía coincidir falsamente con elenddentro dereport "TB: end of stimulus";, decrementandoinnerDepthde 1 (dentro destim_proc) a 0 y terminando prematuramentebodyTexten elend process;real. El emisor entonces escribíaend architecture;justo después dewait;sin unend process;de cierre para el último proceso. Corrección: el nuevo helperMaskStringLiteralsdevuelve una copia de la fuente que preserva la longitud con el contenido de"..."reemplazado por.(las comillas se preservan);SplitArchitectureusa la copia enmascarada para el regex +IndexOf(';')y el original para los retornos de subcadena, de modo que las cadenas dereportsobreviven intactas en la salida elaborada.F-G-10 (sombreado de artefactos del elaborador) — los escáneres omiten
_elaborated_*.vhd.SourceElaborator.ScanProjecty el bucle de escaneo deHeadlessSimulatorenumeran cada archivo.vhd*en el sandbox. Un_elaborated_<entry>.vhdsobrante 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,ElaborateOrNulldevolví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 estableceretain: trueen 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.RunAsyncregistra_cts.Token.Register(() => _engine?.Stop())después deInitializepara que la cancelación del lado del runner realmente rompa el bucle delta del motor.HeadlessSimulator.RunAsyncconstruye un CTS con ámbito de solicitud enPhase("start")(antes del análisis) en lugar de dentro de SimulationRunner (después del análisis), de modo queKMILA_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ímiteParseTrace.MAX_SKIP_ITER = 100_000+ lanzamiento con contexto mediante la nuevaParser.Models.ParserSkipOverflowExceptionen:VariableSyntetizer.Skip{Function,Component,Subtype,Alias,Procedure,Attribute,Disconnect,ToSemicolon}DeclarationmásTryParseParameterList; 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 deVariableSyntetizer- a la entrada de cada
Syntetizede subestructura.
- a la entrada de cada
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 enreportaún funcionan. Siempre activo: ellog[]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.csInterpreter/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 deElementSize × ArrayLengthbits;Variablelleva los nuevos camposElementSize/ArrayLength;DeferredIndex/DynamicIndexsegmentan elemento por elemento cuando la fuente tieneElementSize > 1. El nuevoDynamicIndexen modo expresión reevalúa los índices con forma derom_init(to_integer(unsigned(addr)))en cada delta.ParseIndexedAccessDeferredes consciente de los paréntesis. Trampa: Variable sobrecargaoperator==para comparar nombres — useis null/is not nullpara 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.TryParseNamedAggregatemaneja(0 => pack(170), …, others => …)colocando cada valor en el desplazamientokey × ElementSize.SkipFunctionDeclarationahora analiza la firma + cuerpo y registra unaFunctionDefinitionreal enPackageRegistry.ParseUserDefinedFunctionCallinvoca aFunctionExecutorcuando la función tiene un cuerpo. NuevoCachedScopeenFunctionDefinition— las llamadas subsiguientes reutilizan las mismas instancias deVariablede 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.ExecuteCaseStructureWithSchedulingAsyncahora extrae el segmento cuandoHasSelectorSlicees true;ResolveChoiceForCaseresuelve cada elecciónwhen— literales hexadecimales, cadenas de bits entrecomilladas, bits con comilla simple, y búsquedas de identificadores contra_allSignalspara quewhen OP_LOAD =>coincida con el valor binario de OP_LOAD.Canalización LOAD de P2A + detección de HALT (Fase E).
Operation.ParseFunctionCall::TryConsumeSliceArgemite unDeferredIndexcuando un argumento parece<var>(N)/<var>(H downto L);StandardFunctions.GetStringValue/GetIntValueaprendieron a evaluarlo; la rama INTEGER deOperator.SolveComparationrecurre 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ó aleftW + rightWsegú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 tratabaend ifcomo decremento + reincremento en la siguiente palabra clave, truncando los cuerpos en el primerendinterno. Ahora consumeend <closer>como una sola coincidencia; el prefijo de apertura de buclefor X in Y loop/while X loopsalta más allá de la palabra clavelooptrailing. La funciónkey_indexde P4 fue el disparador.Atributos de arreglo (
'length,'high,'low,'left,'right) —Operation.TryParseSignalAttribute. P4 necesitabato_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
SourceElaborator—SourceElaborator.cs. El nuevoExtractGenericDefaultslee los valores por defecto:=de la cláusula generic de una entidad hija; orden: primero los valores por defecto, luego las anulaciones explícitas degeneric map(...). Los textos de tipo de puerto también se sustituyen (para que los aliasSTD_LOGIC_VECTOR(WIDTH-1 downto 0)lleguen al padre comoSTD_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_operationInputsregistra el conjunto estático de señales de entrada (Variable,DeferredIndex.Source,DynamicIndex.Source+IndexSource; null = "ejecutar siempre" paraDeferredFunction/DynamicIndexen modo expresión)._lastDeltaChangedSignalstoma 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 enParser/Models/Variable.cs:135solo se dispara cuando_valueya no es nulo. El patrón de inicializador de objeto en el sitio de construcción de señales estableceSizeprimero (aún sin valor) yValuedespués, por lo que el marcador quedaba enValueliteralmente — 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 deVectorSizeantes de la construcción, condicionado aVectorSize > 0(de modo queunsigned/signed/bit_vector/std_logic_vectorqueden 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 VariableAggregate_others_Xmediante el constructor de 4 argumentos. PasarSize = 0a ese constructor disparaba la propia ruta de expansión de marcador deVariable.Size, que producía una cadena de longitud cero — elValuede 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)ParseOthersAggregateahora usa la construcción por inicializador de objeto para que el setter de Size nunca se llame, preservando la cadena marcador enValue; (b) el nuevo helperSignalScheduler.ExpandAggregateMarker(newValue, target)se ejecuta en cada escritura con cola drenada enApplyCurrentUpdatesWithLines— si la carga útil es un marcador(others=>X)ytarget.Size > 0, se expande a una cadena de bits del ancho detarget.Sizeusando 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_regrom_data_wobtienen valores reales.** El estancamiento restante del ACC en P3 es un error de contenido de la práctica (el mux de dirección de ROMeq_selaplica 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 ensanchamientoleftW + rightWde 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
subse inserta dos veces con distintos valores de señal de ámbito de arquitectura (k_sigimpulsada desde el puerto de entradakin = 3ykin = 7), y una funciónadd_offdeclarada en la arquitectura desubleek_sigdirectamente. La hipótesis del handoff era que la reutilización de instancias de Variable de parámetro por parte deCachedScopeenlazaría las Operations del cuerpo de la función a lak_sigde 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 entradasFunctionDefinitiondistintas enPackageRegistry, 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'porquexnorno estaba en la lista de casos. Ahoraa xnor b = '1' when a = b else '0'. Coincide con la forma derivada a manonot (a xor b), que había sido la solución alternativa del usuario.Services/Resources.cs—CalculateLUTsimplementado. Recorre el árbol de sentencias (Tasks / Then / Else / Body / Cases / Branches) mediante reflexión y suma los pesos de los operadores: operaciones lógicas1 × bits, aritmética+/−a3 × bits, comparadores amax(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 queStimulusBuilderusaba para presembrar el tick 0. (2) Se propagó un HashSetdirectlyChangeda 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 DEBUGpara 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 deFunctionDefinitionpara cada función y procedimiento que el parser ha omitido. Se limpia entre fixtures junto conEntityRegistry/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 enProcessVhdlCode.- Selectores
case <slice>(#10) —Process.SyntetizeBehaviourahora reconocecase op(3 downto 0) is; los índices de bits se almacenan enCaseStructure.SelectorSliceHigh/Lowpara la síntesis aguas abajo. <sig>'rangey<sig>'reverse_rangeen los buclesfor(#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) —
ParseInitializationValuepliega como constantes las cadenas+ - * /de modo queTOTAL : integer := BASE * TIMESse resuelve correctamente. - Error de detección del fin de arquitectura corregido (#15/#18):
funcProcDepthya 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.SyntetizeyArchitecture.SyntetizeaceptanIReadOnlyList<(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 unMissing ';' before '<name>' at Line N:Mexplí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 unLibraryCompilerpor defecto (sin callbacks de host, con recurso embebido de respaldo) y reenvíaEnsureLoaded().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ñadieronCellKind.TO_INTEGER,TO_VECTOR,RESIZE,EDGE_DETECT. LosSHL/SHR/ROL/ROR/BUFexistentes 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 medianteIEEEPrimitives.Register.Synthesis/ConcurrentAssignSynthesizer.cs— cuando el recorredor de Shunting-yard encuentra unaDeferredFunction, ahora resuelve los argumentos de la llamada a nets y llama aIEEEPrimitives.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ósticoUnknownIEEEFunction.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
Services/Operation.cs::ParseFunctionCall— al recorrer los tokens de argumentos de profundidad 1, si el token actual es un nombre de función conocido (medianteStandardFunctions.IsStandardFunctionoFunctionRegistry.IsKnown) y el siguiente token es(, se llama recursivamente aParseFunctionCallpara consumir la llamada anidada. La llamada recursiva usa su propioparenDepthlocal, por lo que el seguimiento de paréntesis se mantiene correcto.Synthesis/ConcurrentAssignSynthesizer.cs::ResolveDeferredArgs— se añadiócase DeferredFunction nestedque resuelve recursivamente los argumentos de la llamada anidada y la reduce medianteIEEEPrimitives.TryLower. Resultado: grafos de celdas correctamente cableados.Models/DeferredFunction.cs::Evaluate— evalúa recursivamente las DeferredFunctions anidadas a sus resultadosVariableantes de pasarlas aStandardFunctions.EvaluateFunction.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.RawHistoryyWaveformData.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 deWaveformTransition.Labeldegradará visiblemente la reproducción.- La Fase B (metadatos de resaltado de línea por sentencia ejecutada) requerirá instrumentar
DeltaCycleEnginepara emitir un log de frames junto conSignalHistory. El mismo consumidorExecutionTimelineDoc; el contrato está fijado enApp/Kmila/Kmila.Shared/Models/ExecutionTimeline.cs(la forma deTimelineStepconT,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]