• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..--

src/peakrdl_renode/04-Apr-2025-2,4151,826

tests/04-Apr-2025-1,057880

.gitignoreD04-Apr-202553 75

LICENSED04-Apr-202511.1 KiB203169

README.mdD04-Apr-20256.6 KiB231177

pyproject.tomlD04-Apr-2025924 3628

README.md

1# PeakRDL-Renode
2
3Copyright (c) 2024 [Antmicro](https://antmicro.com)
4
5Renode interface exporter plugin for PeakRDL.
6
7## Usage
8
9### Prerequisites
10
11This project requires Python 3.11 or newer and [PeakRDL package](https://pypi.org/project/peakrdl/):
12
13```
14python3 -m pip install peakrdl
15```
16
17### Installing the exporter
18
19Execute the following from `PeakRDL-renode` directory:
20
21```
22python3 -m pip install .
23```
24
25### Using the exporter
26
27Generation of the partial C# class is done by calling the renode plugin to the peakrdl package:
28
29```bash
30peakrdl renode [-h] [-I INCDIR] [-D MACRO[=VALUE]] [-t TOP] [--rename INST_NAME] [-P PARAMETER=VALUE] [--remap-state STATE] -o OUTPUT -N NAMESPACE [-n NAME] [-f FILE] [--peakrdl-cfg CFG] FILE [FILE ...]
31```
32
33* `FILE` - SystemRDL file to read
34* `-n/--name ` - name of the peripheral class to be exported
35* `-N/--namespace` - namespace in which this class should reside. Relative to
36  `Antmicro.Renode.Peripherals`, for example `-N Mocks` will resolve to
37  `Antmicro.Renode.Peripherals.Mocks`
38* `-o OUTPUT` - path/name of the file to export the C# code into
39
40For example:
41
42```
43peakrdl renode -n MyI2CController -N I2C -o MyI2CController_gen.cs i2c_regs.rdl
44```
45
46will generate _MyI2CController_gen.cs_ file containing `MyI2CController` partial class in
47`Antmicro.Renode.Peripherals.I2C` namespace.
48
49### Working with the generated code
50
51The generated C# code contains a
52[partial class](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods)
53that serves as a starting point for writing your peripheral model. Do not edit the generated file.
54Instead write the rest of the implementation in another file, within the same .NET/Mono assembly.
55
56Since there's some initialization code within the generated part, there are a couple small differences in
57how some things are handled in this approach, compared to the examples shown in the
58[Peripheral Modeling Guide](https://renode.readthedocs.io/en/latest/advanced/writing-peripherals.html).
59
60#### Peripheral initialization
61
62First of all, the constructor is defined in the generated code. Use `void Init()` partial method instead
63- it gets called after the generated fields get initialized.
64
65Define an `Init` method and initialize your class fields and properties inside it, for example:
66
67```csharp
68public partial class MyPeripheral
69{
70    List<int> myList; // An example field that needs to be initialized
71
72    partial void Init()
73    {
74        myList = new List<int>;
75    }
76
77    // ... The rest of your code goes here
78}
79```
80
81#### Accessing registers and fields defined in SystemRDL
82
83Registers are available as fields that are instances of classes generated for each `register` entry
84in SystemRDL. Those classes contain fields that correspond to `field` entries in your SystemRDL file.
85
86For example, if your SystemRDL file contains a `register` named `status`, with a field called
87`busy`, like that:
88
89```systemrdl
90register {
91  name = "status";
92  regwidth = 32;
93  field {
94    name = "busy";
95    sw = "rw";
96    hw = "rw";
97  } busy [0:0];
98  // ... more fields
99} status @ 0x4;
100```
101
102You can access it like that:
103
104```csharp
105public partial class MyPeripheral
106{
107    public void AccessBusy()
108    {
109        // Read the register field
110        bool isBusy = this.Status.BUSY.Value
111        // Write to the register's field
112        this.Status.BUSY.Value = false;
113    }
114}
115```
116
117Fields which width is equal to one are represented by a type that implements `IFlagRegisterField`
118interface, wider fields are represented by a type that implements `IValueRegisterField` interface.
119
120The register name is always converted to _CamelCase_, while the field name is converted to
121_UPPPER_CASE_.
122
123#### Binding callbacks to field access
124
125Normally, the callbacks are attached to the register when its defined. However in our case
126the registers are already defined, so adding read/write callbacks has to be done manually after
127the fields and registers get instantiated.
128Starting from [4e23f6c](https://github.com/renode/renode/commit/4e23f6c7bcf3b7bd68d28b429001b6b06727db2a)
129Renode exposes the callbacks as properties of register fields so you can add your logic like in the
130example below:
131
132```csharp
133// Read the flag negated
134this.Status.BUSY.ValueProviderCallback += (value) => !value;
135```
136
137#### Memory
138
139PeakRDL-renode generates special structures and logic for accessing memories defined using `mem`
140nodes. Currently only memories that contain one register (array) are supported.
141
142For each memory a wrapper structure is generated. It defines read/write access methods for the
143software and an indexer method for implementation of the hardware, for example, for the included
144SystemRDL example `tests/models/rdl/mem1.rdl` we get:
145
146```csharp
147protected class Mem1_StructureContainer
148{
149    public Mem1_StructureWrapper this[long index] {
150        get
151        {
152            // ...
153        }
154    }
155
156    public uint ReadDoubleWord(long offset)
157    {
158        // ...
159    }
160
161    public void WriteDoubleWord(long offset, uint value)
162    {
163        // ...
164    }
165
166    // .. More implementation below
167}
168```
169
170This structure is instantiated within the peripheral as a member named after the memory instance:
171```csharp
172/// <summary> Memory "mem1" at 0x10 </summary>
173protected Mem1_StructureContainer Mem1;
174```
175
176The type of an object returned by the indexer method is another wrapper type. It provides access
177to the underlying memory at a given entry index and exposes the entry's fields as
178properties that map to the memory.
179
180An example usage is shown below:
181```csharp
182public partial class MyPeripheral
183{
184    Mem1_StructureContainer Mem1;
185
186    // ...
187
188    public void AccessMemory()
189    {
190        // Read the flag2 field of the first entry
191        bool flag2 = Mem1[0].FLAG2;
192        // Write to the VALUE1 field of the third entry
193        Mem1[2].VALUE1 = 5;
194    }
195}
196```
197
198The types of the properties are assigned depending on the field width:
199* `width == 1` => `bool`
200* `1 < width <= 8` => `byte`
201* `8 < width <= 16` => `ushort`
202* `16 < width <= 32` => `uint`
203* `32 < width <= 64` => `ulong`
204
205Fields wider than 64 bits are not supported.
206
207### Software read/write operations
208
209All of the peripheral's registers and memories are assumed to be described by the SystemRDL code.
210The read and write methods are fully generated, for example:
211```csharp
212uint IDoubleWordPeripheral.ReadDoubleWord(long offset)
213{
214    if(offset >= 16 && offset < 16L + Mem1.Size)
215    {
216        return Mem1.ReadDoubleWord(offset - 16);
217    }
218    return RegistersCollection.Read(offset);
219}
220
221void IDoubleWordPeripheral.WriteDoubleWord(long offset, uint value)
222{
223    if(offset >= 16 && offset < 16L + Mem1.Size)
224    {
225        Mem1.WriteDoubleWord(offset - 16, value);
226        return;
227    }
228    RegistersCollection.Write(offset, value);
229}
230```
231