Skip to content

Commit

Permalink
new vhdlnodeprovider
Browse files Browse the repository at this point in the history
  • Loading branch information
HendrikMennen committed Nov 9, 2024
1 parent f907f0f commit c97ec7b
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 39 deletions.
4 changes: 2 additions & 2 deletions src/OneWare.UniversalFpgaProjectSystem/Models/FpgaModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ private void AddPin(HardwarePin pin)
public void AddNode(FpgaNode node)
{
var model = new FpgaNodeModel(node);
NodeModels.Add(node.Name, model);
VisibleNodeModels.Add(model);
var added = NodeModels.TryAdd(node.Name, model);
if(added) VisibleNodeModels.Add(model);
}

private void AddInterface(HardwareInterface fpgaInterface)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using OneWare.UniversalFpgaProjectSystem.Models;
using OneWare.UniversalFpgaProjectSystem.Parser;
using OneWare.UniversalFpgaProjectSystem.Services;
using Prism.Ioc;

namespace OneWare.UniversalFpgaProjectSystem.ViewModels;

Expand Down Expand Up @@ -54,7 +55,14 @@ public UniversalFpgaProjectPinPlannerViewModel(IWindowService windowService,
var provider = fpgaService.GetNodeProvider(file.Extension);
if (provider is not null)
{
_nodes = provider.ExtractNodes(file).ToArray();
try
{
_nodes = provider.ExtractNodes(file).ToArray();
}
catch (Exception e)
{
ContainerLocator.Container.Resolve<ILogger>().Error(e.Message, e);
}
}
}

Expand Down
146 changes: 110 additions & 36 deletions src/OneWare.Vhdl/Parsing/VhdlNodeProvider.cs
Original file line number Diff line number Diff line change
@@ -1,58 +1,132 @@
using System.Text.RegularExpressions;
using System.Data;
using System.Text.RegularExpressions;
using OneWare.Essentials.Models;
using OneWare.UniversalFpgaProjectSystem.Fpga;
using OneWare.UniversalFpgaProjectSystem.Services;

namespace OneWare.Vhdl.Parsing;

public class VhdlNodeProvider : INodeProvider
// ReSharper disable once ClassNeverInstantiated.Global
public partial class VhdlNodeProvider : INodeProvider
{
public IEnumerable<FpgaNode> ExtractNodes(IProjectFile file)
{
var fileLines = File.ReadAllLines(file.FullPath);
return ExtractEntityPorts(fileLines);
var code = File.ReadAllText(file.FullPath);
return ExtractNodes(code);
}

public static IEnumerable<FpgaNode> ExtractEntityPorts(IEnumerable<string> vhdlLines)
private static List<FpgaNode> ExtractNodes(string vhdlCode)
{
var inPortSection = false;
var portPattern =
@"\b(\w+)\s+:\s+(in|out|inout|buffer)\s+(\w+)(?:\((\d+)\s+downto\s+(\d+)\))?(?:\s+:=\s+[^;]+)?";
var nodes = new List<FpgaNode>();

// First, extract generics
var generics = ExtractGenerics(vhdlCode);

foreach (var line in vhdlLines)
// Extract port declarations
ExtractPorts(vhdlCode, nodes, generics);

return nodes;
}

private static Dictionary<string, int> ExtractGenerics(string vhdlCode)
{
var genericValues = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

var genericMatch = GenericMatch().Match(vhdlCode);
if (genericMatch.Success)
{
var genericContent = genericMatch.Groups[1].Value;
var genericDeclarations = GenericDeclarationMatch().Matches(genericContent);

foreach (Match match in genericDeclarations)
{
var name = match.Groups[1].Value;
var value = match.Groups[2].Value;
genericValues[name] = int.Parse(value);
}
}

return genericValues;
}

private static void ExtractPorts(string vhdlCode, List<FpgaNode> nodes, Dictionary<string, int> genericValues)
{
var portMatch = PortMatch().Match(vhdlCode);
if (!portMatch.Success) return;

var portContent = portMatch.Groups[1].Value;
var portDeclarations = portContent.Split(';', StringSplitOptions.RemoveEmptyEntries);

foreach (var declaration in portDeclarations)
{
if (line.Trim().Replace(" ", "").StartsWith("port(", StringComparison.OrdinalIgnoreCase))
inPortSection = true;
if (line.Trim().StartsWith(");", StringComparison.OrdinalIgnoreCase)) inPortSection = false;
if (string.IsNullOrWhiteSpace(declaration)) continue;

// Match for vector declarations
var vectorMatch = VectorMatch().Match(declaration);

// Match for single std_logic declarations
var logicMatch = LogicMatch().Match(declaration);

if (inPortSection)
if (vectorMatch.Success)
{
var match = Regex.Match(line, portPattern, RegexOptions.IgnoreCase);
if (match.Success)
var name = vectorMatch.Groups[1].Value;
var direction = vectorMatch.Groups[2].Value.ToUpper();
var upperBoundExpr = vectorMatch.Groups[3].Value.Trim();
var lowerBoundExpr = vectorMatch.Groups[4].Value.Trim();

var upperBound = EvaluateExpression(upperBoundExpr, genericValues);
var lowerBound = EvaluateExpression(lowerBoundExpr, genericValues);

// Create nodes for each bit in the vector
for (var i = lowerBound; i <= upperBound; i++)
{
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
var upper = int.Parse(upperBound);
var lower = int.Parse(lowerBound);

for (var i = upper; i >= lower; i--)
yield return new FpgaNode($"{portName}[{i}]",
direction); //$"{portName}({i}) : {direction} {portType}";
}
else
{
// Single port
yield return new FpgaNode(portName, direction); // $"{portName} : {direction} {portType}";
}
nodes.Add(new FpgaNode($"{name}[{i}]", direction));
}
}
else if (logicMatch.Success)
{
nodes.Add(new FpgaNode(logicMatch.Groups[1].Value, logicMatch.Groups[2].Value.ToUpper()));
}
}
}

private static int EvaluateExpression(string expression, Dictionary<string, int> genericValues)
{
// Replace generic constants with their values
foreach (var generic in genericValues)
{
expression = Regex.Replace(expression,
$@"\b{generic.Key}\b",
generic.Value.ToString(),
RegexOptions.IgnoreCase);
}

// Clean up the expression
expression = expression.Replace(" ", "");

try
{
var dt = new DataTable();
return Convert.ToInt32(dt.Compute(expression, ""));
}
catch (Exception)
{
throw new ArgumentException($"Unable to evaluate expression: {expression}");
}
}

[GeneratedRegex(@"(\w+)\s*:\s*(IN|OUT|INOUT)\s*STD_LOGIC(?:\s*:=\s*'[01]')?", RegexOptions.IgnoreCase, "en-US")]
private static partial Regex LogicMatch();

[GeneratedRegex(@"port\s*\(((?:[^()]*|\((?:[^()]*|\([^()]*\))*\))*)\)\s*;", RegexOptions.IgnoreCase | RegexOptions.Singleline, "en-US")]
private static partial Regex PortMatch();

[GeneratedRegex(@"Generic\s*\((.*?)\)", RegexOptions.IgnoreCase | RegexOptions.Singleline, "en-US")]
private static partial Regex GenericMatch();

[GeneratedRegex(@"(\w+)\s*:\s*\w+\s*:=\s*(\d+)")]
private static partial Regex GenericDeclarationMatch();

[GeneratedRegex(@"(\w+)\s*:\s*(IN|OUT|INOUT)\s*STD_LOGIC_VECTOR\s*\(\s*(\d+|\w+(?:\s*[+\-*/]\s*\d+)?)\s*downto\s*(\d+|\w+(?:\s*[+\-*/]\s*\d+)?)\s*\)(?:\s*:=\s*[^;]+)?", RegexOptions.IgnoreCase, "en-US")]
private static partial Regex VectorMatch();
}

0 comments on commit c97ec7b

Please sign in to comment.