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 Antmicro.Renode.Core; 9 using Antmicro.Renode.Logging; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Peripherals.SPI; 12 using Antmicro.Renode.Peripherals.GPIOPort; 13 using Antmicro.Renode.Peripherals.Bus; 14 15 namespace Antmicro.Renode.Peripherals.DMA 16 { 17 public class EOSS3_SPI_DMA : BaseGPIOPort, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize 18 { EOSS3_SPI_DMA(IMachine machine, DesignWare_SPI spi)19 public EOSS3_SPI_DMA(IMachine machine, DesignWare_SPI spi) : base(machine, 1) 20 { 21 sysbus = machine.GetSystemBus(this); 22 this.spi = spi; 23 innerLock = new object(); 24 RegistersCollection = new DoubleWordRegisterCollection(this); 25 26 DefineRegisters(); 27 } 28 ReadDoubleWord(long offset)29 public uint ReadDoubleWord(long offset) 30 { 31 return RegistersCollection.Read(offset); 32 } 33 WriteDoubleWord(long offset, uint value)34 public void WriteDoubleWord(long offset, uint value) 35 { 36 RegistersCollection.Write(offset, value); 37 } 38 Reset()39 public override void Reset() 40 { 41 enabled = false; 42 43 base.Reset(); 44 RegistersCollection.Reset(); 45 } 46 OnGPIO(int number, bool value)47 public override void OnGPIO(int number, bool value) 48 { 49 if(!value) 50 { 51 return; 52 } 53 54 lock(innerLock) 55 { 56 if(!Enabled) 57 { 58 // ignore interrupts when not enabled 59 this.Log(LogLevel.Warning, "DMA not enabled, ignoring an interrupt from SPI Master"); 60 return; 61 } 62 63 // this means that the SPIMaster has some data waiting in the buffer 64 while(spi.TryDequeueFromReceiveBuffer(out var data)) 65 { 66 // now we must check if there is one or two bytes 67 var bytesToHandle = 0u; 68 switch(spi.FrameSize) 69 { 70 case DesignWare_SPI.TransferSize.SingleByte: 71 { 72 bytesToHandle = 1; 73 break; 74 } 75 76 case DesignWare_SPI.TransferSize.DoubleByte: 77 { 78 bytesToHandle = 2; 79 break; 80 } 81 82 default: 83 throw new ArgumentException($"Unexpected transfer size {spi.FrameSize}"); 84 } 85 86 if(bytesToHandle > transferCount.Value) 87 { 88 this.Log(LogLevel.Warning, "DMA configuration mismatch detected - transfer count left is {0} bytes, but there are more ({1}) bytes available in the SPI Master's buffer. Some data will be lost!", transferCount.Value, bytesToHandle); 89 bytesToHandle = (uint)transferCount.Value; 90 } 91 92 // now we must check if there is one or two bytes again 93 switch(bytesToHandle) 94 { 95 case 1: 96 { 97 this.Log(LogLevel.Noisy, "DMA transfer: writing byte 0x{0:X} at offset 0x{1:X}", data, destinationAddress.Value); 98 sysbus.WriteByte(destinationAddress.Value, (byte)data); 99 break; 100 } 101 102 case 2: 103 { 104 this.Log(LogLevel.Noisy, "DMA transfer: writing ushort 0x{0:X} at offset 0x{1:X}", data, destinationAddress.Value); 105 sysbus.WriteWord(destinationAddress.Value, data); 106 break; 107 } 108 } 109 110 // transferCount is in bytes 111 transferCount.Value -= bytesToHandle; 112 destinationAddress.Value += bytesToHandle; 113 114 if(transferCount.Value == 0) 115 { 116 this.Log(LogLevel.Noisy, "That's the end of the DMA transfer"); 117 118 dmaDataAvailable.Value = true; 119 UpdateInterrupts(); 120 break; 121 } 122 } 123 } 124 } 125 126 public long Size => 0x100; 127 public GPIO IRQ { get; } = new GPIO(); 128 129 public DoubleWordRegisterCollection RegistersCollection { get; private set; } 130 131 public bool Enabled 132 { 133 get => enabled; 134 135 set 136 { 137 lock(innerLock) 138 { 139 if(value) 140 { 141 transferCount.Value += 1; 142 } 143 144 enabled = value; 145 } 146 } 147 } 148 UpdateInterrupts()149 private void UpdateInterrupts() 150 { 151 var state = false; 152 153 state |= (dmaDataAvailable.Value && dmaDataAvailableEnable.Value); 154 155 this.Log(LogLevel.Noisy, "Setting interrupt to: {0}", state); 156 IRQ.Set(state); 157 } 158 DefineRegisters()159 private void DefineRegisters() 160 { 161 Registers.Control.Define(this, 0x40) // DMA_HREADY is set on reset 162 .WithFlag(0, FieldMode.Write, name: "dma_start", writeCallback: (_, val) => { if(val) Enabled = true; }) 163 .WithFlag(1, FieldMode.Write, name: "dma_stop", writeCallback: (_, val) => { if(val) Enabled = false; }) 164 .WithTag("dma_ahb_sel", 2, 1) 165 .WithTag("dma_hsel", 3, 1) 166 .WithTag("dma_htrans_0", 4, 1) 167 .WithTag("dma_htrans_1", 5, 1) 168 .WithFlag(6, FieldMode.Read, name: "dma_hready", valueProviderCallback: _ => true) 169 .WithTag("dma_xfr_pending", 7, 1) 170 .WithTag("bridge_xfr_pending", 8, 1) 171 .WithReservedBits(9, 23) 172 ; 173 174 Registers.DestinationAddress.Define(this) 175 .WithValueField(0, 32, out destinationAddress, name: "dma_dest_addr") 176 ; 177 178 Registers.TransferCount.Define(this) 179 .WithValueField(0, 26, out transferCount, name: "dma_xfr_cnt") 180 .WithReservedBits(27, 5) 181 ; 182 183 Registers.ConfigFlashHeader.Define(this) 184 .WithTag("dma_boot_xfr_size", 0, 16) 185 .WithTag("dma_spi_clk_divide", 16, 8) 186 .WithTag("dma_device_id", 24, 8) 187 ; 188 189 Registers.Interrupts.Define(this) 190 .WithTag("dma_herror", 0, 1) // FieldMode.Read | FieldMode.WriteOneToClear 191 .WithFlag(1, out dmaDataAvailable, FieldMode.Read, name: "dmrx_data_available") 192 .WithTag("dmahb_bridge_fifo_overflow", 2, 1) // FieldMode.Read | FieldMode.WriteOneToClear 193 .WithTag("dmspim_ssi_txe_intr", 3, 1) 194 .WithTag("dmspim_ssi_txo_intr", 4, 1) 195 .WithTag("dmspim_ssi_rxf_intr", 5, 1) 196 .WithTag("dmspim_ssi_rxo_intr", 6, 1) 197 .WithTag("dmspim_ssi_rxu_intr", 7, 1) 198 .WithTag("dmspim_ssi_mst_intr", 8, 1) 199 .WithReservedBits(9, 23) 200 .WithWriteCallback((_, __) => UpdateInterrupts()) 201 ; 202 203 Registers.InterruptMask.Define(this, 0x7) 204 .WithTag("dma_herror_mask", 0, 1) 205 .WithFlag(1, out dmaDataAvailableEnable, name: "dmrx_data_available_mask") 206 .WithTag("dmahb_bridge_fifo_overflow_mask", 2, 1) 207 .WithReservedBits(3, 29) 208 .WithWriteCallback((_, __) => UpdateInterrupts()) 209 ; 210 211 Registers.ConfigStateMachineDelay.Define(this) 212 .WithTag("delay_reg", 0, 16) 213 .WithReservedBits(16, 16) 214 ; 215 } 216 217 private IValueRegisterField destinationAddress; 218 private IValueRegisterField transferCount; 219 private IFlagRegisterField dmaDataAvailable; 220 private IFlagRegisterField dmaDataAvailableEnable; 221 private bool enabled; 222 223 private readonly IBusController sysbus; 224 private readonly DesignWare_SPI spi; 225 private readonly object innerLock; 226 227 private enum Registers 228 { 229 Control = 0x00, 230 DestinationAddress = 0x04, 231 TransferCount = 0x08, 232 ConfigFlashHeader = 0x0C, 233 Interrupts = 0x10, 234 InterruptMask = 0x14, 235 ConfigStateMachineDelay = 0x18 236 } 237 } 238 } 239