Files
PeakRDL-BusDecoder/docs/dev_notes/template-layers/4-fields

164 lines
6.6 KiB
Plaintext

--------------------------------------------------------------------------------
Field storage / next value layer
--------------------------------------------------------------------------------
Where all the magic happens!!
Any field that implements storage is defined here.
Bigass struct that only contains storage elements
Each field consists of:
- Entries in the storage element struct
- if implements storage - field value
- user extensible values?
- Entries in the combo struct
- if implements storage:
- Field's "next" value
- load-enable strobe
- If counter
various event strobes (overflow/overflow).
These are convenient to generate alongside the field next state logic
- user extensible values?
- an always_comb block:
- generates the "next value" combinational signal
- May generate other intermediate strobes?
incr/decr?
- series of if/else statements that assign the next value in the storage element
Think of this as a flat list of "next state" conditons, ranked by their precedence as follows:
- reset
Actually, handle this in the always_ff
- sw access (if sw precedence)
- onread/onwrite
- hw access
- Counter
beware of clear events and incr/decr events happening simultaneously
- next
- etc
- sw access (if hw precedence)
- onread/onwrite
- always_comb block to also generate write-enable strobes for the actual
storage element
This is better for low-power design
- an always_ff block
Implements the actual storage element
Also a tidy place to abstract the specifics of activehigh/activelow field reset
selection.
TODO:
Scour the RDL spec.
Does this "next state" precedence model hold true in all situations?
TODO:
Think about user-extensibility
Provide a mechanism for users to extend/override field behavior
TODO:
Does the endianness the user sets matter anywhere?
Implementation
Makes sense to use a listener class
Be sure to skip alias registers
--------------------------------------------------------------------------------
NextStateConditional Class
Describes a single conditional action that determines the next state of a field
Provides information to generate the following content:
if(<conditional>) begin
<assignments>
end
- is_match(self, field: FieldNode) -> bool:
Returns True if this conditional is relevant to the field. If so,
it instructs the FieldBuider that code for this conditional shall be emitted
TODO: better name than "is_match"? More like "is this relevant"
- get_predicate(self, field: FieldNode) -> str:
Returns the rendered conditional text
- get_assignments(self, field: FieldNode) -> List[str]:
Returns a list of rendered assignment strings
This will basically always be two:
<field>.next = <next value>
<field>.load_next = '1;
- get_extra_combo_signals(self, field: FieldNode) -> List[TBD]:
Some conditionals will need to set some extra signals (eg. counter underflow/overflow strobes)
Compiler needs to know to:
- declare these inthe combo struct
- initialize them in the beginning of always_comb
Return something that denotes the following information: (namedtuple?)
- signal name: str
- width: int
- default value assignment: str
Multiple NextStateConditional can declare the same extra combo signal
as long as their definitions agree
--> Assert this
FieldBuilder Class
Describes how to build fields
Contains NextStateConditional definitions
Nested inside the class namespace, define all the NextStateConditional classes
that apply
User can override definitions or add own to extend behavior
NextStateConditional objects are stored in a dictionary as follows:
_conditionals {
assignment_precedence: [
conditional_option_1,
conditional_option_2,
conditional_option_3,
]
}
add_conditional(self, conditional, assignment_precedence):
Inserts the NextStateConditional into the given assignment precedence bin
The first one added to a precedence bin is first in that bin's search order
init_conditionals(self) -> None:
Called from __init__.
loads all possible conditionals into self.conditionals list
This function is to provide a hook for the user to add their own.
Do not do fancy class introspection. Load them explicitly by name like so:
self.add_conditional(MyNextState(), AssignmentPrecedence.SW_ACCESS)
If user wants to extend this class, they can pile onto the bins of conditionals freely!
--------------------------------------------------------------------------------
Misc
--------------------------------------------------------------------------------
What about complex behaviors like a read-clear counter?
if({{software read}})
next = 0
elif({{increment}})
next = prev + 1
--> Implement this by stacking multiple NextStateConditional in the same assignment precedence.
In this case, there would be a special action on software read that would be specific to read-clear counters
this would get inserted ahead of the search order.
Precedence & Search order
There are two layers of priority I need to keep track of:
- Assignment Precedence
RTL precedence of the assignment conditional
- Search order (sp?)
Within an assignment precedence, order in which the NextStateConditional classes are
searched for a match
For assignment precedence, it makes sense to use an integer enumeration for this
since there really aren't too many precedence levels that apply here.
Space out the integer enumerations so that user can reliably insert their own actions, ie:
my_precedence = AssignmentPrecedence.SW_ACCESS + 1
For search order, provide a user API to load a NextStateConditional into
a precedence 'bin'. Pushing into a bin always inserts into the front of the search order
This makes sense since user overrides will always want to be highest priority - and
rule themselves out before falling back to builtin behavior