1 //
2 // Copyright (c) 2010-2020 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 System.IO;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 
16 namespace Antmicro.Renode.Peripherals.Sound
17 {
18     // this model lack support for concatenated channels
19     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)]
20     public abstract class LiteX_I2S : BasicDoubleWordPeripheral, IKnownSize
21     {
LiteX_I2S(IMachine machine, DataFormat format, uint sampleWidth, uint samplingRate, uint fifoIrqThreshold = 256)22         protected LiteX_I2S(IMachine machine, DataFormat format, uint sampleWidth, uint samplingRate, uint fifoIrqThreshold = 256) : base(machine)
23         {
24             if(format != DataFormat.Standard)
25             {
26                 throw new ConstructionException("Only Standard format is supported at the moment");
27             }
28 
29             if(fifoIrqThreshold > fifoDepth)
30             {
31                 throw new ConstructionException($"Wrong fifo IRQ threshold value: {fifoIrqThreshold}. Should be in range 0 - {fifoDepth}");
32             }
33 
34             this.fifoIrqThreshold = fifoIrqThreshold;
35             this.dataFormat = format;
36             this.samplingRate = samplingRate;
37             this.sampleWidth = sampleWidth;
38 
39             buffer = new Queue<uint>();
40             IRQ = new GPIO();
41             DefineRegisters();
42             UpdateInterrupts();
43         }
44 
Reset()45         public override void Reset()
46         {
47             base.Reset();
48             buffer.Clear();
49 
50             UpdateInterrupts();
51         }
52 
53         [ConnectionRegion("buffer")]
WriteToBuffer(long offset, uint value)54         public void WriteToBuffer(long offset, uint value)
55         {
56             // HW allows to access FIFO from any offset
57             TryEnqueueSample(value);
58         }
59 
60         [ConnectionRegion("buffer")]
ReadFromBuffer(long offset)61         public uint ReadFromBuffer(long offset)
62         {
63             // HW allows to access FIFO from any offset
64             if(!TryDequeueSample(out var res))
65             {
66                 this.Log(LogLevel.Warning, "Tried to read from an empty FIFO");
67             }
68 
69             return res;
70         }
71 
72         public long Size => 0x100;
73 
74         public GPIO IRQ { get; }
75 
TryEnqueueSample(uint sample)76         protected bool TryEnqueueSample(uint sample)
77         {
78             this.Log(LogLevel.Noisy, "Trying to enqueue a sample: 0x{0:X}", sample);
79 
80             lock(buffer)
81             {
82                 if(buffer.Count >= fifoDepth)
83                 {
84                     this.Log(LogLevel.Warning, "FIFO overflow, a sample will be lost");
85 
86                     errorEventPending.Value = true;
87                     UpdateInterrupts();
88 
89                     return false;
90                 }
91 
92                 EnqueueSampleInner(sample);
93                 UpdateInterrupts();
94 
95                 return true;
96             }
97         }
98 
TryDequeueSample(out uint sample)99         protected bool TryDequeueSample(out uint sample)
100         {
101             this.Log(LogLevel.Noisy, "Trying to dequeue a sample");
102             lock(buffer)
103             {
104                 if(buffer.Count == 0)
105                 {
106                     this.Log(LogLevel.Noisy, "Tried to read from an empty FIFO, returned 0 by default");
107                     sample = 0;
108 
109                     errorEventPending.Value = true;
110                     UpdateInterrupts();
111 
112                     return false;
113                 }
114 
115                 sample = buffer.Dequeue();
116                 this.Log(LogLevel.Noisy, "Dequeued 0x{0:X}, buffer is now {1} bytes long", sample, buffer.Count);
117 
118                 UpdateInterrupts();
119 
120                 return true;
121             }
122         }
123 
IsReady()124         protected virtual bool IsReady()
125         {
126             return false;
127         }
128 
EnqueueSampleInner(uint sample)129         protected virtual void EnqueueSampleInner(uint sample)
130         {
131             buffer.Enqueue(sample);
132             this.Log(LogLevel.Noisy, "Sample enqueued, buffer is now {0} bytes long", buffer.Count);
133         }
134 
UpdateInterrupts()135         protected void UpdateInterrupts()
136         {
137             lock(buffer)
138             {
139                 var state = false;
140 
141                 readyEventPending.Value = IsReady();
142 
143                 state |= readyEventEnabled.Value && readyEventPending.Value;
144                 state |= errorEventEnabled.Value && errorEventPending.Value;
145 
146                 state &= enabled.Value;
147 
148                 this.Log(LogLevel.Noisy, "Setting IRQ to {0}", state);
149                 IRQ.Set(state);
150             }
151         }
152 
HandleEnable(bool enabled)153         protected virtual void HandleEnable(bool enabled)
154         {
155             // by default do nothing
156         }
157 
158         protected readonly uint fifoIrqThreshold;
159         protected readonly Queue<uint> buffer;
160 
DefineRegisters()161         private void DefineRegisters()
162         {
163             Registers.EventPending.Define32(this)
164                 .WithFlag(0, out readyEventPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "ready")
165                 .WithFlag(1, out errorEventPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "error")
166                 .WithReservedBits(2, 30)
167                 .WithWriteCallback((_, __) => UpdateInterrupts())
168             ;
169 
170             Registers.EventEnable.Define(this)
171                 .WithFlag(0, out readyEventEnabled, name: "ready")
172                 .WithFlag(1, out errorEventEnabled, name: "error")
173                 .WithReservedBits(2, 30)
174                 .WithWriteCallback((_, __) => UpdateInterrupts())
175             ;
176 
177             Registers.Control.Define(this)
178                 .WithFlag(0, out enabled, name: "enable", writeCallback: (_, v) => HandleEnable(v))
179                 .WithFlag(1, FieldMode.Write, name: "fifo_reset", writeCallback: (_, v) =>
180                 {
181                     if(!v)
182                     {
183                         return;
184                     }
185 
186                     this.Log(LogLevel.Noisy, "Flushing FIFO");
187                     buffer.Clear();
188                     UpdateInterrupts();
189                 })
190                 .WithReservedBits(2, 30)
191             ;
192 
193             Registers.Config0.Define(this)
194                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (byte)(samplingRate >> 16), name: "sampling_rate")
195             ;
196 
197             Registers.Config1.Define(this)
198                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (byte)(samplingRate >> 8), name: "sampling_rate")
199             ;
200 
201             Registers.Config2.Define(this)
202                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (byte)(samplingRate >> 0), name: "sampling_rate")
203             ;
204 
205             Registers.Config3.Define(this)
206                 .WithEnumField<DoubleWordRegister, DataFormat>(0, 2, FieldMode.Read, valueProviderCallback: _ => dataFormat, name: "format")
207                 .WithValueField(2, 6, FieldMode.Read, valueProviderCallback: _ => sampleWidth, name: "sample_width")
208             ;
209         }
210 
211         private IFlagRegisterField enabled;
212         private IFlagRegisterField readyEventEnabled;
213         private IFlagRegisterField errorEventEnabled;
214         private IFlagRegisterField readyEventPending;
215         private IFlagRegisterField errorEventPending;
216 
217         private readonly DataFormat dataFormat;
218         private readonly uint sampleWidth;
219         private readonly uint samplingRate;
220 
221         private const int fifoDepth = 512;
222 
223         public enum DataFormat
224         {
225             Standard = 1,
226             LeftJustified = 2
227         }
228 
229         protected enum Registers
230         {
231             EventStatus  = 0x000,
232             EventPending = 0x004,
233             EventEnable  = 0x008,
234             Control      = 0x00C,
235             Status       = 0x010,
236 
237             // this is a single 32-bit LiteX CSR
238             // scattered accross 4 regular 32-bit registers
239             Config0      = 0x020,
240             Config1      = 0x020 + 0x4,
241             Config2      = 0x020 + 0x8,
242             Config3      = 0x020 + 0xC
243         }
244     }
245 }
246