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