TWiki
>
Main Web
>
TWikiUsers
>
PatrickLehmann
>
PatrickLehmannReflection
(revision 5) (raw view)
Edit
Attach
---+ Introspection for VHDL This wiki page relates to the following IEEE 1076 proposals: * P1076.RecordIntrospection => Prop1 * P1076.RecordMemberAttribute => Prop2 * P1076.AnonymousTypes => Prop3 * P1076.EnumAttributes => Prop4 I think we should consider to rename "introspection" to "reflection", because other languages<br />already use this terminology for languages _reflecting_ on languages entities of itself. I withdraw my renaming request. => <a href="https://en.wikipedia.org/wiki/Type_introspection" target="_blank">Wikipedia:Type_introspection</a> Languages with reflection capabilities: * .NET languages: C#, VB.NET, ... * Java * Python * COM/ActiveX components compiled with C++/VB * XML -> DOM * ... *Which reflection capabilities are already present in VHDL?* VHDL has several attributes which can be used to reflect on language entities like types,<br />signals, variables and so on. For example: * mySignal'length can be used to query the number of bits in a std_logic_vector, * mySignal'range can be used to iterate over all vector members in a std_logic_vector or<br />generate a new signal with different type of the same range. So I think it can be stated that existing reflection capabilities in VHDL are mostly (entirely?)<br />attribute based. This results a quite handy syntax and does not pollute the list of reserved words. *How do other languages handle reflection capabilities?* .NET language have a set of data structures (classes) and enums to represent all queriable<br />entities of a language. For example there is a Assembly, a TypeInfo and a MethodInfo class.<br />A <a href="https://msdn.microsoft.com/en-us/library/system.reflection.methodinfo(v=vs.110).aspx" target="_blank" title="MSDN Reference">MethodInfo</a> holds all information on a method (attributes, calling convention, parameters,<br />generic parameters, is_abstract, is_constructor, return_type, ...). It can be retrieved by<br />accessing an Assembly in a file (e.g. from a DLL file), searched for a type (e.g. class) by<br />name and that in turn can be searched for a method name. Finally the MethodInfo can be<br />used to invoke a method. *How does it correlate to Prop1?* The proposed 'elements attribute (from Prop1) goes into that direction. It introduces an<br />enumerated type, which holds all record members. As that type is iterable, it can be used<br />in for/generate loops. Prop1 states that 'elements is an implicit type. I think that's no problem,<br />because all members of a record are known, while parsing the record declaration. Moreover<br />a simple implementation could just copy the VHDL syntax and rewrite it from record to<br />enum declaration. A smarter compiler generates 2 member elements while parsing the<br />record code: 1) the record member as usual and 2) a new enum member. In Python-like pseudo-code, how a VHDL compiler could create the implicit type.<br /><i>I don't want runtime created 'objects'.</i> <verbatim>newRecord = Record("std_record") newImplicitEnum = Enum(newRecord.Name + "_members") # how should it be called. if needed for an error message or so. for recordMember in ASTBranch: newRecord.AddMember(recordMember.Name, recordMember.Type) newImplicitEnum.AddMember(recordMember.Name) newRecord.ImplicitEnumType = newImplicitEnum CurrentScope.AddType(newRecord)</verbatim> An example: <verbatim>type T_MyRecord is Valid : std_logic; Data : std_logic_vector(7 downto 0); SOF : std_logic; EOF : std_logic; end record;</verbatim> The implicit type would be: <verbatim>type T_MyRecord_Members is Valid, Data, SOF, EOF );</verbatim> As enumerated types are ordered (and the implicit enumerated type has the same member order<br />as the record type), it can be use to loop. The position 'pos in the enumerated type is equal to the<br />position in the record => T_MyRecord'members'pos(Valid) = T_MyRecord'pos(Valid) Defining this enumerated types gives us already the possibility to report all record names: <verbatim>for index in std_record'elements loop report "Member " & integer'image(std_record'members'pos(index)) & " Name " & std_record'members'image(index); end loop;</verbatim> This results in <verbatim> Member 0 Name Valid Member 1 Name Data Member 2 Name SOF Member 3 Name EOF</verbatim> *More helper structures and enums:* I think we need further enumerated types, which could also be useful for Jim's "Anonymous<br />Types on Interfaces" proposal (Prop3), too. Currently VHDL has no mechanism to determine if a<br />signal/variable is of a given type class/category (scalar, array, enumerated, record, protected, ...). So my suggestion is to introduce further predefined (not implicit) enumerated types. One in<br />my mind is a type_class enum (is there a better name?) _Possible problem: some members are keywords...._ <verbatim>type type_class is ( scalar, array, enumeration, record, protected );</verbatim> If this enum exists, there would be no need for is_*** functions as proposed by Prop2. A<br />type class type check can be done by this short example: <verbatim>if (mySignal'type_class = tc_array) then -- body end if;</verbatim> So for a basic reflection support in VHDL, every type should have a 'type_class attribute. An alternative implementation could be to define 5 new attributes: * T'is_scalar * T'is_array * T'is_enumeration * T'is_record * T'is_protected But this could not be reused with "Anonymous Types" (see Prop3) (=> type restrictions). I would prefer the 'type_class solution, because it does not require 5 new attributes and it<br />does not rely on spicial is_*** functions. _Why did I name it type_class?_ The LRM has alread a production rule called entity_class (§ 7.2 or page 484). I thought maybe<br />it could have a similar name => 'type_class. Martin Zabel suggested to use 'type_kind as<br />an alternative. Thinking over it, it could also be 'type'kind. _Other sources: Wikipedia ..._ The Wikipedia article on data types has a <a href="https://en.wikipedia.org/wiki/Data_type#Classes_of_data_types" target="_blank">heading</a> for "classes of data types", listing: * primitive * composite * record * array * enumerated Another article on <a href="https://en.wikipedia.org/wiki/Kind_(type_theory)" target="_blank">Kind in type theory</a>. => "Kind is the type of a (data) type". *How can we check the type?* Porp2 suggest an atrribute to get the type of an instance which is compared to a type_mark. <verbatim>if (my_sig'subtype'base = std_logic_vector) then -- ... end if;</verbatim> I like this idea, but I see two possible problems: 1 An attribute returns a type_mark. Currently VHDL has no capability to store such an<br />object in a variable or whatever. 1 In a regular rexpression, two type_marks are compared. Currently VHDL does not<br />support to have type_marks in expressions. Let's look to other languages: Some languges represent types as instances of Type (class).<br />Others have special operator to compare types => is, typeof, instanceof, ... and then there<br />are languages with built-in special functions like isinstanceof(X, std_logic). Personally, I<br />don't like the function based aproach, moreover it implies that type_marks can be passed<br />as parameters to functions ... I see these solutions: 1 Solution<br />A X'type attribute combinded with a new operator pair (is / is not) to compare types.<br />All needed keywords are already reserved. It does not touch common expressions.<br /><br /> 1 Solution<br />A X'instanceof(std_logic) attribute, which expects a type_mark as parameter and returns<br />a boolean.<br /><br /> 1 Solution<br />A built-in function to get the type gettype(X).<br /><br /> 1 Solution<br />Another built-in function isinstanceof(X, std_logic) I would prefer the first solution, because I think it's more VHDL like. Example: <verbatim>if (X'type is std_logic) then -- ... end if;</verbatim> Getting a type_mark from an attribute is a needed capability to do other usefull<br />stuf. E.g. if an intermediate signal is needed in a loop. *What attributes are needed?* Prop1 lists the following attributes: * R'ELEMENTS returns the implicit enumerated type E. * R'LEFT is the leftmost element of record R (or implicit enumerated type E). * R'RIGHT is the rightmost element of record R (or implicit enumerated type E). * R'HIGH is the highest element of record R (or implicit enumerated type E). * R'LOW is the lowest element of record R (or implicit enumerated type E). * R'RANGE is the range R'LEFT to R'RIGHT or R'LEFT downto R'RIGHT . * R'REVERSE_RANGE is the range of R with to and downto reversed. * R'LENGTH is the integer value of the number of elements in record R (or implicit enumerated type E). I suggest to replace 'elements by 'members. The count of record members can be retrieved by R'members'count (see Prop4).<br />Moreover, R could offer 'count by itself (this is a more orthogonal implementation). From my previous paragraphs: * 'type_class returns the class/kind of a type: scalar, array, record, enumerated, protected * 'type returns the type_mark of a type * needed to create local intermediate objects and * needed to access the atributes of a type *How to access record members?* Prop1 uses the dot notation for signal.index, wherin member is a enumerated type<br />member used to access the record member. I think this could be a big change in the language, that an enum member is allowed<br />to access a record member. Arrays already have such a feature, but the enum member<br />is used as an index: signal[index]. 1 Solution:<br /><br />Allow enum members after the dot to access a record member: <verbatim>variable index : myRecordSignal'type'members; -- ... index := Valid; -- a literal is assigned valid <= myRecordSignal.index; -- the record is access by the literal stored in index using the dot-notation</verbatim> 1 Solution:<br /><br />Add array index syntax to records:<br /> <verbatim>variable index : myRecordSignal'type'members; -- ... index := Valid; -- a literal is assigned valid <= myRecordSignal(index); -- the record is access by the literal stored in the index using the array element notation</verbatim> 1 Solution:<br /><br />Introduce a new attribute 'field (better name?; val/value):<br /> <verbatim>variable index : myRecordSignal'type'members; -- ... index := Valid; -- a literal is assigned valid <= myRecordSignal'field(index); -- the record is access by the literal stored in the index using a attribute notation</verbatim> Other names for 'field could be 'value, ... ?? The third solution does not need any kind of dynamic typing or ananymous types. _I suggest to use 'field(enum_member) for the pseudo code in our use cases. It's explicit<br />and can not be confused with existing syntax. When the syntax is going to be written into<br />the LRM, we can discuss it again. It's also possible to see if it's intuitive to use it or solution<br />1 and 2 are better suited._ *Why is this idea not using dynamic things?* Let's look into for...generate loops: <verbatim>entity e is port ( -- ... DataIn : array(7 downto 0) of std_logic_vector(7 downto 0); Valid : std_logic_vector(7 downto 0) ); end entity; architecture a of e is begin gen : for i in 0 to 7 generate signal localValid : std_logic; signal localData : std_logic_vector(7 downto 0); begin localValid <= Valid(i); localData <= DataIn(i); -- ... end generate; end architecture;</verbatim> So what's (propably) going on in the compiler? * The range for the generate is known (as for our implicit enum). * The compiler generates an array of range 0 to 7 for each local signal * The local signal is implicitely access by i in every iteration with a (propably) "anonymous typed pointer/reference"<br /><i>Yes, it's anonymous in the compiler implementation, but not required for VHDL. So a compiler might internally use</i><br /><i>something like "Dim current As Object", but VHDL does not need something like "variable index : anonymous".</i><br /><i>I don't refuse the "Anonymous Types" proposal, I'm just say it might not be needed to implement introspection.</i> * i is of type integer and 0 to 7 is an iterable (the the Python name for things that can be used in for loops) Let's look into iterating over record members: <verbatim>entity myEntity is port ( -- ... DataIn : T_MyRecord ); end entity; architecture arch of myEntity is function length(myRec : T_MyRecord) return integer is variable result : integer; begin result := 0; for i in T_MyRecord'members loop if (T_MyRecord'type_class(i) = scalar) then result = result + 1; elsif (T_MyRecord'type_class(i) = array) then result = result + T_MyRecord'field(i)'length; else report "Member not supported." severity error; end if; end loop; return result; end function; function length_until(myRec : T_MyRecord; pos : T_MyRecord'members) return integer is variable result : integer; begin result := 0; for i in T_MyRecord'low to pos'pred loop if (T_MyRecord'type_class(i) = scalar) then result = result + 1; elsif (T_MyRecord'type_class(i) = array) then result = result + T_MyRecord'field(i)'length; else report "Member not supported." severity error; end if; end loop; return result; end function; constant DataInLength : integer := length(DataIn); signal flattenDataIn : std_logic_vector(DataInLength - 1 downto 0); begin genLoop : for i in DataIn'type'members generate begin genScalar : if DataIn'field(i)'type_class = scalar) generate flattenDataIn(length_until(DataIn, i)) <= DataIn'field(i); elsif DataIn'field(i)'type_class = array) generate flattenDataIn(length_until(DataIn, i'succ)-1 downto length_until(DataIn, i)) <= DataIn'field(i); end generate; end generate; end architecture;</verbatim> What does the compiler know? * All members are known at compile time. * The bounds of the loop are static. * The types of the members are known. So iterating over record members is like in traditional for generate loops. The compiler<br />just needs to update an internal implicit pointer to the current member. So again, a compiler<br />can use some kind of anonymous pointer, but it does not require an anonymous typed iterator<br />in VHDL. *Going further:* _subtyping_ Prop2 introduces the idea of subtyping records. This is the reverse direction compared to<br />inheritance. Instead of adding new members to a class after inherting a set of members from<br />the ancestor, it gives the idea of reducing the amount of members like std_logic can be striped<br />to U01. I'm not quite sure about it, but I have projects that feed configuration settings as generic<br />parameter down the instance hierachy. In my case not all information are needed at the leafs<br />instances. So it could be good to simplify the record from layer to layer. I see the folowing problem: * How to restrict the members. Regarding the enumerated type subtyping mechanism, its<br />only possible to redurce members at the head and/or tail of the list. Does anyone like the idea of extending/inheriting enumerated types and records?<br />Please contact me ! ;) _Applying the concept to entities/architetures_ Introspection for entities/architectures. Cuurently there is no way to instantiate architectures<br />by a string parameter or iterate over all architectures of a entity to test them all in the same<br />testbench (only useful for alternative implementations with similar input/output behavior). So reusing the 'members idea, it's possible to have a E'architectures attribute. Example 1 - test all implementations simultanious in the same testbench: <verbatim>-- The DUT entity myEntity is port ( -- ... ); end entity; architecture arch1 of myEntity is begin end architecture; architecture arch2 of myEntity is begin end architecture; -- The testbench entity test end entity; architecture tb of test is begin genArchs : for index in myEntity'architectures generate arch : entity work.myEntity(index) port map ( -- ... ); end generate; end architecture;</verbatim> Example 2 - Instantiate by a generic parameter. Our PoC library has the goal if vendor indepency. For that purpose, we have several wrappers,<br />which select different implementations by a global constant called VENDOR or if necessary a FPGA<br />specific design by testing DEVICE, FAMILIY, SUBTYPE or even LUT_FAN_IN ... For example if the module <a href="https://github.com/VLSI-EDA/PoC/blob/master/src/io/ddrio/ddrio_out.vhdl" target="_blank">PoC.io.ddrio.out</a> could use this feature it could look like this: <verbatim>entity ddrio_out is generic ( NO_OUTPUT_ENABLE : BOOLEAN := false; BITS : POSITIVE; INIT_VALUE : BIT_VECTOR := x"FFFFFFFF" ); port ( Clock : in STD_LOGIC; ClockEnable : in STD_LOGIC := '1'; OutputEnable : in STD_LOGIC := '1'; DataOut_high : in STD_LOGIC_VECTOR(BITS - 1 downto 0); DataOut_low : in STD_LOGIC_VECTOR(BITS - 1 downto 0); Pad : out STD_LOGIC_VECTOR(BITS - 1 downto 0) ); end entity; architecture rtl of ddrio_out is -- possible alias to shorten the code alias archs is ddrio_out_vendor'architecture; constant index : ddrio_out_vendor'architecture := ddrio_out_vendor'architecture'value(T_VENDOR'image(VENDOR)); begin assert (VENDOR = VENDOR_XILINX) or (VENDOR = VENDOR_ALTERA) report "PoC.io.ddrio.out is not implemented for given DEVICE." severity FAILURE; inst : ddrio_out_vendor(ddrio_out_vendor'architecture'value(T_VENDOR'image(VENDOR))) generic map ( NO_OUTPUT_ENABLE => NO_OUTPUT_ENABLE, BITS => BITS, INIT_VALUE => INIT_VALUE ) port map ( Clock => Clock, ClockEnable => ClockEnable, OutputEnable => OutputEnable, DataOut_high => DataOut_high, DataOut_low => DataOut_low, Pad => Pad ); end generate; end architecture;</verbatim> So this is a full feature reusage :). *Use Cases:* Here is a list of use cases: * All kinds of data structure flattening: * record => std_logic_vector * record => JSON * std_logic_vector => record * record => string *Further improvements:* How can it be even nicer to use this feature? * If a user could define user-defined attributes, with direct to a function/procedure * attribute serialize is alias to_json * attribute neededbits is aliad length * If there are anonymous types * *Open questions:* Should protected types be introspectable, too? Especially if they get public fields, signals<br />or events. <verbatim> </verbatim> -- %USERSIG{PatrickLehmann - 2016-03-03}% ---++ Comments <br />%COMMENT%
Edit
|
Attach
|
P
rint version
|
H
istory
:
r6
<
r5
<
r4
<
r3
<
r2
|
B
acklinks
|
V
iew topic
|
Raw edit
|
More topic actions...
Topic revision: r1 - 2016-03-04 - 22:58:58 -
TWikiGuest
Main
Log In
or
Register
Main Web
Create New Topic
Index
Search
Changes
Notifications
RSS Feed
Statistics
Preferences
Webs
Main
P1076
Ballots
LCS2016_080
P10761
P1647
P16661
P1685
P1734
P1735
P1778
P1800
P1801
Sandbox
TWiki
VIP
VerilogAMS
Copyright © 2008-2026 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki?
Send feedback