164 lines
6.6 KiB
Plaintext
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
|