1 //
2 // Copyright (c) 2010-2023 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 Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Utilities;
13 
14 namespace Antmicro.Renode.Peripherals.Miscellaneous
15 {
16     public class OpenTitan_EntropyDistributionNetwork : BasicDoubleWordPeripheral, IKnownSize
17     {
OpenTitan_EntropyDistributionNetwork(IMachine machine, OpenTitan_CSRNG cryptoRandomGenerator)18         public OpenTitan_EntropyDistributionNetwork(IMachine machine, OpenTitan_CSRNG cryptoRandomGenerator) : base(machine)
19         {
20             this.csrng = cryptoRandomGenerator;
21 
22             DefineRegisters();
23             CommandRequestDone = new GPIO();
24             FatalError = new GPIO();
25             RecoverableAlert = new GPIO();
26             FatalAlert = new GPIO();
27 
28             reseedCommands = new Queue<uint>(ReseedCommandFifoCapacity);
29             generateCommands = new Queue<uint>(GenerateCommandFifoCapacity);
30 
31             Reset();
32         }
33 
34         // it is for intermodule communication between the peripheral devices and the EDN
RequestData()35         public uint RequestData()
36         {
37             if(ednEnable.Value != MultiBitBool4.True)
38             {
39                 this.Log(LogLevel.Warning, "EDN is disabled. Returning 0.");
40                 return 0;
41             }
42 
43             var dataLeft = csrng.RequestData(out var result);
44 
45             if(!dataLeft && bootRequestMode.Value == MultiBitBool4.True)
46             {
47                 this.Log(LogLevel.Debug, "BOOT_REQ_MODE is on. Issueing a new request.");
48                 SendCsrngCommand((uint)bootGenerateCommand.Value);
49             }
50 
51             if(!dataLeft && autoRequestModeOn)
52             {
53                 this.Log(LogLevel.Debug, "AUTO_REQ_MODE is on. Issueing a new request.");
54 
55                 if(generateCommands.TryDequeue(out var generateCommand))
56                 {
57                     SendCsrngCommand(generateCommand);
58                 }
59                 else
60                 {
61                     this.Log(LogLevel.Warning, "No generate commands in the generate command FIFO");
62                 }
63                 requestsBetweenReseeds.Value--;
64 
65                 if(requestsBetweenReseeds.Value == 0)
66                 {
67                     if(reseedCommands.TryDequeue(out var reseedCommand))
68                     {
69                         SendCsrngCommand(reseedCommand);
70                     }
71                     else
72                     {
73                         this.Log(LogLevel.Warning, "No reseed commands in the reseed command FIFO");
74                     }
75                     requestsBetweenReseeds.Value = maxNumberOfRequestsBetweenReseeds; // reload counter
76                 }
77             }
78 
79             return result;
80         }
81 
Reset()82         public override void Reset()
83         {
84             base.Reset();
85         }
86 
87         public long Size => 0x1000;
88 
DefineRegisters()89         private void DefineRegisters()
90         {
91             Registers.InterruptState.Define(this)
92                 .WithFlag(0, out commandRequestDoneInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "edn_cmd_req_done")
93                 .WithFlag(1, out fatalErrorInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "edn_fatal_err")
94                 .WithReservedBits(2, 30)
95                 .WithWriteCallback((_, __) => UpdateInterrupts());
96 
97             Registers.InterruptEnable.Define(this)
98                 .WithFlag(0, out commandRequestDoneInterruptEnable, name: "edn_cmd_req_done")
99                 .WithFlag(1, out fatalErrorInterruptEnable, name: "edn_fatal_err")
100                 .WithReservedBits(2, 30)
101                 .WithWriteCallback((_, __) => UpdateInterrupts());
102 
103             Registers.InterruptTest.Define(this)
104                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) commandRequestDoneInterruptState.Value = true; }, name: "edn_cmd_req_done")
105                 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { if(val) fatalErrorInterruptState.Value = true; }, name: "edn_fatal_err")
106                 .WithReservedBits(2, 30)
107                 .WithWriteCallback((_, __) => UpdateInterrupts());
108 
109             Registers.AlertTest.Define(this)
110                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) RecoverableAlert.Blink(); }, name: "recov_alert")
111                 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalAlert.Blink(); }, name: "fatal_alert")
112                 .WithReservedBits(2, 30);
113 
114             Registers.RegisterWriteEnableForAllControls.Define(this, 0x1)
115                 .WithFlag(0, out allControlsRegisterWriteEnable, FieldMode.Read | FieldMode.WriteZeroToClear, name: "REGWEN")
116                 .WithReservedBits(1, 31);
117 
118             Registers.Control.Define(this, 0x9999)
119                 .WithEnumField(0, 4, out ednEnable, writeCallback: (_, val) =>
120                 {
121                     if(!allControlsRegisterWriteEnable.Value)
122                     {
123                         this.Log(LogLevel.Warning, "Register write is disabled");
124                         return;
125                     }
126 
127                     if(val != MultiBitBool4.True && val != MultiBitBool4.False)
128                     {
129                         this.Log(LogLevel.Warning, "Invalid value for EDN_ENABLE");
130                         ednEnableFieldAlert.Value = true;
131                         return;
132                     }
133                 }, name: "EDN_ENABLE")
134                 .WithEnumField(4, 4, out bootRequestMode, writeCallback: (_, val) =>
135                 {
136                     if(!allControlsRegisterWriteEnable.Value)
137                     {
138                         this.Log(LogLevel.Warning, "Register write is disabled");
139                         return;
140                     }
141 
142                     if(val != MultiBitBool4.True && val != MultiBitBool4.False)
143                     {
144                         this.Log(LogLevel.Warning, "Invalid value for BOOT_REQ_MODE");
145                         bootRequestModeFieldAlert.Value = true;
146                         return;
147                     }
148                 }, name: "BOOT_REQ_MODE")
149                 .WithEnumField(8, 4, out autoRequestMode, writeCallback: (_, val) =>
150                 {
151                     if(!allControlsRegisterWriteEnable.Value)
152                     {
153                         this.Log(LogLevel.Warning, "Register write is disabled");
154                         return;
155                     }
156 
157                     if(val != MultiBitBool4.True && val != MultiBitBool4.False)
158                     {
159                         this.Log(LogLevel.Warning, "Invalid value for AUTO_REQ_MODE");
160                         autoRequestModeFieldAlert.Value = true;
161                         return;
162                     }
163 
164                     if(val == MultiBitBool4.False)
165                     {
166                         autoRequestModeOn = false;
167                     }
168                 }, name: "AUTO_REQ_MODE")
169                 .WithEnumField(12, 4, out commandFifoReset, writeCallback: (_, val) =>
170                 {
171                     if(!allControlsRegisterWriteEnable.Value)
172                     {
173                         this.Log(LogLevel.Warning, "Register write is disabled");
174                         return;
175                     }
176 
177                     if(val != MultiBitBool4.True && val != MultiBitBool4.False)
178                     {
179                         this.Log(LogLevel.Warning, "Invalid value for CMD_FIFO_RST");
180                         commandFifoResetFieldAlert.Value = true;
181                         return;
182                     }
183 
184                     if(val == MultiBitBool4.True)
185                     {
186                         reseedCommands.Clear();
187                         generateCommands.Clear();
188                     }
189                 }, name: "CMD_FIFO_RST")
190                 .WithReservedBits(16, 16);
191 
192             Registers.BootInstantiateCommand.Define(this, 0x901)
193                 .WithValueField(0, 32, out bootInstantiateCommand, writeCallback: (_, val) =>
194                 {
195                     if(bootRequestMode.Value != MultiBitBool4.True)
196                     {
197                         this.Log(LogLevel.Warning, "BOOT_REQ_MODE is disabled. Writing to BOOT_INST_CMD has no effect.");
198                         return;
199                     }
200 
201                     SendCsrngCommand((uint)val);
202                 }, name: "BOOT_INST_CMD");
203 
204             Registers.BootGenerateCommand.Define(this, 0xfff003)
205                 .WithValueField(0, 32, out bootGenerateCommand, writeCallback: (_, val) =>
206                 {
207                     if(bootRequestMode.Value != MultiBitBool4.True)
208                     {
209                         this.Log(LogLevel.Warning, "BOOT_REQ_MODE is disabled. Writing to BOOT_GEN_CMD has no effect.");
210                         return;
211                     }
212 
213                     SendCsrngCommand((uint)val);
214                 }, name: "BOOT_GEN_CMD");
215 
216             Registers.CsrngSoftwareCommandRequest.Define(this)
217                 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, val) =>
218                 {
219                     if(autoRequestModeOn)
220                     {
221                         this.Log(LogLevel.Warning, "Automatic request mode is enabled. Writing to SW_CMD_REQ has no effect.");
222                         return;
223                     }
224 
225                     if(autoRequestMode.Value == MultiBitBool4.True)
226                     {
227                         autoRequestModeOn = true;
228                     }
229 
230                     SendCsrngCommand((uint)val);
231                 }
232                 , name: "SW_CMD_REQ");
233 
234             Registers.CommandStatus.Define(this)
235                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => true /* 1 - Ready to accept commands */, name: "CMD_RDY")
236                 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false /* 0 - Request completed successfully*/, name: "CMD_STS")
237                 .WithReservedBits(2, 30);
238 
239             Registers.CsrngReseedCommand.Define(this)
240                 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, val) =>
241                 {
242                     if(reseedCommands.Count >= ReseedCommandFifoCapacity)
243                     {
244                         this.Log(LogLevel.Warning, "Reseed command FIFO is full");
245                         reseedCommandsError.Value = true;
246                         fifoWriteError.Value = true;
247                         FatalAlert.Blink();
248                         fatalErrorInterruptState.Value = true;
249                         UpdateInterrupts();
250                         return;
251                     }
252                     reseedCommands.Enqueue((uint)val);
253                 }, name: "RESEED_CMD");
254 
255             Registers.CsrngGenerateCommand.Define(this)
256                 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, val) =>
257                 {
258                     if(generateCommands.Count >= GenerateCommandFifoCapacity)
259                     {
260                         this.Log(LogLevel.Warning, "Generate command FIFO is full");
261                         generateCommandsError.Value = true;
262                         fifoWriteError.Value = true;
263                         FatalAlert.Blink();
264                         fatalErrorInterruptState.Value = true;
265                         UpdateInterrupts();
266                         return;
267                     }
268                     generateCommands.Enqueue((uint)val);
269                 }, name: "GENERATE_CMD");
270 
271             Registers.MaximumNumberOfRequestsBetweenReseeds.Define(this)
272                 .WithValueField(0, 32, out requestsBetweenReseeds, writeCallback: (_, val) => maxNumberOfRequestsBetweenReseeds = (uint)val, name: "MAX_NUM_REQS_BETWEEN_RESEEDS");
273 
274             Registers.RecoverableAlertStatus.Define(this)
275                 .WithFlag(0, out ednEnableFieldAlert, FieldMode.Read | FieldMode.WriteZeroToClear, name: "EDN_ENABLE_FIELD_ALERT")
276                 .WithFlag(1, out bootRequestModeFieldAlert, FieldMode.Read | FieldMode.WriteZeroToClear, name: "BOOT_REQ_MODE_FIELD_ALERT")
277                 .WithFlag(2, out autoRequestModeFieldAlert, FieldMode.Read | FieldMode.WriteZeroToClear, name: "AUTO_REQ_MODE_FIELD_ALERT")
278                 .WithFlag(3, out commandFifoResetFieldAlert, FieldMode.Read | FieldMode.WriteZeroToClear, name: "CMD_FIFO_RST_FIELD_ALERT")
279                 .WithReservedBits(4, 8)
280                 .WithTaggedFlag("EDN_BUS_CMP_ALERT", 12)
281                 .WithReservedBits(13, 19);
282 
283             Registers.ErrorCode.Define(this)
284                 .WithFlag(0, out reseedCommandsError, FieldMode.Read, name: "SFIFO_RESCMD_ERR")
285                 .WithFlag(1, out generateCommandsError, FieldMode.Read, name: "SFIFO_GENCMD_ERR")
286                 .WithFlag(2, out outputFifoError, FieldMode.Read, name: "SFIFO_OUTPUT_ERR")
287                 .WithReservedBits(3, 17)
288                 .WithFlag(20, out ednAckStageIllegalStateError, FieldMode.Read, name: "EDN_ACK_SM_ERR")
289                 .WithFlag(21, out ednMainStageIllegalStateError, FieldMode.Read, name: "EDN_MAIN_SM_ERR")
290                 .WithFlag(22, out hardenedCounterError, FieldMode.Read, name: "EDN_CNTR_ERR")
291                 .WithReservedBits(23, 5)
292                 .WithFlag(28, out fifoWriteError, FieldMode.Read, name: "FIFO_WRITE_ERR")
293                 .WithFlag(29, out fifoReadError, FieldMode.Read, name: "FIFO_READ_ERR")
294                 .WithFlag(30, out fifoStateError, FieldMode.Read, name: "FIFO_STATE_ERR")
295                 .WithReservedBits(31, 1);
296 
297             Registers.TestErrorConditions.Define(this)
298                 .WithValueField(0, 5, writeCallback: (_, val) =>
299                 {
300                     // val = bit number of ErrorCode register to be set
301                     switch(val)
302                     {
303                         case 0:
304                             reseedCommandsError.Value = true;
305                             break;
306                         case 1:
307                             generateCommandsError.Value = true;
308                             break;
309                         case 2:
310                             outputFifoError.Value = true;
311                             break;
312                         case 20:
313                             ednAckStageIllegalStateError.Value = true;
314                             break;
315                         case 21:
316                             ednMainStageIllegalStateError.Value = true;
317                             break;
318                         case 22:
319                             hardenedCounterError.Value = true;
320                             break;
321                         case 28:
322                             fifoWriteError.Value = true;
323                             break;
324                         case 29:
325                             fifoReadError.Value = true;
326                             break;
327                         case 30:
328                             fifoStateError.Value = true;
329                             break;
330                         default:
331                             break;
332                     }
333 
334                     switch (val)
335                     {
336                         case 0:
337                         case 1:
338                         case 2:
339                         case 20:
340                         case 21:
341                         case 22:
342                             fatalErrorInterruptState.Value = true;
343                             UpdateInterrupts();
344                             break;
345                         default:
346                             break;
347                     }
348                 }, name: "ERR_CODE_TEST")
349                 .WithReservedBits(5, 27);
350 
351             Registers.MainStateMachineStateDebug.Define(this, (uint)StateMachine.Idle)
352                 .WithValueField(0, 9, FieldMode.Read, name: "MAIN_SM_STATE")
353                 .WithReservedBits(9, 23);
354         }
355 
UpdateInterrupts()356         private void UpdateInterrupts()
357         {
358             CommandRequestDone.Set(commandRequestDoneInterruptState.Value && commandRequestDoneInterruptEnable.Value);
359             FatalError.Set(fatalErrorInterruptState.Value && fatalErrorInterruptEnable.Value);
360         }
361 
SendCsrngCommand(uint command)362         private void SendCsrngCommand(uint command)
363         {
364             csrng.EdnSoftwareCommandRequestWrite(command);
365             commandRequestDoneInterruptState.Value = true;
366             UpdateInterrupts();
367         }
368 
369         public GPIO CommandRequestDone { get; }
370         public GPIO FatalError { get; }
371         public GPIO RecoverableAlert { get; }
372         public GPIO FatalAlert { get; }
373 
374         private IFlagRegisterField commandRequestDoneInterruptState;
375         private IFlagRegisterField fatalErrorInterruptState;
376         private IFlagRegisterField commandRequestDoneInterruptEnable;
377         private IFlagRegisterField fatalErrorInterruptEnable;
378         private IFlagRegisterField allControlsRegisterWriteEnable;
379         private IEnumRegisterField<MultiBitBool4> ednEnable;
380         private IEnumRegisterField<MultiBitBool4> bootRequestMode;
381         private IEnumRegisterField<MultiBitBool4> autoRequestMode;
382         private IEnumRegisterField<MultiBitBool4> commandFifoReset;
383         private IValueRegisterField requestsBetweenReseeds;
384         private IFlagRegisterField ednEnableFieldAlert;
385         private IFlagRegisterField bootRequestModeFieldAlert;
386         private IFlagRegisterField autoRequestModeFieldAlert;
387         private IFlagRegisterField commandFifoResetFieldAlert;
388         private IValueRegisterField bootInstantiateCommand;
389         private IValueRegisterField bootGenerateCommand;
390         private IFlagRegisterField reseedCommandsError;
391         private IFlagRegisterField generateCommandsError;
392         private IFlagRegisterField outputFifoError;
393         private IFlagRegisterField ednAckStageIllegalStateError;
394         private IFlagRegisterField ednMainStageIllegalStateError;
395         private IFlagRegisterField hardenedCounterError;
396         private IFlagRegisterField fifoWriteError;
397         private IFlagRegisterField fifoReadError;
398         private IFlagRegisterField fifoStateError;
399 
400         private readonly Queue<uint> reseedCommands;
401         private readonly Queue<uint> generateCommands;
402         private readonly OpenTitan_CSRNG csrng;
403 
404         private bool autoRequestModeOn;
405         private uint maxNumberOfRequestsBetweenReseeds;
406 
407         private const int ReseedCommandFifoCapacity = 13;
408         private const int GenerateCommandFifoCapacity = 13;
409 
410         // https://github.com/lowRISC/opentitan/blob/master/hw/ip/edn/rtl/edn_pkg.sv
411         private enum StateMachine
412         {
413             Idle              = 0b110000101, // idle
414             BootLoadIns       = 0b110110111, // boot: load the instantiate command
415             BootLoadGen       = 0b000000011, // boot: load the generate command
416             BootInsAckWait    = 0b011010010, // boot: wait for instantiate command ack
417             BootCaptGenCnt    = 0b010111010, // boot: capture the gen fifo count
418             BootSendGenCmd    = 0b011100100, // boot: send the generate command
419             BootGenAckWait    = 0b101101100, // boot: wait for generate command ack
420             BootPulse         = 0b100001010, // boot: signal a done pulse
421             BootDone          = 0b011011111, // boot: stay in done state until reset
422             AutoLoadIns       = 0b001110000, // auto: load the instantiate command
423             AutoFirstAckWait  = 0b001001101, // auto: wait for first instantiate command ack
424             AutoAckWait       = 0b101100011, // auto: wait for instantiate command ack
425             AutoDispatch      = 0b110101110, // auto: determine next command to be sent
426             AutoCaptGenCnt    = 0b000110101, // auto: capture the gen fifo count
427             AutoSendGenCmd    = 0b111111000, // auto: send the generate command
428             AutoCaptReseedCnt = 0b000100110, // auto: capture the reseed fifo count
429             AutoSendReseedCmd = 0b101010110, // auto: send the reseed command
430             SWPortMode        = 0b100111001, // swport: no hw request mode
431             Error             = 0b010010001  // illegal state reached and hang
432         }
433 
434         public enum Registers
435         {
436             InterruptState = 0x0,
437             InterruptEnable = 0x4,
438             InterruptTest = 0x8,
439             AlertTest = 0xc,
440             RegisterWriteEnableForAllControls = 0x10,
441             Control = 0x14,
442             BootInstantiateCommand = 0x18,
443             BootGenerateCommand = 0x1c,
444             CsrngSoftwareCommandRequest = 0x20,
445             CommandStatus = 0x24,
446             CsrngReseedCommand = 0x28,
447             CsrngGenerateCommand = 0x2c,
448             MaximumNumberOfRequestsBetweenReseeds = 0x30,
449             RecoverableAlertStatus = 0x34,
450             ErrorCode = 0x38,
451             TestErrorConditions = 0x3c,
452             MainStateMachineStateDebug = 0x40,
453         }
454     }
455 }
456