forked from esl-epfl/x-heep
-
Notifications
You must be signed in to change notification settings - Fork 0
/
structs_gen.py
412 lines (309 loc) · 15.2 KB
/
structs_gen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
import hjson
import string
import argparse
import sys
from datetime import date
############################################################
# This module generates the structures for the registers #
# of a peripheral and writes them into a file formatted #
# using a template. #
############################################################
# Bit length of each register
reg_length = 32
# Entry name for the reserved bits
reserved_name = "_reserved"
# Tab definition as 4 blank spaces #
tab_spaces = " "
# ENUM definitions #
enum_start = "typedef enum {}_enum {{\n"
enum_end = "}} {}_t;\n\n"
# UNION definitions #
union_start = tab_spaces + "union\n" + tab_spaces + "{\n"
union_end = tab_spaces + "}} {};\n\n"
# STRUCT definitions #
struct_typedef_start = (2 * tab_spaces) + "struct \n" + (
2 * tab_spaces) + "{{\n" # used to define a new struct and format the name
struct_entry = (3 * tab_spaces) + "{}" + "{}" + ":{}" # type, name and amount of bits
struct_typedef_end = (2 * tab_spaces) + "} b ;" # define the end of the new struct definition and the format for the new type-name
# Documentation comments definitions #
comment_align_space = 50
line_comment_start = "/*!< "
line_comment_end = "*/"
struct_comment = "Structure used for bit access"
word_comment = "Type used for word access"
def read_json(json_file):
"""
Opens the json file taken as input and returns its content
"""
# Open the hjson file #
f = open(json_file)
j_data = hjson.load(f)
f.close()
return j_data
def write_template(tpl, structs, enums, struct_name):
"""
Opens a given template and substitutes the structs and enums fields.
Returns a string with the content of the updated template
"""
lower_case_name = struct_name.lower()
upper_case_name = struct_name.upper()
start_addr_def = "{}_peri ((volatile {} *) {}_START_ADDRESS)".format(lower_case_name, struct_name, upper_case_name)
today = date.today()
today = today.strftime("%d/%m/%Y")
# To print the final result into the template
with open(tpl) as t:
template = string.Template(t.read())
return template.substitute( structures_definitions=structs,
enums_definitions=enums,
peripheral_name=struct_name,
peripheral_name_upper=upper_case_name,
date=today,
start_address_define=start_addr_def)
def write_output(out_file, out_string):
"""
Writes the final out_string into the specified out_file
"""
with open(out_file, "w") as f:
f.write(out_string)
def generate_enum(enum_field, name):
"""
Generates an enum with the values specified.
The enum is generated basing on the 'enum_field' parameter that contains all the values and names
:param enum_field: list containing, for each entry, the value, the name and the description for each enum field
:param name: name of the register field associated to the enum
:return: the string containing the formatted enum
"""
enum = enum_start.format(name)
# first_entry = True
last_entry = False
for key in enum_field:
enum += tab_spaces + format(key["name"], "<15") + "=" + tab_spaces + str(key["value"])
if not last_entry:
enum += ","
# if first_entry:
# enum += tab_spaces + format(key["name"], "<15") + "=" + tab_spaces + str(key["value"])
# first_entry = False
# else:
# enum += ",\n" + tab_spaces + format(key["name"], "<15") + "=" + tab_spaces + str(key["value"])
if "desc" in key:
enum += format("", "<25") + line_comment_start + format(key["desc"].replace("\n", " "), "<100") + line_comment_end
enum += "\n"
enum += enum_end.format(name)
return enum
def count_bits(bits_range):
"""
Used to determine if the "bits_range" input string contains only a single bit index or a range of bits.
In the latter case the range is supposed to be identified with the following format "end_bit:start_bit".
Ex: "7:0" will correspond to 8 bits from 0 to 7.
It returns the amount of bits specified in the range
:param bits_range: string containing the nuber of bit (or range or bits) of a specific field
:return: the amount of bits
"""
if bits_range.find(":") != -1:
start_bit = bits_range.split(":")[1]
end_bit = bits_range.split(":")[0]
return int(end_bit) - int(start_bit) + 1
else:
return 1
def select_type(amount_of_bits):
"""
Used to select the C type to give to a specific bit field. The type depends on the amount of bits
that the filed has.
:param amount_of_bits: amount of bits of the field to which to assign a type
:return: the string containing the type selected
"""
if 1 <= amount_of_bits < 9:
return "uint8_t"
elif 9 <= amount_of_bits < 17:
return "uint16_t"
elif 17 <= amount_of_bits < 33:
return "uint32_t"
elif 33 <= amount_of_bits < 65:
return "uint64_t"
def intr_regs_auto_gen():
"""
Generate hardcoded registers for interrupts
:return: string with the register varibles to be added to the struct
"""
res = ""
line = tab_spaces + "uint32_t {};".format("INTR_STATE")
reg_comment = line_comment_start + "Interrupt State Register" + line_comment_end + "\n\n"
res += line.ljust(comment_align_space) + reg_comment
line = tab_spaces + "uint32_t {};".format("INTR_ENABLE")
reg_comment = line_comment_start + "Interrupt Enable Register" + line_comment_end + "\n\n"
res += line.ljust(comment_align_space) + reg_comment
line = tab_spaces + "uint32_t {};".format("INTR_TEST")
reg_comment = line_comment_start + "Interrupt Test Register" + line_comment_end + "\n\n"
res += line.ljust(comment_align_space) + reg_comment
return res
def alert_regs_auto_gen():
"""
Generate hardcoded registers for alerts
:return: string with the register varibles to be added to the struct
"""
res = ""
line = tab_spaces + "uint32_t {};".format("ALERT_TEST")
reg_comment = line_comment_start + "Alert Test Register" + line_comment_end + "\n\n"
res += line.ljust(comment_align_space) + reg_comment
return res
def add_fields(register_json):
"""
Loops through the fields of the json of a register, passed as parameter.
Returns the structs and enums entries relative to the register, already
indented.
:param register_json: the json-like description of a register
:return: the strings of the the struct fields, the enum (if present)
"""
struct_fields = ""
enum = ""
bits_counter = 0 # to count the bits used for all the fields of the register
# loops through the fields of the register
for field in register_json["fields"]:
field_bits = count_bits(field["bits"])
field_type = select_type(field_bits)
# Check if there is an ENUM, if yes it generates it and set the type of the associated field
if "enum" in field:
field_type = "{}_t".format(field["name"])
enum += generate_enum(field["enum"], field["name"])
bits_counter += int(field_bits)
# Handles the case in which the field has no name (it's given the same name as the register)
if "name" in field:
field_name = field["name"]
else:
field_name = register_json["name"]
# insert a new struct in the structs string
struct_fields += struct_entry.format(format(field_type, "<15"), format(field_name, "<20"),
format(str(field_bits) + ";", "<5"))
# if there is a description, it adds a comment
if "desc" in field:
struct_fields += line_comment_start + format("bit: {}".format(field["bits"]), "<10") + format(
field["desc"].replace("\n", " "), "<100") + line_comment_end
struct_fields += "\n"
# add an entry for the reserved bits (if present)
if bits_counter < reg_length:
reserved_bits = reg_length - bits_counter
reserved_type = select_type(reserved_bits)
struct_fields += struct_entry.format(format(reserved_type, "<15"), format(reserved_name, "<20"),
format(str(reserved_bits) + ";", "<5"))
struct_fields += "\n"
return struct_fields, enum
def add_registers(peripheral_json):
"""
Reads the json description of a peripheral and generates structures for every
register.
:param peripheral_json: the json-like description of the registers of a peripheral
:return: the strings containing the indented structs and enums relative to the registers
"""
reg_struct = "\n"
reg_enum = ""
# number of "reserved" fields. Used to name them with a progressive ID
num_of_reserved = 0
# Keeps track of the offset in Bytes from the base address of the peripheral.
# It is usefult to compute how many Bytes to reserve in case a "skipto"
# keywork is encountered
bytes_offset = 0
# To handle INTR specific registers #
if "interrupt_list" in peripheral_json:
if 'no_auto_intr_regs' not in peripheral_json:
reg_struct += intr_regs_auto_gen()
# To handle the ALERT registers #
if "alert_list" in peripheral_json:
if "no_auto_alert_regs" not in peripheral_json:
reg_struct += alert_regs_auto_gen()
# loops through the registers of the hjson
for elem in peripheral_json['registers']:
# check and handle the multireg case
if "multireg" in elem:
multireg = elem["multireg"]
count_var = multireg["count"]
# search the multireg count default value
# This is the number of bitfields needed
for p in peripheral_json["param_list"]:
if count_var == p["name"]:
count = int(p["default"])
# counts the bits needed by the multireg register
n_bits = 0
for f in multireg["fields"]:
n_bits += count_bits(f["bits"])
# computes the number of registers needed to pack all the bit fields needed
n_multireg = int((count * n_bits) / int(peripheral_json["regwidth"]))
# generate the multiregisters
for r in range(n_multireg):
reg_name = multireg["name"] + str(r)
line = tab_spaces + "uint32_t {};".format(reg_name)
reg_comment = line_comment_start + multireg["desc"].replace("\n", " ") + line_comment_end + "\n\n"
reg_struct += line.ljust(comment_align_space) + reg_comment
bytes_offset += 4 # one register is 4 bytes
# check and handle the "window" case
elif "window" in elem:
window = elem["window"]
validbits = int(window["validbits"])
line = tab_spaces + "{} {};".format(select_type(validbits), window["name"])
reg_comment = line_comment_start + window["desc"].replace("\n", " ") + line_comment_end + "\n\n"
reg_struct += line.ljust(comment_align_space) + reg_comment
# if no multireg or window, just generate the reg
elif "name" in elem:
line = tab_spaces + "uint32_t {};".format(elem["name"])
reg_comment = line_comment_start + elem["desc"].replace("\n", " ") + line_comment_end + "\n\n"
reg_struct += line.ljust(comment_align_space) + reg_comment
bytes_offset += 4 # in order to properly generate subsequent "multireg cases"
if "skipto" in elem:
new_address = elem["skipto"]
# check if the new address is in hexadecimal or decimal
# and convert it to decimal
if(new_address[:2] == "0x"):
new_address = int(new_address, base=16)
else:
new_address = int(new_address)
offset_value = int((new_address - bytes_offset) / 4)
line = tab_spaces + "uint32_t _reserved_{}[{}];".format(num_of_reserved, int(offset_value))
reg_comment = line_comment_start + "reserved addresses" + line_comment_end + "\n\n"
reg_struct += line.ljust(comment_align_space) + reg_comment
bytes_offset += offset_value * 4
num_of_reserved += 1
## OLD VERSION WITH UNION AND BIT FIELDS ##
# reg_struct += union_start + struct_typedef_start.format(elem["name"])
# # generate the struct entries relative to the fields of the register
# new_field, new_enum = add_fields(elem)
# reg_struct += new_field
# reg_enum += new_enum
# reg_struct += struct_typedef_end
# reg_struct += format(line_comment_start, ">43") + format(struct_comment, "<100") + "*/\n"
# reg_struct += (2 * tab_spaces) + "uint32_t" + " w;"
# reg_struct += format(line_comment_start, ">37") + format(word_comment, "<110") + "*/\n"
# reg_struct += union_end.format(elem["name"])
return reg_struct, reg_enum
# def gen(input_template, input_hjson_file):
# if __name__ == '__main__':
def main(arg_vect):
parser = argparse.ArgumentParser(prog="Structure generator",
description="Given a template and a json file as input, it generates "
"suitable structs and enums and prints them into a file, following the "
"structure provided by the template.")
parser.add_argument("--template_filename",
help="filename of the template for the final file generation")
# parser.add_argument("--peripheral_name",
# help="name of the peripheral for which the structs are generated")
parser.add_argument("--json_filename",
help="filename of the input json basing on which the structs and enums will begenerated")
parser.add_argument("--output_filename",
help="name of the file in which to write the final formatted template with the structs "
"and enums generated")
args = parser.parse_args(arg_vect)
input_template = args.template_filename
input_hjson_file = args.json_filename
output_filename = args.output_filename
# peripheral_name = args.peripheral_name
data = read_json(input_hjson_file)
# Two strings used to store all the structs and enums #
structs_definitions = "typedef struct {\n" # used to store all the struct definitions to write in the template in the end
enums_definitions = "" # used to store all the enums definitions, if present
# START OF THE GENERATION #
reg_structs, reg_enums = add_registers(data)
structs_definitions += reg_structs
enums_definitions += reg_enums
structs_definitions += "}} {};".format(data["name"])
final_output = write_template(input_template, structs_definitions, enums_definitions, data["name"])
write_output(output_filename, final_output)
if __name__ == "__main__":
main(sys.argv[1:])