1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Collections.Generic; 9 using System.Linq; 10 using System.Text; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Exceptions; 13 using Sprache; 14 15 namespace Antmicro.Renode.PlatformDescription 16 { 17 public class AccessConditionParser 18 { ParseCondition(string condition)19 public static DnfFormula ParseCondition(string condition) 20 { 21 return DnfFormula.FromDnfTree(ParseExpression(condition).ToDnf()); 22 } 23 EvaluateWithStateBits(DnfFormula formula, Func<string, IReadOnlyDictionary<string, int>> getStateBits)24 public static Dictionary<string, List<StateMask>> EvaluateWithStateBits(DnfFormula formula, Func<string, IReadOnlyDictionary<string, int>> getStateBits) 25 { 26 var result = new Dictionary<string, List<StateMask>>(); 27 28 // A term can have multiple conditions but up to one initiator. 29 foreach(var term in formula.Terms) 30 { 31 var initiator = term.Conditions.OfType<InitiatorConditionNode>().SingleOrDefault()?.Initiator; 32 33 // Empty string is used as a key if an initiator wasn't specified. 34 var initiatorKey = initiator ?? string.Empty; 35 if(!result.ContainsKey(initiatorKey)) 36 { 37 result[initiatorKey] = new List<StateMask>(); 38 } 39 40 // If `initiator` is null then `stateBits` will contain common state bits for all IPeripheralWithTransactionState. 41 // 42 // It can be null if: 43 // 1. `initiator` does not implement IPeripheralWithTransactionState, 44 // 2. `initiator` is null and there's no peripheral implementing IPeripheralWithTransactionState. 45 var stateBits = getStateBits(initiator); 46 47 var conditions = term.Conditions.Where(c => !(c is InitiatorConditionNode)); 48 if(conditions.Any()) 49 { 50 if(stateBits == null || !stateBits.Any()) 51 { 52 var message = new StringBuilder("Conditions provided (") 53 .Append(string.Join(" && ", conditions)) 54 .Append(") but ") 55 .Append(initiator == null 56 ? $"there are no peripherals implementing {nameof(Peripherals.IPeripheralWithTransactionState)} or they have no common state bits" 57 : $"the initiator '{initiator}' doesn't implement {nameof(Peripherals.IPeripheralWithTransactionState)} or has no state bits" 58 ).ToString(); 59 throw new RecoverableException(message); 60 } 61 } 62 63 var termStateMask = new StateMask(); 64 foreach(var condition in conditions) 65 { 66 var initiatorString = initiator == null 67 ? $"peripherals implementing {nameof(Peripherals.IPeripheralWithTransactionState)} (initiator not specified)" 68 : $"the initiator '{initiator}'"; 69 70 if(!stateBits.TryGetValue(condition.Condition, out var bitPosition)) 71 { 72 var supportedBitsString = "'" + string.Join("', '", stateBits.Select(pair => pair.Key)) + "'"; 73 throw new RecoverableException($"Provided condition is unsupported by {initiatorString}: {condition.Condition}; supported conditions: {supportedBitsString}"); 74 } 75 76 if(termStateMask.HasMaskBit(bitPosition)) 77 { 78 throw new RecoverableException($"Conditions conflict detected for {initiatorString}: {condition.Condition}"); 79 } 80 81 // The given StateMask bit should be unset if the condition is negated, otherwise set. 82 var bitShouldBeSet = !condition.Negated; 83 termStateMask = termStateMask.WithBitValue(bitPosition, bitShouldBeSet); 84 } 85 result[initiatorKey].Add(termStateMask); 86 } 87 return result; 88 } 89 ParseExpression(string expr)90 private static AstNode ParseExpression(string expr) 91 { 92 return OrTerm.Parse(expr); 93 } 94 95 private static Parser<IEnumerable<char>> OrOp => Parse.String("||").Token(); 96 97 private static Parser<AstNode> OrTerm => 98 Parse.ChainOperator(OrOp, AndTerm, (op, l, r) => new OrNode(l, r)); 99 100 private static Parser<IEnumerable<char>> AndOp => Parse.String("&&").Token(); 101 102 private static Parser<AstNode> AndTerm => 103 Parse.ChainOperator(AndOp, NegateTerm, (op, l, r) => new AndNode(l, r)); 104 105 private static Parser<AstNode> NegateTerm => 106 NegatedFactor 107 .Or(Factor); 108 109 private static Parser<AstNode> NegatedFactor => 110 from _ in Parse.Char('!').Token() 111 from expr in Factor 112 select new NotNode(expr); 113 114 private static Parser<AstNode> Factor => 115 SubExpression 116 .Or(InitiatorCondition) 117 .Or(Condition); 118 119 private static Parser<AstNode> SubExpression => 120 from lparen in Parse.Char('(').Token() 121 from expr in OrTerm 122 from rparen in Parse.Char(')').Token() 123 select expr; 124 125 private static Parser<string> GenericToken => Parse.Regex(@"\w+").Token(); 126 127 private static Parser<AstNode> Condition => 128 GenericToken 129 .Select(name => new ConditionNode(name)); 130 131 private static Parser<IEnumerable<char>> InitiatorKeyword => Parse.String("initiator").Token(); 132 133 private static Parser<IEnumerable<char>> EqualsOp => Parse.String("==").Token(); 134 135 private static Parser<AstNode> InitiatorCondition => 136 from _ in InitiatorKeyword 137 from __ in EqualsOp 138 from initiator in GenericToken 139 select new InitiatorConditionNode(initiator); 140 141 public abstract class AstNode 142 { ToDnf()143 public abstract AstNode ToDnf(); 144 } 145 146 public class AndNode : AstNode 147 { AndNode(AstNode left, AstNode right)148 public AndNode(AstNode left, AstNode right) 149 { 150 Left = left; 151 Right = right; 152 } 153 ToDnf()154 public override AstNode ToDnf() 155 { 156 var leftDnf = Left.ToDnf(); 157 var rightDnf = Right.ToDnf(); 158 if(leftDnf is OrNode leftOr) 159 { 160 // (l.l || l.r) && r to (l.l && r) || (l.r && r) 161 // Recursively call ToDnf on it to handle cases like `(a || b) && (c || d)` 162 return new OrNode(new AndNode(leftOr.Left, rightDnf), new AndNode(leftOr.Right, rightDnf)).ToDnf(); 163 } 164 if(rightDnf is OrNode rightOr) 165 { 166 // l && (r.l || r.r) to (l && r.l) || (l && r.r) 167 return new OrNode(new AndNode(leftDnf, rightOr.Left), new AndNode(leftDnf, rightOr.Right)).ToDnf(); 168 } 169 return new AndNode(leftDnf, rightDnf); 170 } 171 ToString()172 public override string ToString() => $"({Left} && {Right})"; 173 174 public readonly AstNode Left; 175 public readonly AstNode Right; 176 } 177 178 public class OrNode : AstNode 179 { OrNode(AstNode left, AstNode right)180 public OrNode(AstNode left, AstNode right) 181 { 182 Left = left; 183 Right = right; 184 } 185 ToDnf()186 public override AstNode ToDnf() 187 { 188 return new OrNode(Left.ToDnf(), Right.ToDnf()); 189 } 190 ToString()191 public override string ToString() => $"({Left} || {Right})"; 192 193 public readonly AstNode Left; 194 public readonly AstNode Right; 195 } 196 197 public class NotNode : AstNode 198 { NotNode(AstNode operand)199 public NotNode(AstNode operand) 200 { 201 Operand = operand; 202 } 203 ToDnf()204 public override AstNode ToDnf() 205 { 206 if(Operand is AndNode and) 207 { 208 // !(l && r) to (!l) || (!r) 209 return new OrNode(new NotNode(and.Left).ToDnf(), new NotNode(and.Right).ToDnf()); 210 } 211 if(Operand is OrNode or) 212 { 213 // !(p || q) to (!p) && (!q) 214 return new AndNode(new NotNode(or.Left).ToDnf(), new NotNode(or.Right).ToDnf()); 215 } 216 if(Operand is NotNode not) 217 { 218 return not.Operand.ToDnf(); 219 } 220 return this; 221 } 222 ToString()223 public override string ToString() => $"!{Operand}"; 224 225 public readonly AstNode Operand; 226 } 227 228 public class ConditionNode : AstNode 229 { ConditionNode(string condition, bool negated = false)230 public ConditionNode(string condition, bool negated = false) 231 { 232 Condition = condition; 233 Negated = negated; 234 } 235 ToDnf()236 public override AstNode ToDnf() => this; 237 ToString()238 public override string ToString() => $"{(Negated ? "!" : "")}{Condition}"; 239 public ConditionNode Negation => new ConditionNode(Condition, !Negated); 240 241 public readonly string Condition; 242 public readonly bool Negated; 243 } 244 245 public class InitiatorConditionNode : ConditionNode 246 { InitiatorConditionNode(string initiator)247 public InitiatorConditionNode(string initiator) : base($"initiator == {initiator}") 248 { 249 Initiator = initiator; 250 } 251 252 public readonly string Initiator; 253 } 254 255 public class DnfTerm 256 { DnfTerm(IReadOnlyList<ConditionNode> conditions)257 public DnfTerm(IReadOnlyList<ConditionNode> conditions) 258 { 259 Conditions = conditions; 260 } 261 ToString()262 public override string ToString() => $"({string.Join(" && ", Conditions)})"; 263 264 public readonly IReadOnlyList<ConditionNode> Conditions; 265 } 266 267 public class DnfFormula 268 { DnfFormula(IReadOnlyList<DnfTerm> terms)269 public DnfFormula(IReadOnlyList<DnfTerm> terms) 270 { 271 Terms = terms; 272 } 273 FromDnfTree(AstNode root)274 public static DnfFormula FromDnfTree(AstNode root) 275 { 276 var terms = new List<DnfTerm>(); 277 GatherDnfTerms(root, terms); 278 return new DnfFormula(terms); 279 } 280 GatherDnfTerms(AstNode node, List<DnfTerm> terms)281 private static void GatherDnfTerms(AstNode node, List<DnfTerm> terms) 282 { 283 if(node is OrNode orNode) 284 { 285 GatherDnfTerms(orNode.Left, terms); 286 GatherDnfTerms(orNode.Right, terms); 287 } 288 else if(node is AndNode || node is ConditionNode || node is NotNode) 289 { 290 var conditions = new List<ConditionNode>(); 291 GatherConditions(node, conditions); 292 terms.Add(new DnfTerm(conditions)); 293 } 294 else 295 { 296 throw new InvalidOperationException($"Unexpected node type: {node.GetType().FullName}"); 297 } 298 } 299 GatherConditions(AstNode node, List<ConditionNode> conditions)300 private static void GatherConditions(AstNode node, List<ConditionNode> conditions) 301 { 302 if(node is ConditionNode conditionNode) 303 { 304 conditions.Add(conditionNode); 305 } 306 else if(node is NotNode notNode) 307 { 308 conditions.Add(((ConditionNode)notNode.Operand).Negation); 309 } 310 else if(node is AndNode andNode) 311 { 312 GatherConditions(andNode.Left, conditions); 313 GatherConditions(andNode.Right, conditions); 314 } 315 else 316 { 317 throw new InvalidOperationException($"Unexpected node type: {node.GetType().FullName}"); 318 } 319 } 320 ToString()321 public override string ToString() => $"{string.Join(" || ", Terms)}"; 322 323 public readonly IReadOnlyList<DnfTerm> Terms; 324 } 325 } 326 } 327