1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Peripherals; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.Logging; 15 16 namespace Antmicro.Renode.Peripherals.GPIOPort 17 { 18 public class SAM4S_PIO : BaseGPIOPort, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize 19 { 20 // Status register reset value is defined at product level, thus there's an appropriate variable in the constructor SAM4S_PIO(IMachine machine, uint statusRegisterResetVal = 0xFFFFFFFF)21 public SAM4S_PIO(IMachine machine, uint statusRegisterResetVal = 0xFFFFFFFF) : base(machine, NumberOfPins) 22 { 23 innerLock = new object(); 24 IRQ = new GPIO(); 25 irqManager = new GPIOInterruptManager(IRQ, State); 26 enabled = new IFlagRegisterField[NumberOfPins]; 27 useAdditionalIrqMode = new IFlagRegisterField[NumberOfPins]; 28 selectEdgeLevel= new IFlagRegisterField[NumberOfPins]; 29 selectFallRiseLowHigh = new IFlagRegisterField[NumberOfPins]; 30 outputData = new IFlagRegisterField[NumberOfPins]; 31 outputDataWriteEnabled = new IFlagRegisterField[NumberOfPins]; 32 33 irqManager = new GPIOInterruptManager(IRQ, State); 34 ResetDirection(); 35 RegistersCollection = new DoubleWordRegisterCollection(this); 36 DefineRegisters(statusRegisterResetVal); 37 } 38 ReadDoubleWord(long offset)39 public uint ReadDoubleWord(long offset) 40 { 41 lock(innerLock) 42 { 43 return RegistersCollection.Read(offset); 44 } 45 } 46 WriteDoubleWord(long offset, uint value)47 public void WriteDoubleWord(long offset, uint value) 48 { 49 lock(innerLock) 50 { 51 RegistersCollection.Write(offset, value); 52 } 53 } 54 OnGPIO(int number, bool value)55 public override void OnGPIO(int number, bool value) 56 { 57 if(!CheckPinNumber(number)) 58 { 59 return; 60 } 61 62 lock(innerLock) 63 { 64 if((irqManager.PinDirection[number] & GPIOInterruptManager.Direction.Input) == 0) 65 { 66 this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number); 67 return; 68 } 69 70 base.OnGPIO(number, value); 71 irqManager.RefreshInterrupts(); 72 } 73 } 74 Reset()75 public override void Reset() 76 { 77 lock(innerLock) 78 { 79 base.Reset(); 80 irqManager.Reset(); 81 ResetDirection(); 82 RegistersCollection.Reset(); 83 } 84 } 85 86 public DoubleWordRegisterCollection RegistersCollection { get; } 87 88 public GPIO IRQ { get; } 89 public long Size => 0x168; 90 ResetDirection()91 private void ResetDirection() 92 { 93 for(int i = 0; i < NumberOfPins; i++) 94 { 95 irqManager.PinDirection[i] = GPIOInterruptManager.Direction.Input; 96 } 97 } 98 DefineRegisters(uint statusRegisterResetVal)99 private void DefineRegisters(uint statusRegisterResetVal) 100 { 101 Registers.PioEnable.Define(this) 102 .WithFlags(0, 32, FieldMode.WriteOneToClear, 103 writeCallback: (i, _, value) => 104 { 105 if(value) 106 { 107 enabled[i].Value = true; 108 UpdateIOLine(i); 109 } 110 }, 111 name: "PER") 112 ; 113 114 Registers.PioDisable.Define(this) 115 .WithFlags(0, 32, FieldMode.WriteOneToClear, 116 writeCallback: (i, _, value) => 117 { 118 if(value) 119 { 120 enabled[i].Value = false; 121 UpdateIOLine(i); 122 } 123 }, 124 name: "PDR") 125 ; 126 127 Registers.PioStatus.Define(this, statusRegisterResetVal) 128 .WithFlags(0, 32, out enabled, FieldMode.Read, name: "PSR") 129 ; 130 131 Registers.OutputEnable.Define(this) 132 .WithFlags(0, 32, FieldMode.WriteOneToClear, 133 writeCallback: (i, _, value) => 134 { 135 if(value) 136 { 137 irqManager.PinDirection[i] |= GPIOInterruptManager.Direction.Output; 138 UpdateIOLine(i); 139 } 140 }, 141 name: "OER") 142 ; 143 144 Registers.OutputDisable.Define(this) 145 .WithFlags(0, 32, FieldMode.WriteOneToClear, 146 writeCallback: (i, _, value) => 147 { 148 if(value) 149 { 150 irqManager.PinDirection[i] &= ~GPIOInterruptManager.Direction.Output; 151 UpdateIOLine(i); 152 } 153 }, 154 name: "ODR") 155 ; 156 157 Registers.OutputStatus.Define(this) 158 .WithFlags(0, 32, FieldMode.Read, 159 valueProviderCallback: (i, _) => 160 { 161 return (irqManager.PinDirection[i] & GPIOInterruptManager.Direction.Output) != 0; 162 }, 163 name: "OSR") 164 ; 165 166 Registers.GlitchInputFilterEnable.Define(this) 167 .WithTaggedFlags("IFER", 0, 32) 168 ; 169 170 Registers.GlitchInputFilterDisable.Define(this) 171 .WithTaggedFlags("IFDR", 0, 32) 172 ; 173 174 Registers.GlitchInputFilterStatus.Define(this) 175 .WithTaggedFlags("IFSR", 0, 32) 176 ; 177 178 Registers.SetOutputData.Define(this) 179 .WithFlags(0, 32, FieldMode.WriteOneToClear, 180 writeCallback: (i, _, value) => 181 { 182 if(value) 183 { 184 outputData[i].Value = true; 185 UpdateIOLine(i); 186 } 187 }, 188 name: "SODR") 189 ; 190 191 Registers.ClearOutputData.Define(this) 192 .WithFlags(0, 32, FieldMode.WriteOneToClear, 193 writeCallback: (i, _, value) => 194 { 195 if(value) 196 { 197 outputData[i].Value = false; 198 UpdateIOLine(i); 199 } 200 }, 201 name: "CODR") 202 ; 203 204 Registers.OutputDataStatus.Define(this) 205 .WithFlags(0, 32, out outputData, 206 writeCallback: (i, _, value) => 207 { 208 // Bits in this register can be Read/Write if 1 is set in OutputWriteEnable 209 if(outputDataWriteEnabled[i].Value) 210 { 211 outputData[i].Value = value; 212 UpdateIOLine(i); 213 } 214 }, 215 name: "ODSR") 216 ; 217 218 Registers.PinDataStatus.Define(this) 219 .WithFlags(0, 32, FieldMode.Read, valueProviderCallback: (i, _) => State[i], name: "PDSR") 220 ; 221 222 Registers.InterruptEnable.Define(this) 223 .WithFlags(0, 32, FieldMode.WriteOneToClear, 224 writeCallback: (i, _, value) => 225 { 226 if(value) 227 { 228 irqManager.InterruptEnable[i] = true; 229 } 230 }, 231 name: "IER") 232 ; 233 234 Registers.InterruptDisable.Define(this) 235 .WithFlags(0, 32, FieldMode.WriteOneToClear, 236 writeCallback: (i, _, value) => 237 { 238 if(value) 239 { 240 irqManager.InterruptEnable[i] = false; 241 } 242 }, 243 name: "IDR") 244 ; 245 246 Registers.InterruptMask.Define(this) 247 .WithFlags(0, 32, FieldMode.Read, valueProviderCallback: (i, _) => irqManager.InterruptEnable[i], name: "IMR") 248 ; 249 250 Registers.InterruptStatus.Define(this) 251 .WithFlags(0, 32, FieldMode.Read, 252 valueProviderCallback: (i, _) => 253 { 254 var result = irqManager.ActiveInterrupts.ElementAt(i); 255 irqManager.ClearInterrupt(i); 256 return result; 257 }, 258 name: "ISR") 259 ; 260 261 Registers.MultiDriverEnable.Define(this) 262 .WithTaggedFlags("MDER", 0, 32) 263 ; 264 265 Registers.MultiDriverDisable.Define(this) 266 .WithTaggedFlags("MDDR", 0, 32) 267 ; 268 269 Registers.MultiDriverStatus.Define(this) 270 .WithTaggedFlags("MDSR", 0, 32) 271 ; 272 273 Registers.PullUpDisable.Define(this) 274 .WithTaggedFlags("PUDR", 0, 32) 275 ; 276 277 Registers.PullUpEnable.Define(this) 278 .WithTaggedFlags("PUER", 0, 32) 279 ; 280 281 Registers.PadPullUpStatus.Define(this) 282 .WithTaggedFlags("PUSR", 0, 32) 283 ; 284 285 Registers.PeripheralSelect1.Define(this) 286 .WithTaggedFlags("ABCDSR1", 0, 32) 287 ; 288 289 Registers.PeripheralSelect2.Define(this) 290 .WithTaggedFlags("ABCDSR2", 0, 32) 291 ; 292 293 Registers.InputFilterSlowClockDisable.Define(this) 294 .WithTaggedFlags("IFSCDR", 0, 32) 295 ; 296 297 Registers.InputFilterSlowClockEnable.Define(this) 298 .WithTaggedFlags("IFSCER", 0, 32) 299 ; 300 301 Registers.InputFilterSlowClockStatus.Define(this) 302 .WithTaggedFlags("IFSCSR", 0, 32) 303 ; 304 305 Registers.SlowClockDividerDebouncing.Define(this) 306 .WithTag("DIV", 0, 14) 307 .WithReservedBits(14, 18) 308 ; 309 310 Registers.PadPullDownDisable.Define(this) 311 .WithTaggedFlags("PPDDR", 0, 32) 312 ; 313 314 Registers.PadPullDownEnable.Define(this) 315 .WithTaggedFlags("PPDER", 0, 32) 316 ; 317 318 Registers.PadPullDownStatus.Define(this) 319 .WithTaggedFlags("PPDSR", 0, 32) 320 ; 321 322 Registers.OutputWriteEnable.Define(this) 323 .WithFlags(0, 32, FieldMode.WriteOneToClear, 324 writeCallback: (i, _, value) => 325 { 326 if(value) 327 { 328 outputDataWriteEnabled[i].Value = true; 329 } 330 }, 331 name: "OWER") 332 ; 333 334 Registers.OutputWriteDisable.Define(this) 335 .WithFlags(0, 32, FieldMode.WriteOneToClear, 336 writeCallback: (i, _, value) => 337 { 338 if(value) 339 { 340 outputDataWriteEnabled[i].Value = false; 341 } 342 }, 343 name: "OWDR") 344 ; 345 346 Registers.OutputWriteStatus.Define(this) 347 .WithFlags(0, 32, out outputDataWriteEnabled, FieldMode.Read, name: "OWSR") 348 ; 349 350 Registers.AdditionalInterruptModesEnable.Define(this) 351 .WithFlags(0, 32, FieldMode.WriteOneToClear, 352 writeCallback: (i, _, value) => 353 { 354 if(value) 355 { 356 useAdditionalIrqMode[i].Value = true; 357 UpdateInterruptType(i); 358 } 359 }, 360 name: "AIMER") 361 ; 362 363 Registers.AdditionalInterruptModesDisable.Define(this) 364 .WithFlags(0, 32, FieldMode.WriteOneToClear, 365 writeCallback: (i, _, value) => 366 { 367 if(value) 368 { 369 useAdditionalIrqMode[i].Value = false; 370 UpdateInterruptType(i); 371 } 372 }, 373 name: "AIMDR") 374 ; 375 376 Registers.AdditionalInterruptModesMask.Define(this) 377 .WithFlags(0, 32, out useAdditionalIrqMode, FieldMode.Read, name: "AIMMR") 378 ; 379 380 Registers.EdgeSelect.Define(this) 381 .WithFlags(0, 32, FieldMode.WriteOneToClear, 382 writeCallback: (i, _, value) => 383 { 384 if(value) 385 { 386 selectEdgeLevel[i].Value = false; 387 UpdateInterruptType(i); 388 } 389 }, 390 name: "ESR") 391 ; 392 393 Registers.LevelSelect.Define(this) 394 .WithFlags(0, 32, FieldMode.WriteOneToClear, 395 writeCallback: (i, _, value) => 396 { 397 if(value) 398 { 399 selectEdgeLevel[i].Value = true; 400 UpdateInterruptType(i); 401 } 402 }, 403 name: "LSR") 404 ; 405 406 Registers.EdgeLevelStatus.Define(this) 407 .WithFlags(0, 32, out selectEdgeLevel, FieldMode.Read, name: "ELSR") 408 ; 409 410 Registers.FallingEdgeLowLevelSelect.Define(this) 411 .WithFlags(0, 32, FieldMode.WriteOneToClear, 412 writeCallback: (i, _, value) => 413 { 414 if(value) 415 { 416 selectFallRiseLowHigh[i].Value = false; 417 UpdateInterruptType(i); 418 } 419 }, 420 name: "FELLSR") 421 ; 422 423 Registers.RisingEdgeHighLevelSelect.Define(this) 424 .WithFlags(0, 32, FieldMode.WriteOneToClear, 425 writeCallback: (i, _, value) => 426 { 427 if(value) 428 { 429 selectFallRiseLowHigh[i].Value = true; 430 UpdateInterruptType(i); 431 } 432 }, 433 name: "REHLSR") 434 ; 435 436 Registers.FallRiseLowHighStatus.Define(this) 437 .WithFlags(0, 32, out selectFallRiseLowHigh, FieldMode.Read, name: "FRLHSR") 438 ; 439 440 Registers.LockStatus.Define(this) 441 .WithTaggedFlags("LOCKSR", 0, 32) 442 ; 443 444 Registers.WriteProtectionMode.Define(this) 445 .WithTaggedFlag("WPEN", 0) 446 .WithReservedBits(1, 7) 447 .WithTag("WPKEY", 8, 24) 448 ; 449 450 Registers.WriteProtectionStatus.Define(this) 451 .WithTaggedFlag("WPVS", 0) 452 .WithReservedBits(1, 7) 453 .WithTag("WPVSRC", 8, 16) 454 .WithReservedBits(24, 8) 455 ; 456 457 Registers.SchmittTrigger.Define(this) 458 .WithTaggedFlags("SCHMITT", 0, 32) 459 ; 460 461 Registers.ParallelCaptureMode.Define(this) 462 .WithTaggedFlag("PCEN", 0) 463 .WithReservedBits(1, 3) 464 .WithTag("DSIZE", 4, 2) 465 .WithReservedBits(6, 3) 466 .WithTaggedFlag("ALWYS", 9) 467 .WithTaggedFlag("HALFS", 10) 468 .WithTaggedFlag("FRSTS", 11) 469 .WithReservedBits(12, 20) 470 ; 471 472 Registers.ParallelCaptureInterruptEnable.Define(this) 473 .WithTaggedFlag("DRDY", 0) 474 .WithTaggedFlag("OVRE", 1) 475 .WithTaggedFlag("ENDRX", 2) 476 .WithTaggedFlag("RXBUFF", 3) 477 .WithReservedBits(4, 28) 478 ; 479 480 Registers.ParallelCaptureInterruptDisable.Define(this) 481 .WithTaggedFlag("DRDY", 0) 482 .WithTaggedFlag("OVRE", 1) 483 .WithTaggedFlag("ENDRX", 2) 484 .WithTaggedFlag("RXBUFF", 3) 485 .WithReservedBits(4, 28) 486 ; 487 488 Registers.ParallelCaptureInterruptMask.Define(this) 489 .WithTaggedFlag("DRDY", 0) 490 .WithTaggedFlag("OVRE", 1) 491 .WithTaggedFlag("ENDRX", 2) 492 .WithTaggedFlag("RXBUFF", 3) 493 .WithReservedBits(4, 28) 494 ; 495 496 Registers.ParallelCaptureInterruptStatus.Define(this) 497 .WithTaggedFlag("DRDY", 0) 498 .WithTaggedFlag("OVRE", 1) 499 .WithTaggedFlag("ENDRX", 2) 500 .WithTaggedFlag("RXBUFF", 3) 501 .WithReservedBits(4, 28) 502 ; 503 504 Registers.ParallelCaptureReceptionHolding.Define(this) 505 .WithTag("RDATA", 0, 32) 506 ; 507 } 508 UpdateIOLine(int number)509 private void UpdateIOLine(int number) 510 { 511 if((irqManager.PinDirection[number] & GPIOInterruptManager.Direction.Output) != 0 && enabled[number].Value) 512 { 513 this.Log(LogLevel.Debug, "Setting connection on pin {0} to {1}", number, outputData[number].Value); 514 Connections[number].Set(outputData[number].Value); 515 } 516 } 517 UpdateInterruptType(int number)518 private void UpdateInterruptType(int number) 519 { 520 if(!useAdditionalIrqMode[number].Value) 521 { 522 irqManager.InterruptType[number] = GPIOInterruptManager.InterruptTrigger.BothEdges; 523 } 524 else if(selectEdgeLevel[number].Value && selectFallRiseLowHigh[number].Value) 525 { 526 irqManager.InterruptType[number] = GPIOInterruptManager.InterruptTrigger.ActiveHigh; 527 } 528 else if(!selectEdgeLevel[number].Value && selectFallRiseLowHigh[number].Value) 529 { 530 irqManager.InterruptType[number] = GPIOInterruptManager.InterruptTrigger.RisingEdge; 531 } 532 else if(selectEdgeLevel[number].Value && !selectFallRiseLowHigh[number].Value) 533 { 534 irqManager.InterruptType[number] = GPIOInterruptManager.InterruptTrigger.ActiveLow; 535 } 536 else 537 { 538 irqManager.InterruptType[number] = GPIOInterruptManager.InterruptTrigger.FallingEdge; 539 } 540 this.Log(LogLevel.Debug, "Setting interrupt trigger type to {0}", irqManager.InterruptType[number]); 541 } 542 543 private IFlagRegisterField[] enabled; 544 private IFlagRegisterField[] outputData; 545 private IFlagRegisterField[] outputDataWriteEnabled; 546 private IFlagRegisterField[] useAdditionalIrqMode; 547 private IFlagRegisterField[] selectEdgeLevel; 548 private IFlagRegisterField[] selectFallRiseLowHigh; 549 private readonly GPIOInterruptManager irqManager; 550 private readonly object innerLock; 551 552 private const int NumberOfPins = 32; 553 554 public enum Registers 555 { 556 PioEnable = 0x0000, // WO 557 PioDisable = 0x0004, // WO 558 PioStatus = 0x0008, // RO 559 // Reserved = 0x000C 560 OutputEnable = 0x0010, // WO 561 OutputDisable = 0x0014, // WO 562 OutputStatus = 0x0018, // RO 563 // Reserved = 0x001C 564 GlitchInputFilterEnable = 0x0020, // WO 565 GlitchInputFilterDisable = 0x0024, // WO 566 GlitchInputFilterStatus = 0x0028, // RO 567 // Reserved = 0x002C 568 SetOutputData = 0x0030, // WO 569 ClearOutputData = 0x0034, // WO 570 OutputDataStatus = 0x0038, // (RO or RW) 571 PinDataStatus = 0x003C, // RO 572 InterruptEnable = 0x0040, // WO 573 InterruptDisable = 0x0044, // WO 574 InterruptMask = 0x0048, // RO 575 InterruptStatus = 0x004C, //RO 576 MultiDriverEnable = 0x0050, // WO 577 MultiDriverDisable = 0x0054, // WO 578 MultiDriverStatus = 0x0058, // RO 579 // Reserved = 0x005C 580 PullUpDisable = 0x0060, // WO 581 PullUpEnable = 0x0064, // WO 582 PadPullUpStatus = 0x0068, // RO 583 // Reserved = 0x006C 584 PeripheralSelect1 = 0x0070, // RW 585 PeripheralSelect2 = 0x0074, // RW 586 // Reserved = 0x0078–0x007C 587 InputFilterSlowClockDisable = 0x0080, // WO 588 InputFilterSlowClockEnable = 0x0084, // WO 589 InputFilterSlowClockStatus = 0x0088, // RO 590 SlowClockDividerDebouncing = 0x008C, // RW 591 PadPullDownDisable = 0x0090, // WO 592 PadPullDownEnable = 0x0094, // WO 593 PadPullDownStatus = 0x0098, // RO 594 // Reserved = 0x009C 595 OutputWriteEnable = 0x00A0, // WO 596 OutputWriteDisable = 0x00A4, // WO 597 OutputWriteStatus = 0x00A8, // RO 598 // Reserved = 0x00AC 599 AdditionalInterruptModesEnable = 0x00B0, // WO 600 AdditionalInterruptModesDisable = 0x00B4, // WO 601 AdditionalInterruptModesMask = 0x00B8, // RO 602 // Reserved = 0x00BC 603 EdgeSelect = 0x00C0, // WO 604 LevelSelect = 0x00C4, // WO 605 EdgeLevelStatus = 0x00C8, // RO 606 // Reserved = 0x00CC 607 FallingEdgeLowLevelSelect = 0x00D0, // WO 608 RisingEdgeHighLevelSelect = 0x00D4, // WO 609 FallRiseLowHighStatus = 0x00D8, // RO 610 // Reserved = 0x00DC 611 LockStatus = 0x00E0, // RO 612 WriteProtectionMode = 0x00E4, // RW 613 WriteProtectionStatus = 0x00E8, // RO 614 // Reserved = 0x00EC–0x00FC 615 SchmittTrigger = 0x0100, // RW 616 // Reserved = 0x0104–0x010C 617 // Reserved = 0x0110 618 // Reserved = 0x0114–0x011C 619 // Reserved = 0x0120–0x014C 620 ParallelCaptureMode = 0x0150, // RW 621 ParallelCaptureInterruptEnable = 0x0154, // WO 622 ParallelCaptureInterruptDisable = 0x0158, // WO 623 ParallelCaptureInterruptMask = 0x015C, // RO 624 ParallelCaptureInterruptStatus = 0x0160, // RO 625 ParallelCaptureReceptionHolding = 0x0164, // RO 626 } 627 } 628 } 629