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.Reflection; 9 using System.Text; 10 using System.Linq; 11 using System.IO; 12 using System.Collections.Generic; 13 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.CPU 18 { 19 public static class RiscvOpcodesExtensions 20 { EnableRiscvOpcodesCounting(this BaseRiscV cpu, ReadFilePath file)21 public static void EnableRiscvOpcodesCounting(this BaseRiscV cpu, ReadFilePath file) 22 { 23 foreach(var x in RiscVOpcodesParser.Parse(file)) 24 { 25 cpu.InstallOpcodeCounterPattern(x.Item1, x.Item2); 26 } 27 28 cpu.EnableOpcodesCounting = true; 29 } 30 EnableRiscvOpcodesCounting(this BaseRiscV cpu)31 public static void EnableRiscvOpcodesCounting(this BaseRiscV cpu) 32 { 33 foreach(var set in cpu.ArchitectureSets) 34 { 35 cpu.EnableRiscvOpcodesCounting(set); 36 } 37 } 38 EnableRiscvOpcodesCountingFromEmbeddedResource(this BaseRiscV cpu, string name)39 public static void EnableRiscvOpcodesCountingFromEmbeddedResource(this BaseRiscV cpu, string name) 40 { 41 var assembly = Assembly.GetExecutingAssembly(); 42 if(!assembly.TryFromResourceToTemporaryFile($"{ResourceNamePrefix}.{name}", out var file)) 43 { 44 var availableResources = string.Join("\n", cpu.GetRiscvOpcodesEmbeddedResourceNames()); 45 throw new RecoverableException($"Couldn't load the {name} resource - please choose from:\n{availableResources}"); 46 } 47 cpu.EnableRiscvOpcodesCounting(file); 48 } 49 GetRiscvOpcodesEmbeddedResourceNames(this BaseRiscV cpu)50 public static IEnumerable<string> GetRiscvOpcodesEmbeddedResourceNames(this BaseRiscV cpu) 51 { 52 var assembly = Assembly.GetExecutingAssembly(); 53 return assembly.GetManifestResourceNames().Where(x => x.StartsWith(ResourceNamePrefix)).Select(x => x.Substring(ResourceNamePrefix.Length + 1)); 54 } 55 EnableRiscvOpcodesCounting(this BaseRiscV cpu, BaseRiscV.InstructionSet instructionSet)56 public static void EnableRiscvOpcodesCounting(this BaseRiscV cpu, BaseRiscV.InstructionSet instructionSet) 57 { 58 Dictionary<BaseRiscV.InstructionSet, IEnumerable<string>> map = null; 59 if(cpu.Architecture == "riscv") 60 { 61 map = opcodesFilesMap32; 62 } 63 else if(cpu.Architecture == "riscv64") 64 { 65 map = opcodesFilesMap64; 66 } 67 else 68 { 69 throw new RecoverableException($"Unsupported CPU type: {cpu.GetType()}"); 70 } 71 72 if(!map.TryGetValue(instructionSet, out var resourceNames)) 73 { 74 throw new RecoverableException($"Opcodes counting for the {instructionSet} extension not supported"); 75 } 76 77 foreach(var resourceName in resourceNames) 78 { 79 var assembly = Assembly.GetExecutingAssembly(); 80 if(!assembly.TryFromResourceToTemporaryFile(resourceName, out var file)) 81 { 82 throw new RecoverableException($"Couldn't load the {resourceName} resource - this might indicate a broken compilation"); 83 } 84 85 cpu.EnableRiscvOpcodesCounting(file); 86 } 87 } 88 89 private const string ResourceNamePrefix = "Antmicro.Renode.Cores.RiscV.Opcodes"; 90 91 private static readonly Dictionary<BaseRiscV.InstructionSet, IEnumerable<string>> opcodesFilesMap32 = new Dictionary<BaseRiscV.InstructionSet, IEnumerable<string>> 92 { 93 { BaseRiscV.InstructionSet.I, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.System", "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32i" } }, 94 { BaseRiscV.InstructionSet.M, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32m" } }, 95 { BaseRiscV.InstructionSet.A, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32a" } }, 96 { BaseRiscV.InstructionSet.F, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32f" } }, 97 { BaseRiscV.InstructionSet.D, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32d", "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32d-zfh" } }, 98 { BaseRiscV.InstructionSet.C, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rvc", "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32c" } }, 99 { BaseRiscV.InstructionSet.V, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rvv", "Antmicro.Renode.Cores.RiscV.Opcodes.Rvv-pseudo" } }, 100 { BaseRiscV.InstructionSet.S, Array.Empty<string>() }, 101 { BaseRiscV.InstructionSet.U, Array.Empty<string>() }, 102 }; 103 104 private static readonly Dictionary<BaseRiscV.InstructionSet, IEnumerable<string>> opcodesFilesMap64 = new Dictionary<BaseRiscV.InstructionSet, IEnumerable<string>> 105 { 106 { BaseRiscV.InstructionSet.I, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.System", "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32i", "Antmicro.Renode.Cores.RiscV.Opcodes.Rv64i" } }, 107 { BaseRiscV.InstructionSet.M, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32m", "Antmicro.Renode.Cores.RiscV.Opcodes.Rv64m" } }, 108 { BaseRiscV.InstructionSet.A, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32a", "Antmicro.Renode.Cores.RiscV.Opcodes.Rv64a" } }, 109 { BaseRiscV.InstructionSet.F, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32f", "Antmicro.Renode.Cores.RiscV.Opcodes.Rv64f" } }, 110 { BaseRiscV.InstructionSet.D, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rv32d", "Antmicro.Renode.Cores.RiscV.Opcodes.Rv64d" } }, 111 { BaseRiscV.InstructionSet.C, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rvc", "Antmicro.Renode.Cores.RiscV.Opcodes.Rv64c" } }, 112 { BaseRiscV.InstructionSet.V, new [] { "Antmicro.Renode.Cores.RiscV.Opcodes.Rvv", "Antmicro.Renode.Cores.RiscV.Opcodes.Rvv-pseudo" } }, 113 { BaseRiscV.InstructionSet.S, Array.Empty<string>() }, 114 { BaseRiscV.InstructionSet.U, Array.Empty<string>() }, 115 }; 116 } 117 118 public static class RiscVOpcodesParser 119 { Parse(string file)120 public static IEnumerable<Tuple<string, string>> Parse(string file) 121 { 122 var result = new List<Tuple<string, string>>(); 123 124 try 125 { 126 foreach(var line in File.ReadLines(file).Select(x => RemoveComments(x))) 127 { 128 if(line.Length == 0) 129 { 130 continue; 131 } 132 133 result.Add(ParseLine(line)); 134 } 135 } 136 catch(IOException e) 137 { 138 throw new RecoverableException($"There was na error when parsing RISC-V opcodes from {file}:\n{e.Message}"); 139 } 140 141 return result; 142 } 143 RemoveComments(string line)144 private static string RemoveComments(string line) 145 { 146 var pos = line.IndexOf("#"); 147 if(pos != -1) 148 { 149 line = line.Remove(pos); 150 } 151 return line.Trim(); 152 } 153 ParseLine(string lineContent, int opcodeLength = 32)154 private static Tuple<string, string> ParseLine(string lineContent, int opcodeLength = 32) 155 { 156 var pattern = new StringBuilder(new String('_', opcodeLength)); 157 158 var elems = lineContent.Split(LineSplitPatterns, StringSplitOptions.RemoveEmptyEntries); 159 if(elems.Length < 2) 160 { 161 throw new RecoverableException($"Couldn't split line: {lineContent}"); 162 } 163 164 var instructionName = elems[0]; 165 foreach(var elem in elems.Skip(1)) 166 { 167 var parts = elem.Split(PartSplitPatterns); 168 if(parts.Length != 2) 169 { 170 // let's ignore all non-explicit ranges 171 continue; 172 } 173 174 if(!BitsRange.TryParse(parts[0], out var range)) 175 { 176 throw new RecoverableException($"Couldn't parse range: {parts[0]}"); 177 } 178 179 if(!BitsValue.TryParse(parts[1], out var value)) 180 { 181 throw new RecoverableException($"Couldn't parse value: {parts[1]}"); 182 } 183 184 if(!range.TryApply(pattern, value)) 185 { 186 throw new RecoverableException($"Couldn't apply value {value} in range {range} to pattern {pattern}"); 187 } 188 } 189 190 return Tuple.Create(instructionName, pattern.ToString()); 191 } 192 193 private static readonly char[] PartSplitPatterns = new [] { '=' }; 194 private static readonly char[] LineSplitPatterns = new [] { ' ', '\t' }; 195 private static readonly string[] BitRangeSplitPatterns = new [] { ".." }; 196 197 private struct BitsValue 198 { TryParseAntmicro.Renode.Peripherals.CPU.RiscVOpcodesParser.BitsValue199 public static bool TryParse(string s, out BitsValue bv) 200 { 201 if(s == "ignore") 202 { 203 bv = new BitsValue(-1, ignored: true); 204 return true; 205 } 206 207 if(SmartParser.Instance.TryParse(s, typeof(int), out var result)) 208 { 209 bv = new BitsValue((int)result); 210 return true; 211 } 212 213 bv = new BitsValue(-1); 214 return false; 215 } 216 TryGetBinaryPatternAntmicro.Renode.Peripherals.CPU.RiscVOpcodesParser.BitsValue217 public bool TryGetBinaryPattern(int length, out string result) 218 { 219 if(Ignored) 220 { 221 result = new String('x', length); 222 return true; 223 } 224 225 result = Convert.ToString(Value, 2); 226 if(result.Length > length) 227 { 228 return false; 229 } 230 231 result = result.PadLeft(length, '0'); 232 return true; 233 } 234 BitsValueAntmicro.Renode.Peripherals.CPU.RiscVOpcodesParser.BitsValue235 private BitsValue(int value, bool ignored = false) 236 { 237 Value = value; 238 Ignored = ignored; 239 } 240 241 public int Value { get; } 242 public bool Ignored { get; } 243 ToStringAntmicro.Renode.Peripherals.CPU.RiscVOpcodesParser.BitsValue244 public override string ToString() 245 { 246 return $"[BitsValue: {Value}, ignored: {Ignored}]"; 247 } 248 } 249 250 private struct BitsRange 251 { 252 // There are two expected formats: 253 // * hi..lo (hi, lo being integers) 254 // * bit (bit being an integer; treated as bit..bit) TryParseAntmicro.Renode.Peripherals.CPU.RiscVOpcodesParser.BitsRange255 public static bool TryParse(string s, out BitsRange br) 256 { 257 var x = s.Split(BitRangeSplitPatterns, StringSplitOptions.None); 258 if(x.Length == 1) 259 { 260 if(int.TryParse(x[0], out var bit)) 261 { 262 br = new BitsRange(bit, bit); 263 return true; 264 } 265 } 266 else if(x.Length == 2) 267 { 268 if(int.TryParse(x[0], out var upperBit) 269 && int.TryParse(x[1], out var lowerBit)) 270 { 271 br = new BitsRange(lowerBit, upperBit); 272 return true; 273 } 274 } 275 276 br = new BitsRange(-1, -1); 277 return false; 278 } 279 TryApplyAntmicro.Renode.Peripherals.CPU.RiscVOpcodesParser.BitsRange280 public bool TryApply(StringBuilder s, BitsValue v) 281 { 282 if(!v.TryGetBinaryPattern(this.Width, out var pattern)) 283 { 284 return false; 285 } 286 287 if(Higher >= s.Length) 288 { 289 return false; 290 } 291 292 var pIdx = pattern.Length - 1; 293 var sIdx = s.Length - Lower - 1; 294 295 for(var i = Lower; i <= Higher; i++, pIdx--, sIdx--) 296 { 297 if(s[sIdx] == '1' || s[sIdx] == '0') 298 { 299 return false; 300 } 301 302 s[sIdx] = pattern[pIdx]; 303 } 304 305 return true; 306 } 307 ToStringAntmicro.Renode.Peripherals.CPU.RiscVOpcodesParser.BitsRange308 public override string ToString() 309 { 310 return $"[BitsRange: {Lower} - {Higher}]"; 311 } 312 BitsRangeAntmicro.Renode.Peripherals.CPU.RiscVOpcodesParser.BitsRange313 private BitsRange(int lower, int higher) 314 { 315 Lower = lower; 316 Higher = higher; 317 } 318 319 public int Lower { get; } 320 public int Higher { get; } 321 322 public int Width => Higher - Lower + 1; 323 } 324 } 325 } 326