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.Collections.Generic; 8 using Antmicro.Renode.Core; 9 using Antmicro.Renode.Core.Structure; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.Bus; 13 14 namespace Antmicro.Renode.Peripherals.SPI 15 { 16 public sealed class Quark_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize 17 { Quark_SPI(IMachine machine)18 public Quark_SPI(IMachine machine) : base(machine) 19 { 20 IRQ = new GPIO(); 21 CreateRegisters(); 22 Reset(); 23 } 24 Reset()25 public override void Reset() 26 { 27 registers.Reset(); 28 RefreshInterrupt(); 29 } 30 ReadDoubleWord(long offset)31 public uint ReadDoubleWord(long offset) 32 { 33 return registers.Read(offset); 34 } 35 WriteDoubleWord(long offset, uint value)36 public void WriteDoubleWord(long offset, uint value) 37 { 38 registers.Write(offset, value); 39 } 40 41 public long Size 42 { 43 get { return 0xFF; } 44 } 45 46 public GPIO IRQ { get; set; } 47 CreateRegisters()48 private void CreateRegisters() 49 { 50 var registersMap = new Dictionary<long, DoubleWordRegister> 51 { 52 /* 53 * Although some registers are locked if ssiEnabled is set, we do not have an abstraction to define it here. 54 */ 55 {(long)Registers.Control0, new DoubleWordRegister(this, 0x70000) 56 .WithTag("Frame Format FRF", 4, 2) //rw 57 .WithTag("Serial Clock Phase SCPH", 6, 1) //rwl 58 .WithTag("Serial Clock Polarity SCPOL", 7, 1) //rwl 59 .WithEnumField(8, 2, out transferMode, FieldMode.Read | FieldMode.Write, name:"Transfer Mode TMOD") //rwl 60 .WithTag("Slave Output Enable SLV_OE", 10, 1) //rwl 61 .WithTag("Shift Register Loop SRL", 11, 1) //rwl 62 .WithTag("Control Frame Size CFS", 12, 4) //rwl 63 .WithTag("Data Frame Size in 32-bit mode DFS_32", 16, 7) //rwl 64 65 }, 66 {(long)Registers.Control1, new DoubleWordRegister(this, 0) 67 .WithValueField(0, 16, name: "Number of Data Frames NDF") 68 }, 69 {(long)Registers.SSIEnable, new DoubleWordRegister(this, 0) 70 .WithFlag(0, out ssiEnabled, FieldMode.Read | FieldMode.Write, writeCallback: DisableSSI, name: "SSI Enable SSIENR") 71 }, 72 {(long)Registers.SlaveEnable, new DoubleWordRegister(this, 0) 73 .WithValueField(0, 4, changeCallback: (oldValue, newValue) => { 74 if(newValue != 1 && newValue != 0) 75 { 76 this.Log(LogLevel.Warning, "Unhandled write to slave enable."); 77 } 78 }) 79 }, 80 {(long)Registers.BaudRateSelect, new DoubleWordRegister(this, 0) 81 .WithValueField(0, 15, name: "SSI Clock Divider SCKDV") 82 }, 83 {(long)Registers.TransmitFIFOThresholdLevel, new DoubleWordRegister(this, 0) 84 .WithValueField(0, 3, name: "Transmit FIFO Threshold TXFTLR") // it does not matter since our transmission ends immediately 85 }, 86 {(long)Registers.ReceiveFIFOThresholdLevel, new DoubleWordRegister(this, 0) 87 .WithValueField(0, 3, out receiveFifoInterruptThreshold, name: "Receive FIFO Threshold RFT") 88 .WithValueField(3, 29, name: "Reserved") // reserved but written 89 }, 90 {(long)Registers.TransmitFIFOLevel, new DoubleWordRegister(this, 0) 91 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: (_) => 0, name: "Transmit FIFO Level TXTFL") 92 }, 93 {(long)Registers.ReceiveFIFOLevel, new DoubleWordRegister(this, 0) 94 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: (_) => (uint)receiveFifo.Count, name: "Receive FIFO Level RXFLR") 95 }, 96 {(long)Registers.Status, new DoubleWordRegister(this, 6) 97 .WithTag("SSI Busy BUSY", 0, 1)//ro, should probably always read 0 98 .WithFlag(1, FieldMode.Read, name: "Transmit FIFO Not Full TFNF") 99 .WithFlag(2, FieldMode.Read, name: "Transmit FIFO Empty TFE") // transmit fifo is always empty 100 .WithFlag(3, FieldMode.Read, valueProviderCallback: x => receiveFifo.Count > 0, name: "Receive FIFO Not Empty RFNE") 101 .WithTag("Receive FIFO Full RFF", 4, 1) //ro 102 .WithTag("Transmission Error TXE", 5, 1) //ro 103 }, 104 {(long)Registers.InterruptMask, new DoubleWordRegister(this, 3) 105 .WithFlag(0, out transmitFifoEmptyInterruptEnabled, name: "Transmit FIFO Empty Interrupt Mask TXEIM") 106 .WithFlag(1, name: "Transmit FIFO Overflow Interrupt Mask TXOIM") 107 .WithFlag(2, name: "Receive FIFO Underflow Interrupt Mask RXUIM") 108 .WithFlag(3, name: "Receive FIFO Overflow Interrupt Mask RXUIM") 109 .WithFlag(4, out receiveFifoFullInterruptEnabled, name: "Receive FIFO Full Interrupt Mask RXFIM") 110 .WithWriteCallback((_, __) => RefreshInterrupt()) 111 }, 112 {(long)Registers.InterruptStatus, new DoubleWordRegister(this, 0) 113 .WithFlag(0, FieldMode.Read, valueProviderCallback: x => transmitFifoEmptyInterruptEnabled.Value, name: "Transmit FIFO Empty Interrupt Status TXEIS") 114 .WithFlag(1, FieldMode.Read, name: "Transmit FIFO Overflow Interrupt Status TXOIS") 115 .WithFlag(2, FieldMode.Read, name: "Receive FIFO Underflow Interrupt Status RXUIS") 116 .WithFlag(3, FieldMode.Read, name: "Receive FIFO Overflow Interrupt Status RXUIS") 117 .WithFlag(4, FieldMode.Read, valueProviderCallback: x => receiveFifo.Count > (int)receiveFifoInterruptThreshold.Value && receiveFifoFullInterruptEnabled.Value, name: "Receive FIFO Full Interrupt Status RXFIS") 118 }, 119 {(long)Registers.RawInterruptStatus, new DoubleWordRegister(this, 0) 120 .WithFlag(0, FieldMode.Read, valueProviderCallback: x => true, name: "Transmit FIFO Empty Raw Interrupt Status TXEIR") 121 .WithTag("Transmit FIFO Overflow Raw Interrupt Status TXOIR", 1, 1) //ro 122 .WithTag("Receive FIFO Underflow Raw Interrupt Status RXUIR", 2, 1) //ro 123 .WithTag("Receive FIFO Overflow Raw Interrupt Status RXUIR", 3, 1) //ro 124 .WithFlag(4, out receiveFifoFullRawInterrupt, FieldMode.Read, valueProviderCallback: x => receiveFifo.Count > (int)receiveFifoInterruptThreshold.Value, name: "Receive FIFO Full Raw Interrupt Status RXFIR") 125 } 126 }; 127 var dataRegister = new DoubleWordRegister(this, 0).WithValueField(0, 16, writeCallback: (_, val) => WriteData((uint)val), valueProviderCallback: _ => ReadData()); 128 for(var i = 0; i < 36; i++) 129 { 130 //the fifo elements are not addressable, so reading/writing to any of these has the same effect -> it can be a single register. 131 registersMap.Add((long)Registers.Data + i, dataRegister); 132 } 133 134 registers = new DoubleWordRegisterCollection(this, registersMap); 135 } 136 ReadData()137 private uint ReadData() 138 { 139 return receiveFifo.Count > 0 ? receiveFifo.Dequeue() : (byte)0x00; 140 } 141 WriteData(uint data)142 private void WriteData(uint data) 143 { 144 if(!ssiEnabled.Value) 145 { 146 return; 147 } 148 if(transferMode.Value == TransferMode.ReceiveOnly || transferMode.Value == TransferMode.EEPROMRead) 149 { 150 // note that number of data frames (NDF field in second control register) is important in this transfer mode 151 // see datasheet for details 152 this.Log(LogLevel.Error, "Unhandled transfer mode {0}.", transferMode); 153 } 154 RefreshInterrupt(true); // although we immediately transfer the byte, the interrupt line has to be turned off for the moment 155 var result = RegisteredPeripheral.Transmit((byte)data); 156 if(transferMode.Value == TransferMode.TransmitAndReceive) 157 { 158 receiveFifo.Enqueue(result); 159 } 160 161 RefreshInterrupt(); 162 } 163 DisableSSI(bool oldValue, bool newValue)164 private void DisableSSI(bool oldValue, bool newValue) 165 { 166 this.Log(LogLevel.Debug, "SSI {0}.", newValue ? "enabled" : "disabled"); 167 receiveFifo.Clear(); 168 RefreshInterrupt(); 169 if(!newValue && oldValue) 170 { 171 RegisteredPeripheral.FinishTransmission(); 172 } 173 } 174 RefreshInterrupt(bool transmitFifoEmptySuppressed = false)175 private void RefreshInterrupt(bool transmitFifoEmptySuppressed = false) 176 { 177 var value = ssiEnabled.Value && ((!transmitFifoEmptySuppressed && transmitFifoEmptyInterruptEnabled.Value) || 178 (receiveFifoFullRawInterrupt.Value && receiveFifoFullInterruptEnabled.Value)); 179 IRQ.Set(value); 180 } 181 182 private DoubleWordRegisterCollection registers; 183 184 private IFlagRegisterField ssiEnabled; 185 private IEnumRegisterField<TransferMode> transferMode; 186 187 private IFlagRegisterField transmitFifoEmptyInterruptEnabled; 188 private IFlagRegisterField receiveFifoFullInterruptEnabled; 189 private IFlagRegisterField receiveFifoFullRawInterrupt; 190 private IValueRegisterField receiveFifoInterruptThreshold; 191 192 private readonly Queue<byte> receiveFifo = new Queue<byte>(); 193 194 private enum TransferMode 195 { 196 TransmitAndReceive = 0x0, 197 TransmitOnly = 0x1, 198 ReceiveOnly = 0x2, 199 EEPROMRead = 0x3 200 } 201 202 private enum Registers 203 { 204 Control0 = 0x0, 205 Control1 = 0x4, 206 SSIEnable = 0x8, 207 MicrowireControl = 0xC, 208 SlaveEnable = 0x10, 209 BaudRateSelect = 0x14, 210 TransmitFIFOThresholdLevel = 0x18, 211 ReceiveFIFOThresholdLevel = 0x1C, 212 TransmitFIFOLevel = 0x20, 213 ReceiveFIFOLevel = 0x24, 214 Status = 0x28, 215 InterruptMask = 0x2C, 216 InterruptStatus = 0x30, 217 RawInterruptStatus = 0x34, 218 TransmitFIFOOverflowInterruptClear = 0x38, 219 ReceiveFIFOOverflowInterruptClear = 0x3C, 220 ReceiveFIFOUnderflowInterruptClear = 0x40, 221 MultiMasterInterruptClear = 0x44, //only reserved fields? 222 InterruptClear = 0x48, 223 DMAControl = 0x4C, 224 DMATransmitDataLevel = 0x50, 225 DMAReceiveDataLevel = 0x54, 226 Identification = 0x58, 227 CoreKitVersionID = 0x5C, 228 Data = 0x60, //DR0 up to DR35 at 0xEC 229 RXSampleDelay = 0xF0 230 } 231 } 232 } 233