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.Linq;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Core.Structure.Registers;
14 using Antmicro.Renode.Peripherals.Memory;
15 
16 namespace Antmicro.Renode.Peripherals.MTD
17 {
18     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)]
19     public class CC2538FlashController : IDoubleWordPeripheral, IKnownSize
20     {
CC2538FlashController(IMachine machine, MappedMemory flash)21         public CC2538FlashController(IMachine machine, MappedMemory flash)
22         {
23             this.flash = flash;
24             var registersMap = new Dictionary<long, DoubleWordRegister>
25             {
26                 {(long)Registers.FlashControl, new DoubleWordRegister(this, 0x4)
27                     .WithFlag(0, valueProviderCallback: _ => false, changeCallback: (_, value) =>
28                             {
29                                 if(value)
30                                 {
31                                     Erase();
32                                 }
33                             }, name: "ERASE")
34                     .WithFlag(1, out write, name: "WRITE")
35                     .WithTag("CACHE_MODE", 2, 2)
36                     .WithReservedBits(4, 1)
37                     .WithTag("ABORT", 5, 1)
38                     .WithFlag(6, FieldMode.Read, name: "FULL")
39                     .WithFlag(7, FieldMode.Read, valueProviderCallback: _ => write.Value, name: "BUSY")
40                     .WithTag("SEL_INFO_PAGE", 8, 1)
41                     .WithTag("UPPER_PAGE_ACCESS", 9, 1)
42                     .WithReservedBits(10, 22)
43                 },
44                 {(long)Registers.FlashAddress, new DoubleWordRegister(this)
45                     .WithValueField(0, 16, valueProviderCallback: _ => writeAddress >> 2, writeCallback: (_, value) => { writeAddress = (uint)value; }, name : "FADDR")
46                     .WithReservedBits(17, 15)
47                 },
48                 {(long)Registers.FlashData, new DoubleWordRegister(this)
49                     .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, value) => Write((uint)value), name: "FWDATA")
50                 },
51                 {(long)Registers.DieConfig0, new DoubleWordRegister(this, 0xB9640580)
52                     .WithValueField(0, 32, FieldMode.Read)
53                 },
54                 {(long)Registers.DieConfig1, new DoubleWordRegister(this)
55                     .WithValueField(0, 32, FieldMode.Read)
56                 },
57                 {(long)Registers.DieConfig2, new DoubleWordRegister(this, 0x2000)
58                     .WithValueField(0, 32, FieldMode.Read)
59                 },
60             };
61 
62             registers = new DoubleWordRegisterCollection(this, registersMap);
63             Reset();
64         }
65 
ReadDoubleWord(long offset)66         public uint ReadDoubleWord(long offset)
67         {
68             return registers.Read(offset);
69         }
70 
WriteDoubleWord(long offset, uint value)71         public void WriteDoubleWord(long offset, uint value)
72         {
73             registers.Write(offset, value);
74         }
75 
Reset()76         public void Reset()
77         {
78             registers.Reset();
79             writeAddress = 0;
80             // Clear the whole flash memory
81             for(uint i = 0; i < PageNumber; ++i)
82             {
83                 flash.WriteBytes(0x800 * i, ErasePattern, 0, PageSize);
84             }
85         }
86 
87         public long Size => 0x1000;
88 
Write(uint newValue)89         private void Write(uint newValue)
90         {
91             var targetAddress = writeAddress;
92             if(!write.Value)
93             {
94                 this.Log(LogLevel.Warning, "Writing 0x{0:X} to 0x{1:X} when not in write mode", newValue, targetAddress);
95                 return;
96             }
97             if(targetAddress > flash.Size)
98             {
99                 this.Log(LogLevel.Error, "Trying to write outside the flash memory at 0x{0:X}", targetAddress);
100                 return;
101             }
102             var oldValue = flash.ReadDoubleWord(targetAddress);
103             if(oldValue != 0xffffffff)
104             {
105                 this.Log(LogLevel.Warning, "Writing to a dirty word at address 0x{0:X}", targetAddress);
106             }
107             this.Log(LogLevel.Noisy, "Writing 0x{0:X} to 0x{1:X}", newValue, targetAddress);
108             flash.WriteDoubleWord(targetAddress, oldValue & newValue);
109             write.Value = false;
110         }
111 
Erase()112         private void Erase()
113         {
114             flash.WriteBytes((long)((writeAddress) & ~(PageSize - 1)), ErasePattern, 0, PageSize);
115             this.Log(LogLevel.Noisy, "Erasing on address 0x{0:X}", (writeAddress & ~(PageSize - 1)));
116         }
117 
118         private uint writeAddress;
119         private readonly IFlagRegisterField write;
120 
121         private readonly DoubleWordRegisterCollection registers;
122         private readonly MappedMemory flash;
123 
124         private const int PageSize = 2048;
125         private const int PageNumber = 256;
126         private readonly byte[] ErasePattern = (byte[])Enumerable.Repeat((byte)0xFF, PageSize).ToArray();
127 
128         private enum Registers : long
129         {
130             FlashControl = 0x08,
131             FlashAddress = 0x0c,
132             FlashData = 0x10,
133             DieConfig0 = 0x14,
134             DieConfig1 = 0x18,
135             DieConfig2 = 0x1c
136         }
137     }
138 }
139