1 //
2 // Copyright (c) 2010-2019 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.Collections.Generic;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.SPI
17 {
18     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)]
19     public class PicoRV_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize
20     {
PicoRV_SPI(IMachine machine)21         public PicoRV_SPI(IMachine machine) : base(machine)
22         {
23             bbHelper = new BitBangHelper(8, loggingParent: this, outputMsbFirst: true);
24 
25             var registers = new Dictionary<long, DoubleWordRegister>
26             {
27                 {(long)Registers.Config1, new DoubleWordRegister(this)
28                     .WithFlag(0, out var data0, FieldMode.Write, name: "data0")
29                     .WithFlag(1, FieldMode.Write, name: "data1") // data 1-3 used in QSPI only
30                     .WithFlag(2, FieldMode.Write, name: "data2")
31                     .WithFlag(3, FieldMode.Write, name: "data3")
32                     .WithFlag(4, out var clock, FieldMode.Write, name: "clk")
33                     .WithFlag(5, out var chipSelectNegated, FieldMode.Write, name: "cs_n")
34                     .WithReservedBits(6, 2)
35 
36                     .WithWriteCallback((_, val) =>
37                     {
38                         if(memioEnable.Value)
39                         {
40                             this.Log(LogLevel.Warning, "MEMIO mode not enabled - bit banging not supported in this mode");
41                             return;
42                         }
43 
44                         if(qspiEnable.Value)
45                         {
46                             this.Log(LogLevel.Warning, "QSPI mode not yet supported");
47                             return;
48                         }
49 
50                         if(RegisteredPeripheral == null)
51                         {
52                             this.Log(LogLevel.Warning, "Trying to send bytes over SPI, but there is no device attached");
53                             return;
54                         }
55 
56                         if(chipSelectNegated.Value && !previousChipSelectNegated)
57                         {
58                             this.Log(LogLevel.Noisy, "ChipSelect signal down - finishing the transmission");
59                             RegisteredPeripheral.FinishTransmission();
60                             bbHelper.ResetOutput();
61                         }
62                         previousChipSelectNegated = chipSelectNegated.Value;
63 
64                         // do not latch bits when MEMIO is enabled or chipSelect is not set
65                         if(bbHelper.Update(clock.Value, data0.Value, !memioEnable.Value && !chipSelectNegated.Value))
66                         {
67                             this.Log(LogLevel.Noisy, "Sending byte 0x{0:X}", bbHelper.DecodedOutput);
68                             var input = RegisteredPeripheral.Transmit((byte)bbHelper.DecodedOutput);
69                             this.Log(LogLevel.Noisy, "Received byte 0x{0:X}", input);
70 
71                             bbHelper.SetInputBuffer(input);
72                         }
73                     })
74                 },
75 
76                 {(long)Registers.Config2, new DoubleWordRegister(this)
77                     // those are not handled, but implemented to avoid warnings in logs
78                     .WithFlag(0, name: "data0_output_en")
79                     .WithFlag(1, name: "data1_output_en")
80                     .WithFlag(2, name: "data2_output_en")
81                     .WithFlag(3, name: "data3_output_en")
82                     .WithFlag(4, name: "clk_output_en")
83                     .WithFlag(5, name: "cs_output_en")
84                     .WithReservedBits(6, 2)
85                 },
86 
87                 {(long)Registers.Config3, new DoubleWordRegister(this)
88                     .WithTag("read latency (dummy) cycles", 0, 4)
89                     .WithTag("CRM enable", 4, 1)
90                     .WithFlag(5, out qspiEnable, FieldMode.Write, name: "qspi_en")
91                     .WithTag("DDR enable", 6, 1)
92                     .WithReservedBits(7, 1)
93                 },
94 
95                 {(long)Registers.Config4, new DoubleWordRegister(this)
96                     .WithReservedBits(0, 7)
97                     .WithFlag(7, out memioEnable, name: "memio_en")
98                 },
99 
100                 {(long)Registers.Stat1, new DoubleWordRegister(this)
101                     .WithReservedBits(0, 1)
102                     .WithFlag(1, FieldMode.Read, name: "MISO", valueProviderCallback: _ => bbHelper.EncodedInput)
103                     .WithReservedBits(2, 6)
104                 }
105             };
106 
107             registersCollection = new DoubleWordRegisterCollection(this, registers);
108             Reset();
109         }
110 
Reset()111         public override void Reset()
112         {
113             bbHelper.Reset();
114             registersCollection.Reset();
115             previousChipSelectNegated = true;
116         }
117 
ReadDoubleWord(long offset)118         public uint ReadDoubleWord(long offset)
119         {
120             return registersCollection.Read(offset);
121         }
122 
123         [ConnectionRegionAttribute("xip")]
XipReadDoubleWord(long offset)124         public uint XipReadDoubleWord(long offset)
125         {
126             if(!memioEnable.Value)
127             {
128                 this.Log(LogLevel.Warning, "Trying to read from XIP region at offset 0x{0:X}, but memio is disabled", offset);
129                 return 0;
130             }
131 
132             return (RegisteredPeripheral as IDoubleWordPeripheral)?.ReadDoubleWord(offset) ?? 0;
133         }
134 
WriteDoubleWord(long offset, uint value)135         public void WriteDoubleWord(long offset, uint value)
136         {
137             registersCollection.Write(offset, value);
138         }
139 
140         [ConnectionRegionAttribute("xip")]
XipWriteDoubleWord(long offset, uint value)141         public void XipWriteDoubleWord(long offset, uint value)
142         {
143             this.Log(LogLevel.Warning, "Trying to write 0x{0:X} to XIP region at offset 0x{1:x}. Direct writing is not supported", value, offset);
144         }
145 
146         public long Size => 0x20;
147 
148         private bool previousChipSelectNegated;
149 
150         private readonly DoubleWordRegisterCollection registersCollection;
151         private readonly BitBangHelper bbHelper;
152         private readonly IFlagRegisterField memioEnable;
153         private readonly IFlagRegisterField qspiEnable;
154 
155         private enum Registers
156         {
157             Config1 = 0x0,
158             Config2 = 0x4,
159             Config3 = 0x8,
160             Config4 = 0xC,
161             Stat1 = 0x10,
162             Stat2 = 0x14,
163             Stat3 = 0x18,
164             Stat4 = 0x1c,
165         }
166     }
167 }
168