diff --git a/src/OneWare.Core/Styles/Icons.axaml b/src/OneWare.Core/Styles/Icons.axaml index 521bc9ee..97dfb0d2 100644 --- a/src/OneWare.Core/Styles/Icons.axaml +++ b/src/OneWare.Core/Styles/Icons.axaml @@ -1,6 +1,15 @@  + + + + + + + + + diff --git a/src/OneWare.IceBreaker/IceBreakerModule.cs b/src/OneWare.IceBreaker/IceBreakerModule.cs index e0a8365a..b58ab346 100644 --- a/src/OneWare.IceBreaker/IceBreakerModule.cs +++ b/src/OneWare.IceBreaker/IceBreakerModule.cs @@ -15,6 +15,6 @@ public void RegisterTypes(IContainerRegistry containerRegistry) public void OnInitialized(IContainerProvider containerProvider) { - containerProvider.Resolve().AddFpga(new IceBreakerV10EViewModel()); + containerProvider.Resolve().AddFpga(); } } \ No newline at end of file diff --git a/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs b/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs index b6ce4189..b9410f63 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModelBase.cs @@ -46,6 +46,22 @@ public NodeModel? SelectedNode public RelayCommand DisconnectCommand { get; } + private string _searchTextPins = string.Empty; + + public string SearchTextPins + { + get => _searchTextPins; + set => SetProperty(ref _searchTextPins, value); + } + + private string _searchTextNodes = string.Empty; + + public string SearchTextNodes + { + get => _searchTextNodes; + set => SetProperty(ref _searchTextNodes, value); + } + public FpgaModelBase() { ConnectCommand = new RelayCommand(Connect, () => SelectedNode is not null && SelectedPin is not null); @@ -63,18 +79,49 @@ public FpgaModelBase() ConnectCommand.NotifyCanExecuteChanged(); DisconnectCommand.NotifyCanExecuteChanged(); }); + + this.WhenValueChanged(x => x.SearchTextPins).Subscribe(SearchPins); + this.WhenValueChanged(x => x.SearchTextNodes).Subscribe(SearchNodes); + + } + + private void SearchPins(string? search) + { + if (string.IsNullOrWhiteSpace(search)) + { + SelectedPin = null; + return; + } + + SelectedPin = VisiblePins.FirstOrDefault(x => x.Name.Contains(search, StringComparison.OrdinalIgnoreCase) + || x.Description.Contains(search, StringComparison.OrdinalIgnoreCase)); + } + + private void SearchNodes(string? search) + { + if (string.IsNullOrWhiteSpace(search)) + { + SelectedNode = null; + return; + } + + SelectedNode = VisibleNodes.FirstOrDefault(x => x.Name.Contains(search, StringComparison.OrdinalIgnoreCase)); } private void Connect() { - SelectedPin!.Connection = SelectedNode; + if (SelectedPin is null || SelectedNode is null) return; + SelectedPin.Connection = SelectedNode; + SelectedNode.Connection = SelectedPin; ConnectCommand.NotifyCanExecuteChanged(); DisconnectCommand.NotifyCanExecuteChanged(); } private void Disconnect() { + if (SelectedPin is null || SelectedNode is null) return; SelectedPin!.Connection = null; + SelectedNode!.Connection = null; ConnectCommand.NotifyCanExecuteChanged(); DisconnectCommand.NotifyCanExecuteChanged(); } diff --git a/src/OneWare.UniversalFpgaProjectSystem/Models/NodeModel.cs b/src/OneWare.UniversalFpgaProjectSystem/Models/NodeModel.cs index 00793a79..1ffc6f8f 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/Models/NodeModel.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/Models/NodeModel.cs @@ -1,12 +1,28 @@ -namespace OneWare.UniversalFpgaProjectSystem.Models; +using CommunityToolkit.Mvvm.ComponentModel; -public class NodeModel +namespace OneWare.UniversalFpgaProjectSystem.Models; + +public class NodeModel : ObservableObject { public string Name { get; } + + public string Direction { get; } + + + private FpgaPinModel? _connection; + public FpgaPinModel? Connection + { + get => _connection; + set + { + this.SetProperty(ref _connection, value); + } + } - public NodeModel(string name) + public NodeModel(string name, string direction) { Name = name; + Direction = direction; } public override string ToString() diff --git a/src/OneWare.UniversalFpgaProjectSystem/Services/FpgaService.cs b/src/OneWare.UniversalFpgaProjectSystem/Services/FpgaService.cs index c303e87c..86a9bac7 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/Services/FpgaService.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/Services/FpgaService.cs @@ -5,10 +5,18 @@ namespace OneWare.UniversalFpgaProjectSystem.Services; public class FpgaService { - public ObservableCollection FpgaModels { get; } = new(); + public ObservableCollection FpgaModels { get; } = new(); - public void AddFpga(FpgaModelBase fpgaModelBase) + public void AddFpga() where T : FpgaModelBase { - FpgaModels.Add(fpgaModelBase); + FpgaModels.Add(typeof(T)); + } + + public IEnumerable GetFpgas() + { + foreach (var t in FpgaModels) + { + yield return Activator.CreateInstance(t) as FpgaModelBase; + } } } \ No newline at end of file diff --git a/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs b/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs index 061b99aa..502ac54c 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/ViewModels/UniversalFpgaProjectCompileViewModel.cs @@ -14,7 +14,7 @@ public class UniversalFpgaProjectCompileViewModel : ObservableObject private readonly UniversalFpgaProjectRoot _project; public string Title => "Connect and Compile - " + _project.Header; - public ObservableCollection FpgaModels { get; } + public List FpgaModels { get; } private FpgaModelBase? _selectedFpga; public FpgaModelBase? SelectedFpga @@ -34,7 +34,7 @@ public UniversalFpgaProjectCompileViewModel(FpgaService fpgaService, NodeProvide { _project = project; - FpgaModels = fpgaService.FpgaModels; + FpgaModels = fpgaService.GetFpgas().ToList(); SelectedFpga = FpgaModels.FirstOrDefault(); diff --git a/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml b/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml index 7e7396f7..b1a83925 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml +++ b/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml @@ -16,8 +16,7 @@ x:DataType="viewModels:UniversalFpgaProjectCompileViewModel" Name="UniversalFpgaProjectCompileViewView"> - + @@ -41,30 +40,32 @@ IsVisible="{Binding Converter={x:Static converters:SharedConverters.ViewFoundConverter}}" Content="{Binding }" /> - - + + + - - - - - - - - - - - - + + + + + + + + + + - + - - + + - - - - - - - - - - + VerticalContentAlignment="Center" SearchButtonVisible="False" SearchText="{Binding SearchTextNodes}"/> + + + + + + + + diff --git a/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml.cs b/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml.cs index ab68b387..a6dea3be 100644 --- a/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml.cs +++ b/src/OneWare.UniversalFpgaProjectSystem/Views/UniversalFpgaProjectCompileView.axaml.cs @@ -1,4 +1,5 @@ -using OneWare.Shared.Controls; +using DynamicData.Binding; +using OneWare.Shared.Controls; namespace OneWare.UniversalFpgaProjectSystem.Views; @@ -7,5 +8,10 @@ public partial class UniversalFpgaProjectCompileView : FlexibleWindow public UniversalFpgaProjectCompileView() { InitializeComponent(); + + VisiblePinDataGrid.WhenValueChanged(x => x.SelectedItem).Subscribe(x => + { + if(x is not null) VisiblePinDataGrid.ScrollIntoView(x, null); + }); } } \ No newline at end of file diff --git a/src/OneWare.Vhdl/Parsing/VhdlNodeProvider.cs b/src/OneWare.Vhdl/Parsing/VhdlNodeProvider.cs index f8f1978f..956faf58 100644 --- a/src/OneWare.Vhdl/Parsing/VhdlNodeProvider.cs +++ b/src/OneWare.Vhdl/Parsing/VhdlNodeProvider.cs @@ -10,34 +10,46 @@ public class VhdlNodeProvider : INodeProvider public IEnumerable ExtractNodes(IProjectFile file) { var fileLines = File.ReadAllLines(file.FullPath); - - bool inEntity = false; - foreach (var line in fileLines) - { - // Check for entity start - if (Regex.IsMatch(line, @"\bentity\b", RegexOptions.IgnoreCase)) - { - inEntity = true; - continue; - } + return ExtractEntityPorts(fileLines); + } - // Check for entity end - if (inEntity && Regex.IsMatch(line, @"\bend entity\b", RegexOptions.IgnoreCase)) - { - inEntity = false; - continue; - } + public static IEnumerable ExtractEntityPorts(IEnumerable vhdlLines) + { + bool inPortSection = false; + var portPattern = @"\b(\w+)\s+:\s+(in|out|inout|buffer)\s+(\w+)(?:\((\d+)\s+downto\s+(\d+)\))?(?:\s+:=\s+[^;]+)?"; + + foreach (var line in vhdlLines) + { + if (line.Trim().StartsWith("port (", StringComparison.OrdinalIgnoreCase)) inPortSection = true; + if (line.Trim().StartsWith(");", StringComparison.OrdinalIgnoreCase)) inPortSection = false; - // Extract inputs and outputs - if (inEntity) + if (inPortSection) { - MatchCollection matches = Regex.Matches(line, @"\b(\w+)\s*:\s*(in|out)\s*\w+", RegexOptions.IgnoreCase); - foreach (Match match in matches) + var match = Regex.Match(line, portPattern, RegexOptions.IgnoreCase); + if (match.Success) { - string portName = match.Groups[1].Value; - string direction = match.Groups[2].Value; - - yield return new NodeModel(portName); + var portName = match.Groups[1].Value; + var direction = match.Groups[2].Value; + var portType = match.Groups[3].Value; + var upperBound = match.Groups[4].Value; + var lowerBound = match.Groups[5].Value; + + if (!string.IsNullOrEmpty(upperBound) && !string.IsNullOrEmpty(lowerBound)) + { + // Expand std_logic_vector into individual ports + int upper = int.Parse(upperBound); + int lower = int.Parse(lowerBound); + + for (var i = upper; i >= lower; i--) + { + yield return new NodeModel($"{portName}[{i}]", direction); //$"{portName}({i}) : {direction} {portType}"; + } + } + else + { + // Single port + yield return new NodeModel(portName, direction); // $"{portName} : {direction} {portType}"; + } } } }