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 System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.CAN;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.CAN
17 {
18     public class LPC_CAN : BasicDoubleWordPeripheral, ICAN
19     {
LPC_CAN(IMachine machine)20         public LPC_CAN(IMachine machine) : base(machine)
21         {
22             transmitBuffers = new TransmitBuffer[NumberOfTransmitBuffers];
23             for(int i = 0; i < NumberOfTransmitBuffers; ++i)
24             {
25                 transmitBuffers[i] = new TransmitBuffer
26                 {
27                     parent = this,
28                     data = new IValueRegisterField[2], // 2 double words
29                 };
30             }
31             receiveFifo = new Queue<CANMessageFrame>();
32             TxIRQ = new GPIO();
33             RxIRQ = new GPIO();
34 
35             DefineRegisters();
36             Reset();
37         }
38 
39         public GPIO TxIRQ { get; }
40         public GPIO RxIRQ { get; }
41 
Reset()42         public override void Reset()
43         {
44             receiveFifo.Clear();
45             TxIRQ.Unset();
46             RxIRQ.Unset();
47             base.Reset();
48         }
49 
OnFrameReceived(CANMessageFrame message)50         public void OnFrameReceived(CANMessageFrame message)
51         {
52             this.DebugLog("Received {0} bytes [{1}] on id 0x{2:X}", message.Data.Length, message.DataAsHex, message.Id);
53             receiveFifo.Enqueue(message);
54             UpdateInterrupts();
55         }
56 
57         public event Action<CANMessageFrame> FrameSent;
58 
TransmitMessage(CANMessageFrame message)59         private void TransmitMessage(CANMessageFrame message)
60         {
61             var fs = FrameSent;
62             if(fs == null)
63             {
64                 this.WarningLog("Tried to transmit {0} bytes [{1}] to id 0x{2:X} while not connected to the medium",
65                     message.Data.Length, message.DataAsHex, message.Id);
66                 return;
67             }
68 
69             this.DebugLog("Transmitting {0} bytes [{1}] to id 0x{2:X}", message.Data.Length, message.DataAsHex, message.Id);
70             fs(message);
71         }
72 
UpdateInterrupts()73         private void UpdateInterrupts()
74         {
75             receiveInterruptFlag.Value = receiveFifo.Any();
76 
77             var txIrqValue = transmitBuffers.Any(b => b.transmitInterruptEnable.Value && b.transmitInterruptFlag.Value);
78             var rxIrqValue = receiveInterruptEnable.Value && receiveInterruptFlag.Value;
79             if(TxIRQ.IsSet == txIrqValue && RxIRQ.IsSet == rxIrqValue)
80             {
81                 return;
82             }
83 
84             this.NoisyLog("Setting interrupts: TX={0}, RX={1}", txIrqValue, rxIrqValue);
85             TxIRQ.Set(txIrqValue);
86             RxIRQ.Set(rxIrqValue);
87         }
88 
DefineRegisters()89         private void DefineRegisters()
90         {
91             Registers.OperatingMode.Define(this)
92                 .WithTaggedFlag("RM", 0)
93                 .WithTaggedFlag("LOM", 1)
94                 .WithTaggedFlag("STM", 2)
95                 .WithTaggedFlag("TPM", 3)
96                 .WithTaggedFlag("SM", 4)
97                 .WithTaggedFlag("RPM", 5)
98                 .WithReservedBits(6, 1)
99                 .WithTaggedFlag("TM", 7)
100                 .WithReservedBits(8, 24);
101 
102             Registers.Command.Define(this)
103                 .WithFlag(0, mode: FieldMode.WriteOneToClear, name: "TR", writeCallback: (_, value) =>
104                 {
105                     if(value)
106                     {
107                         var buffersToTransmit = transmitBuffers.Where(b => b.select.Value).OrderBy(b => b.priority.Value).ToList();
108                         if(!buffersToTransmit.Any())
109                         {
110                             // If no buffers are selected with the STB bits, we always transmit buffer 1.
111                             transmitBuffers[0].DoTransmission();
112                             // In this case we also force its transmit interrupt enable flag to on.
113                             transmitBuffers[0].transmitInterruptEnable.Value = true;
114                         }
115                         else
116                         {
117                             foreach(var buffer in buffersToTransmit)
118                             {
119                                 buffer.DoTransmission();
120                             }
121                         }
122                     }
123                 })
124                 .WithTaggedFlag("AT", 1)
125                 .WithFlag(2, mode: FieldMode.WriteOneToClear, name: "RRB", writeCallback: (_, value) =>
126                 {
127                     if(value)
128                     {
129                         receiveFifo.TryDequeue(out var __); // Release Receive Buffer, discard the current message
130                     }
131                 })
132                 .WithTaggedFlag("CDO", 3)
133                 .WithTaggedFlag("SRR", 4)
134                 .WithFlag(5, out transmitBuffers[0].select, name: "STB1")
135                 .WithFlag(6, out transmitBuffers[1].select, name: "STB2")
136                 .WithFlag(7, out transmitBuffers[2].select, name: "STB3")
137                 .WithReservedBits(8, 24)
138                 .WithWriteCallback((_, __) => UpdateInterrupts());
139 
140             Registers.GlobalStatus.Define(this, 0x3C)
141                 .WithFlag(0, mode: FieldMode.Read, name: "RBS",
142                     valueProviderCallback: _ => receiveFifo.Any())
143                 .WithTaggedFlag("DOS", 1)
144                 .WithTaggedFlag("TBS", 2)
145                 .WithTaggedFlag("TCS", 3)
146                 .WithTaggedFlag("RS", 4)
147                 .WithTaggedFlag("TS", 5)
148                 .WithTaggedFlag("ES", 6)
149                 .WithTaggedFlag("BS", 7)
150                 .WithReservedBits(8, 8)
151                 .WithTag("RXERR", 16, 8)
152                 .WithTag("TXERR", 24, 8);
153 
154             Registers.InterruptCapture.Define(this)
155                 .WithFlag(0, out receiveInterruptFlag, mode: FieldMode.Read, name: "RI")
156                 .WithFlag(1, out transmitBuffers[0].transmitInterruptFlag, mode: FieldMode.ReadToClear, name: "TI1")
157                 .WithTaggedFlag("EI", 2)
158                 .WithTaggedFlag("DOI", 3)
159                 .WithTaggedFlag("WUI", 4)
160                 .WithTaggedFlag("EPI", 5)
161                 .WithTaggedFlag("ALI", 6)
162                 .WithTaggedFlag("BEI", 7)
163                 .WithTaggedFlag("IDI", 8)
164                 .WithFlag(9, out transmitBuffers[1].transmitInterruptFlag, mode: FieldMode.ReadToClear, name: "TI2")
165                 .WithFlag(10, out transmitBuffers[2].transmitInterruptFlag, mode: FieldMode.ReadToClear, name: "TI3")
166                 .WithReservedBits(11, 5)
167                 .WithTag("ERRBIT4_0", 16, 5)
168                 .WithTaggedFlag("ERRDIR", 21)
169                 .WithTag("ERRC1_0", 22, 2)
170                 .WithTag("ALCBIT", 24, 8)
171                 .WithReadCallback((_, __) => UpdateInterrupts());
172 
173             Registers.InterruptEnable.Define(this)
174                 .WithFlag(0, out receiveInterruptEnable, name: "RIE")
175                 .WithFlag(1, out transmitBuffers[0].transmitInterruptEnable, name: "TIE1")
176                 .WithTaggedFlag("EIE", 2)
177                 .WithTaggedFlag("DOIE", 3)
178                 .WithTaggedFlag("WUIE", 4)
179                 .WithTaggedFlag("EPIE", 5)
180                 .WithTaggedFlag("ALIE", 6)
181                 .WithTaggedFlag("BEIE", 7)
182                 .WithTaggedFlag("IDIE", 8)
183                 .WithFlag(9, out transmitBuffers[1].transmitInterruptEnable, name: "TIE2")
184                 .WithFlag(10, out transmitBuffers[2].transmitInterruptEnable, name: "TIE3")
185                 .WithReservedBits(11, 21)
186                 .WithWriteCallback((_, __) => UpdateInterrupts());
187 
188             Registers.BusTiming.Define(this, 0x1C0000)
189                 .WithTag("BRP", 0, 10)
190                 .WithReservedBits(10, 4)
191                 .WithTag("SJW", 14, 2)
192                 .WithTag("TESG1", 16, 4)
193                 .WithTag("TESG2", 20, 3)
194                 .WithTaggedFlag("SAM", 23)
195                 .WithReservedBits(24, 8);
196 
197             Registers.ErrorWarningLimit.Define(this, 0x60)
198                 .WithTag("EWL", 0, 8)
199                 .WithReservedBits(8, 24);
200 
201             Registers.Status.Define(this, 0x3C3C3C)
202                 .WithFlag(0, mode: FieldMode.Read, name: "RBS_1",
203                     valueProviderCallback: _ => receiveFifo.Any()) // Same as RBS in GlobalStatus
204                 .WithTaggedFlag("DOS_1", 1)
205                 .WithTaggedFlag("TBS1_1", 2)
206                 .WithTaggedFlag("TCS1_1", 3)
207                 .WithTaggedFlag("RS_1", 4)
208                 .WithTaggedFlag("TS1_1", 5)
209                 .WithTaggedFlag("ES_1", 6)
210                 .WithTaggedFlag("BS_1", 7)
211                 .WithFlag(8, mode: FieldMode.Read, name: "RBS_2",
212                     valueProviderCallback: _ => receiveFifo.Any()) // Same as RBS in GlobalStatus
213                 .WithTaggedFlag("DOS_2", 9)
214                 .WithTaggedFlag("TBS2_2", 10)
215                 .WithTaggedFlag("TCS2_2", 11)
216                 .WithTaggedFlag("RS_2", 12)
217                 .WithTaggedFlag("TS2_2", 13)
218                 .WithTaggedFlag("ES_2", 14)
219                 .WithTaggedFlag("BS_2", 15)
220                 .WithFlag(16, mode: FieldMode.Read, name: "RBS_3",
221                     valueProviderCallback: _ => receiveFifo.Any()) // Same as RBS in GlobalStatus
222                 .WithTaggedFlag("DOS_3", 17)
223                 .WithTaggedFlag("TBS3_3", 18)
224                 .WithTaggedFlag("TCS3_3", 19)
225                 .WithTaggedFlag("RS_3", 20)
226                 .WithTaggedFlag("TS3_3", 21)
227                 .WithTaggedFlag("ES_3", 22)
228                 .WithTaggedFlag("BS_3", 23)
229                 .WithReservedBits(24, 8);
230 
231             Registers.ReceiveFrameStatus.Define(this)
232                 .WithTag("IDINDEX", 0, 10)
233                 .WithTaggedFlag("BP", 10)
234                 .WithReservedBits(11, 5)
235                 .WithValueField(16, 4, mode: FieldMode.Read, name: "DLC",
236                     valueProviderCallback: _ => (ulong)Math.Min(receiveFifo.FirstOrDefault()?.Data.Length ?? 0, FrameMaxLength))
237                 .WithReservedBits(20, 10)
238                 .WithTaggedFlag("RTR", 30)
239                 .WithTaggedFlag("FF", 31);
240 
241             Registers.ReceivedIdentifier.Define(this)
242                 .WithValueField(0, 11, mode: FieldMode.Read, name: "ID",
243                     valueProviderCallback: _ => (ulong)receiveFifo.FirstOrDefault()?.Id)
244                 .WithReservedBits(11, 21);
245 
246             Registers.ReceivedDataBytes1To4.DefineMany(this, 2, stepInBytes: 4, setup: (register, registerIndex) => register
247                 .WithValueFields(0, 8, 4, FieldMode.Read,
248                     valueProviderCallback: (i, _) => receiveFifo.FirstOrDefault()?.Data.ElementAtOrDefault(registerIndex * 4 + i) ?? 0)
249             );
250 
251             // Each transmit buffer consists of these 4 registers
252             Registers.TransmitBuffer1FrameInfo.DefineMany(this, NumberOfTransmitBuffers, stepInBytes: TransmitBufferStride, setup: (register, i) => register
253                 .WithValueField(0, 8, out transmitBuffers[i].priority, name: "PRIO")
254                 .WithReservedBits(8, 8)
255                 .WithValueField(16, 4, out transmitBuffers[i].dataLengthCode, name: "DLC")
256                 .WithReservedBits(20, 10)
257                 .WithTaggedFlag("RTR", 30)
258                 .WithTaggedFlag("FF", 31)
259             );
260 
261             Registers.TransmitBuffer1Identifier.DefineMany(this, NumberOfTransmitBuffers, stepInBytes: TransmitBufferStride, setup: (register, i) => register
262                 .WithValueField(0, 11, out transmitBuffers[i].id, name: "ID")
263                 .WithReservedBits(11, 21) // 29-bit ID mode (FrameInfo.FF=1) is not implemented
264             );
265 
266             Registers.TransmitBuffer1DataBytes1To4.DefineMany(this, NumberOfTransmitBuffers, stepInBytes: TransmitBufferStride, setup: (register, i) => register
267                 .WithValueField(0, 32, out transmitBuffers[i].data[0], name: "DATA[1:4]")
268             );
269 
270             Registers.TransmitBuffer1DataBytes5To8.DefineMany(this, NumberOfTransmitBuffers, stepInBytes: TransmitBufferStride, setup: (register, i) => register
271                 .WithValueField(0, 32, out transmitBuffers[i].data[1], name: "DATA[5:8]")
272             );
273         }
274 
275         private readonly TransmitBuffer[] transmitBuffers;
276         private readonly Queue<CANMessageFrame> receiveFifo;
277 
278         private IFlagRegisterField receiveInterruptFlag;
279         private IFlagRegisterField receiveInterruptEnable;
280 
281         private const int NumberOfTransmitBuffers = 3;
282         private const int FrameMaxLength = 8;
283         private const int TransmitBufferStride = Registers.TransmitBuffer2FrameInfo - Registers.TransmitBuffer1FrameInfo;
284 
285         private struct TransmitBuffer
286         {
DoTransmissionAntmicro.Renode.Peripherals.CAN.LPC_CAN.TransmitBuffer287             public void DoTransmission()
288             {
289                 var length = Math.Min((int)dataLengthCode.Value, FrameMaxLength);
290                 if(length == 0)
291                 {
292                     return;
293                 }
294 
295                 var bytes = data.SelectMany(d => BitConverter.GetBytes((uint)d.Value)).Take(length).ToArray();
296                 parent.TransmitMessage(new CANMessageFrame((uint)id.Value, bytes));
297                 transmitInterruptFlag.Value = true;
298             }
299 
300             public LPC_CAN parent;
301             public IFlagRegisterField transmitInterruptEnable;
302             public IFlagRegisterField transmitInterruptFlag;
303             public IFlagRegisterField select;
304             public IValueRegisterField priority;
305             public IValueRegisterField id;
306             public IValueRegisterField dataLengthCode;
307             public IValueRegisterField[] data;
308         }
309 
310         private enum Registers
311         {
312             OperatingMode = 0x00,
313             Command = 0x04,
314             GlobalStatus = 0x08,
315             InterruptCapture = 0x0c,
316             InterruptEnable = 0x10,
317             BusTiming = 0x14,
318             ErrorWarningLimit = 0x18,
319             Status = 0x1c,
320             // Receive buffer
321             ReceiveFrameStatus = 0x20,
322             ReceivedIdentifier = 0x24,
323             ReceivedDataBytes1To4 = 0x28,
324             ReceivedDataBytes5To8 = 0x2c,
325             // Transmit buffer #1
326             TransmitBuffer1FrameInfo = 0x30,
327             TransmitBuffer1Identifier = 0x34,
328             TransmitBuffer1DataBytes1To4 = 0x38,
329             TransmitBuffer1DataBytes5To8 = 0x3c,
330             // Transmit buffer #2
331             TransmitBuffer2FrameInfo = 0x40,
332             TransmitBuffer2Identifier = 0x44,
333             TransmitBuffer2DataBytes1To4 = 0x48,
334             TransmitBuffer2DataBytes5To8 = 0x4c,
335             // Transmit buffer #3
336             TransmitBuffer3FrameInfo = 0x50,
337             TransmitBuffer3Identifier = 0x54,
338             TransmitBuffer3DataBytes1To4 = 0x58,
339             TransmitBuffer3DataBytes5To8 = 0x5c,
340         }
341     }
342 }
343