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