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 
8 using System;
9 using System.Linq;
10 using Antmicro.Renode.Logging;
11 using System.Numerics;
12 using System.Collections.Generic;
13 using Antmicro.Renode.Core;
14 using Antmicro.Renode.Exceptions;
15 using Antmicro.Renode.Peripherals.Memory;
16 using Antmicro.Renode.Peripherals.Miscellaneous;
17 using Antmicro.Renode.Utilities;
18 using Antmicro.Renode.Utilities.Binding;
19 using Antmicro.Renode.Peripherals.Bus;
20 using ELFSharp.ELF;
21 
22 namespace Antmicro.Renode.Peripherals.CPU
23 {
24     public class OpenTitan_BigNumberAcceleratorCore : RiscV32, IOpenTitan_BigNumberAcceleratorCore
25     {
OpenTitan_BigNumberAcceleratorCore(OpenTitan_BigNumberAccelerator parent, OpenTitan_ScrambledMemory instructionsMemory, OpenTitan_ScrambledMemory dataMemory)26         public OpenTitan_BigNumberAcceleratorCore(OpenTitan_BigNumberAccelerator parent, OpenTitan_ScrambledMemory instructionsMemory, OpenTitan_ScrambledMemory dataMemory)
27             : base(timeProvider: null, cpuType: "rv32im_zicsr", machine: null, hartId: 0, privilegedArchitecture: PrivilegedArchitecture.Priv1_10, endianness: Endianess.LittleEndian)
28         {
29             this.parent = parent;
30             this.instructionsMemory = instructionsMemory;
31             this.dataMemory = dataMemory;
32 
33             this.random = EmulationManager.Instance.CurrentEmulation.RandomGenerator;
34             this.loopStack = new Stack<LoopContext>();
35 
36             foreach(var segment in instructionsMemory.MappedSegments)
37             {
38                 this.MapMemory(segment);
39             }
40 
41             this.EnableExternalWindowMmu(true);
42             var insnFetchWindow = (uint)this.AcquireExternalMmuWindow((int)ExternalMmuBase.Privilege.Execute); //Insn fetch only
43             this.SetMmuWindowStart(insnFetchWindow, 0x0);
44             this.SetMmuWindowEnd(insnFetchWindow, (ulong)instructionsMemory.Size);
45             this.SetMmuWindowAddend(insnFetchWindow, 0);
46             this.SetMmuWindowPrivileges(insnFetchWindow, (int)ExternalMmuBase.Privilege.Execute);
47             this.AddHookAtInterruptBegin(HandleException);
48 
49             foreach(var segment in dataMemory.MappedSegments)
50             {
51                 var wrappedSegment = new SystemBus.MappedSegmentWrapper(segment, VirtualDataOffset, segment.Size, this);
52                 this.MapMemory(wrappedSegment);
53             }
54 
55             var dataWindow = (uint)this.AcquireExternalMmuWindow((int)ExternalMmuBase.Privilege.ReadAndWrite); // Data read and write
56             this.SetMmuWindowStart(dataWindow, 0x0);
57             this.SetMmuWindowEnd(dataWindow, (ulong)dataMemory.Size);
58             this.SetMmuWindowAddend(dataWindow, VirtualDataOffset);
59             this.SetMmuWindowPrivileges(dataWindow, (int)ExternalMmuBase.Privilege.ReadAndWrite);
60 
61             // Add the X1 register handling
62             this.EnablePostGprAccessHooks(1);
63             this.InstallPostGprAccessHookOn(1, HandleX1Access, 1);
64             this.x1Stack = new Stack<uint>();
65 
66             this.flagsGroup = new CustomFlags[FlagsGroupsCount];
67 
68             wideDataRegisters = new WideRegister[WideDataRegistersCount];
69             for(var index = 0; index < WideDataRegistersCount; index++)
70             {
71                 wideDataRegisters[index] = new WideRegister();
72             }
73 
74             wideSpecialPurposeRegisters = new WideRegister[WideSpecialPurposeRegistersCount];
75             wideSpecialPurposeRegisters[(int)WideSPR.Mod] = new WideRegister();
76             wideSpecialPurposeRegisters[(int)WideSPR.Rnd] = new WideRegister(readOnly: true);
77             wideSpecialPurposeRegisters[(int)WideSPR.URnd] = new WideRegister(readOnly: true);
78             wideSpecialPurposeRegisters[(int)WideSPR.Acc] = new WideRegister();
79             wideSpecialPurposeRegisters[(int)WideSPR.KeyShare0Low] = new WideRegister(readOnly: true);
80             wideSpecialPurposeRegisters[(int)WideSPR.KeyShare0High] = new WideRegister(readOnly: true);
81             wideSpecialPurposeRegisters[(int)WideSPR.KeyShare1Low] = new WideRegister(readOnly: true);
82             wideSpecialPurposeRegisters[(int)WideSPR.KeyShare1High] = new WideRegister(readOnly: true);
83 
84             RegisterCustomCSRs();
85             RegisterCustomOpcodes();
86         }
87 
Reset()88         public override void Reset()
89         {
90             base.Reset();
91 
92             PC = 0x0;
93             loopStack.Clear();
94             x1Stack.Clear();
95 
96             // clear WDRs
97             foreach(var wdr in wideDataRegisters)
98             {
99                 wdr.Clear();
100             }
101 
102             // clear all RW WSPRs
103             foreach(var wspr in wideSpecialPurposeRegisters.Where(x => !x.ReadOnly))
104             {
105                 wspr.Clear();
106             }
107 
108             // clear flags
109             for(var index = 0; index < flagsGroup.Length; index++)
110             {
111                 flagsGroup[index] = default(CustomFlags);
112             }
113         }
114 
GetWideRegister(int index, bool special = false)115         public BigInteger GetWideRegister(int index, bool special = false)
116         {
117             if(special)
118             {
119                 return wideSpecialPurposeRegisters[index].AsBigInteger;
120             }
121             else
122             {
123                 return wideDataRegisters[index].AsBigInteger;
124             }
125         }
126 
SetWideRegister(int index, BigInteger value, bool special = false)127         public void SetWideRegister(int index, BigInteger value, bool special = false)
128         {
129             if(special)
130             {
131                 wideSpecialPurposeRegisters[index].SetTo(value);
132             }
133             else
134             {
135                 wideDataRegisters[index].SetTo(value);
136             }
137         }
138 
139         public CoreError LastError { get; private set; }
140 
141         public string FixedRandomPattern
142         {
143             get => fixedRandomPattern;
144             set
145             {
146                 fixedRandomPattern = value;
147                 fixedRandomBytes = (value != null)
148                     ? ParseHexPattern(value, 256)
149                     : null;
150             }
151         }
152 
153         public string KeyShare0
154         {
155             get => keyShare0;
156             set
157             {
158                 keyShare0 = value;
159                 UpdateKeyShare(value, (int)WideSPR.KeyShare0Low, (int)WideSPR.KeyShare0High);
160             }
161         }
162 
163         public string KeyShare1
164         {
165             get => keyShare1;
166             set
167             {
168                 keyShare1 = value;
169                 UpdateKeyShare(value, (int)WideSPR.KeyShare1Low, (int)WideSPR.KeyShare1High);
170             }
171         }
172 
UpdateKeyShare(string value, int lowRegisterId, int highRegisterId)173         private void UpdateKeyShare(string value, int lowRegisterId, int highRegisterId)
174         {
175             if(value != null)
176             {
177                 var bytes = ParseHexPattern(value, 64);
178                 if(bytes.Length > 64)
179                 {
180                     throw new RecoverableException($"Provided key share is too long (expected up to 64 bytes): {value}");
181                 }
182 
183                 wideSpecialPurposeRegisters[lowRegisterId].SetTo(new BigInteger(bytes.Take(32).ToArray()));
184 
185                 if(bytes.Length > 32)
186                 {
187                     wideSpecialPurposeRegisters[highRegisterId].SetTo(new BigInteger(bytes.Skip(32).ToArray()));
188                 }
189             }
190             else
191             {
192                 wideSpecialPurposeRegisters[lowRegisterId].Clear();
193                 wideSpecialPurposeRegisters[highRegisterId].Clear();
194             }
195         }
196 
HandleException(ulong exceptionType)197         private void HandleException(ulong exceptionType)
198         {
199             Log(LogLevel.Debug, $"Handling exception of type 0x{exceptionType:X}");
200             switch(exceptionType)
201             {
202                 case 0x0: // RISCV_EXCP_INST_ADDR_MIS
203                 case 0x1: // RISCV_EXCP_INST_ACCESS_FAULT
204                 case 0xc: // RISCV_EXCP_INST_PAGE_FAULT          /* since: priv-1.10.0 */
205                     ThrowError(CoreError.BadInstructionAddress, false);
206                     break;
207                 case 0x2: // RISCV_EXCP_ILLEGAL_INST
208                     ThrowError(CoreError.IllegalInstruction, false);
209                     break;
210                 case 0x4: // RISCV_EXCP_LOAD_ADDR_MIS
211                 case 0x5: // RISCV_EXCP_LOAD_ACCESS_FAULT
212                 case 0x6: // RISCV_EXCP_STORE_AMO_ADDR_MIS
213                 case 0x7: // RISCV_EXCP_STORE_AMO_ACCESS_FAULT
214                 case 0xd: // RISCV_EXCP_LOAD_PAGE_FAULT          /* since: priv-1.10.0 */
215                 case 0xf: // RISCV_EXCP_STORE_PAGE_FAULT         /* since: priv-1.10.0 */
216                     ThrowError(CoreError.BadDataAddress, false);
217                     break;
218                 case 0x3: // RISCV_EXCP_BREAKPOINT
219                 case 0x8: // RISCV_EXCP_U_ECALL
220                 case 0x9: // RISCV_EXCP_S_ECALL
221                 case 0xa: // RISCV_EXCP_H_ECALL
222                 case 0xb: // RISCV_EXCP_M_ECALL
223                     // just ignore
224                     break;
225             }
226         }
227 
Log(LogLevel logLevel, string message)228         private void Log(LogLevel logLevel, string message)
229         {
230             parent.Log(logLevel, "OTBN Core: {0}", message);
231         }
232 
RegisterCustomCSRs()233         private void RegisterCustomCSRs()
234         {
235             RegisterCSR((ulong)CustomCSR.FlagGroup0,
236                         () => (ulong)flagsGroup[0],
237                         val => { flagsGroup[0] = (CustomFlags)val; },
238                         name: "FG0");
239 
240             RegisterCSR((ulong)CustomCSR.FlagGroup1,
241                         () => (ulong)flagsGroup[1],
242                         val => { flagsGroup[1] = (CustomFlags)val; },
243                         name: "FG1");
244 
245             RegisterCSR((ulong)CustomCSR.Flags,
246                         () => (ulong)((uint)flagsGroup[0] | ((uint)flagsGroup[1] << 4)),
247                         val => { flagsGroup[0] = (CustomFlags)(val & 0xF); flagsGroup[1] = (CustomFlags)(val >> 4); },
248                         name: "FLAGS");
249 
250             for(var x = 0; x < 8; x++)
251             {
252                 var index = x;
253                 RegisterCSR((ulong)(CustomCSR.Mod0 + index),
254                             () => (ulong)(wideSpecialPurposeRegisters[(int)WideSPR.Mod].PartialGet(index * sizeof(uint), sizeof(uint))),
255                             val => { wideSpecialPurposeRegisters[(int)WideSPR.Mod].PartialSet(new BigInteger(val), index * sizeof(uint), 4); },
256                             name: $"MOD{index}");
257             }
258 
259             RegisterCSR((ulong)CustomCSR.RndPrefetch,
260                         () => 0,
261                         val => { }, // there is no need for any prefetch action in the simulation
262                         name: "RND_PREFETCH");
263 
264             // both RND and URND are implemented the same way in the simulation
265             RegisterCSR((ulong)CustomCSR.Rnd,
266                         () => GetPseudoRandom(),
267                         val => { },
268                         name: "RND");
269 
270             RegisterCSR((ulong)CustomCSR.URnd,
271                         () => GetPseudoRandom(),
272                         val => { },
273                         name: "URND");
274         }
275 
ThrowError(CoreError error, bool stopExecution = true)276         private void ThrowError(CoreError error, bool stopExecution = true)
277         {
278             LastError = error;
279             if(stopExecution)
280             {
281                 this.TlibRequestTranslationBlockInterrupt(0);
282             }
283         }
284 
HandleX1Access(bool isWrite)285         private void HandleX1Access(bool isWrite)
286         {
287             Log(LogLevel.Debug, string.Format("Handling X1 {0} hook; current depth of the stack is {1}", isWrite ? "write" : "read", x1Stack.Count));
288 
289             if(isWrite)
290             {
291                 if(x1Stack.Count == MaximumStackCapacity)
292                 {
293                     Log(LogLevel.Error, "x1 register: stack overflow");
294                     ThrowError(CoreError.CallStack);
295                     return;
296                 }
297                 x1Stack.Push(this.GetRegister(1));
298             }
299             else
300             {
301                 if(x1Stack.Count == 0)
302                 {
303                     Log(LogLevel.Error, "x1 register: trying to pop value from empty stack");
304                     ThrowError(CoreError.CallStack);
305                     return;
306                 }
307 
308                 this.SetRegister(1, x1Stack.Pop());
309             }
310         }
311 
RegisterCustomOpcodes()312         private void RegisterCustomOpcodes()
313         {
314             InstallCustomInstruction(BnAddIPattern, BnImmHandler, name: "BN.ADD.I");
315             InstallCustomInstruction(BnSubIPattern, BnImmHandler, name: "BN.SUB.I");
316             InstallCustomInstruction(BnRShiPattern, BnRShiftIHandler, name: "BN.RSHI");
317             InstallCustomInstruction(BnAddPattern, BnAddSubHandler, name: "BN.ADD");
318             InstallCustomInstruction(BnAddCPattern, BnAddSubHandler, name: "BN.ADD.C");
319             InstallCustomInstruction(BnAddMPattern, BnAddSubHandler, name: "BN.ADD.M");
320             InstallCustomInstruction(BnSubPattern, BnAddSubHandler, name: "BN.SUB");
321             InstallCustomInstruction(BnSubBPattern, BnAddSubHandler, name: "BN.SUB.B");
322             InstallCustomInstruction(BnSubMPattern, BnAddSubHandler, name: "BN.SUB.M");
323             InstallCustomInstruction(BnMulQAccPattern, BnMulQAccHandler, name: "BN.MUL.QACC");
324             InstallCustomInstruction(BnMulQAccWoPattern, BnMulQAccWithWriteHandler, name: "BN.MUL.QACC.WO");
325             InstallCustomInstruction(BnMulQAccSoPattern, BnMulQAccWithWriteHandler, name: "BN.MUL.QACC.SO");
326             InstallCustomInstruction(BnAndPattern, BnBitwiseHelper, name: "BN.AND");
327             InstallCustomInstruction(BnOrPattern, BnBitwiseHelper, name: "BN.OR");
328             InstallCustomInstruction(BnNotPattern, BnBitwiseHelper, name: "BN.NOT");
329             InstallCustomInstruction(BnXOrPattern, BnBitwiseHelper, name: "BN.XOR");
330             InstallCustomInstruction(BnSelPattern, BnSelHandler, name: "BN.SEL");
331             InstallCustomInstruction(BnCmpPattern, BnCmpHandler, name: "BN.CMP");
332             InstallCustomInstruction(BnCmpBPattern, BnCmpHandler, name: "BN.CMP.B");
333             InstallCustomInstruction(BnMovPattern, BnMovHandler, name: "BN.MOV");
334             InstallCustomInstruction(BnLidPattern, BnLoadStoreHandler, name: "BN.LID");
335             InstallCustomInstruction(BnSidPattern, BnLoadStoreHandler, name: "BN.SID");
336             InstallCustomInstruction(BnMovrPattern, BnMovrHandler, name: "BN.MOVR");
337             InstallCustomInstruction(BnWsrrPattern, WsrWriteReadHandler, name: "BN.WSRR");
338             InstallCustomInstruction(BnWsrwPattern, WsrWriteReadHandler, name: "BN.WSRW");
339             InstallCustomInstruction(ECallPattern, ECallHandler, name: "ECALL");
340 
341             InstallCustomInstruction(LoopPattern, LoopHandler, name: "LOOP");
342             InstallCustomInstruction(LoopiPattern, LoopiHandler, name: "LOOPI");
343         }
344 
ExecuteInstructions(int numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions)345         public ExecutionResult ExecuteInstructions(int numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions)
346         {
347             Log(LogLevel.Debug, string.Format("Executing #{0} instruction(s) at {1}", numberOfInstructionsToExecute, PC));
348             LastError = CoreError.None;
349             executionFinished = false;
350 
351             if(loopStack.Count != 0)
352             {
353                 ExecuteLoop(out numberOfExecutedInstructions);
354             }
355             else
356             {
357                 // normal execution
358                 base.ExecuteInstructions((ulong)numberOfInstructionsToExecute, out numberOfExecutedInstructions);
359             }
360 
361             if(LastError != CoreError.None)
362             {
363                 return ExecutionResult.Aborted;
364             }
365 
366             return executionFinished
367                 ? ExecutionResult.Interrupted
368                 : ExecutionResult.Ok;
369         }
370 
ExecuteLoop(out ulong numberOfExecutedInstructions)371         private void ExecuteLoop(out ulong numberOfExecutedInstructions)
372         {
373             // handle loop - since we must detect the end of the loop, we switch to stepping
374             var currentLoopContext = loopStack.Peek();
375             Log(LogLevel.Debug, string.Format("Handling loop at 0x{0:X}, current context: {1}", PC, currentLoopContext));
376 
377             if(currentLoopContext.NumberOfIterations == 0)
378             {
379                 Log(LogLevel.Error, "Unexpected end of loop");
380                 loopStack.Pop();
381                 numberOfExecutedInstructions = 0;
382 
383                 ThrowError(CoreError.Loop);
384                 return;
385             }
386 
387             var isLastInstructionInTheIteration = (PC == currentLoopContext.EndPC);
388             var result = base.ExecuteInstructions(1, out numberOfExecutedInstructions);
389 
390             if(isLastInstructionInTheIteration)
391             {
392                 currentLoopContext.NumberOfIterations--;
393                 if(currentLoopContext.NumberOfIterations == 0)
394                 {
395                     loopStack.Pop();
396                     Log(LogLevel.Debug, string.Format("Finished the loop; loop stack depth is: {0}", loopStack.Count));
397                 }
398                 else
399                 {
400                     Log(LogLevel.Debug, string.Format("Reached the end of loop body, decreasing number of iterations to {0}; loop stack depth is: {1}", currentLoopContext.NumberOfIterations, loopStack.Count));
401                     PC = currentLoopContext.StartPC;
402                 }
403             }
404         }
405 
ParseHexPattern(string input, int width)406         private byte[] ParseHexPattern(string input, int width)
407         {
408             if(input.StartsWith("0x"))
409             {
410                 input = input.Substring(2);
411             }
412 
413             // fill with leading 0's
414             if(input.Length < width * 2)
415             {
416                 input = new string('0', (width * 2) - input.Length) + input;
417             }
418 
419             try
420             {
421                 return Misc.HexStringToByteArray(input, reverse: true);
422             }
423             catch(Exception e)
424             {
425                 throw new RecoverableException($"Couldn't parse hex pattern: {e.Message}");
426             }
427         }
428 
LoopHandler(ulong opcode)429         private void LoopHandler(ulong opcode)
430         {
431             // decode parameters
432             var grs = (int)BitHelper.GetValue(opcode, 15, 5);
433 
434             var bodySize = (uint)BitHelper.GetValue(opcode, 20, 12) + 1;
435             var numberOfIterations = (uint)this.GetRegister(grs).RawValue;
436 
437             InnerLoopHandler(bodySize, numberOfIterations);
438         }
439 
LoopiHandler(ulong opcode)440         private void LoopiHandler(ulong opcode)
441         {
442             // decode parameters
443             var iterations1 = (uint)BitHelper.GetValue(opcode, 15, 5);
444             var iterations0 = (uint)BitHelper.GetValue(opcode, 7, 5);
445 
446             var numberOfIterations = (iterations1 << 5) + iterations0;
447             var bodySize = (uint)BitHelper.GetValue(opcode, 20, 12) + 1;
448 
449             InnerLoopHandler(bodySize, numberOfIterations);
450         }
451 
InnerLoopHandler(uint bodySize, uint numberOfIterations)452         private void InnerLoopHandler(uint bodySize, uint numberOfIterations)
453         {
454             if(numberOfIterations == 0)
455             {
456                 Log(LogLevel.Error, "Number of loop iterations cannot be 0");
457                 ThrowError(CoreError.Loop);
458                 return;
459             }
460 
461             if(loopStack.Count == MaxLoopStackHeight)
462             {
463                 Log(LogLevel.Error, "Maximum loop stack height");
464                 ThrowError(CoreError.Loop);
465                 return;
466             }
467 
468             if(loopStack.Any(x => x.EndPC == PC))
469             {
470                 Log(LogLevel.Error, "Loop instruction cannot appear as the last instruction of a loop body");
471                 ThrowError(CoreError.Loop);
472                 return;
473             }
474 
475             var newContext = new LoopContext
476             {
477                 // each instruction is encoded on 4 bytes
478                 StartPC = PC + 4u,
479                 EndPC = PC + bodySize * 4,
480                 NumberOfIterations = numberOfIterations
481             };
482 
483             loopStack.Push(newContext);
484             Log(LogLevel.Debug, string.Format("Added new loop context at 0: {1}", PC, newContext));
485         }
486 
WsrWriteReadHandler(ulong opcode)487         private void WsrWriteReadHandler(ulong opcode)
488         {
489             var isWrite = BitHelper.IsBitSet((uint)opcode, 31);
490             var wsr = (int)BitHelper.GetValue(opcode, 20, 8);
491             var wrd = (int)BitHelper.GetValue(opcode, 7, 5);
492             var wrs = (int)BitHelper.GetValue(opcode, 15, 5);
493 
494             if(wsr > wideSpecialPurposeRegisters.Length)
495             {
496                 Log(LogLevel.Error, $"The wsr is argument is too high: 0x{wsr:X}");
497                 ThrowError(CoreError.IllegalInstruction);
498                 return;
499             }
500 
501             if(isWrite)
502             {
503                 if(wideSpecialPurposeRegisters[wsr].ReadOnly)
504                 {
505                     // ignore writes to RO WSRS
506                     return;
507                 }
508                 wideSpecialPurposeRegisters[wsr].SetTo(wideDataRegisters[wrs].AsBigInteger);
509             }
510             else
511             {
512                 if(wsr == (int)WideSPR.Rnd || wsr == (int)WideSPR.URnd)
513                 {
514                     byte[] data;
515                     if(fixedRandomBytes != null)
516                     {
517                         data = fixedRandomBytes;
518                     }
519                     else
520                     {
521                         data = new byte[256];
522                         random.NextBytes(data);
523                     }
524                     wideDataRegisters[wrd].SetTo(new BigInteger(data));
525                 }
526                 else
527                 {
528                     wideDataRegisters[wrd].SetTo(wideSpecialPurposeRegisters[wsr].AsBigInteger);
529                 }
530             }
531         }
532 
BnMulQAccHandler(ulong opcode)533         private void BnMulQAccHandler(ulong opcode)
534         {
535             ParseRTypeFields(opcode, out var _, out var __, out var wrs1, out var wrs2, out var ___, out var ____, out var _____);
536 
537             var zeroAccumulator = BitHelper.IsBitSet(opcode, 12);
538             var shift = (int)BitHelper.GetValue(opcode, 13, 2) << 6;
539             var rs1QuarterWord = (int)BitHelper.GetValue(opcode, 25, 2);
540             var rs2QuarterWord = (int)BitHelper.GetValue(opcode, 27, 2);
541 
542             var rs1 = wideDataRegisters[wrs1].PartialGet(rs1QuarterWord * sizeof(ulong), sizeof(ulong));
543             var rs2 = wideDataRegisters[wrs2].PartialGet(rs2QuarterWord * sizeof(ulong), sizeof(ulong));
544             var result = rs1 * rs2;
545             result = (result << shift) & WideRegister.MaxValueMask;
546             if(!zeroAccumulator)
547             {
548                 var acc = wideSpecialPurposeRegisters[(int)WideSPR.Acc].AsBigInteger;
549                 result = result + acc;
550             }
551             wideSpecialPurposeRegisters[(int)WideSPR.Acc].SetTo(result);
552         }
553 
BnMulQAccWithWriteHandler(ulong opcode)554         private void BnMulQAccWithWriteHandler(ulong opcode)
555         {
556             ParseRTypeFields(opcode, out var f3, out var wrd, out var wrs1, out var wrs2, out var _, out var __, out var flagGroup);
557 
558             var zeroAccumulator = BitHelper.IsBitSet(opcode, 12);
559             var shift = (int)BitHelper.GetValue(opcode, 13, 2) << 6;
560             var rs1QuarterWord = (int)BitHelper.GetValue(opcode, 25, 2);
561             var rs2QuarterWord = (int)BitHelper.GetValue(opcode, 27, 2);
562             var halfwordSelect = BitHelper.GetValue(opcode, 29, 1);
563             var fullWordWriteback = !BitHelper.IsBitSet(opcode, 30);
564 
565             if(zeroAccumulator)
566             {
567                 wideSpecialPurposeRegisters[(int)WideSPR.Acc].Clear();
568             }
569 
570             var rs1 = wideDataRegisters[wrs1].PartialGet(rs1QuarterWord * sizeof(ulong), sizeof(ulong));
571             var rs2 = wideDataRegisters[wrs2].PartialGet(rs2QuarterWord * sizeof(ulong), sizeof(ulong));
572             var result = rs1 * rs2;
573             result = (result << shift) & WideRegister.MaxValueMask;
574             result += wideSpecialPurposeRegisters[(int)WideSPR.Acc].AsBigInteger;
575 
576             if(fullWordWriteback)
577             {
578                 wideSpecialPurposeRegisters[(int)WideSPR.Acc].SetTo(result);
579                 wideDataRegisters[wrd].SetTo(result);
580                 flagsGroup[flagGroup] = GetFlags(result);
581             }
582             else
583             {
584                 var mask = (new BigInteger(1ul) << 128) - 1;
585                 var lowPart = result & mask;
586                 var highPart = (result >> 128) & mask;
587                 var oldWord = wideDataRegisters[wrd].AsBigInteger;
588 
589                 var hwShift = 128 * halfwordSelect;
590                 var hwMask = mask << (int)hwShift;
591                 var newWord = (oldWord & ~hwMask) | (lowPart << (int)hwShift);
592                 wideDataRegisters[wrd].SetTo(newWord);
593                 wideSpecialPurposeRegisters[(int)WideSPR.Acc].SetTo(highPart);
594 
595                 var oldFlags = flagsGroup[flagGroup];
596                 flagsGroup[flagGroup] = GetMulWithWriteCustomFlags(oldFlags, lowPart, halfwordSelect);
597             }
598         }
599 
GetMulWithWriteCustomFlags(CustomFlags oldFlags, BigInteger lowPart, ulong halfwordSelect)600         private CustomFlags GetMulWithWriteCustomFlags(CustomFlags oldFlags, BigInteger lowPart, ulong halfwordSelect)
601         {
602             var newFlags = CustomFlags.Empty;
603 
604             if(oldFlags.HasFlag(CustomFlags.Carry))
605             {
606                 newFlags |= CustomFlags.Carry;
607             }
608             if(halfwordSelect == 0)
609             {
610                 if(oldFlags.HasFlag(CustomFlags.Msb))
611                 {
612                     newFlags |= CustomFlags.Msb;
613                 }
614                 if((lowPart & 0b1) == 1)
615                 {
616                     newFlags |= CustomFlags.Lsb;
617                 }
618                 if(lowPart == 0)
619                 {
620                     newFlags |= CustomFlags.Zero;
621                 }
622             }
623             else
624             {
625                 if(((lowPart >> 127) & 0b1) == 1)
626                 {
627                     newFlags |= CustomFlags.Msb;
628                 }
629                 if(oldFlags.HasFlag(CustomFlags.Lsb))
630                 {
631                     newFlags |= CustomFlags.Lsb;
632                 }
633                 if(oldFlags.HasFlag(CustomFlags.Zero) & (lowPart == 0))
634                 {
635                     newFlags |= CustomFlags.Zero;
636                 }
637             }
638             return newFlags;
639         }
640 
BnCmpHandler(ulong opcode)641         private void BnCmpHandler(ulong opcode)
642         {
643             ParseRTypeFields(opcode, out var f3, out var _, out var wrs1, out var wrs2, out var shiftBits, out var shiftRight, out var flagGroup);
644 
645             var rs1 = wideDataRegisters[wrs1].AsBigInteger;
646             var rs2 = wideDataRegisters[wrs2].AsBigInteger;
647             var isBorrowVariant = (f3 == 0b011);
648 
649             var result = rs1 - rs2;
650             if(isBorrowVariant)
651             {
652                 var carryVal = flagsGroup[flagGroup].HasFlag(CustomFlags.Carry) ? 1 : 0;
653                 result -= carryVal;
654             }
655 
656             flagsGroup[flagGroup] = GetFlags(result);
657         }
658 
BnSelHandler(ulong opcode)659         private void BnSelHandler(ulong opcode)
660         {
661             ParseRTypeFields(opcode, out var _, out var wrd, out var wrs1, out var wrs2, out var __, out var ___, out var flagGroup);
662             var selFlag = (int)BitHelper.GetValue(opcode, 25, 2);
663             var flagType = (CustomFlags)(1 << selFlag);
664             var flagSet = flagsGroup[flagGroup].HasFlag(flagType);
665 
666             var wrs = flagSet ? wrs1 : wrs2;
667             wideDataRegisters[wrd].SetTo(wideDataRegisters[wrs].AsBigInteger);
668         }
669 
BnAddSubHandler(ulong opcode)670         private void BnAddSubHandler(ulong opcode)
671         {
672             ParseRTypeFieldsAndShiftR2(opcode, out var f3, out var wrd, out var rs1, out var rs2, out var flagGroup);
673 
674             var isAdd = ((f3 == 0b101) && !BitHelper.IsBitSet(opcode, 30)) || ((f3 & 0b1) == 0);
675             var modulo = (f3 >> 1) == 0b10;
676             var carry = (f3 >> 1) == 0b01;
677 
678             var result = isAdd ? rs1 + rs2 : rs1 - rs2;
679 
680             if(modulo)
681             {
682                 var modVal = wideSpecialPurposeRegisters[(int)WideSPR.Mod].AsBigInteger;
683                 if(isAdd && (result >= modVal))
684                 {
685                     result -= modVal;
686                 }
687                 else if(!isAdd && (result < BigInteger.Zero))
688                 {
689                     result += modVal;
690                 }
691             }
692             else if(carry)
693             {
694                 var carryVal = flagsGroup[flagGroup].HasFlag(CustomFlags.Carry) ? 1 : 0;
695                 result = isAdd ? result + carryVal : result - carryVal;
696             }
697             wideDataRegisters[wrd].SetTo(result);
698             if(!modulo)
699             {
700                 flagsGroup[flagGroup] = GetFlags(result);
701             }
702         }
703 
GetFlags(BigInteger value)704         private CustomFlags GetFlags(BigInteger value)
705         {
706             var flags = CustomFlags.Empty;
707             if(((value >> 256) & 1) != 0)
708             {
709                 flags |= CustomFlags.Carry;
710             }
711             if(value == 0)
712             {
713                 flags |= CustomFlags.Zero;
714             }
715             else
716             {
717                 if(((value >> 255) & 1) != 0)
718                 {
719                     flags |= CustomFlags.Msb;
720                 }
721                 if((value & 1) != 0)
722                 {
723                     flags |= CustomFlags.Lsb;
724                 }
725             }
726             return flags;
727         }
728 
ECallHandler(ulong opcode)729         private void ECallHandler(ulong opcode)
730         {
731             Log(LogLevel.Debug, "ECALL triggered");
732 
733             executionFinished = true;
734             // Not an error, we just need it to cut the block and exit
735             ThrowError(CoreError.None);
736         }
737 
BnRShiftIHandler(ulong opcode)738         private void BnRShiftIHandler(ulong opcode)
739         {
740             ParseRTypeFields(opcode, out var _, out var wrd, out var wrs1, out var wrs2, out var __, out var ___, out var ____);
741             var imm = (int)((BitHelper.GetValue(opcode, 25, 7) << 1) | BitHelper.GetValue(opcode, 14, 1));
742             var rs1 = wideDataRegisters[wrs1].AsBigInteger;
743             var rs2 = wideDataRegisters[wrs2].AsBigInteger;
744             var result = ((rs1 << 256) ^ rs2) >> imm;
745             wideDataRegisters[wrd].SetTo(result);
746         }
747 
BnImmHandler(ulong opcode)748         private void BnImmHandler(ulong opcode)
749         {
750             ParseRTypeFields(opcode, out var f3, out var wrd, out var wrs1, out var _, out var  __, out var ___, out var flagGroup);
751             var imm = (int)BitHelper.GetValue(opcode, 20, 10);
752             var isAdd = !BitHelper.IsBitSet(opcode, 30);
753             var rs = wideDataRegisters[wrs1].AsBigInteger;
754             var result = isAdd ? rs + imm : rs - imm;
755             flagsGroup[flagGroup] = GetFlags(result);
756             wideDataRegisters[wrd].SetTo(result);
757         }
758 
BnMovrHandler(ulong opcode)759         void BnMovrHandler(ulong opcode)
760         {
761             ParseSTypeFields(opcode, out var _, out var grs, out var grd, out var __, out var ___, out var incrementRd);
762             var incrementRs = BitHelper.IsBitSet(opcode, 9);
763 
764             var grsVal = (int)this.GetRegister(grs).RawValue;
765             var grdVal = (int)this.GetRegister(grd).RawValue;
766 
767             if((incrementRs && incrementRd) || grdVal > 31 || grsVal > 31)
768             {
769                 ThrowError(CoreError.IllegalInstruction);
770                 return;
771             }
772 
773             if(incrementRd)
774             {
775                 this.SetRegister(grd, grdVal + 1);
776             }
777             else if(incrementRs)
778             {
779                 this.SetRegister(grs, grsVal + 1);
780             }
781 
782             wideDataRegisters[grdVal].SetTo(wideDataRegisters[grsVal].AsBigInteger);
783         }
784 
BnLoadStoreHandler(ulong opcode)785         private void BnLoadStoreHandler(ulong opcode)
786         {
787             ParseSTypeFields(opcode, out var f3, out var grs1, out var grd, out var offset, out var incrementRs1, out var incrementRd);
788 
789             var isLoad = (f3 == 0b100);
790             var grs1Val = (int)this.GetRegister(grs1).RawValue;
791             var grdVal = (int)this.GetRegister(grd).RawValue;
792             var addr = (long)(grs1Val + offset);
793 
794             if((incrementRs1 && incrementRd) || grdVal > 31)
795             {
796                 ThrowError(CoreError.IllegalInstruction);
797                 return;
798             }
799 
800             if(incrementRd)
801             {
802                 this.SetRegister(grd, grdVal + 1);
803             }
804             if(incrementRs1)
805             {
806                 this.SetRegister(grs1, grs1Val + 32);
807             }
808 
809             if(isLoad)
810             {
811                 var array = new byte[WideDataRegisterWidthInBytes];
812                 dataMemory.ReadBytes(addr, array.Length, array, 0);
813                 wideDataRegisters[grdVal].SetTo(new BigInteger(array));
814             }
815             else
816             {
817                 var array = wideDataRegisters[grdVal].AsByteArray;
818                 dataMemory.WriteBytes(addr, array);
819             }
820         }
821 
BnMovHandler(ulong opcode)822         void BnMovHandler(ulong opcode)
823         {
824             ParseRTypeFields(opcode, out var _, out var wrd, out var wrs, out var __, out var ___, out var ____, out var _____);
825             wideDataRegisters[wrd].SetTo(wideDataRegisters[wrs].AsBigInteger);
826         }
827 
BnBitwiseHelper(ulong opcode)828         private void BnBitwiseHelper(ulong opcode)
829         {
830             ParseRTypeFieldsAndShiftR2(opcode, out var f3, out var wrd, out var rs1, out var rs2, out var flagGroup);
831 
832             BigInteger result;
833             switch(f3)
834             {
835                 case 0b100:
836                     result = rs1 | rs2;
837                     break;
838                 case 0b010:
839                     result = rs1 & rs2;
840                     break;
841                 case 0b110:
842                     result = rs1 ^ rs2;
843                     break;
844                 case 0b101:
845                     result = ~rs2;
846                     break;
847                 default:
848                     ThrowError(CoreError.IllegalInstruction, false);
849                     return;
850             }
851             wideDataRegisters[wrd].SetTo(result);
852             flagsGroup[flagGroup] = GetFlags(result);
853         }
854 
ParseRTypeFieldsAndShiftR2(ulong opcode, out ulong f3, out int wrd, out BigInteger rs1, out BigInteger rs2, out int flagGroup)855         private void ParseRTypeFieldsAndShiftR2(ulong opcode, out ulong f3, out int wrd, out BigInteger rs1, out BigInteger rs2, out int flagGroup)
856         {
857             ParseRTypeFields(opcode, out f3, out wrd, out var wsr1, out var wsr2, out var shiftBits, out var shiftRight, out flagGroup);
858             rs1 = wideDataRegisters[wsr1].AsBigInteger;
859             rs2 = wideDataRegisters[wsr2].AsBigInteger;
860             if(shiftBits > 0)
861             {
862                 rs2 = shiftRight ? rs2 >> shiftBits : rs2 << shiftBits;
863                 rs2 &= WideRegister.MaxValueMask;
864             }
865         }
ParseRTypeFields(ulong opcode, out ulong f3, out int wrd, out int wrs1, out int wrs2, out int shiftBits, out bool shiftRight, out int flagGroup)866         private void ParseRTypeFields(ulong opcode, out ulong f3, out int wrd, out int wrs1, out int wrs2, out int shiftBits, out bool shiftRight, out int flagGroup)
867         {
868             f3 = BitHelper.GetValue(opcode, 12, 3);
869             wrd = (int)BitHelper.GetValue(opcode, 7, 5);
870             wrs1 = (int)BitHelper.GetValue(opcode, 15, 5);
871             wrs2 = (int)BitHelper.GetValue(opcode, 20, 5);
872             // The resolution of the shift is 8 bits
873             shiftBits = (int)BitHelper.GetValue(opcode, 25, 5) * 8;
874             shiftRight = BitHelper.IsBitSet(opcode, 30);
875             flagGroup = (int)BitHelper.GetValue(opcode, 31, 1);
876         }
877 
ParseSTypeFields(ulong opcode, out ulong f3, out int grs1, out int grd, out int offset, out bool incrementR1, out bool incrementRd)878         private void ParseSTypeFields(ulong opcode, out ulong f3, out int grs1, out int grd, out int offset, out bool incrementR1, out bool incrementRd)
879         {
880             grd = (int)BitHelper.GetValue(opcode, 20, 5);
881             grs1 = (int)BitHelper.GetValue(opcode, 15, 5);
882             f3 = BitHelper.GetValue(opcode, 12, 3);
883             incrementR1 = BitHelper.IsBitSet(opcode, 8);
884             incrementRd = BitHelper.IsBitSet(opcode, 7);
885             offset = (int)((BitHelper.GetValue(opcode, 25, 7) << 3) | BitHelper.GetValue(opcode, 9, 3)) << 5;
886             offset /= 8;
887         }
888 
GetPseudoRandom()889         private ulong GetPseudoRandom()
890         {
891             var result = ((ulong)random.Next() << 32) + (ulong)random.Next();
892             Log(LogLevel.Noisy, $"Generating random value of 0x{result:X}");
893             return result;
894         }
895 
896         private string keyShare0;
897         private string keyShare1;
898         private string fixedRandomPattern;
899         private byte[] fixedRandomBytes;
900         private bool executionFinished;
901 
902         private readonly WideRegister[] wideDataRegisters;
903         private readonly WideRegister[] wideSpecialPurposeRegisters;
904 
905         private readonly OpenTitan_ScrambledMemory instructionsMemory;
906         private readonly OpenTitan_ScrambledMemory dataMemory;
907 
908         private readonly CustomFlags[] flagsGroup;
909         private readonly Stack<uint> x1Stack;
910         private readonly Stack<LoopContext> loopStack;
911 
912         private readonly PseudorandomNumberGenerator random;
913         private readonly OpenTitan_BigNumberAccelerator parent;
914 
915         // Big Number opcodes
916         /* I-type
917                                                    30        20   15 12    7      0
918                                                  add|      Imm| WRS|f3| WRD|opcode| */
919         private const string BnAddIPattern =      "-0---------------100-----0101011";
920         private const string BnSubIPattern =      "-1---------------100-----0101011";
921         /* I2-type
922                                                         25   20   15 12    7      0
923                                                       Imm|Wrs2|Wrs1|f3| WRD|opcode| */
924         private const string BnRShiPattern =      "------------------11-----1111011";
925         /*  R-type
926                                                         25   20   15 12    7      0
927                                                       add| rs2| rs1|f3|  rd|opcode| */
928         private const string BnAddPattern =       "-----------------000-----0101011";
929         private const string BnAddCPattern =      "-----------------010-----0101011";
930         private const string BnAddMPattern =      "-0---------------101-----0101011";
931         private const string BnSubPattern =       "-----------------001-----0101011";
932         private const string BnSubBPattern =      "-----------------011-----0101011";
933         private const string BnSubMPattern =      "-1---------------101-----0101011";
934         private const string BnMulQAccPattern =   "-00----------------------0111011";
935         private const string BnMulQAccWoPattern = "-01----------------------0111011";
936         private const string BnMulQAccSoPattern = "-1-----------------------0111011";
937         private const string LoopPattern =        "-----------------000-----1111011";
938         private const string LoopiPattern =       "-----------------001-----1111011";
939         private const string BnAndPattern =       "-----------------010-----1111011";
940         private const string BnOrPattern =        "-----------------100-----1111011";
941         private const string BnNotPattern =       "-----------------101-----1111011";
942         private const string BnXOrPattern =       "-----------------110-----1111011";
943         private const string BnSelPattern =       "-----------------000-----0001011";
944         private const string BnCmpPattern =       "-----------------001-----0001011";
945         private const string BnCmpBPattern =      "-----------------011-----0001011";
946         private const string BnMovPattern =       "0----------------110-----0001011";
947         /* S-type
948                                                        25   20    15 12    7      0
949                                                      off| Grd| Grs1|f3| add|opcode| */
950         private const string BnLidPattern =       "-----------------100-----0001011";
951         private const string BnSidPattern =       "-----------------101-----0001011";
952         private const string BnMovrPattern =      "1----------------110-----0001011";
953         /* WSR reg-type
954                                                      28     20    15 12    7      0
955                                                    add|   Wsr|  Wrs|f3| Wrd|opcode| */
956         private const string BnWsrrPattern =      "0----------------111-----0001011";
957         private const string BnWsrwPattern =      "1----------------111-----0001011";
958         private const string ECallPattern =       "00000000000000000000000001110011";
959 
960         private const ulong VirtualDataOffset = 0x2000;
961         private const int WideDataRegistersCount = 32;
962         private const int WideSpecialPurposeRegistersCount = 8;
963         private const int WideDataRegisterWidthInBytes = 32;
964         private const int FlagsGroupsCount = 2;
965         private const int MaximumStackCapacity = 8;
966         private const int MaxLoopStackHeight = 8;
967 
968         private enum CustomCSR
969         {
970             FlagGroup0 = 0x7c0,
971             FlagGroup1 = 0x7c1,
972             Flags = 0x7c8,
973             Mod0 = 0x7d0,
974             Mod1 = 0x7d1,
975             Mod2 = 0x7d2,
976             Mod3 = 0x7d3,
977             Mod4 = 0x7d4,
978             Mod5 = 0x7d5,
979             Mod6 = 0x7d6,
980             Mod7 = 0x7d7,
981             RndPrefetch = 0x7d8,
982             Rnd = 0xfc0,
983             URnd = 0xfc1,
984         }
985 
986         private enum WideSPR
987         {
988             Mod = 0x0,
989             Rnd = 0x1,
990             URnd = 0x2,
991             Acc = 0x3,
992             KeyShare0Low = 0x4,
993             KeyShare0High = 0x5,
994             KeyShare1Low = 0x6,
995             KeyShare1High = 0x7,
996         }
997 
998         [Flags]
999         private enum CustomFlags
1000         {
1001             Empty = 0x0,
1002             Carry = 0x1,
1003             Msb = 0x2,
1004             Lsb = 0x4,
1005             Zero = 0x8,
1006         }
1007 
1008         private class LoopContext
1009         {
1010             public uint StartPC { get; set; }
1011             public uint EndPC { get; set; }
1012             public uint NumberOfIterations { get; set; }
1013 
ToString()1014             public override string ToString()
1015             {
1016                 return $"StartPC = 0x{StartPC:X}, EndPC = 0x{EndPC:X}, NumberOfIterations = {NumberOfIterations}";
1017             }
1018         }
1019 
1020         private class WideRegister
1021         {
1022             public static readonly BigInteger MaxValueMask = ((new BigInteger(1)) << 256) - 1;
1023 
WideRegister(bool readOnly = false)1024             public WideRegister(bool readOnly = false)
1025             {
1026                 underlyingValue = 0;
1027                 ReadOnly = readOnly;
1028             }
1029 
Clear()1030             public void Clear()
1031             {
1032                 underlyingValue = 0;
1033             }
1034 
1035             /* Cuts the BigInteger to 256 bits and sets the register to it */
SetTo(BigInteger bigValue)1036             public void SetTo(BigInteger bigValue)
1037             {
1038                 underlyingValue = bigValue & MaxValueMask;
1039             }
1040 
PartialSet(BigInteger value, int byteOffset, int bytesCount)1041             public void PartialSet(BigInteger value, int byteOffset, int bytesCount)
1042             {
1043                 var mask = (new BigInteger(0x1) << (bytesCount * 8)) - 1;
1044                 var shiftedValue = (value & mask) << (byteOffset * 8);
1045 
1046                 underlyingValue &= ~(mask << (byteOffset * 8));
1047                 underlyingValue |= shiftedValue;
1048             }
1049 
PartialGet(int byteOffset, int bytesCount)1050             public BigInteger PartialGet(int byteOffset, int bytesCount)
1051             {
1052                 var mask = (new BigInteger(0x1) << (bytesCount * 8)) - 1;
1053                 return (underlyingValue >> (byteOffset * 8)) & mask;
1054             }
1055 
EqualsTo(WideRegister r)1056             public bool EqualsTo(WideRegister r)
1057             {
1058                 return r.AsBigInteger.Equals(this.AsBigInteger);
1059             }
1060 
1061             public bool ReadOnly { get; }
1062 
1063             public BigInteger AsBigInteger => underlyingValue & MaxValueMask;
1064 
1065             public byte[] AsByteArray => underlyingValue.ToByteArray(ByteArrayLength);
1066 
ToString()1067             public override string ToString()
1068             {
1069                 return underlyingValue.ToLongString(ByteArrayLength);
1070             }
1071 
1072             private BigInteger underlyingValue;
1073 
1074             private readonly int ByteArrayLength = 32;
1075         }
1076     }
1077 }
1078