Skip to content

Commit

Permalink
Merge pull request #37 from dinkelk/packed_record_enhancements
Browse files Browse the repository at this point in the history
Packed Record and Packed Array Enhancements
  • Loading branch information
dinkelk authored May 2, 2024
2 parents 2bddc09 + 7f95fa9 commit 5352e8f
Show file tree
Hide file tree
Showing 52 changed files with 2,054 additions and 1,009 deletions.
4 changes: 2 additions & 2 deletions doc/example_architecture/array_register_set/main.adb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ with System.Storage_Elements; use System.Storage_Elements;
procedure Main is
use Example_Register;
-- Define hardware register array:
Registers : Example_Packed_Register_Array.Register_T;
Registers : Example_Packed_Register_Array.Register_T_Le;
for Registers'Address use To_Address (Integer_Address (16#0070_0014#));
-- Define register copy:
Reg_Copy : Example_Register.T_Le;
Expand All @@ -20,7 +20,7 @@ begin
-- compiler will ensure that the ENTIRE register is read/written
-- during the following operations.
if Registers (1).Hw_1_Enabled = Enable and then
Registers (4).Hw_2_Enabled = Enable
Registers (4).Hw_2_Enabled = Enable
then
Registers (7).Threshold := 22;
end if;
Expand Down
2 changes: 1 addition & 1 deletion doc/example_architecture/example_register_set.record.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ fields:
type: Example_Register.Register_T_Le
- name: Reg_3
description: The third register.
type: Example_Register.Register_T
type: Example_Register.Register_T_Le
8 changes: 8 additions & 0 deletions doc/example_architecture/nested_record/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import os

this_dir = os.path.dirname(os.path.realpath(__file__))
# Overwrite the normal build path mechanism and just use
# this directory as the build path. This prevents any
# name conflicts we might get from using the regular
# ".all_path".
os.environ["EXTRA_BUILD_PATH"] = this_dir + os.sep + ".."
16 changes: 16 additions & 0 deletions doc/example_architecture/nested_record/nested_record.record.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: This is an example packed record that demonstrates the nesting of other packed records within.
fields:
- name: Simple_Field
description: A simple float, we must provide a format
type: Short_Float
# Required
format: F32
- name: Nested_Field_1
description: This field is of type Example Record, which is itself a packed record definition.
type: Example_Record.T
# ^ No format specification is necessary, since the
# bit representation of a packed record is known
- name: Nested_Field_2
description: Another field of type Example Record.
type: Example_Record.T
20 changes: 14 additions & 6 deletions doc/example_architecture/record_conversion/main.adb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ with Example_Record;

procedure Main is
use Example_Record;
-- A packed version of the type.
Packed_Type : Example_Record.T := (Value_1 => 2, Value_2 => -1, Value_3 => Green, Value_4 => 0.5);
-- A packed big-endian version of the type.
Packed_Be_Type : Example_Record.T := (Value_1 => 2, Value_2 => -1, Value_3 => Green, Value_4 => 0.5);
-- A packed little-endian version of the type.
Packed_Le_Type : Example_Record.T_Le;
-- An unpacked version of the type.
Unpacked_Type : Example_Record.U;
begin
-- Convert from packed to unpacked:
Unpacked_Type := U (Packed_Type);
-- Convert from unpacked to packed:
Packed_Type := T (Unpacked_Type);
-- Convert from big-endian packed to unpacked:
Unpacked_Type := Unpack (Packed_Be_Type);
-- Convert from unpacked to big-endian packed:
Packed_Be_Type := Pack (Unpacked_Type);
-- Convert from big-endian to little-endian:
Packed_Le_Type := Swap_Endianness (Packed_Be_Type);
-- Convert from little-endian to big-endian:
Packed_Be_Type := Swap_Endianness (Packed_Le_Type);
-- Convert from little-endian packed to unpacked:
Unpacked_Type := Unpack (Packed_Le_Type);
end Main;
2 changes: 1 addition & 1 deletion doc/example_architecture/record_register_set/main.adb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ with System.Storage_Elements; use System.Storage_Elements;
procedure Main is
use Example_Register;
-- Define hardware register set:
Registers : Example_Register_Set.Register_T;
Registers : Example_Register_Set.Register_T_Le;
for Registers'Address use To_Address (Integer_Address (16#0060_0014#));
-- Define a register copy:
Reg_Copy : Example_Register.T_Le;
Expand Down
Binary file modified doc/user_guide/user_guide.pdf
Binary file not shown.
95 changes: 73 additions & 22 deletions doc/user_guide/user_guide.tex

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions gen/generators/packed_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"record/name-validation.adb",
"record/name-assertion.ads",
"record/name-assertion.adb",
"record/name-c.ads",
"record/name.py",
"record/name_type_ranges.adb",
]
Expand All @@ -25,6 +26,7 @@
"array/name-validation.ads",
"array/name-validation.adb",
"array/name-assertion.ads",
"array/name-c.ads",
"array/name.py",
"array/name_type_ranges.adb",
]
Expand Down
62 changes: 61 additions & 1 deletion gen/models/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,75 @@ def _load(self):
# Initialize object members:
self.length = None

# Define the endiannesses available for this packed record. Depending
# on the packed record definition, the endiannesses supported are:
#
# big - The single packed type T is big endian
# little - The single packed type T_Le is little endian
# either - Two packed types exists T, big endian, and T_Le, little endian
# mixed - The single packed type has both little and big endian parts
# ^ This one is not yet supported, but could be in the future.
#
self.endianness = "either" # This is the default
self.nested = False

# Call base class load:
super(array, self)._load()

# Extract length and set appropriate fields:
self.length = self.data["length"]

# If element is arrayed then the array components must be <= 8 bits otherwise
# endianness cannot be guaranteed. In this case, the user should be using a
# packed array to declare the field type instead.
if (
self.element.format
and self.element.format.length
and self.element.format.length > 1
and self.element.format.unit_size > 8
):
raise ModelException(
"Array '"
+ self.name
+ '" cannot specify component "'
+ self.element.name
+ "' of type '"
+ self.element.type
+ "' and format '"
+ str(self.element.format)
+ "'. Components of array type must be <=8 bits in size to guarantee endianness."
+ " Use a packed array to defined arrays with components >8 bits in size."
)

# Calculate the number of fields in the array:
if self.element.is_packed_type:
self.num_fields = self.element.type_model.num_fields * self.length
self.nested = True

if (
self.element.type.endswith(".T")
or self.element.type.endswith(".Volatile_T")
or self.element.type.endswith(".Atomic_T")
or self.element.type.endswith(".Register_T")
):
self.endianness = "big"
elif (
self.element.type.endswith(".T_Le")
or self.element.type.endswith(".Volatile_T_Le")
or self.element.type.endswith(".Atomic_T_Le")
or self.element.type.endswith(".Register_T_Le")
):
self.endianness = "little"
else:
raise ModelException(
"Array '"
+ self.name
+ '" cannot specify element "'
+ self.element.name
+ "' of type '"
+ self.element.type
+ "'. Nested packed types must either be '.*T' or '.*T_Le' types."
)
else:
self.num_fields = self.length

Expand All @@ -49,7 +109,7 @@ def _load(self):
raise ModelException(
"Packed array '"
+ self.name
+ "' cannot specify variable length element. Packed arrays must have " +
+ "' cannot specify variable length element. Packed arrays must have "
+ "statically sized elements."
)

Expand Down
129 changes: 115 additions & 14 deletions gen/models/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ def _load(self):
self.statically_sized_fields = OrderedDict()
self.variable_length_sizing_fields = OrderedDict()

#
# Define the endiannesses available for this packed record. Depending
# on the packed record definition, the endiannesses supported are:
#
# big - The single packed type T is big endian
# little - The single packed type T_Le is little endian
# either - Two packed types exists T, big endian, and T_Le, little endian
# mixed - The single packed type has both little and big endian parts
# ^ This one is not yet supported, but could be in the future.
#
self.endianness = "either" # This is the default
self.nested = False

# Populate the object with the contents of the
# file data:
start_bit = 0
Expand All @@ -46,6 +59,93 @@ def _load(self):
start_bit = the_field.end_bit + 1
self.num_fields = the_field.end_field_number

# If field is arrayed then the array components must be <= 8 bits otherwise
# endianness cannot be guaranteed. In this case, the user should be using a
# packed array to declare the field type instead.
if (
the_field.format
and the_field.format.length
and the_field.format.length > 1
and the_field.format.unit_size > 8
):
raise ModelException(
"Record '"
+ self.name
+ '" cannot specify field "'
+ the_field.name
+ "' of type '"
+ the_field.type
+ "' and format '"
+ str(the_field.format)
+ "'. Array components must be <=8 bits in size to guarantee endianness."
+ " Use a packed array to defined arrays with components >8 bits in size."
)

# Handle fields that are packed records or arrays themselves, ie. (nested packed records)
if the_field.is_packed_type:
self.nested = True

# Check endianness. The endianness rules are as follows:
#
# 1. A simple packed record with no nesting of other packed records/arrays will support
# "either" endianness
# 2. A nested packed record containing fields of .T types only support "big" endian
# 3. A nested packed record containing fields of .T_Le types only supports "little" endian
# 4. Packed records can not contain both little and big endian fields (for now) or unpacked
# fields.
#
if self.endianness == "either":
if (
the_field.type.endswith(".T")
or the_field.type.endswith(".Volatile_T")
or the_field.type.endswith(".Atomic_T")
or the_field.type.endswith(".Register_T")
):
self.endianness = "big"
elif (
the_field.type.endswith(".T_Le")
or the_field.type.endswith(".Volatile_T_Le")
or the_field.type.endswith(".Atomic_T_Le")
or the_field.type.endswith(".Register_T_Le")
):
self.endianness = "little"
else:
raise ModelException(
"Record '"
+ self.name
+ '" cannot specify field "'
+ the_field.name
+ "' of type '"
+ the_field.type
+ "'. Nested packed types must either be '.*T' or '.*T_Le' types."
)
else:
if self.endianness == "big" and (
the_field.type.endswith(".T")
or the_field.type.endswith(".Volatile_T")
or the_field.type.endswith(".Atomic_T")
or the_field.type.endswith(".Register_T")
):
pass # all is good
elif self.endianness == "little" and (
the_field.type.endswith(".T_Le")
or the_field.type.endswith(".Volatile_T_Le")
or the_field.type.endswith(".Atomic_T_Le")
or the_field.type.endswith(".Register_T_Le")
):
pass # all is good
else:
raise ModelException(
"Record '"
+ self.name
+ '" cannot specify field "'
+ the_field.name
+ "' of type '"
+ the_field.type
+ "'. Nested packed types must ALL be either '.*T' or '.*T_Le' types. "
+ "Mixed endianness is not currently supported for packed records."
)

# Handle variable length fields:
if the_field.variable_length:
self.variable_length = True
Expand Down Expand Up @@ -86,9 +186,9 @@ def _load(self):
+ str(e)
)
self.variable_length_fields[the_field.name] = the_field
self.variable_length_sizing_fields[
the_field.variable_length
] = the_field.variable_length_field
self.variable_length_sizing_fields[the_field.variable_length] = (
the_field.variable_length_field
)

# Handle a field that might have a variable length type:
elif the_field.is_packed_type and the_field.type_model.variable_length:
Expand Down Expand Up @@ -287,17 +387,18 @@ def _load(self):
[f.type_package for f in self.fields.values() if f.type_package]
)
)
self.type_uses = list(
OrderedDict.fromkeys(
self.type_includes
+ [
f.type_package
if f.is_packed_type
else (f.type_package + "." + f.type_model.name)
for f in complex_typed_fields
]
)
)

# Create type uses list for assertion package. This is complicated, but needed.
type_includes_no_var = []
for f in self.fields.values():
if f.type_model and f.is_packed_type:
if not f.type_model.variable_length:
type_includes_no_var.append(f.type_package)
elif f.type_model and not f.is_packed_type:
type_includes_no_var.append(f.type_package + "." + f.type_model.name)
elif f.type_package:
type_includes_no_var.append(f.type_package)
self.type_uses = list(OrderedDict.fromkeys(type_includes_no_var))

# Store the includes for any complex types (those that have models).
self.modeled_type_includes = list(
Expand Down
12 changes: 5 additions & 7 deletions gen/templates/array/name-assertion.ads
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ with {{ element.type_package }}.Assertion;

package {{ name }}.Assertion is

{% if is_volatile_type %}
-- Assertion not supported for volatile record. Convert to a regular record for
-- a validation checking function.
procedure Dummy_Assertion;
{% else %}
-- Basic assertion packages for packed array:
package {{ name }}_U_Assert is new Smart_Assert.Basic ({{ name }}.U, {{ name }}.Representation.Image);
{% if endianness in ["either", "big"] %}
package {{ name }}_Assert is new Smart_Assert.Basic ({{ name }}.T, {{ name }}.Representation.Image);
{% endif %}
{% if endianness in ["either", "little"] %}
package {{ name }}_Le_Assert is new Smart_Assert.Basic ({{ name }}.T_Le, {{ name }}.Representation.Image);
package {{ name }}_U_Assert is new Smart_Assert.Basic ({{ name }}.U, {{ name }}.Representation.Image);
{% endif %}

-- Specialized smart assert package for the element in this array type:
{% if element.is_packed_type %}
Expand All @@ -35,6 +34,5 @@ package {{ name }}.Assertion is
{% else %}
package Element_Assert is new Smart_Assert.Basic ({{ element.type }}, {{ name }}.Representation.Element_Image);
{% endif %}
{% endif %}

end {{ name }}.Assertion;
Loading

0 comments on commit 5352e8f

Please sign in to comment.