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