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 Antmicro.Renode.Core;
10 using Antmicro.Renode.Utilities;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Memory;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Core.Structure.Registers;
15 
16 namespace Antmicro.Renode.Peripherals.SPI
17 {
18     // This peripheral only implements the generic operation mode
19     [AllowedTranslations(AllowedTranslation.DoubleWordToByte)]
20     public class OpenTitan_SpiDevice : BasicDoubleWordPeripheral, IBytePeripheral, ISPIPeripheral, IKnownSize
21     {
OpenTitan_SpiDevice(IMachine machine)22         public OpenTitan_SpiDevice(IMachine machine) : base(machine)
23         {
24             underlyingSramMemory = new ArrayMemory(BufferWindowSizeInDoublewords * 4);
25 
26             DefineRegisters();
27             rxFifo = new SRAMCircularFifoRange(0u, InitialRxFifoBoundary, underlyingSramMemory);
28             txFifo = new SRAMCircularFifoRange(InitialRxFifoBoundary + 1, InitialTxFifoBoundary, underlyingSramMemory);
29 
30             GenericRxFull = new GPIO();
31             GenericRxWatermark = new GPIO();
32             GenericTxWatermark = new GPIO();
33             GenericRxError = new GPIO();
34             GenericRxOverflow = new GPIO();
35             GenericTxUnderflow = new GPIO();
36             UploadCmdFifoNotEmpty = new GPIO();
37             UploadPayloadNotEmpty = new GPIO();
38             UploadPayloadOverflow = new GPIO();
39             ReadBufferWatermark = new GPIO();
40             ReadBufferFlip = new GPIO();
41             TPMHeaderNotEmpty = new GPIO();
42 
43             FatalAlert = new GPIO();
44             Reset();
45         }
46 
47         // Byte accesses are needed for the fifo SRAM interface
WriteByte(long offset, byte value)48         public void WriteByte(long offset, byte value)
49         {
50             if(offset >= (long)Registers.Buffer)
51             {
52                 var bufferOffset = offset - (long)Registers.Buffer;
53                 if(IsOffsetInRxFifoRange(bufferOffset))
54                 {
55                     this.Log(LogLevel.Debug, "Accesses the Rx fifo contents directly using the direct memory write");
56                 }
57                 // We may use direct sram memory write as the driver is responsible to handle the pointers in that case
58                 underlyingSramMemory.WriteByte(bufferOffset, value);
59             }
60             else
61             {
62                 this.Log(LogLevel.Error, "Byte interface should only be used on the buffer. Ignoring write of value 0x{0:X}, to offset 0x{1:X}", value, offset);
63             }
64         }
65 
ReadByte(long offset)66         public byte ReadByte(long offset)
67         {
68             if(offset >= (long)Registers.Buffer)
69             {
70                 var bufferOffset = offset - (long)Registers.Buffer;
71                 return underlyingSramMemory.ReadByte(bufferOffset);
72             }
73             else
74             {
75                 this.Log(LogLevel.Error, "Byte interface should only be used on the buffer. Ignoring read from offset 0x{1:X}", offset);
76                 return 0;
77             }
78         }
79 
WriteDoubleWord(long offset, uint value)80         public override void WriteDoubleWord(long offset, uint value)
81         {
82             if(offset >= (long)Registers.Buffer)
83             {
84                 // Access to the sram buffer
85                 var bufferOffset = offset - (long)Registers.Buffer;
86                 if(IsOffsetInTxFifoRange(bufferOffset))
87                 {
88                     var dataInBytes = BitHelper.GetBytesFromValue(value, sizeof(uint));
89                     if(!txOrderLsbFirst.Value)
90                     {
91                         Array.Reverse(dataInBytes);
92                     }
93                     foreach(var b in dataInBytes)
94                     {
95                         var txFifoStatus = txFifo.WriteByte(b);
96                     }
97                 }
98                 else
99                 {
100                     underlyingSramMemory.WriteDoubleWord(offset - (long)Registers.Buffer, value);
101                 }
102             }
103             else
104             {
105                 base.WriteDoubleWord(offset, value);
106             }
107         }
108 
ReadDoubleWord(long offset)109         public override uint ReadDoubleWord(long offset)
110         {
111             if(offset >= (long)Registers.Buffer)
112             {
113                 // Acces to the sram buffer
114                 this.Log(LogLevel.Debug, "Reading from sram memory; value at addr {0}", offset - 0x1000);
115                 var readValue = underlyingSramMemory.ReadDoubleWord(offset - (long)Registers.Buffer);
116                 if(IsOffsetInRxFifoRange(offset))
117                 {
118                     readValue = Misc.ByteArrayRead(0, BitHelper.GetBytesFromValue(readValue, sizeof(uint)));
119                 }
120                 return readValue;
121             }
122             else
123             {
124                 return base.ReadDoubleWord(offset);
125             }
126         }
127 
Reset()128         public override void Reset()
129         {
130             base.Reset();
131             FatalAlert.Unset();
132         }
133 
Transmit(byte data)134         public byte Transmit(byte data)
135         {
136             byte output = 0;
137             SRAMCircularFifoRange.FifoStatus rxFifoStatus, txFifoStatus = SRAMCircularFifoRange.FifoStatus.Empty;
138 
139             if(txFifo.CurrentDepth > 0)
140             {
141                 txFifoStatus = txFifo.ReadByte(out output);
142 
143                 if(txFifo.CurrentDepth < txFifoWatermarkLevel.Value)
144                 {
145                     txWatermarkInterruptState.Value = true;
146                 }
147             }
148             else
149             {
150                 txFifoStatus = SRAMCircularFifoRange.FifoStatus.Underflow;
151             }
152 
153             rxFifoStatus = rxFifo.WriteByte(data);
154 
155             if(rxFifo.CurrentDepth > rxFifoWatermarkLevel.Value)
156             {
157                 rxWatermarkInterruptState.Value = true;
158             }
159 
160             HandleFifoStatusesAndUpdateInterrupts(rxFifoStatus, txFifoStatus);
161 
162             return output;
163         }
164 
IsOffsetInRxFifoRange(long bufferOffset)165         private bool IsOffsetInRxFifoRange(long bufferOffset)
166         {
167             return (bufferOffset >= rxFifo.Base) && (bufferOffset <= rxFifo.Limit);
168         }
169 
IsOffsetInTxFifoRange(long offset)170         private bool IsOffsetInTxFifoRange(long offset)
171         {
172             return (offset >= txFifo.Base) && (offset <= txFifo.Limit);
173         }
174 
175         /* Translates the fifo states into interrupts */
HandleFifoStatusesAndUpdateInterrupts(SRAMCircularFifoRange.FifoStatus rxFifoStatus, SRAMCircularFifoRange.FifoStatus txFifoStatus)176         private void HandleFifoStatusesAndUpdateInterrupts(SRAMCircularFifoRange.FifoStatus rxFifoStatus, SRAMCircularFifoRange.FifoStatus txFifoStatus)
177         {
178             switch(rxFifoStatus)
179             {
180                 case SRAMCircularFifoRange.FifoStatus.Overflow:
181                     rxOverflowInterruptState.Value = true;
182                     break;
183                 case SRAMCircularFifoRange.FifoStatus.Full:
184                     rxFullInterruptState.Value = true;
185                     break;
186                 default:
187                     break;
188             }
189             switch(txFifoStatus)
190             {
191                 case SRAMCircularFifoRange.FifoStatus.Underflow:
192                     txUnderflowInterruptState.Value = true;
193                     break;
194                 default:
195                     break;
196             }
197             UpdateInterrupts();
198         }
199 
FinishTransmission()200         public void FinishTransmission()
201         {
202             // Intentionaly left blank
203         }
204 
205         // Common Interrupt Offsets
206         public GPIO GenericRxFull { get; }
207         public GPIO GenericRxWatermark { get; }
208         public GPIO GenericTxWatermark { get; }
209         public GPIO GenericRxError { get; }
210         public GPIO GenericRxOverflow { get; }
211         public GPIO GenericTxUnderflow { get; }
212         public GPIO UploadCmdFifoNotEmpty { get; }
213         public GPIO UploadPayloadNotEmpty { get; }
214         public GPIO UploadPayloadOverflow { get; }
215         public GPIO ReadBufferWatermark { get; }
216         public GPIO ReadBufferFlip { get; }
217         public GPIO TPMHeaderNotEmpty { get; }
218         public GPIO FatalAlert { get; private set; }
219 
220         public long Size => 0x2000;
221 
DefineRegisters()222         private void DefineRegisters()
223         {
224             Registers.InterruptState.Define(this)
225                 .WithFlag(0, out rxFullInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "generic_rx_full")
226                 .WithFlag(1, out rxWatermarkInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "generic_rx_watermark")
227                 .WithFlag(2, out txWatermarkInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "generic_tx_watermark")
228                 .WithFlag(3, out rxErrorInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "generic_rx_error")
229                 .WithFlag(4, out rxOverflowInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "generic_rx_overflow")
230                 .WithFlag(5, out txUnderflowInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "generic_tx_underflow")
231                 .WithFlag(6, out cmdfifoNotEmptyInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "upload_cmdfifo_not_empty")
232                 .WithFlag(7, out payloadNotEmptyInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "upload_payload_not_empty")
233                 .WithFlag(8, out payloadOverflowInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "upload_payload_overflow")
234                 .WithFlag(9, out readbufWatermarkInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "readbuf_watermark")
235                 .WithFlag(10, out readbufFlipInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "readbuf_flip")
236                 .WithFlag(11, out tpmHeaderNotEmptyInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "tpm_header_not_empty")
237                 .WithReservedBits(12, 20)
238                 .WithWriteCallback((_, __) => UpdateInterrupts());
239 
240             Registers.InterruptEnable.Define(this)
241                 .WithFlag(0, out rxFullInterruptEnable, name: "generic_rx_full")
242                 .WithFlag(1, out rxWatermarkInterruptEnable, name: "generic_rx_watermark")
243                 .WithFlag(2, out txWatermarkInterruptEnable, name: "generic_tx_watermark")
244                 .WithFlag(3, out rxErrorInterruptEnable, name: "generic_rx_error")
245                 .WithFlag(4, out rxOverflowInterruptEnable, name: "generic_rx_overflow")
246                 .WithFlag(5, out txUnderflowInterruptEnable, name: "generic_tx_underflow")
247                 .WithFlag(6, out cmdfifoNotEmptyInterruptEnable, name: "upload_cmdfifo_not_empty")
248                 .WithFlag(7, out payloadNotEmptyInterruptEnable, name: "upload_payload_not_empty")
249                 .WithFlag(8, out payloadOverflowInterruptEnable, name: "upload_payload_overflow")
250                 .WithFlag(9, out readbufWatermarkInterruptEnable, name: "readbuf_watermark")
251                 .WithFlag(10, out readbufFlipInterruptEnable, name: "readbuf_flip")
252                 .WithFlag(11, out tpmHeaderNotEmptyInterruptEnable, name: "tpm_header_not_empty")
253                 .WithReservedBits(12, 20)
254                 .WithWriteCallback((_, __) => UpdateInterrupts());
255 
256             Registers.InterruptTest.Define(this)
257                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { rxFullInterruptState.Value = val; }, name: "generic_rx_full")
258                 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { rxWatermarkInterruptState.Value = val; }, name: "generic_rx_watermark")
259                 .WithFlag(2, FieldMode.Write, writeCallback: (_, val) => { txWatermarkInterruptState.Value = val; }, name: "generic_tx_watermark")
260                 .WithFlag(3, FieldMode.Write, writeCallback: (_, val) => { rxErrorInterruptState.Value = val; }, name: "generic_rx_error")
261                 .WithFlag(4, FieldMode.Write, writeCallback: (_, val) => { rxOverflowInterruptState.Value = val; }, name: "generic_rx_overflow")
262                 .WithFlag(5, FieldMode.Write, writeCallback: (_, val) => { txUnderflowInterruptState.Value = val; }, name: "generic_tx_underflow")
263                 .WithFlag(6, FieldMode.Write, writeCallback: (_, val) => { cmdfifoNotEmptyInterruptState.Value = val; }, name: "upload_cmdfifo_not_empty")
264                 .WithFlag(7, FieldMode.Write, writeCallback: (_, val) => { payloadNotEmptyInterruptState.Value = val; }, name: "upload_payload_not_empty")
265                 .WithFlag(8, FieldMode.Write, writeCallback: (_, val) => { payloadOverflowInterruptState.Value = val; }, name: "upload_payload_overflow")
266                 .WithFlag(9, FieldMode.Write, writeCallback: (_, val) => { readbufWatermarkInterruptState.Value = val; }, name: "readbuf_watermark")
267                 .WithFlag(10, FieldMode.Write, writeCallback: (_, val) => { readbufFlipInterruptState.Value = val; }, name: "readbuf_flip")
268                 .WithFlag(11, FieldMode.Write, writeCallback: (_, val) => { tpmHeaderNotEmptyInterruptState.Value = val; }, name: "tpm_header_not_empty")
269                 .WithReservedBits(12, 20)
270                 .WithWriteCallback((_, val) => { if(val != 0) UpdateInterrupts(); });
271 
272             Registers.AlertTest.Define(this)
273                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalAlert.Blink(); }, name: "fatal_fault")
274                 .WithReservedBits(1, 31);
275 
276             Registers.Control.Define(this, 0x80000010)
277                 .WithFlag(0, name: "ABORT")
278                 .WithReservedBits(1, 3)
279                 .WithEnumField<DoubleWordRegister, DeviceMode>(4, 2, out mode,
280                     writeCallback: (_, val) =>
281                     {
282                         if(val != DeviceMode.Fw)
283                         {
284                             this.Log(LogLevel.Error, "{0} operation mode is not implemented. Setting mode back to FwMode", val);
285                             mode.Value = DeviceMode.Fw;
286                         }
287                     }, name: "MODE")
288                 .WithReservedBits(6, 9)
289                 .WithFlag(16, writeCallback: (_, val) => { if(val) txFifo.ResetPointers(); }, name: "rst_txfifo")
290                 .WithFlag(17, writeCallback: (_, val) => { if(val) rxFifo.ResetPointers(); }, name: "rst_rxfifo")
291                 .WithReservedBits(18, 13)
292                 .WithTaggedFlag("sram_clk_en", 31);
293 
294             Registers.Configuration.Define(this, 0x7f00)
295                 .WithTaggedFlag("CPOL", 0)
296                 .WithTaggedFlag("CPHA", 1)
297                 .WithFlag(2, out txOrderLsbFirst, name:"tx_order")
298                 .WithFlag(3, out rxOrderLsbFirst, name:"rx_order")
299                 .WithReservedBits(4, 4)
300                 .WithTag("timer_v", 8, 8)
301                 .WithTaggedFlag("addr_4b_en", 16)
302                 .WithTaggedFlag("mailbox_en", 24)
303                 .WithReservedBits(25, 7);
304 
305             Registers.FifoLevel.Define(this, 0x80)
306                 // Despite the misleading name this register holds the watermark levels - at least thats what the spec suggests
307                 .WithValueField(0, 16, out rxFifoWatermarkLevel, name: "rxlvl")    // "If the RX SRAM FIFO level exceeds this value, it triggers interrupt."
308                 .WithValueField(16, 16, out txFifoWatermarkLevel, name: "txlvl");  // "If the TX SRAM FIFO level drops below this value, it triggers interrupt."
309 
310             Registers.AsyncFifoLevel.Define(this)
311                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => rxFifo.CurrentDepth, name: "rxlvl")
312                 .WithValueField(16, 8, FieldMode.Read, valueProviderCallback: _ => txFifo.CurrentDepth, name: "txlvl")
313                 .WithReservedBits(24, 8);
314 
315             Registers.SPIDevicestatus.Define(this, 0x3a)
316                 .WithFlag(0, FieldMode.Read, valueProviderCallback : _ => rxFifo.IsFull, name: "rxf_full")
317                 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => rxFifo.IsEmpty, name: "rxf_empty")
318                 .WithFlag(2, FieldMode.Read, valueProviderCallback : _ => txFifo.IsFull, name: "txf_full")
319                 .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => txFifo.IsEmpty, name: "txf_empty")
320                 .WithFlag(4, FieldMode.Read, name: "abort_done")
321                 .WithFlag(5, FieldMode.Read, name: "csb")
322                 .WithReservedBits(6, 26);
323 
324             Registers.ReceiverFifoSramPointers.Define(this)
325                 .WithValueField(0, 16, valueProviderCallback: _ => rxFifo.ReadPointerWithPhaseBit, changeCallback: (_, val) =>
326                     {
327                         this.Log(LogLevel.Debug, "Setting the read pointer to {0:x}", val);
328                         if(rxFifo.TrySetReadPointer(val))
329                         {
330                             this.Log(LogLevel.Error, "Pointer outside of the range. This will be ignored");
331                         }
332                     }, name: "RPTR")
333                 .WithValueField(16, 16, FieldMode.Read, valueProviderCallback: _ => rxFifo.WritePointerWithPhaseBit, name: "WPTR");
334 
335             Registers.TransmitterFifoSramPointers.Define(this)
336                 .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: _ =>  txFifo.ReadPointerWithPhaseBit, name: "RPTR")
337                 .WithValueField(16, 16, valueProviderCallback: _ => txFifo.WritePointerWithPhaseBit, changeCallback: (_, val) =>
338                     {
339                         this.Log(LogLevel.Debug, "Setting the write pointer to {0:x}", val);
340                         if(txFifo.TrySetWritePointer(val))
341                         {
342                             this.Log(LogLevel.Error, "Pointer outside of the range. This will be ignored");
343                         }
344                     }, name: "WPTR");
345 
346             Registers.ReceiverFifoSramAddresses.Define(this, 0x1fc0000)
347                 .WithValueField(0, 16, out rxFifoBase, name: "base")
348                 .WithValueField(16, 16, out rxFifoLimit, name: "limit")
349                 .WithWriteCallback((_, __) =>
350                     {
351                         if(!rxFifo.TryUpdateParameters((uint)rxFifoBase.Value, (uint)rxFifoLimit.Value))
352                         {
353                             this.Log(LogLevel.Error, "Parameters were rejected as an invalid range");
354                         }
355                     });
356 
357             Registers.TransmitterFifoSramAddresses.Define(this, 0x3fc0200)
358                 .WithValueField(0, 16, out txFifoBase, name: "base")
359                 .WithValueField(16, 16, out txFifoLimit, name: "limit")
360                 .WithWriteCallback((_, __) =>
361                     {
362                         if(!txFifo.TryUpdateParameters((uint)txFifoBase.Value, (uint)txFifoLimit.Value))
363                         {
364                             this.Log(LogLevel.Error, "Parameters were rejected as an invalid range");
365                         }
366                     });
367 
368             Registers.InterceptEnable.Define(this)
369                 .WithTaggedFlag("status", 0)
370                 .WithTaggedFlag("jedec", 1)
371                 .WithTaggedFlag("sfdp", 2)
372                 .WithTaggedFlag("mbx", 3)
373                 .WithReservedBits(4, 28);
374 
375             Registers.LastReadAddress.Define(this)
376                 .WithTag("addr", 0, 32);
377 
378             Registers.FlashStatus.Define(this)
379                 .WithTaggedFlag("busy", 0)
380                 .WithTag("status", 1, 23)
381                 .WithReservedBits(24, 8);
382 
383             Registers.JedecCc.Define(this, 0x7f)
384                 .WithTag("cc", 0, 8)
385                 .WithTag("num_cc", 8, 8)
386                 .WithReservedBits(16, 16);
387 
388             Registers.JedecId.Define(this)
389                 .WithTag("id", 0, 16)
390                 .WithTag("mf", 16, 8)
391                 .WithReservedBits(24, 8);
392 
393             Registers.ReadThreshold.Define(this)
394                 .WithTag("threshold", 0, 10)
395                 .WithReservedBits(10, 22);
396 
397             Registers.MailboxAddress.Define(this)
398                 .WithTag("addr", 0, 32);
399 
400             Registers.UploadStatus.Define(this)
401                 .WithTag("cmdfifo_depth", 0, 5)
402                 .WithTaggedFlag("cmdfifo_notempty", 7)
403                 .WithTag("addrfifo_depth", 8, 5)
404                 .WithTaggedFlag("addrfifo_notempty", 5)
405                 .WithReservedBits(16, 16);
406 
407             Registers.UploadStatus2.Define(this)
408                 .WithTag("payload_depth", 0, 9)
409                 .WithTag("payload_start_idx", 16, 8)
410                 .WithReservedBits(24, 8);
411 
412             Registers.UploadCmdFifo.Define(this)
413                 .WithTag("data", 0, 8)
414                 .WithReservedBits(8, 24);
415 
416             Registers.UploadAddrFifo.Define(this)
417                 .WithTag("data", 0, 32);
418 
419             Registers.CommandFilter.DefineMany(this, SpiDevicesCount, (register, idx) =>
420                 {
421                     register
422                     .WithTaggedFlag($"filter_{0 + 32 * idx}", 0)
423                     .WithTaggedFlag($"filter_{1 + 32 * idx}", 1)
424                     .WithTaggedFlag($"filter_{2 + 32 * idx}", 2)
425                     .WithTaggedFlag($"filter_{3 + 32 * idx}", 3)
426                     .WithTaggedFlag($"filter_{4 + 32 * idx}", 4)
427                     .WithTaggedFlag($"filter_{5 + 32 * idx}", 5)
428                     .WithTaggedFlag($"filter_{6 + 32 * idx}", 6)
429                     .WithTaggedFlag($"filter_{7 + 32 * idx}", 7)
430                     .WithTaggedFlag($"filter_{8 + 32 * idx}", 8)
431                     .WithTaggedFlag($"filter_{9 + 32 * idx}", 9)
432                     .WithTaggedFlag($"filter_{10 + 32 * idx}", 10)
433                     .WithTaggedFlag($"filter_{11 + 32 * idx}", 11)
434                     .WithTaggedFlag($"filter_{12 + 32 * idx}", 12)
435                     .WithTaggedFlag($"filter_{13 + 32 * idx}", 13)
436                     .WithTaggedFlag($"filter_{14 + 32 * idx}", 14)
437                     .WithTaggedFlag($"filter_{15 + 32 * idx}", 15)
438                     .WithTaggedFlag($"filter_{16 + 32 * idx}", 16)
439                     .WithTaggedFlag($"filter_{17 + 32 * idx}", 17)
440                     .WithTaggedFlag($"filter_{18 + 32 * idx}", 18)
441                     .WithTaggedFlag($"filter_{19 + 32 * idx}", 19)
442                     .WithTaggedFlag($"filter_{20 + 32 * idx}", 20)
443                     .WithTaggedFlag($"filter_{21 + 32 * idx}", 21)
444                     .WithTaggedFlag($"filter_{22 + 32 * idx}", 22)
445                     .WithTaggedFlag($"filter_{23 + 32 * idx}", 23)
446                     .WithTaggedFlag($"filter_{24 + 32 * idx}", 24)
447                     .WithTaggedFlag($"filter_{25 + 32 * idx}", 25)
448                     .WithTaggedFlag($"filter_{26 + 32 * idx}", 26)
449                     .WithTaggedFlag($"filter_{27 + 32 * idx}", 27)
450                     .WithTaggedFlag($"filter_{28 + 32 * idx}", 28)
451                     .WithTaggedFlag($"filter_{29 + 32 * idx}", 29)
452                     .WithTaggedFlag($"filter_{30 + 32 * idx}", 30)
453                     .WithTaggedFlag($"filter_{31 + 32 * idx}", 31);
454                 });
455 
456             Registers.AddressSwapMask.Define(this)
457                 .WithTag("mask", 0, 32);
458 
459             Registers.AddressSwapData.Define(this)
460                 .WithTag("data", 0, 32);
461 
462             Registers.PayloadSwapMask.Define(this)
463                 .WithTag("mask", 0, 32);
464 
465             Registers.PayloadSwapData.Define(this)
466                 .WithTag("data", 0, 32);
467 
468             Registers.CommandInfo.DefineMany(this, DeviceCmdInfoCount, (register, idx) =>
469                 {
470                     register
471                     .WithTag("opcode_{idx}", 0, 8)
472                     .WithTag("addr_mode_{idx}", 8, 2)
473                     .WithTaggedFlag($"addr_swap_en_{idx}", 10)
474                     .WithTaggedFlag($"mbyte_en_{idx}", 11)
475                     .WithTag("dummy_size_{idx}", 12, 3)
476                     .WithTaggedFlag($"dummy_en_{idx}", 15)
477                     .WithTag("payload_en_{idx}", 16, 4)
478                     .WithTaggedFlag($"payload_dir_{idx}", 20)
479                     .WithTaggedFlag($"payload_swap_en_{idx}", 21)
480                     .WithReservedBits(22, 2)
481                     .WithTaggedFlag($"upload_{idx}", 24)
482                     .WithTaggedFlag($"busy_{idx}", 25)
483                     .WithReservedBits(26, 5)
484                     .WithTaggedFlag($"valid_{idx}", 31);
485                 });
486 
487             Registers.CommandInfoEn4b.Define(this)
488                 .WithTag("opcode", 0, 8)
489                 .WithTaggedFlag("valid", 31);
490 
491             Registers.OpcodeForEX4B.Define(this)
492                 .WithTag("opcode", 0, 8)
493                 .WithTaggedFlag("valid", 31);
494 
495             Registers.OpcodeforWriteEnable.Define(this)
496                 .WithTag("opcode", 0, 8)
497                 .WithTaggedFlag("valid", 31);
498 
499             Registers.OpcodeForWriteDisable.Define(this)
500                 .WithTag("opcode", 0, 8)
501                 .WithTaggedFlag("valid", 31);
502 
503             // TPM capabilities not implemented
504             Registers.TPMCapability.Define(this, 0x20100)
505                 .WithTag("rev", 0, 8)
506                 .WithTaggedFlag("locality", 8)
507                 .WithTag("max_xfer_size", 16, 3)
508                 .WithReservedBits(19, 13);
509 
510             Registers.TPMConfig.Define(this)
511                 .WithTaggedFlag("en", 0)
512                 .WithTaggedFlag("tpm_mode", 1)
513                 .WithTaggedFlag("hw_reg_dis", 2)
514                 .WithTaggedFlag("tpm_reg_chk_dis", 3)
515                 .WithTaggedFlag("invalid_locality", 4)
516                 .WithReservedBits(5, 27);
517 
518             Registers.TPMStatus.Define(this)
519                 .WithTaggedFlag("cmdaddr_notempty", 0)
520                 .WithTaggedFlag("rdfifo_notempty", 1)
521                 .WithTag("rdfifo_depth", 4, 3)
522                 .WithTag("wrfifo_depth", 8, 3)
523                 .WithReservedBits(11, 21);
524 
525             Registers.TPMAccess0.Define(this)
526                 .WithTag("access_0", 0, 8)
527                 .WithTag("access_1", 8, 8)
528                 .WithTag("access_2", 16, 8)
529                 .WithTag("access_3", 24, 8);
530 
531             Registers.TPMAccess1.Define(this)
532                 .WithTag("access_4", 0, 8)
533                 .WithReservedBits(8, 24);
534 
535             Registers.TPMSts.Define(this)
536                 .WithTag("sts", 0, 32);
537 
538             Registers.TPMIntfCapability.Define(this)
539                 .WithTag("intf_capability", 0, 32);
540 
541             Registers.TPMIntCapability.Define(this)
542                 .WithTag("int_enable", 0, 32);
543 
544             Registers.TPMIntVector.Define(this)
545                 .WithTag("int_vector", 0, 8)
546                 .WithReservedBits(8, 24);
547 
548             Registers.TPMIntStatus.Define(this)
549                 .WithTag("int_status", 0, 32);
550 
551             Registers.TPMDidVid.Define(this)
552                 .WithTag("vid", 0, 16)
553                 .WithTag("did", 16, 16);
554 
555             Registers.TPMRid.Define(this)
556                 .WithTag("rid", 0, 8)
557                 .WithReservedBits(8, 24);
558 
559             Registers.TPMCommandAndAddressBuffer.Define(this)
560                 .WithTag("addr", 0, 24)
561                 .WithTag("cmd", 24, 8);
562 
563             Registers.TPMReadFifo.Define(this)
564                 .WithTag("value", 0, 8)
565                 .WithReservedBits(8, 24);
566 
567             Registers.TPMWriteFifo.Define(this)
568                 .WithTag("value", 0, 8)
569                 .WithReservedBits(8, 24);
570 
571             Registers.Buffer.DefineMany(this, BufferWindowSizeInDoublewords, (register, idx) =>
572                 {
573                     // This range is handled by the read functions
574                     register.WithTag($"SPIinternalbuffer{idx}", 0, 3);
575                 });
576         }
577 
UpdateInterrupts()578         private void UpdateInterrupts()
579         {
580             GenericRxFull.Set(rxFullInterruptState.Value && rxFullInterruptEnable.Value);
581             GenericRxWatermark.Set(rxWatermarkInterruptState.Value && rxWatermarkInterruptEnable.Value);
582             GenericTxWatermark.Set(txWatermarkInterruptState.Value && txWatermarkInterruptEnable.Value);
583             GenericRxOverflow.Set(rxOverflowInterruptState.Value && rxOverflowInterruptEnable.Value);
584             GenericTxUnderflow.Set(txUnderflowInterruptState.Value && txUnderflowInterruptEnable.Value);
585             // Below interrupts are not implemented and are here just for the interrupt test sake
586             GenericRxError.Set(rxErrorInterruptState.Value && rxErrorInterruptEnable.Value);
587             UploadCmdFifoNotEmpty.Set(cmdfifoNotEmptyInterruptState.Value && cmdfifoNotEmptyInterruptEnable.Value);
588             UploadPayloadNotEmpty.Set(payloadNotEmptyInterruptState.Value && payloadNotEmptyInterruptEnable.Value);
589             UploadPayloadOverflow.Set(payloadOverflowInterruptState.Value && payloadOverflowInterruptEnable.Value);
590             ReadBufferWatermark.Set(readbufWatermarkInterruptState.Value && readbufWatermarkInterruptEnable.Value);
591             ReadBufferFlip.Set(readbufFlipInterruptState.Value && readbufFlipInterruptEnable.Value);
592             TPMHeaderNotEmpty.Set(tpmHeaderNotEmptyInterruptState.Value && tpmHeaderNotEmptyInterruptEnable.Value);
593         }
594 
595         // Sram Entries. Word size is 32bit width.
596         private const uint BufferWindowSizeInDoublewords = 1024;
597         private const uint InitialRxFifoBoundary = 2047;
598         private const uint InitialTxFifoBoundary = 4095;
599 
600         // Define the number of Command Info slots.
601         private const uint DeviceCmdInfoCount = 24;
602 
603         // Define the number of SPI_DEVICE
604         private const uint SpiDevicesCount = 8;
605 
606         // The number of locality TPM module supports.
607         private const uint SpiDeviceNumLocality = 5;
608 
609         private IFlagRegisterField rxFullInterruptState;
610         private IFlagRegisterField rxWatermarkInterruptState;
611         private IFlagRegisterField txWatermarkInterruptState;
612         private IFlagRegisterField rxErrorInterruptState;
613         private IFlagRegisterField rxOverflowInterruptState;
614         private IFlagRegisterField txUnderflowInterruptState;
615         private IFlagRegisterField cmdfifoNotEmptyInterruptState;
616         private IFlagRegisterField payloadNotEmptyInterruptState;
617         private IFlagRegisterField payloadOverflowInterruptState;
618         private IFlagRegisterField readbufWatermarkInterruptState;
619         private IFlagRegisterField readbufFlipInterruptState;
620         private IFlagRegisterField tpmHeaderNotEmptyInterruptState;
621         private IFlagRegisterField rxFullInterruptEnable;
622         private IFlagRegisterField rxWatermarkInterruptEnable;
623         private IFlagRegisterField txWatermarkInterruptEnable;
624         private IFlagRegisterField rxErrorInterruptEnable;
625         private IFlagRegisterField rxOverflowInterruptEnable;
626         private IFlagRegisterField txUnderflowInterruptEnable;
627         private IFlagRegisterField cmdfifoNotEmptyInterruptEnable;
628         private IFlagRegisterField payloadNotEmptyInterruptEnable;
629         private IFlagRegisterField payloadOverflowInterruptEnable;
630         private IFlagRegisterField readbufWatermarkInterruptEnable;
631         private IFlagRegisterField readbufFlipInterruptEnable;
632         private IFlagRegisterField tpmHeaderNotEmptyInterruptEnable;
633         private IFlagRegisterField txOrderLsbFirst;
634         private IFlagRegisterField rxOrderLsbFirst;
635         private IEnumRegisterField<DeviceMode> mode;
636 
637         private IValueRegisterField rxFifoBase;
638         private IValueRegisterField rxFifoLimit;
639         private IValueRegisterField txFifoBase;
640         private IValueRegisterField txFifoLimit;
641         private IValueRegisterField rxFifoWatermarkLevel;
642         private IValueRegisterField txFifoWatermarkLevel;
643 
644         private SRAMCircularFifoRange rxFifo;
645         private SRAMCircularFifoRange txFifo;
646         private ArrayMemory underlyingSramMemory;
647 
648         private enum DeviceMode
649         {
650             Fw = 0x0,
651             Flash = 0x1,
652             Passthrough = 0x2,
653         }
654 
655         public enum Registers
656         {
657             InterruptState = 0x0,
658             InterruptEnable = 0x4,
659             InterruptTest = 0x8,
660             AlertTest = 0xc,
661             Control = 0x10,
662             Configuration = 0x14,
663             FifoLevel = 0x18,
664             AsyncFifoLevel = 0x1c,
665             SPIDevicestatus = 0x20,
666             ReceiverFifoSramPointers = 0x24,
667             TransmitterFifoSramPointers = 0x28,
668             ReceiverFifoSramAddresses = 0x2c,
669             TransmitterFifoSramAddresses = 0x30,
670             InterceptEnable = 0x34,
671             LastReadAddress = 0x38,
672             FlashStatus = 0x3c,
673             JedecCc = 0x40,
674             JedecId = 0x44,
675             ReadThreshold = 0x48,
676             MailboxAddress = 0x4c,
677             UploadStatus = 0x50,
678             UploadStatus2 = 0x54,
679             UploadCmdFifo = 0x58,
680             UploadAddrFifo = 0x5c,
681             CommandFilter = 0x60,
682             AddressSwapMask = 0x80,
683             AddressSwapData = 0x84,
684             PayloadSwapMask = 0x88,
685             PayloadSwapData = 0x8c,
686             CommandInfo = 0x90,
687             CommandInfoEn4b = 0xf0,
688             OpcodeForEX4B = 0xf4,
689             OpcodeforWriteEnable = 0xf8,
690             OpcodeForWriteDisable = 0xfc,
691             TPMCapability = 0x800,
692             TPMConfig = 0x804,
693             TPMStatus = 0x808,
694             TPMAccess0 = 0x80c,
695             TPMAccess1 = 0x810,
696             TPMSts = 0x814,
697             TPMIntfCapability = 0x818,
698             TPMIntCapability = 0x81c,
699             TPMIntVector = 0x820,
700             TPMIntStatus = 0x824,
701             TPMDidVid = 0x828,
702             TPMRid = 0x82c,
703             TPMCommandAndAddressBuffer = 0x830,
704             TPMReadFifo = 0x834,
705             TPMWriteFifo = 0x838,
706             Buffer = 0x1000,
707         }
708 
709         private class SRAMCircularFifoRange
710         {
SRAMCircularFifoRange(uint baseOffset, uint limit, ArrayMemory underlyingMemory)711             public SRAMCircularFifoRange(uint baseOffset, uint limit, ArrayMemory underlyingMemory)
712             {
713                 this.underlyingMemory = underlyingMemory;
714                 if(!TryUpdateParameters(baseOffset, limit))
715                 {
716                     throw new ArgumentException("SRAMCircularFifo constructor parameters were rejected." +
717                                                 " The range does not fit into the underlying memory or base is bigger than limit");
718                 }
719             }
720 
TryUpdateParameters(uint baseOffset, uint limitOffset)721             public bool TryUpdateParameters(uint baseOffset, uint limitOffset)
722             {
723                 var baseOffsetInvalid = baseOffset >= (underlyingMemory.Size  * 8);
724                 var limitOffsetInvalid = limitOffset >= (underlyingMemory.Size * 8);
725                 var addressesUnordered = baseOffset > limitOffset;
726 
727                 if(baseOffsetInvalid || limitOffsetInvalid || addressesUnordered)
728                 {
729                     return false;
730                 }
731                 UpdateParameters((ushort)baseOffset, (ushort)limitOffset);
732                 return true;
733             }
734 
ResetPointers()735             public void ResetPointers()
736             {
737                 readPointer = 0;
738                 readPhase = false;
739                 writePointer = 0;
740                 writePhase = false;
741 
742                 status = FifoStatus.Empty;
743             }
744 
TrySetWritePointer(ulong newValue)745             public bool TrySetWritePointer(ulong newValue)
746             {
747                 ExtractPhaseAndPointer(newValue, out var phase, out var pointer);
748                 if(PointerInDefinedRange(pointer))
749                 {
750                     writePointer = checked((ushort)pointer);
751                     writePhase = phase;
752                     return true;
753                 }
754                 return false;
755             }
756 
WriteByte(byte data)757             public FifoStatus WriteByte(byte data)
758             {
759                 underlyingMemory.WriteByte(baseOffset + writePointer, data);
760 
761                 if(writePointer == limitOffset)
762                 {
763                     writePointer = baseOffset;
764                     writePhase = !writePhase;
765                 }
766                 else
767                 {
768                     writePointer++;
769                 }
770                 UpdateStatus();
771                 return status;
772             }
773 
ReadByte(out byte value)774             public FifoStatus ReadByte(out byte value)
775             {
776                 value = underlyingMemory.ReadByte(baseOffset + readPointer);
777                 readPointer += 1;
778 
779                 if(readPointer == limitOffset)
780                 {
781                     readPointer = baseOffset;
782                     readPhase = !readPhase;
783                 }
784                 UpdateStatus();
785                 return status;
786             }
787 
TrySetReadPointer(ulong newValue)788             public bool TrySetReadPointer(ulong newValue)
789             {
790                 ExtractPhaseAndPointer(newValue, out var phase, out var pointer);
791                 if(PointerInDefinedRange(pointer))
792                 {
793                     readPointer = checked((ushort)pointer);
794                     readPhase = phase;
795                     return true;
796                 }
797                 return false;
798             }
799 
800             // As the phase bit is separated from the address, we need to put it back in place
801             public uint ReadPointerWithPhaseBit => readPointer | (uint)((readPhase ? 1 : 0) << 11);
802             public uint WritePointerWithPhaseBit => writePointer | (uint)((writePhase ? 1 : 0) << 11);
803             public bool IsEmpty => (writePointer == readPointer) && (writePhase == readPhase);
804             public bool IsFull => (writePointer == readPointer) && (writePhase != readPhase);
805             public uint Base => baseOffset;
806             public uint Limit => limitOffset;
807 
808             public uint CurrentDepth
809             {
810                 get
811                 {
812                     // The depth is guaranteed to be positive as we don't ever read on empty buffer
813                     var depth = writePointer - readPointer;
814                     var phasesDiffer = writePhase != readPhase;
815 
816                     if(phasesDiffer)
817                     {
818                         depth += limitOffset + baseOffset;
819                     }
820                     return (uint)depth;
821                 }
822             }
823 
UpdateParameters(ushort baseOffset, ushort limitOffset)824             private void UpdateParameters(ushort baseOffset, ushort limitOffset)
825             {
826                 this.baseOffset = baseOffset;
827                 this.limitOffset = limitOffset;
828                 ResetPointers();
829             }
830 
ExtractPhaseAndPointer(ulong value, out bool phase, out ushort pointer)831             private void ExtractPhaseAndPointer(ulong value, out bool phase, out ushort pointer)
832             {
833                 phase = BitHelper.IsBitSet(value, PointerBitsCount + 1);
834                 pointer = checked((ushort)(BitHelper.GetValue(value, 0, PointerBitsCount)));
835             }
836 
PointerInDefinedRange(uint newValue)837             private bool PointerInDefinedRange(uint newValue)
838             {
839                 return (newValue < (limitOffset - baseOffset));
840             }
841 
UpdateStatus()842             private void UpdateStatus()
843             {
844                 var phasesDifferent = (readPhase != writePhase);
845                 var fifoSize = limitOffset - baseOffset + 1;
846                 var addresessDiff = (int)((writePointer - readPointer) + (phasesDifferent ? (fifoSize) : 0));
847                 var lastStatus = status;
848 
849                 status = FifoStatus.Normal;
850 
851                 if(addresessDiff == 0)
852                 {
853                     status = FifoStatus.Empty;
854                 }
855                 else if(addresessDiff == fifoSize)
856                 {
857                     status = FifoStatus.Full;
858                 }
859                 else if(addresessDiff > fifoSize)
860                 {
861                     status = FifoStatus.Overflow;
862                 }
863                 else if(addresessDiff < 0)
864                 {
865                     status = FifoStatus.Underflow;
866                 }
867             }
868 
869             private ushort baseOffset, limitOffset;
870             private ushort readPointer;
871             private ushort writePointer;
872 
873             // This two denotes the state of the buffer pointers. They are flipped on every overflow
874             private bool readPhase;
875             private bool writePhase;
876             private FifoStatus status;
877 
878             private readonly ArrayMemory underlyingMemory;
879 
880             private const int PointerBitsCount = 11;
881 
882             public enum FifoStatus
883             {
884                 Empty,
885                 Normal,
886                 Full,
887                 Overflow,
888                 Underflow,
889             }
890         }
891     } // End class OpenTitan_SpiDevice
892 }
893