-------------------------------------------------------------------------------- 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() begin 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: .next = .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