Skip to content

Commit e5b1a6c

Browse files
authored
Merge pull request #577 from LH-and-FPGA/Debug2-PS
Debug2 ps
2 parents e8e7fe6 + 5960a00 commit e5b1a6c

8 files changed

Lines changed: 172 additions & 43 deletions

File tree

docs/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ See [Getting Started](gettingStarted.html) for how to put Issie on your laptop o
1111

1212
See [User Tutorial](userGuide.html) for a useful introduction to Issie on one page which you can follow or read.
1313

14+
See [Parameter System](parameterSystem.html) for a detailed explanation of symbolic parameters, expressions, constraints, and simulation integration.
15+
1416
<br>
1517

1618
## What is ISSIE?

docs/parameterSystem.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ The parser uses recursive descent with separate functions for each precedence le
206206
- `parseFactors`: Processes multiplication, division, modulo
207207
- `parseExpressionTokens`: Handles addition and subtraction
208208

209+
Notes and caveats:
210+
- Tokenizer restricts inputs to digits/letters/operators/whitespace; unsupported characters are reported precisely.
211+
- Division and modulo are evaluated during constant-folding; add a MinVal constraint to prevent zero divisors where needed.
212+
213+
Code: `src/Renderer/Common/ParameterTypes.fs` (`parseExpression`, tokenizer regex, and helpers)
214+
209215
## Parameter Scoping & Precedence
210216

211217
### Scope Levels
@@ -474,6 +480,41 @@ resolveParametersForComponent: ParamBindings -> Map<ParamSlot, ConstrainedExpr>
474480
evaluateConstraints: ParamBindings -> ConstrainedExpr list -> (Msg -> unit) -> Result<Unit, ParamConstraint list>
475481
```
476482

483+
## Resolution Mechanics Deep-Dive
484+
485+
- UI evaluation: `ParameterTypes.evaluateParamExpression` performs recursive substitution and constant-folding with detailed errors. Used by `ParameterView` for validation and preview.
486+
- Graph evaluation: `GraphMerger.resolveParametersInSimulationGraph` uses internal `evalExpr` (returns `Option<int>`) and `applySlotValue` to write concrete values into `SimulationGraph` component types after merge.
487+
- Validation evaluation: `CanvasStateAnalyser.checkCustomComponentForOkIOs` embeds a minimal evaluator supporting only `PInt` and `PParameter` to resolve port widths quickly for label checking.
488+
- Slot access: Lenses `ParameterView.compSlot_` and `ParameterView.modelToSlot_` provide strongly typed access into `Component.Type` for `Buswidth`, `NGateInputs`, and `IO label`.
489+
490+
## Developer Notes (Files & Responsibilities)
491+
492+
- `src/Renderer/Common/ParameterTypes.fs`: Types (`ParamExpression`, `ParamConstraint`, `ParamSlot`, `ParameterDefs`), parser (`parseExpression`), evaluator (`evaluateParamExpression`), renderer (`renderParamExpression`).
493+
- `src/Renderer/UI/ParameterView.fs`: Sheet defaults and slot bindings CRUD, constraint checking, component updates, and parameter UI fields/popups.
494+
- `src/Renderer/UI/CatalogueView.fs`: Merges parent sheet defaults with sub-sheet defaults, resolves canvas before extracting `InputLabels`/`OutputLabels`, sets `ParameterBindings` on instances.
495+
- `src/Renderer/Simulator/GraphMerger.fs`: Two-stage resolution during merge; instance bindings first, then sheet defaults; recursion into nested custom components.
496+
- `src/Renderer/Simulator/CanvasStateAnalyser.fs`: Lightweight parameter resolution for port label validation.
497+
498+
## Development History
499+
500+
Key commits that shaped the current system (from `git log`):
501+
- a67fa72f Fix parameter resolution in simulation graph creation
502+
- Passes `loadedDependencies` into merger; applies instance-specific `ParameterBindings` for custom components.
503+
- 83bb0b0b Fix forward reference issue in parameter resolution
504+
- Introduces two-stage resolution: resolve custom component instance bindings first, then sheet-level defaults.
505+
- b510fe4b Parameter System Redo
506+
- Reworks UI binding flow and simulation integration; clearer separation of concerns.
507+
- edf61e87 Parameter System Support
508+
- Integrates `ParameterTypes.fs`, updates merger/validation, and adds comprehensive documentation.
509+
510+
For a side-by-side comparison with an earlier streamlined approach, see `PARAMETER_SYSTEM_COMPARISON.md`.
511+
512+
## Known Limitations
513+
514+
- Integer-only parameters today (`ParamInt = int`); very large constants may require future `bigint`.
515+
- No explicit guard on divide/modulo by zero during constant-folding; enforce with constraints.
516+
- Parameter names are unqualified; deeper inheritance across sheet hierarchies may require qualification if extended.
517+
477518
## Best Practices
478519

479520
1. **Use descriptive parameter names**: `DATA_WIDTH` instead of `W`
@@ -504,4 +545,4 @@ evaluateConstraints: ParamBindings -> ConstrainedExpr list -> (Msg -> unit) -> R
504545
### Simulation Failure
505546
- Verify all parameters resolve to valid integers
506547
- Check for circular parameter dependencies
507-
- Ensure component types match parameter slots
548+
- Ensure component types match parameter slots

src/Renderer/Common/ParameterTypes.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ type CompSlotName =
4949
| NGateInputs
5050
| IO of Label: string
5151
| CustomCompParam of ParamName: string // TODO: implement this case
52+
// SplitN-specific parameterised slots
53+
| SplitNWidth of Index: int
54+
| SplitNLSB of Index: int
5255

5356
/// A slot in a component instance that can be bound to a parameter expression
5457
/// CompId should be a ComponentId but then we would need these types to be defined after CommonTypes.

src/Renderer/Simulator/GraphMerger.fs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,13 @@ let rec resolveParametersInSimulationGraph
428428
// IO ports
429429
| IO _, Input1 (_, dv) -> Input1 (value, dv)
430430
| IO _, Output _ -> Output value
431+
// SplitN output slots
432+
| SplitNWidth idx, SplitN (n, widths, lsbs) when idx >= 0 && idx < List.length widths ->
433+
let newWidths = widths |> List.mapi (fun i w -> if i = idx then value else w)
434+
SplitN (n, newWidths, lsbs)
435+
| SplitNLSB idx, SplitN (n, widths, lsbs) when idx >= 0 && idx < List.length lsbs ->
436+
let newLsbs = lsbs |> List.mapi (fun i l -> if i = idx then value else l)
437+
SplitN (n, widths, newLsbs)
431438
| _ -> compType
432439

433440
// Process a single component
@@ -442,12 +449,13 @@ let rec resolveParametersInSimulationGraph
442449
| Some value ->
443450
Ok { c with Type = applySlotValue c.Type slot.CompSlot value }
444451
| None ->
445-
Error ({
452+
let err: SimGraphTypes.SimulationError = {
446453
ErrType = GenericSimError "Parameter expression could not be fully evaluated"
447454
InDependency = Some currDiagramName
448455
ComponentsAffected = [compId]
449-
ConnectionsAffected = []
450-
}: SimulationError)
456+
ConnectionsAffected = []
457+
}
458+
Error err
451459
)
452460
) (Ok comp)
453461

src/Renderer/UI/ParameterView.fs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,20 @@ let compSlot_ (compSlotName:CompSlotName) : Optics.Lens<Component, int> =
101101
| Input1 (busWidth, _) -> busWidth
102102
| Output busWidth -> busWidth
103103
| _ -> failwithf $"Invalid component {comp.Type} for IO"
104+
| SplitNWidth idx ->
105+
match comp.Type with
106+
| SplitN (_, widths, _) ->
107+
if idx >= 0 && idx < List.length widths then
108+
widths[idx]
109+
else failwithf $"SplitNWidth index %d{idx} out of range"
110+
| _ -> failwithf $"Invalid component {comp.Type} for SplitNWidth"
111+
| SplitNLSB idx ->
112+
match comp.Type with
113+
| SplitN (_, _, lsbs) ->
114+
if idx >= 0 && idx < List.length lsbs then
115+
lsbs[idx]
116+
else failwithf $"SplitNLSB index %d{idx} out of range"
117+
| _ -> failwithf $"Invalid component {comp.Type} for SplitNLSB"
104118
| CustomCompParam paramName ->
105119
match comp.Type with
106120
| Custom customComp ->
@@ -152,6 +166,20 @@ let compSlot_ (compSlotName:CompSlotName) : Optics.Lens<Component, int> =
152166
| Input1 (_, defaultValue) -> Input1 (value, defaultValue)
153167
| Output _ -> Output value
154168
| _ -> failwithf $"Invalid component {comp.Type} for IO"
169+
| SplitNWidth idx ->
170+
match comp.Type with
171+
| SplitN (n, widths, lsbs) ->
172+
if idx < 0 || idx >= List.length widths then failwithf $"SplitNWidth index %d{idx} out of range"
173+
let newWidths = widths |> List.mapi (fun i w -> if i = idx then value else w)
174+
SplitN (n, newWidths, lsbs)
175+
| _ -> failwithf $"Invalid component {comp.Type} for SplitNWidth"
176+
| SplitNLSB idx ->
177+
match comp.Type with
178+
| SplitN (n, widths, lsbs) ->
179+
if idx < 0 || idx >= List.length lsbs then failwithf $"SplitNLSB index %d{idx} out of range"
180+
let newLsbs = lsbs |> List.mapi (fun i l -> if i = idx then value else l)
181+
SplitN (n, widths, newLsbs)
182+
| _ -> failwithf $"Invalid component {comp.Type} for SplitNLSB"
155183
| CustomCompParam paramName ->
156184
match comp.Type with
157185
| Custom customComp ->
@@ -232,8 +260,8 @@ let evaluateConstraints
232260
else Error result
233261

234262

235-
/// Generates a ParameterExpression from input text
236-
/// Operators are left-associative
263+
// Generates a ParameterExpression from input text
264+
// Operators are left-associative
237265
// parseExpression has been moved to ParameterTypes module
238266

239267

@@ -306,6 +334,20 @@ let updateComponent dispatch model slot value =
306334
match comp.Type with
307335
| GateN (gateType, _) -> model.Sheet.ChangeGate sheetDispatch compId gateType value
308336
| _ -> failwithf $"Gate cannot have type {comp.Type}"
337+
| SplitNWidth idx ->
338+
match comp.Type with
339+
| SplitN (n, widths, lsbs) ->
340+
if idx < 0 || idx >= List.length widths then failwithf $"SplitNWidth index %d{idx} out of range"
341+
let newWidths = widths |> List.mapi (fun i w -> if i = idx then value else w)
342+
model.Sheet.ChangeSplitN sheetDispatch compId n newWidths lsbs
343+
| _ -> failwithf $"SplitNWidth cannot be applied to {comp.Type}"
344+
| SplitNLSB idx ->
345+
match comp.Type with
346+
| SplitN (n, widths, lsbs) ->
347+
if idx < 0 || idx >= List.length lsbs then failwithf $"SplitNLSB index %d{idx} out of range"
348+
let newLsbs = lsbs |> List.mapi (fun i l -> if i = idx then value else l)
349+
model.Sheet.ChangeSplitN sheetDispatch compId n widths newLsbs
350+
| _ -> failwithf $"SplitNLSB cannot be applied to {comp.Type}"
309351
| CustomCompParam paramName ->
310352
// For custom component parameters, we need to update the parameter bindings
311353
match comp.Type with
@@ -885,7 +927,19 @@ let resolveParametersForComponent
885927
| Buswidth -> currentType |> (evaluatedValue ^= buswidthPrism)
886928
| NGateInputs -> currentType |> (evaluatedValue ^= ngateInputsPrism)
887929
| IO _ -> currentType |> (evaluatedValue ^= ioPortPrism)
888-
| _ -> currentType
930+
| SplitNWidth idx ->
931+
match currentType with
932+
| SplitN (n, widths, lsbs) when idx >= 0 && idx < List.length widths ->
933+
let newWidths = widths |> List.mapi (fun i w -> if i = idx then evaluatedValue else w)
934+
SplitN (n, newWidths, lsbs)
935+
| _ -> currentType
936+
| SplitNLSB idx ->
937+
match currentType with
938+
| SplitN (n, widths, lsbs) when idx >= 0 && idx < List.length lsbs ->
939+
let newLsbs = lsbs |> List.mapi (fun i l -> if i = idx then evaluatedValue else l)
940+
SplitN (n, widths, newLsbs)
941+
| _ -> currentType
942+
| CustomCompParam _ -> currentType
889943
(newType, None)
890944
| Error err -> (currentType, Some err)
891945
)
@@ -1127,6 +1181,8 @@ let private makeSlotsField (model: ModelType.Model) (comp:LoadedComponent) dispa
11271181
| Buswidth -> "Buswidth"
11281182
| NGateInputs -> "Num inputs"
11291183
| IO label -> $"Input/output {label}"
1184+
| SplitNWidth idx -> $"SplitN output {idx} width"
1185+
| SplitNLSB idx -> $"SplitN output {idx} LSB"
11301186
| CustomCompParam paramName -> $"Custom parameter {paramName}"
11311187

11321188
let name = if Map.containsKey (ComponentId slot.CompId) model.Sheet.Wire.Symbol.Symbols then

src/Renderer/UI/SelectedComponentView.fs

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -574,41 +574,48 @@ let private changeSplitN model (comp:Component) dispatch =
574574
[Style [Color Red]]
575575
[str errText]
576576

577-
let constraints = [
578-
MinVal (PInt 2, "Must have at least 2 outputs")
579-
MaxVal (PInt Constants.maxSplitMergeBranches, $"Cannot have more than {Constants.maxSplitMergeBranches} outputs")
580-
]
581-
582-
ParameterView.paramInputField model title 2 (Some nInp) constraints (Some comp) NGateInputs dispatch
583-
div [Style [Display DisplayOptions.Flex; MarginLeft "180px"]] [
584-
// Add headers for the "Width" and "LSB" columns
585-
Label.label [Label.Props [Style [TextAlign TextAlignOptions.Center; MarginRight "20px"]]] [str "Width"]
586-
Label.label [Label.Props [Style [TextAlign TextAlignOptions.Center; MarginLeft "20px"]]] [str "LSB"]
577+
// Plain numeric input for number of outputs (no parameter box)
578+
Field.div [] [
579+
Label.label [] [ str title ]
580+
Input.number [
581+
Input.Props [ Style [ Width "80px" ]; Min 2; Max Constants.maxSplitMergeBranches ]
582+
Input.DefaultValue (string nInp)
583+
Input.OnChange (getIntEventValue >> fun newNum ->
584+
let newWidths = changeWidths widths newNum 1
585+
let newLsbs = changeLsbs lsbs newWidths newNum
586+
model.Sheet.ChangeSplitN sheetDispatch (ComponentId comp.Id) newNum newWidths newLsbs)
587+
]
587588
]
588-
List.mapi2 (fun index defaultWidth defaultLsb ->
589-
let portTitle =
590-
match defaultWidth with
591-
| n when n > 1 -> sprintf "Output Port %d" index
592-
| _ -> sprintf "Output Port %d" index
593-
let bits =
594-
match defaultWidth with
595-
| n when n > 1 -> sprintf "(%d:%d)" (n+defaultLsb-1) defaultLsb
596-
| _ -> sprintf "(%d)" defaultLsb
597-
intFormField2 portTitle bits "60px" defaultWidth defaultLsb 1 0
598-
(fun newWidth ->
599-
let neWidths =
600-
widths
601-
|> List.mapi (fun i x -> if i = index then newWidth else x)
602-
model.Sheet.ChangeSplitN sheetDispatch (ComponentId comp.Id) nInp neWidths lsbs
603-
)
604-
(fun lsb ->
605-
let newLsbs =
606-
lsbs
607-
|> List.mapi (fun i x -> if i = index then lsb else x)
608-
model.Sheet.ChangeSplitN sheetDispatch (ComponentId comp.Id) nInp widths newLsbs
609-
)
610-
) widths lsbs
611-
|> div [Style [MarginBottom "20px"]]
589+
590+
// Parameter boxes for each output's Width and LSB
591+
widths
592+
|> List.mapi (fun index defaultWidth ->
593+
let defaultLsb = lsbs.[index]
594+
div [ Style [ MarginLeft "10px"; MarginBottom "10px" ] ] [
595+
Label.label [] [ str (sprintf "Output Port %d" index) ]
596+
// Width parameter
597+
ParameterView.paramInputField
598+
model
599+
"Width"
600+
1
601+
(Some defaultWidth)
602+
[ MinVal (PInt 1, "Width must be at least 1") ]
603+
(Some comp)
604+
(SplitNWidth index)
605+
dispatch
606+
// LSB parameter
607+
ParameterView.paramInputField
608+
model
609+
"LSB"
610+
0
611+
(Some defaultLsb)
612+
[ MinVal (PInt 0, "LSB must be non-negative") ]
613+
(Some comp)
614+
(SplitNLSB index)
615+
dispatch
616+
]
617+
)
618+
|> div [Style [MarginBottom "20px"]]
612619
]
613620

614621

src/Renderer/UI/Style.fs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,17 @@ let ttGridHiddenColumnProps gridWidth= [
220220
]
221221

222222
let ttGridContainerStyle model =
223-
let widthRightSec = rightSectionWidth model
223+
// Compute number of visible columns (total IOs minus hidden columns)
224+
let totalCols = model.TTConfig.IOOrder.Length
225+
let hiddenCols = model.TTConfig.HiddenColumns.Length
226+
// Ensure at least 1 to avoid invalid CSS when table is empty
227+
let visibleCols = max 1 (totalCols - hiddenCols)
224228
Style [
225229
Display DisplayOptions.Grid
226230
GridAutoFlow "column"
231+
// Make grid span the container width and distribute columns evenly
232+
Width "100%"
233+
GridTemplateColumns (sprintf "repeat(%d, minmax(0, 1fr))" visibleCols)
227234
]
228235

229236

src/Renderer/UI/TruthTable/TruthTableUpdate.fs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,12 @@ let truthTableUpdate (model: Model) (msg:TTMsg) : (Model * Cmd<Msg>) =
250250
/// Recursive function to hide columns and adjust the positions of the remaining
251251
/// visible columns.
252252
let rec correctProps (index: int) (acc: list<CellIO*list<CSSProp>>) (lst: CellIO list): list<CellIO*list<CSSProp>>=
253-
let hiddenProps = ttGridHiddenColumnProps model.TTConfig.IOOrder.Length
253+
// Number of visible columns is total minus hidden
254+
let visibleCount =
255+
(Array.toList model.TTConfig.IOOrder
256+
|> List.except model.TTConfig.HiddenColumns
257+
|> List.length)
258+
let hiddenProps = ttGridHiddenColumnProps visibleCount
254259
match lst with
255260
| [] -> acc
256261
| io::tl ->

0 commit comments

Comments
 (0)