1 //
2 // Copyright (c) 2010-2021 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 Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 
13 namespace Antmicro.Renode.Peripherals.Miscellaneous
14 {
15     public sealed class NRF52840_RNG : BasicDoubleWordPeripheral, IKnownSize, INRFEventProvider
16     {
NRF52840_RNG(IMachine machine)17         public NRF52840_RNG(IMachine machine) : base(machine)
18         {
19             rng = EmulationManager.Instance.CurrentEmulation.RandomGenerator;
20             IRQ = new GPIO();
21             DefineRegisters();
22         }
23 
Reset()24         public override void Reset()
25         {
26             base.Reset();
27             IRQ.Unset();
28         }
29 
30         public long Size => 0x1000;
31 
32         public GPIO IRQ { get; }
33 
34         public event Action<uint> EventTriggered;
35 
DefineRegisters()36         private void DefineRegisters()
37         {
38             Registers.Start.Define(this)
39                 .WithFlag(0, out started, FieldMode.Write, name: "TASKS_START")
40                 .WithReservedBits(1, 31)
41                 .WithWriteCallback((_, __) => Update())
42             ;
43 
44             Registers.Stop.Define(this)
45                 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => started.Value &= !value, name: "TASKS_STOP")
46                 .WithReservedBits(1, 31)
47                 .WithWriteCallback((_, __) => Update())
48             ;
49 
50             Registers.ValueReady.Define(this)
51                 .WithFlag(0, writeCallback: (_, value) =>
52                 {
53                     if(!value)
54                     {
55                         IRQ.Unset();
56                     }
57                     Update();
58                 }, valueProviderCallback: _ => started.Value, name: "EVENTS_VALRDY")
59                 .WithReservedBits(1, 31)
60             ;
61 
62             Registers.Shorts.Define(this, name: "SHORTS")
63                 .WithFlag(0, out readyToStopShortEnabled, name: "VALRDY_STOP")
64                 .WithReservedBits(1, 31)
65                 .WithWriteCallback((_, __) => Update())
66             ;
67 
68             Registers.InterruptEnableSet.Define(this, name: "INTENSET")
69                 .WithFlag(0, out interruptEnabled, FieldMode.Set | FieldMode.Read, name: "VALRDY")
70                 .WithReservedBits(1, 31)
71                 .WithWriteCallback((_, __) => Update())
72             ;
73 
74             Registers.InterruptEnableClear.Define(this, name: "INTENCLR")
75                 .WithFlag(0, writeCallback: (_, value) => interruptEnabled.Value &= !value, valueProviderCallback: _ => interruptEnabled.Value, name: "VALRDY")
76                 .WithReservedBits(1, 31)
77                 .WithWriteCallback((_, __) => Update())
78             ;
79 
80             Registers.Config.Define(this, name: "CONFIG")
81                 .WithTaggedFlag("DERCEN", 0)
82                 .WithReservedBits(1, 31)
83             ;
84 
85             Registers.Value.Define(this, name: "VALUE")
86                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => started.Value ? (uint)rng.Next(0, byte.MaxValue) : 0u, name: "VALUE")
87                 .WithReservedBits(8, 24)
88             ;
89         }
90 
Update()91         private void Update()
92         {
93             var value = started.Value && interruptEnabled.Value;
94             if(value)
95             {
96                 this.Log(LogLevel.Noisy, "Generated new interrupt for RNG");
97                 EventTriggered?.Invoke(0);
98             }
99             IRQ.Set(value);
100             if(started.Value && readyToStopShortEnabled.Value)
101             {
102                 started.Value = false;
103                 IRQ.Set(false);
104             }
105         }
106 
107         private readonly PseudorandomNumberGenerator rng;
108         private IFlagRegisterField started;
109         private IFlagRegisterField readyToStopShortEnabled;
110         private IFlagRegisterField interruptEnabled;
111 
112         private enum Registers
113         {
114             Start = 0x0,
115             Stop = 0x4,
116             ValueReady = 0x100,
117             Shorts = 0x200,
118             InterruptEnableSet = 0x304,
119             InterruptEnableClear = 0x308,
120             Config = 0x504,
121             Value = 0x508
122         }
123     }
124 }
125