diff --git a/lr_automaton_gateware.py b/lr_automaton_gateware.py index 2b6c8ef2a43c648f7eeec28edd34455816445380..c70d3a160ee508a6d090953dd21a4b01fc063a3f 100644 --- a/lr_automaton_gateware.py +++ b/lr_automaton_gateware.py @@ -9,6 +9,12 @@ from skidbuffer import RegisteredSkidBuffer class ParseStack(Elaboratable): + PUSH = 0 + POP = 1 + MULTIPOP = 2 + INTROSPECT = 3 + READ_SP = 4 + READ_TOS = 5 def __init__(self, width, depth): # Parameters self.width = width @@ -34,9 +40,9 @@ class ParseStack(Elaboratable): self.strobe = Signal(1) # when 1, execute the command # Data inputs - self.in_data_port = Signal(1) + self.in_data_port = Signal(width) self.in_aux_port = Signal(range(depth)) - self.in_valid = Signal(width) + self.in_valid = Signal(1) # Control outputs self.valid_out = Signal(1) @@ -50,1140 +56,163 @@ class ParseStack(Elaboratable): self.mem = Memory(width=self.width, depth=self.depth) - def elaborate(self, platform): - - stack_pointer = Signal(range(self.depth)) - - item_in_slot_zero = Signal(1) # to disambiguate between sp=0 with nothing in 0 and sp=0 with one item pushed - - m = Module() - return m - - - -class InspectableRegister(Elaboratable): - def __init__(self, width): - self.width = width - # interface - self.write_enable = Signal(1) - self.write_port = Signal(width) #data to write - - self.read_port = Signal(width) #always-on output - - def elaborate(self, platform): - m = Module() - data = Signal(self.width) # internal register - - m.d.comb += self.read_port.eq(data) - - with m.If(self.write_enable == 1): - m.d.sync += data.eq(self.write_port) - - return m - - -class StacksInSync(Elaboratable): - def __init__(self, bigstack_item_width, sidestack_item_width, stack_depth): - - self.bigstack_item_width = bigstack_item_width - self.sidestack_item_width = sidestack_item_width - - self.depth = stack_depth - - self.command_in = Signal(2) # whether to push or pop - self.command_in_strobe = Signal(1) # start the command - - self.big_push_port = Signal(bigstack_item_width) # input data - - self.side_push_port = Signal(sidestack_item_width) # input data - - - self.bigpop_port_valid = Signal(1) # ouput data is valid - self.bigpop_port = Signal(bigstack_item_width) # output data - - self.sidepop_port_valid = Signal(1) # ouput data is valid - self.sidepop_port = Signal(sidestack_item_width) # output data - - - self.occupancy_bitmap = Signal(self.depth) - - self.internal_fault = Signal(1) - - - # read-only access to the top K entries, connected to the - # reduction-detection logic - - self.bigperm_out = Array([Signal(bigstack_item_width) for _ in range(stack_depth)]) - self.sideperm_out = Array([Signal(sidestack_item_width) for _ in range(stack_depth)]) - - def elaborate(self, platform): - m = Module() - - bigstack = InspectableStack(self.bigstack_item_width, self.depth) - sidestack = InspectableStack(self.sidestack_item_width, self.depth) - m.submodules += bigstack - m.submodules += sidestack - - m.d.comb += bigstack.command_in.eq(self.command_in) - m.d.comb += bigstack.command_in_strobe.eq(self.command_in_strobe) - m.d.comb += bigstack.push_port.eq(self.big_push_port) - m.d.comb += self.bigpop_port.eq(bigstack.pop_port) - m.d.comb += self.bigpop_port_valid.eq(bigstack.pop_port_valid) - - for idx, sig in enumerate(bigstack.perm_out): - m.d.comb += (self.bigperm_out[idx]).eq(sig) - - - m.d.comb += sidestack.command_in.eq(self.command_in) - m.d.comb += sidestack.command_in_strobe.eq(self.command_in_strobe) - m.d.comb += sidestack.push_port.eq(self.side_push_port) - m.d.comb += self.sidepop_port.eq(sidestack.pop_port) - m.d.comb += self.sidepop_port_valid.eq(sidestack.pop_port_valid) - - for idx, sig in enumerate(sidestack.perm_out): - m.d.comb += (self.sideperm_out[idx]).eq(sig) - - - - - m.d.comb += self.occupancy_bitmap.eq(bigstack.occupancy_bitmap) - - m.d.comb += self.internal_fault.eq(bigstack.fault | sidestack.fault) - - return m - -class InspectableStack(Elaboratable): - def __init__(self, item_width, stack_depth): - self.item_width = item_width - self.depth = stack_depth - -# self.command_ready = Signal(1) # output 1 if we're ready for a new command - - self.command_in = Signal(2) # whether to push or pop - self.command_in_strobe = Signal(1) # start the command - - self.push_port = Signal(item_width) # input data - - self.pop_port_valid = Signal(1) # ouput data is valid - self.pop_port = Signal(item_width) # output data - self.occupancy_bitmap = Signal(self.depth) - - - self.overfill = Signal(1) - self.underfill = Signal(1) - - self.fault = Signal(1) - - - # read-only access to the top K entries, connected to the - # reduction-detection logic - - self.perm_out = Array([Signal(item_width) for _ in range(stack_depth)]) - - - def elaborate(self, platform): - m = Module() - - # first create the stack item registers and their driving signals - reversal = Signal(1) # 1 if popping, 0 if pushing - move_em = Signal(1) # write_enable for the entire stack - - all_registers = [] - i = 0 - for z in range(self.depth): - element_z = InspectableRegister(self.item_width) - all_registers.append(element_z) - m.submodules += element_z - m.d.comb += (self.perm_out[i]).eq(element_z.read_port) - - m.d.comb += element_z.write_enable.eq(move_em) - i = i + 1 - - assert(len(all_registers) > 3) # will need to special case len 3 - head_write_port = Signal(self.item_width) - tail_write_port = Signal(self.item_width) - - - for cursor, postcursor in zip(all_registers[1:-2], all_registers[2:-1]): - with m.If(0 == reversal): - m.d.comb += postcursor.write_port.eq(cursor.read_port) - with m.Else(): - m.d.comb += cursor.write_port.eq(postcursor.read_port) - - # and now the edge cases - with m.If(0 == reversal): - # when we add a TAIL READ PORT add something here to connect it - - m.d.comb += all_registers[0].write_port.eq(head_write_port) - m.d.comb += all_registers[1].write_port.eq(all_registers[0].read_port) - - m.d.comb += all_registers[-1].write_port.eq(all_registers[-2].read_port) - - with m.Else(): - # when we add a HEAD READ PORT add something here to connect it - - m.d.comb += all_registers[0].write_port.eq(all_registers[1].read_port) - m.d.comb += all_registers[-1].write_port.eq(tail_write_port) - - m.d.comb += all_registers[-2].write_port.eq(all_registers[-1].read_port) - - # now for the command logic - - with m.If(1 == self.command_in_strobe): - with m.If(2 == self.command_in): # push - m.d.comb += head_write_port.eq(self.push_port) - m.d.comb += reversal.eq(0) - m.d.comb += move_em.eq(1) - m.d.sync += self.occupancy_bitmap.eq(Cat(1,self.occupancy_bitmap[0:-1])) - m.d.comb += self.overfill.eq(self.occupancy_bitmap[-1]) - with m.Elif(1 == self.command_in): # pop - m.d.comb += self.pop_port.eq(all_registers[0].read_port) - m.d.comb += reversal.eq(1) - m.d.comb += move_em.eq(1) - m.d.sync += self.occupancy_bitmap.eq(Cat(self.occupancy_bitmap[1:],0)) - m.d.comb += self.underfill.eq(~self.occupancy_bitmap[0]) - - - m.d.comb += self.fault.eq(self.overfill | self.underfill) - - return m - - - -# HitOrMiss is the current rule lookup engine. -# -# It is fully combinatorial; every cycle it ingests the current state -# of the stack and the new item, and determines: -# -# 1. Valid Item: Whether the new item can be immediately rejected. -# This corresponds to a blank space in an LR parser table -# -# 2. Force Shift: Whether the new item must be shifted onto the stack -# *without* looking at possible reduction rules to apply to the current -# state of the stack. -# -# 3. Reduce Select: Which reduction rule applies to the current stack. -# -# Note that this list is in decreasing priority -- if the item is invalid, -# the results of Force Shift or Reduce Select are immaterial. Similarly, if -# Force Shift triggers, we shall ignore the results of Reduce Select. -# -# Valid Item allows to, well, immediately reject invalid data and avoid -# getting the stack in an invalid state. Force Shift allows the resolving of -# Shift/Reduce conflicts -- otherwise, the parser would immediately reduce -# everything it sees, as far as possible. Reduce Select resolves Reduce/Reduce -# conflicts. -# -# When generating HDL, HitOrMiss must be provided a rulepack. The rulepack is -# composed of several rulesets: -# -# 1. Stack State Descriptions -# 2. Disambiguation Pairwise Priority -# 3. Valid Item Ruleset -# 4. Force Shift Ruleset -# 5. Reduce Ruleset -# -# Stack State Descriptions contains descriptions of stack states. -# A stack state is described by a list of "stack conditions". A stack state -# match holds when every stack condition is valid. -# -# Currently, each each "stack condition" and each "new item" condition -# is a tuple composed of: (index of stack, tag mask, tag value), but later -# we will add provisions for more flexible matching predicates; -# for example, range matching. -# -# Example: if our rule is [(0, 0xff00, 0x4200), (1, 0xf000, 0x6000), -# it will match a stack iff the top item of the stack has a tag A and -# the item below it has a tag B such that: -# (A & (0xff00)) == 0x4200 -# (B & (0xf000)) == 0x6000 -# -# The mask value allows us to store data inside the stack items that does -# not affect the grammar's rules. This semantic data -- which may be -# raw literals or indices into an external RAM are known as "data tags". -# -# The same (tag mask, tag value) scheme is used to describe the New Item. -# -# The Pairwise Priority mechanism works as follows: if the current stack state -# matches multiple stack state descriptions, the list of -# (inhibited rule, dominator rule) pairs determines pairwise priority amongst -# rules -- and the winning rule is the one selected for application. This is -# to handle reduce/reduce conflicts if there is a rule whose stack state is -# a subset of an other -- in order to allow the more specific rule to match. - - - - - -# Once we have determined which stack state description corresponds to the -# current state of the stack, we know which row of the traditional LR table -# is relevant. The current stack state (a one hot signal) acts as an index -# into the three following tables: -# -# The Valid Item Ruleset is composed of a list of rules, one per stack state. -# Each rule has the form: {list of all new items that can be accepted}. -# -# The Force Shift Ruleset is composed of a list of rules, one per stack state. -# Each rule has the form: {list of all new items that must be shifted}. -# -# The Reduce Ruleset is composed of a list of rules, one per stack state. -# Each rule is a list of tuples {(new item A, rule number n), -# (new item B, rule number m), ...}. -# -# -# -# We therefore have a "master" ruleset which is indexed by stack state and -# contains the following information: -# 1) Which new items can be accepted? -# 2) If an item can be accepted, should it be pushed or should we consult the reduce rules? -# 3) If we should consult the reduce rules (WITHOUT pushing the new item), -# what reduction rules apply. -# - -class HitOrMiss(Elaboratable): - def __init__(self, item_width, stack_depth, reduction_rule_count, stack_state_descriptions, pairwise_priority_ruleset, validitem_ruleset, forceshift_ruleset, reduce_ruleset, endofparse_marker): - # Parameters - self.item_width = item_width - self.stack_depth = stack_depth - self.reduction_rule_count = reduction_rule_count - # Rulepack - self.stack_state_descriptions = stack_state_descriptions - self.pairwise_priority_ruleset = pairwise_priority_ruleset - - self.validitem_ruleset = validitem_ruleset - self.forceshift_ruleset = forceshift_ruleset - self.reduce_ruleset = reduce_ruleset - - self.endofparse_marker = endofparse_marker - - assert(len(stack_state_descriptions) == len(validitem_ruleset)) # blank spaces in LR table - assert(len(stack_state_descriptions) == len(forceshift_ruleset)) # shift entries in LR table - assert(len(stack_state_descriptions) == len(reduce_ruleset)) # reduce entries in LR table - - # Input signals - self.new_item_in = Signal(item_width) - self.stack_view_in = Array([Signal(item_width) for _ in range(stack_depth)]) - self.occupancy_bitmap_in = Signal(stack_depth) - - # Output signals - self.internal_fault = Signal(1) - - self.endofparse_reached = Signal(1) - - self.invalid_item = Signal(1) - self.force_shift = Signal(1) - self.match_index_out = Signal(reduction_rule_count) - - - def elaborate(self, platform): - m = Module() - - - # Unified stack state matching (with pairwise priority mechanism to resolve subsetting) - - # Normally each stack state should be matched by only one possible stack state description, but it - # is possible that one stack state description is a subset of an other such that both stack state - # descriptions are triggered for a current stack. Think, with top of stack at idx 0, - # (A, B, C) and (A, B, C, D, E). A stack that looks like (A, B, C, D, E) will show both stack state - # descriptions as matching, but we want to only trigger the latter rule. This is an artefact of the - # fact that we are ignoring the unique numbering of stack states that a more conventional implementation - # of LR parser would use. - # - # This disambiguation is carried out by an ancillary list of pairwise priority values. This list of - # (inhibited rule number, dominating rule number) is generated by the software that processes a - # language description into LR parser tables -- not by this software nor by the RTL in real-time. - - - stack_state_bitmap = [] - for stack_state in self.stack_state_descriptions: - stack_as_described = Signal(1) - stack_predicates = [] - - for stack_predicate in stack_state: - (stack_index, mask, value) = stack_predicate - stack_predicate_status = Signal(1) - m.d.comb += stack_predicate_status.eq(((self.stack_view_in[stack_index] & mask) == value) & self.occupancy_bitmap_in[stack_index]) - stack_predicates.append(stack_predicate_status) - - m.d.comb += stack_as_described.eq(reduce(lambda x, y: x & y, stack_predicates)) - stack_state_bitmap.append(stack_as_described) - - # Now we apply the Disambiguation Pairwise Priorities - for priority_pair in self.pairwise_priority_ruleset: - (inhibited_rule_number, dominator_rule_number) = priority_pair - dom_value = stack_state_bitmap[dominator_rule_number] - inh_value = stack_state_bitmap[inhibited_rule_number] - new_inh = Signal(1) - m.d.comb += new_inh.eq(inh_value & (~dom_value)) - stack_state_bitmap[inhibited_rule_number] = new_inh - - stack_state_onehot = Signal(len(self.stack_state_descriptions)) - stack_state_onehot = Cat(stack_state_bitmap) - - # verify it's one-hot - one_hot = Signal(1) - m.d.comb += one_hot.eq(0 == (stack_state_onehot & (stack_state_onehot - 1))) - - - - - # we've identified which stack state description corresponds to the current state of the stack, - # aka, which row of the LR table we are on. Now we will identify which outcome applies for the - # combination of the current stack state and also the new item. - all_new_item_predicates = [] - all_shift_predicates = [] - - reduce_rule_bitmap = [] - - # Random-access (at synth time) bitmap without combinatorial loop issues - for i in range(self.reduction_rule_count): - reduce_rules_bitmap_component = Signal(1) - reduce_rule_bitmap.append(reduce_rules_bitmap_component) - - with m.If(one_hot == 1): # only do this if the signal is one-hot - validitem_result = Signal(1) - forceshift_result = Signal(1) - for rule_idx in range(len(self.stack_state_descriptions)): - with m.If(stack_state_onehot[rule_idx] == 1): - validitem_rule = self.validitem_ruleset[rule_idx] - forceshift_rule = self.forceshift_ruleset[rule_idx] - reduce_rule = self.reduce_ruleset[rule_idx] - - new_item_in_acceptset = Signal(1) - new_item_predicates = [] - - for candidate in validitem_rule: - (mask, value) = candidate - new_item_predicate_status = Signal(1) - m.d.comb += new_item_predicate_status.eq(((self.new_item_in & mask) == value)) - new_item_predicates.append(new_item_predicate_status) - m.d.comb += new_item_in_acceptset.eq(reduce(lambda x, y: x | y, new_item_predicates)) - - - new_item_in_shiftset = Signal(1) - shift_item_predicates = [] - if (forceshift_rule == []): - m.d.comb += new_item_in_shiftset.eq(0) - else: - for candidate in forceshift_rule: - (mask, value) = candidate - shift_item_predicate_status = Signal(1) - m.d.comb += shift_item_predicate_status.eq(((self.new_item_in & mask) == value)) - shift_item_predicates.append(shift_item_predicate_status) - m.d.comb += new_item_in_shiftset.eq(reduce(lambda x, y: x | y, shift_item_predicates)) - - for candidate in reduce_rule: - reduce_item_predicate_status = Signal(1) - (newitem, reduce_rule_number) = candidate - (mask, value) = newitem - m.d.comb += reduce_item_predicate_status.eq(((self.new_item_in & mask) == value)) - with m.If(reduce_item_predicate_status == 1): - m.d.comb += reduce_rule_bitmap[reduce_rule_number].eq(1) - - all_new_item_predicates.append(new_item_in_acceptset) - all_shift_predicates.append(new_item_in_shiftset) - - - m.d.comb += validitem_result.eq(reduce(lambda x, y: x | y, all_new_item_predicates)) - m.d.comb += forceshift_result.eq(reduce(lambda x, y: x | y, all_shift_predicates)) - - # We now have: - # 1) a single-bit signal whether the new item is a valid new item or not - # 2) a single-bit signal whether the new item must be shifted - # 3) a one-hot signal that tells us which reduce rule (if any) matched - - # and we must output three signals: match_index_out force_shift invalid_item - - with m.If(validitem_result == 0): # Invalid item, raise error - m.d.comb += self.invalid_item.eq(1) - m.d.comb += self.force_shift.eq(0) - m.d.comb += self.match_index_out.eq(0) - - with m.If(validitem_result == 1): # Valid item - m.d.comb += self.invalid_item.eq(0) - - with m.If(forceshift_result == 1): # Shift - m.d.comb += self.force_shift.eq(1) - m.d.comb += self.match_index_out.eq(0) - - with m.If(forceshift_result == 0): # Reduce - m.d.comb += self.force_shift.eq(0) - matching_reduce_rules_onehot = Signal(self.reduction_rule_count) - m.d.comb += matching_reduce_rules_onehot.eq(Cat(reduce_rule_bitmap)) - - m.d.comb += self.internal_fault.eq(0 != (matching_reduce_rules_onehot & (matching_reduce_rules_onehot - 1))) - - with m.If(self.internal_fault == 0): - m.d.comb += self.match_index_out.eq(matching_reduce_rules_onehot) - - - with m.If(one_hot == 0): - m.d.comb += self.internal_fault.eq(1) - - (mask, value) = self.endofparse_marker - with m.If((self.stack_view_in[0] & mask) == value): - m.d.comb += self.endofparse_reached.eq(1) - - - return m - - -# Rule Execution Engine: input is a one-hot signal for the rule that got triggered, -# output is: 1) how many items we can destroy off the top of the stack and -# 2) what item (language item tag, data tag) we replace it with -# -# The execution ruleset is composed of a list of execution rules. Each execution -# rule has: -# 1) An implicit index number (which must match the implicit rule number in the Matcher ruleset) -# 2) A number of items to pop -# 3) The language tag of the item to push. -# -# Later, there will be a way to specify what computation is done with the data tags -# of the popped items -- and what data tag is affixed to the pushed item. - -# Also we have a view to the stack so we can extract the data tags of the popped items -# and do computation on them with a lambda that is fed to us in the execution ruleset - - -class RuleExecutor(Elaboratable): - def __init__(self, item_width, stack_depth, execution_ruleset): - # Parameters - self.execution_ruleset = execution_ruleset - self.item_width = item_width - self.stack_depth = stack_depth - - self.match_index_in = Signal(len(execution_ruleset)) - - self.stack_view_in = Array([Signal(item_width) for _ in range(stack_depth)]) - - self.number_to_pop = Signal(range(stack_depth)) - self.created_item = Signal(item_width) - def elaborate(self, platform): m = Module() - for rule_idx, rule in enumerate(self.execution_ruleset[::-1]): - (items_to_pop, new_item_func) = rule + m.submodules.rport = rport = (self.mem).read_port() + m.submodules.wport = wport = (self.mem).write_port() + stack_pointer = Signal(range(self.depth), reset=0) - with m.If(self.match_index_in[len(self.execution_ruleset) - rule_idx -1] == 1): - m.d.comb += self.number_to_pop.eq(items_to_pop) - m.d.comb += self.created_item.eq(new_item_func(self.stack_view_in)) - return m -# We create a serialized parse tree from the bottom up, with + # To represent the state of a k-item stack, we need an additional bit of state to + # disambiguate the case where there are zero items on the stack and the case where + # there is a single item pushed on the stack. -class TreeSerializer(Elaboratable): - def __init__(self, item_width, indices_width, stack_depth, serialized_tree_length, serializing_ruleset): - # Parameters - self.item_width = item_width - self.stack_depth = stack_depth - self.indices_width = indices_width - self.serialized_tree_length = serialized_tree_length - self.mem_address_width = indices_width - self.serializing_ruleset = serializing_ruleset - self.serialized_tree_length = serialized_tree_length - - # inputs - - # Activated only once per reduction - self.start_reduction = Signal(1) - # When ^ goes high, the three below are registered - - self.number_to_pop = Signal(range(stack_depth)) - self.reduce_rule_number = Signal(item_width) - self.item_created_by_reduce_rule = Signal(item_width) - - # Varies with every popped item - self.destroyed_item_valid_in = Signal(1) - self.destroyed_item_in = Signal(item_width) # off main stack - self.destroyed_item_index_in = Signal(indices_width) # off side stack + # bottom_of_stack_valid = 0 implies zero items on the stack (which means s_p must be 0), and + # popping generates an error - # outputs - # output to state machine - self.ready_out = Signal(1) - self.internal_fault = Signal(1) + # bottom_of_stack_valid = 1 implies that there is at least one item on the stack, and s_p can take + # any value. - self.serialized_index = Signal(indices_width) # push *this* onto side stack for the - # newly created item - # interface with serialization memory - self.memory_write_port = Signal(item_width + 1) - self.memory_address_port = Signal(self.mem_address_width) - self.memory_write_enable = Signal(1) + # 0 items on stack (bottom_of_stack_valid = 0, stack_pointer = 0) + # PUSH + # 1 item on stack (bottom_of_stack_valid = 1, stack_pointer = 0) + # PUSH + # 2 items on stack (bottom_of_stack_valid = 1, stack_pointer = 1) + # PUSH + # 3 items on stack (bottom_of_stack_valid = 1, stack_pointer = 2) + # and for popping, it proceeds in reverse. - self.mem = Memory(width=(self.item_width + 1), depth=serialized_tree_length) - - def elaborate(self, platform): - m = Module() - - start_of_record = Signal(self.mem_address_width) # start of next/yet-unwritten node record, advanced only - # after each reduce + bottom_of_stack_valid = Signal(1) - m.submodules.parse_tree = wport = (self.mem).write_port() - - - - m.d.comb += wport.en.eq(self.memory_write_enable), - m.d.comb += wport.addr.eq(self.memory_address_port), - m.d.comb += wport.data.eq(self.memory_write_port) - - - # Per-reduce registered signals: - number_of_children = Signal(range(self.stack_depth)) - - reduce_rule_number = Signal(self.item_width) - item_created_by_reduce_rule = Signal(self.item_width) - - # incremented each cycle - number_written = Signal(range(self.stack_depth)) - m.d.comb += self.serialized_index.eq(start_of_record) + multipop_left = Signal(range(self.depth)) with m.FSM() as fsm: - with m.State("INITIALIZE"): - m.d.comb += self.ready_out.eq(0) - m.d.comb += self.internal_fault.eq(0) - - m.d.sync += start_of_record.eq(0) - m.d.sync += number_written.eq(0) - - m.next="NODE" - - - with m.State("NODE"): - m.d.comb += self.ready_out.eq(1) - m.d.comb += self.internal_fault.eq(0) - - #m.d.sync += reduce_rule_number.eq(self.reduce_rule_number) - m.d.sync += item_created_by_reduce_rule.eq(self.item_created_by_reduce_rule) - - with m.If(self.start_reduction == 1): - with m.If(self.destroyed_item_index_in == 0): - m.d.comb += self.memory_write_port.eq(self.destroyed_item_in) - with m.Else(): - m.d.comb += self.memory_write_port.eq(self.destroyed_item_index_in) - m.d.comb += self.memory_address_port.eq(start_of_record + 2) - m.d.comb += self.memory_write_enable.eq(1) - - m.d.sync += number_of_children.eq(self.number_to_pop) - m.d.sync += number_written.eq(1) - - m.next = "SUBNODES" - - with m.If(self.memory_address_port > (self.serialized_tree_length - 1)): - m.next = "ABORT" - - with m.State("SUBNODES"): - m.d.comb += self.ready_out.eq(0) - m.d.comb += self.internal_fault.eq(0) - - with m.If(self.destroyed_item_valid_in == 1): - with m.If(self.destroyed_item_index_in == 0): - m.d.comb += self.memory_write_port.eq(self.destroyed_item_in) - with m.Else(): - m.d.comb += self.memory_write_port.eq(self.destroyed_item_index_in) - m.d.comb += self.memory_address_port.eq(start_of_record + 2 + number_written) - m.d.comb += self.memory_write_enable.eq(1) - - m.d.sync += number_written.eq(number_written + 1) - - - with m.If(number_written == number_of_children): - m.d.comb += self.memory_write_port.eq(item_created_by_reduce_rule) - m.d.comb += self.memory_address_port.eq(start_of_record) - m.d.comb += self.memory_write_enable.eq(1) - - m.next = "FIXUP" - - with m.If(self.memory_address_port > (self.serialized_tree_length - 1)): - m.next = "ABORT" - - with m.State("FIXUP"): - m.d.comb += self.ready_out.eq(0) - m.d.comb += self.internal_fault.eq(0) - - m.d.comb += self.memory_write_port.eq(number_of_children) - m.d.comb += self.memory_address_port.eq(start_of_record + 1) - m.d.comb += self.memory_write_enable.eq(1) - m.d.sync += start_of_record.eq(start_of_record + 2 + number_of_children) - - m.next = "NODE" - - with m.If(self.memory_address_port > (self.serialized_tree_length - 1)): - m.next = "ABORT" - - with m.State("ABORT"): - m.d.comb += self.ready_out.eq(0) + with m.State("EMPTY"): + with m.If(self.strobe == 1): + with m.Switch(self.command_port): + with m.Case(self.PUSH): + with m.If(self.in_valid == 1): + m.d.comb += wport.addr.eq(stack_pointer) + m.d.comb += wport.data.eq(self.in_data_port) + m.d.comb += wport.en.eq(1) + m.next = "AT_LEAST_ONE_ITEM" + with m.Case(self.POP): + m.d.comb += self.internal_fault.eq(1) + m.next = "FAULTED" + with m.Case(self.MULTIPOP): + m.d.comb += self.internal_fault.eq(1) + m.next = "FAULTED" + with m.Case(self.INTROSPECT): + m.d.comb += self.internal_fault.eq(1) + m.next = "FAULTED" + with m.Case(self.READ_TOS): + m.d.comb += self.internal_fault.eq(1) + m.next = "FAULTED" + with m.Case(self.READ_SP): + m.d.comb += self.internal_fault.eq(1) + m.next = "FAULTED" + + with m.State("AT_LEAST_ONE_ITEM"): + with m.If(self.strobe == 1): + with m.Switch(self.command_port): + with m.Case(self.PUSH): + with m.If(self.in_valid == 1): + m.d.sync += stack_pointer.eq(stack_pointer + 1) + m.d.comb += wport.addr.eq(stack_pointer + 1) + m.d.comb += wport.data.eq(self.in_data_port) + m.d.comb += wport.en.eq(1) + m.next = "AT_LEAST_ONE_ITEM" + with m.Case(self.POP): + m.d.comb += rport.addr.eq(stack_pointer) + m.d.comb += self.out_data_port.eq(rport.data) + + with m.If(stack_pointer == 0): + m.next = "EMPTY" + with m.Else(): + m.next = "AT_LEAST_ONE_ITEM" + m.d.sync += stack_pointer.eq(stack_pointer - 1) + with m.Case(self.MULTIPOP): + m.d.comb += self.internal_fault.eq(1) + m.next = "FAULTED" + with m.Case(self.INTROSPECT): + m.d.comb += self.internal_fault.eq(1) + m.next = "FAULTED" + with m.Case(self.READ_TOS): + m.d.comb += self.internal_fault.eq(1) + m.next = "FAULTED" + with m.Case(self.READ_SP): + m.d.comb += self.internal_fault.eq(1) + m.next = "FAULTED" + with m.State("FAULTED"): m.d.comb += self.internal_fault.eq(1) + m.next = "FAULTED" - return m - -# This is the master state machine -- it interfaces with the outside -# and feeds data to the above subcomponents as appropriate. For -# multi-cycle operations (popping multiple items off the InspectableStack) -# it stalls the input. - -class MasterStateMachine(Elaboratable): - def __init__(self, item_width, indices_width, stack_depth, serialized_tree_length, stack_state_descriptions, validitem_ruleset, forceshift_ruleset, pairwise_priority_ruleset, reduce_ruleset, execute_rules, startofparse_marker, endofparse_marker): - self.item_width = item_width - self.stack_depth = stack_depth - self.indices_width = indices_width - - # Rule pack - self.validitem_ruleset = validitem_ruleset - self.forceshift_ruleset = forceshift_ruleset - self.pairwise_priority_ruleset = pairwise_priority_ruleset - self.reduce_ruleset = reduce_ruleset - self.execute_rules = execute_rules - self.stack_state_descriptions = stack_state_descriptions - - self.endofparse_marker = endofparse_marker - self.startofparse_marker = startofparse_marker - - # Data stream in - self.data_in = Signal(item_width) - self.data_in_valid = Signal(1) - self.data_in_ready = Signal(1) - - # Data stream out - #self.data_out = Signal(item_width) - #self.data_out_valid = Signal(1) - #self.data_out_ready = Signal(1) - - # Sideband signals - self.parse_complete_out = Signal(1) - self.parse_success_out = Signal(1) - self.internal_fault = Signal(1) - self.last_index_to_smem = Signal(32) - self.serializer = TreeSerializer(item_width=self.item_width, indices_width=(self.indices_width+1), stack_depth=self.stack_depth, serialized_tree_length=serialized_tree_length, serializing_ruleset=[]) - self.tapir = self.serializer.mem + - def elaborate(self, platform): - m = Module() - #stack = InspectableStack(item_width=self.item_width, stack_depth=self.stack_depth) - doublestacks = StacksInSync(bigstack_item_width=self.item_width, sidestack_item_width=self.indices_width + 1, stack_depth = self.stack_depth) - - rule_matcher = HitOrMiss(item_width=self.item_width, stack_depth=self.stack_depth, - validitem_ruleset = self.validitem_ruleset, - forceshift_ruleset = self.forceshift_ruleset, - pairwise_priority_ruleset = self.pairwise_priority_ruleset, - reduce_ruleset = self.reduce_ruleset, - reduction_rule_count=len(self.execute_rules), stack_state_descriptions = self.stack_state_descriptions, endofparse_marker = self.endofparse_marker) - rex = RuleExecutor(item_width=self.item_width, stack_depth=self.stack_depth, execution_ruleset=self.execute_rules) - skbuffer = RegisteredSkidBuffer(width = self.item_width) -# serializer = TreeSerializer(item_width=self.item_width, indices_width=(self.indices_width+1), stack_depth=self.stack_depth, serializing_ruleset=[]) - #m.submodules.Stack = stack - m.submodules.Stacks = doublestacks - m.submodules.RuleMatcher = rule_matcher - m.submodules.RuleExecute = rex - m.submodules.skidbuffer = skbuffer - - serializer = m.submodules.Serializer = self.serializer - - - # Skid buffer - fsm_ready = Signal(1) - new_item_valid = Signal(1) - new_item = Signal(self.item_width) - - - m.d.comb += skbuffer.upstream_valid_in.eq(self.data_in_valid) - m.d.comb += self.data_in_ready.eq(skbuffer.upstream_ready_out) - m.d.comb += skbuffer.upstream_data_in.eq(self.data_in) - - m.d.comb += skbuffer.downstream_ready_in.eq(fsm_ready) - m.d.comb += new_item.eq(skbuffer.downstream_data_out) - m.d.comb += new_item_valid.eq(skbuffer.downstream_valid_out) - - - execution_result = Signal(self.item_width) - number_to_pop = Signal(8) - - - m.d.comb += rex.match_index_in.eq(rule_matcher.match_index_out) - m.d.comb += rule_matcher.occupancy_bitmap_in.eq(doublestacks.occupancy_bitmap) - for idx, x in enumerate(doublestacks.bigperm_out): - m.d.comb += rule_matcher.stack_view_in[idx].eq(x) - m.d.comb += rex.stack_view_in[idx].eq(x) - - m.d.comb += rule_matcher.new_item_in.eq(new_item) - - - # Rule Matcher output signals are: - # - # internal_fault (one bit) - # invalid_item (one bit) - # force_shift (one bit) - # match_index_out (one hot) - - - # LR parser state machine - with m.FSM() as fsm: - - with m.State("INITIALIZE"): - m.next = "SHIFTREDUCE" - m.d.comb += fsm_ready.eq(0) - m.d.comb += doublestacks.command_in_strobe.eq(1) - m.d.comb += doublestacks.big_push_port.eq(self.startofparse_marker) - m.d.comb += doublestacks.side_push_port.eq(0x0) # XXX FIXME - m.d.comb += doublestacks.command_in.eq(2) - - with m.If(doublestacks.internal_fault | serializer.internal_fault == 1): - m.next="ABORT" - - - with m.State("SHIFTREDUCE"): - with m.If(new_item_valid == 1): - with m.If(rule_matcher.internal_fault == 1): - m.next = "ABORT" - m.d.comb += fsm_ready.eq(0) - m.d.comb += doublestacks.command_in_strobe.eq(0) - - with m.If(doublestacks.internal_fault | serializer.internal_fault == 1): - m.next="ABORT" - - with m.If(rule_matcher.invalid_item == 1): - m.next = "ABORT" - m.d.comb += fsm_ready.eq(0) - m.d.comb += doublestacks.command_in_strobe.eq(0) - - with m.If(rule_matcher.force_shift == 1): - m.d.comb += doublestacks.command_in.eq(2) - m.d.comb += doublestacks.command_in_strobe.eq(1) - m.d.comb += doublestacks.big_push_port.eq(new_item) - m.d.comb += doublestacks.side_push_port.eq(0x0) # XXX FIXME - m.d.comb += fsm_ready.eq(1) - - with m.If((rule_matcher.force_shift == 0) & (rule_matcher.match_index_out == 0)): - m.next = "ABORT" - m.d.comb += fsm_ready.eq(0) - m.d.comb += doublestacks.command_in_strobe.eq(0) - - with m.If((serializer.ready_out == 1)& (rule_matcher.match_index_out != 0)): - m.next = "REDUCE" - m.d.comb += fsm_ready.eq(0) - #m.d.comb += stack.command_in_strobe.eq(0) - # register the data from the combinatorial CAMs since the stack will change when we manipulate it - - m.d.sync += number_to_pop.eq(rex.number_to_pop - 1) - m.d.sync += execution_result.eq(rex.created_item) - - - m.d.comb += serializer.start_reduction.eq(1) - m.d.comb += serializer.destroyed_item_valid_in.eq(1) - - m.d.comb += serializer.destroyed_item_in.eq(doublestacks.bigpop_port) - m.d.comb += serializer.destroyed_item_index_in.eq(doublestacks.sidepop_port) - - - m.d.comb += serializer.number_to_pop.eq(rex.number_to_pop) - m.d.comb += serializer.item_created_by_reduce_rule.eq(rex.created_item) - m.d.comb += serializer.reduce_rule_number.eq(rule_matcher.match_index_out) - - with m.If(rex.number_to_pop != 0): - m.d.comb += doublestacks.command_in.eq(1) - m.d.comb += doublestacks.command_in_strobe.eq(1) - - with m.If(rule_matcher.endofparse_reached == 1): - m.next = "SUCCESS" - m.d.comb += self.internal_fault.eq(1) - m.d.comb += self.parse_success_out.eq(1) - m.d.comb += self.parse_complete_out.eq(1) - - with m.State("SUCCESS"): - m.next = "SUCCESS" - m.d.comb += self.internal_fault.eq(1) - m.d.comb += self.parse_success_out.eq(1) - m.d.comb += self.parse_complete_out.eq(1) - m.d.sync += self.last_index_to_smem.eq(serializer.serialized_index) - - with m.If(doublestacks.internal_fault | serializer.internal_fault == 1): - m.next="ABORT" - - - with m.State("REDUCE"): - m.d.comb += fsm_ready.eq(0) - - # FIXME XXX make this into an FSM - with m.If(number_to_pop != 0): - - m.d.comb += serializer.destroyed_item_valid_in.eq(1) - m.d.comb += serializer.destroyed_item_in.eq(doublestacks.bigpop_port) - m.d.comb += serializer.destroyed_item_index_in.eq(doublestacks.sidepop_port) - - m.d.comb += doublestacks.command_in.eq(1) - m.d.comb += doublestacks.command_in_strobe.eq(1) - m.d.sync += number_to_pop.eq(number_to_pop - 1) - - with m.If(number_to_pop == 0): - m.d.comb += doublestacks.command_in.eq(2) - m.d.comb += doublestacks.big_push_port.eq(execution_result) - m.d.comb += doublestacks.side_push_port.eq(serializer.serialized_index | (1<<(self.indices_width))) - m.d.comb += doublestacks.command_in_strobe.eq(1) - -# m.d.comb += fsm_ready.eq(1) - - m.next = "SHIFTREDUCE" - - with m.If(doublestacks.internal_fault | serializer.internal_fault == 1): - m.next="ABORT" - - - with m.State("ABORT"): - m.d.comb += fsm_ready.eq(0) - m.d.comb += doublestacks.command_in_strobe.eq(0) - - m.d.comb += self.internal_fault.eq(1) - m.d.comb += self.parse_success_out.eq(0) - m.d.comb += self.parse_complete_out.eq(1) return m - class DummyPlug(Elaboratable): - def elaborate(self, platform): - cntr = Signal(8) - resetted = Signal(1) - - valid_data_out = Signal(1) - romem_ready = Signal(1) - data_port = Signal(16) + #def __init__(self): - BOTTOM = 0x5a00 - ENDOFPARSE = 0x5500 - - EXPRESSION = 0xEE00 - TERM = 0xE700 - FACTOR = 0xEF00 - INTEGER = 0xE100 - - OPENPAREN = 0xCA00 - CLOSEPAREN = 0xCB00 - - ADDOP = 0xAA00 - MULTOP = 0xAB00 - STDMASK = 0xff00 + def elaborate(self, platform): m = Module() - # BOTTOM = start of parse - mem = Memory(width=16, depth=256, init=[OPENPAREN, OPENPAREN, 0XE102, CLOSEPAREN, ADDOP, 0XE103, CLOSEPAREN, ADDOP, 0XE101, ENDOFPARSE]) - m.submodules.parse_data = rdport = mem.read_port() - - - common_stack_states = [ - # State 0 in the paper - # Bottom of parse stack - [(0, STDMASK, BOTTOM)], - - # State 1 in the paper - # Bottom of parse stack Expression - [(1, STDMASK, BOTTOM), (0, STDMASK, EXPRESSION)], - - # state 2 in paper - # TERM - [(0, STDMASK, TERM)], - - # state 3 in paper - # FACTOR - [(0, STDMASK, FACTOR)], - - # State 4 in paper - # OPEN PAREN - [(0, STDMASK, OPENPAREN)], - - # State 5 in paper - # INTEGER - [(0, STDMASK, INTEGER)], - - # State 6 in paper - # EXPRESSION PLUS - [(1, STDMASK, EXPRESSION), (0, STDMASK, ADDOP)], - - # State 7 in paper - # TERM MULTIPLY - [(1, STDMASK, TERM), (0, STDMASK, MULTOP)], - - # State 8 in paper - # OPEN PAREN EXPRESSION - [(1, STDMASK, OPENPAREN), (0, STDMASK, EXPRESSION)], - - # State 9 in paper - # EXPRESSION PLUS TERM - [(2, STDMASK, EXPRESSION), (1, STDMASK, ADDOP), (0, STDMASK, TERM)], - - # State 10 in paper - # TERM MULTIPLY FACTOR - [(2, STDMASK, TERM), (1, STDMASK, MULTOP), (0, STDMASK, FACTOR)], - - # State 11 in paper - # OPEN PAREN EXPRESSION CLOSE PAREN - [(2, STDMASK, OPENPAREN), (1, STDMASK, EXPRESSION), (0, STDMASK, CLOSEPAREN)] - ] - - pairwise_priority_ruleset = [(1,8),(2,9), (3,10)] - - - - validitem_ruleset = [ - # For state 0: - [(STDMASK, INTEGER), (STDMASK, OPENPAREN)], - # For state 1: - [(STDMASK, ADDOP), (STDMASK, ENDOFPARSE)], - # For state 2: - [(STDMASK, ADDOP), (STDMASK, MULTOP), (STDMASK, CLOSEPAREN), (STDMASK, ENDOFPARSE)], - # For state 3: - [(STDMASK, ADDOP), (STDMASK, MULTOP), (STDMASK, CLOSEPAREN), (STDMASK, ENDOFPARSE)], - # For state 4: - [(STDMASK, INTEGER), (STDMASK, OPENPAREN)], - # For state 5: - [(STDMASK, ADDOP), (STDMASK, MULTOP), (STDMASK, CLOSEPAREN), (STDMASK, ENDOFPARSE)], - # For state 6: - [(STDMASK, INTEGER), (STDMASK, OPENPAREN)], - # For state 7: - [(STDMASK, INTEGER), (STDMASK, OPENPAREN)], - # For state 8: - [(STDMASK, ADDOP), (STDMASK, CLOSEPAREN)], - # For state 9: - [(STDMASK, ADDOP), (STDMASK, MULTOP), (STDMASK, CLOSEPAREN), (STDMASK, ENDOFPARSE)], - # For state 10: - [(STDMASK, ADDOP), (STDMASK, MULTOP), (STDMASK, CLOSEPAREN), (STDMASK, ENDOFPARSE)], - # For state 11: - [(STDMASK, ADDOP), (STDMASK, MULTOP), (STDMASK, CLOSEPAREN), (STDMASK, ENDOFPARSE)], - ] - - - forceshift_ruleset = [ - # For state 0: - [(STDMASK, INTEGER), (STDMASK, OPENPAREN)], - # For state 1: - [(STDMASK, ADDOP), (STDMASK, ENDOFPARSE)], - # For state 2: - [(STDMASK, MULTOP)], - # For state 3: - [], - # For state 4: - [(STDMASK, INTEGER), (STDMASK, OPENPAREN)], - # For state 5: - [], - # For state 6: - [(STDMASK, INTEGER), (STDMASK, OPENPAREN)], - # For state 7: - [(STDMASK, INTEGER), (STDMASK, OPENPAREN)], - # For state 8: - [(STDMASK, ADDOP), (STDMASK, CLOSEPAREN)], - # For state 9: - [(STDMASK, MULTOP)], - # For state 10: - [], - # For state 11: - [] - ] - - - reduce_ruleset = [ - # For state 0: - [], - # For state 1: - [], - # For state 2: - [((STDMASK, ADDOP),1), ((STDMASK, CLOSEPAREN),1), ((STDMASK, ENDOFPARSE),1)], - # For state 3: - [((STDMASK, ADDOP),3), ((STDMASK, MULTOP),3), ((STDMASK, CLOSEPAREN),3), ((STDMASK, ENDOFPARSE),3)], - # For state 4: - [], - # For state 5: - [((STDMASK, ADDOP),5), ((STDMASK, MULTOP),5), ((STDMASK, CLOSEPAREN),5), ((STDMASK, ENDOFPARSE),5)], - # For state 6: - [], - # For state 7: - [], - # For state 8: - [], - # For state 9: - [((STDMASK, ADDOP),0), ((STDMASK, CLOSEPAREN),0), ((STDMASK, ENDOFPARSE),0)], - # For state 10: - [((STDMASK, ADDOP),2), ((STDMASK, MULTOP),2), ((STDMASK, CLOSEPAREN),2), ((STDMASK, ENDOFPARSE),2)], - # For state 11: - [((STDMASK, ADDOP),4), ((STDMASK, MULTOP),4), ((STDMASK, CLOSEPAREN),4), ((STDMASK, ENDOFPARSE),4)], - ] - - def extractor(x): return (x & 0x00ff) - execute_rules = [ - (3, (lambda stackview: EXPRESSION + (extractor(stackview[0]) + extractor(stackview[2])))), - - (1, (lambda stackview: EXPRESSION + extractor(stackview[0]))), - - (3, (lambda stackview: TERM + (extractor(stackview[0]) * extractor(stackview[2])))), - - (1, (lambda stackview: TERM + extractor(stackview[0]))), - - (3, (lambda stackview: FACTOR + extractor(stackview[1]))), - - (1, (lambda stackview: FACTOR + extractor(stackview[0]))) - ] - - msm = MasterStateMachine(item_width=16, indices_width=16, stack_depth=10, - validitem_ruleset = validitem_ruleset, - pairwise_priority_ruleset = pairwise_priority_ruleset, - forceshift_ruleset = forceshift_ruleset, - reduce_ruleset=reduce_ruleset, - execute_rules=execute_rules, stack_state_descriptions=common_stack_states, startofparse_marker = 0x5a00, endofparse_marker=(0xffff, ENDOFPARSE)) - - m.submodules.StateMachine = msm - - - with m.If(resetted == 0): - m.d.sync += resetted.eq(1) - with m.If(resetted == 1): - m.d.comb += msm.data_in_valid.eq(1) - - stall_recovery = Signal(1) - cashew_register = Signal(16) - m.d.sync += stall_recovery.eq(msm.data_in_ready) # one-cycle-delayed + m.submodules.stack = ParseStack(8,8) + counter = Signal(8) + m.d.sync += counter.eq(counter+1) - with m.If(msm.data_in_ready == 1): - m.d.sync += cntr.eq(cntr + 1) - m.d.comb += msm.data_in.eq((rdport).data) + with m.If(counter==1): + m.d.comb += m.submodules.stack.strobe.eq(1) + m.d.comb += m.submodules.stack.in_valid.eq(1) + m.d.comb += m.submodules.stack.in_data_port.eq(42) - with m.If((msm.data_in_ready == 0) & (stall_recovery == 1)): - m.d.sync += cashew_register.eq(rdport.data) - m.d.comb += msm.data_in.eq((rdport).data) - with m.If(stall_recovery == 0): - m.d.comb += msm.data_in.eq(cashew_register) + with m.If(counter==2): + m.d.comb += m.submodules.stack.command_port.eq(0) + m.d.comb += m.submodules.stack.strobe.eq(1) + m.d.comb += m.submodules.stack.in_valid.eq(1) + m.d.comb += m.submodules.stack.in_data_port.eq(43) + with m.If(counter==3): + m.d.comb += m.submodules.stack.command_port.eq(0) + m.d.comb += m.submodules.stack.strobe.eq(1) + m.d.comb += m.submodules.stack.in_valid.eq(1) + m.d.comb += m.submodules.stack.in_data_port.eq(44) - m.d.comb += (rdport).addr.eq(cntr) + with m.If(counter==4): + m.d.comb += m.submodules.stack.command_port.eq(1) + m.d.comb += m.submodules.stack.strobe.eq(1) + with m.If(counter==5): + m.d.comb += m.submodules.stack.command_port.eq(1) + m.d.comb += m.submodules.stack.strobe.eq(1) -# egg_0 = platform.request("led",0) -# egg_1 = platform.request("led",1) -# egg_2 = platform.request("led",2) -# egg_3 = platform.request("led",3) -# egg_4 = platform.request("led",4) -# egg_5 = platform.request("led",5) -# egg_6 = platform.request("led",6) -# egg_7 = platform.request("led",7) -# -# oeuf = Cat(egg_0, egg_1, egg_2, egg_3, egg_4, egg_5, egg_6, egg_7) -# m.d.comb += oeuf.eq(msm.data_out[8:15] ^ msm.data_out[0:7]) + with m.If(counter==6): + m.d.comb += m.submodules.stack.command_port.eq(1) + m.d.comb += m.submodules.stack.strobe.eq(1) + with m.If(counter==7): + m.d.comb += m.submodules.stack.command_port.eq(1) + m.d.comb += m.submodules.stack.strobe.eq(1) return m if __name__ == '__main__': - platform = ICE40HX8KBEVNPlatform() baka =DummyPlug() main(baka) #platform.build(DummyPlug())