def elab(self, m): results_counter = Signal.like(self.num_results.payload) accumulator = Signal.like(self.accumulated.payload) with m.FSM(): with m.State('IDLE'): m.d.comb += self.num_results.ready.eq(1) with m.If(self.num_results.is_transferring()): m.d.sync += results_counter.eq(self.num_results.payload) m.next = 'ACCUMULATING' with m.State('ACCUMULATING'): m.d.comb += self.results.ready.eq(1) with m.If(self.results.is_transferring()): m.d.sync += accumulator.eq(accumulator + self.results.payload) with m.If(results_counter > 1): m.d.sync += results_counter.eq(results_counter - 1) with m.Else(): m.d.sync += results_counter.eq(0) m.next = 'DONE' with m.State('DONE'): m.d.sync += self.accumulated.payload.eq(accumulator) m.d.sync += self.accumulated.valid.eq(1) m.d.sync += accumulator.eq(0) with m.If(self.accumulated.is_transferring()): m.d.sync += self.accumulated.valid.eq(0) m.next = 'IDLE'
def elab(self, m): # Track previous restart, next was_restart = Signal() m.d.sync += was_restart.eq(self.restart) was_next = Signal() m.d.sync += was_next.eq(self.next) # Decide address to be output (determines data available next cycle) last_mem_addr = Signal.like(self.mem_addr) m.d.sync += last_mem_addr.eq(self.mem_addr) incremented_addr = Signal.like(self.limit) m.d.comb += incremented_addr.eq( Mux(last_mem_addr == self.limit - 1, 0, last_mem_addr + 1)) with m.If(self.restart): m.d.comb += self.mem_addr.eq(0) with m.Elif(was_next | was_restart): m.d.comb += self.mem_addr.eq(incremented_addr) with m.Else(): m.d.comb += self.mem_addr.eq(last_mem_addr) # Decide data to be output last_data = Signal.like(self.data) m.d.sync += last_data.eq(self.data) with m.If(was_restart | was_next): m.d.comb += self.data.eq(self.mem_data) with m.Else(): m.d.comb += self.data.eq(last_data)
def elab(self, m): # number of bytes received already byte_count = Signal(range(4)) # output channel for next byte received pixel_index = Signal(range(self._num_pixels)) # shift registers to buffer 3 incoming values shift_registers = Array( [Signal(24, name=f"sr_{i}") for i in range(self._num_pixels)]) last_pixel_index = Signal.like(pixel_index) m.d.sync += last_pixel_index.eq(Mux(self.half_mode, self._num_pixels // 2 - 1, self._num_pixels - 1)) next_pixel_index = Signal.like(pixel_index) m.d.comb += next_pixel_index.eq(Mux(pixel_index == last_pixel_index, 0, pixel_index + 1)) m.d.comb += self.input.ready.eq(1) with m.If(self.input.is_transferring()): sr = shift_registers[pixel_index] with m.If(byte_count == 3): # Output current value and shift register m.d.comb += self.output.valid.eq(1) payload = Cat(sr, self.input.payload) m.d.comb += self.output.payload.eq(payload) with m.Else(): # Save input to shift register m.d.sync += sr[-8:].eq(self.input.payload) m.d.sync += sr[:-8].eq(sr[8:]) # Increment pixel index m.d.sync += pixel_index.eq(next_pixel_index) with m.If(pixel_index == last_pixel_index): m.d.sync += pixel_index.eq(0) m.d.sync += byte_count.eq(byte_count + 1) # allow rollover
def elab(self, m): with m.If(self.reset): m.d.sync += self.value.eq(0) with m.Else(): value_p1 = Signal.like(self.count) next_value = Signal.like(self.value) m.d.comb += [ value_p1.eq(self.value + 1), self.last.eq(value_p1 == self.count), next_value.eq(Mux(self.last, 0, value_p1)), ] with m.If(self.next): m.d.sync += self.value.eq(next_value)
def _elab_write(self, m, dps, r_full): w_curr_buf = Signal() # Address within current buffer w_addr = Signal.like(self.input_depth) # Connect to memory write port for n, dp in enumerate(dps): m.d.comb += [ dp.w_addr.eq(Cat(w_addr[2:], w_curr_buf)), dp.w_data.eq(self.w_data), dp.w_en.eq(self.w_en & self.w_ready & (n == w_addr[:2])), ] # Ready to write current buffer if reading is not allowed m.d.comb += self.w_ready.eq(~r_full[w_curr_buf]) # Write address increment with m.If(self.w_en & self.w_ready): with m.If(w_addr == self.input_depth - 1): # at end of buffer - mark buffer ready for reading and go to # next m.d.sync += [ w_addr.eq(0), r_full[w_curr_buf].eq(1), w_curr_buf.eq(~w_curr_buf), ] with m.Else(): m.d.sync += w_addr.eq(w_addr + 1) with m.If(self.restart): m.d.sync += [ w_addr.eq(0), w_curr_buf.eq(0), ]
def _elab_read(self, m, dps, r_full): r_curr_buf = Signal() # Address within current buffer r_addr = Signal.like(self.input_depth) # Create and connect sequential memory readers smrs = [ SequentialMemoryReader(max_depth=self.max_depth // 2, width=32) for _ in range(4) ] for (n, dp, smr) in zip(range(4), dps, smrs): m.submodules[f"smr_{n}"] = smr m.d.comb += [ dp.r_addr.eq(Cat(smr.mem_addr, r_curr_buf)), smr.mem_data.eq(dp.r_data), smr.limit.eq((self.input_depth + 3 - n) >> 2), ] smr_nexts = Array(smr.next for smr in smrs) # Ready if current buffer is full full = Signal() m.d.comb += full.eq(r_full[r_curr_buf]) last_full = Signal() m.d.sync += last_full.eq(full) with m.If(full & ~last_full): m.d.sync += self.r_ready.eq(1) m.d.comb += [smr.restart.eq(1) for smr in smrs] # Increment address with m.If(self.r_next & self.r_ready): with m.If(r_addr == self.input_depth - 1): m.d.sync += r_addr.eq(0) with m.Else(): m.d.sync += r_addr.eq(r_addr + 1) m.d.comb += smr_nexts[r_addr[:2]].eq(1) # Get data smr_datas = Array(smr.data for smr in smrs) m.d.comb += self.r_data.eq(smr_datas[r_addr[:2]]) # On finished (overrides r_next) with m.If(self.r_finished): m.d.sync += [ r_addr.eq(0), r_full[r_curr_buf].eq(0), last_full.eq(0), self.r_ready.eq(0), r_curr_buf.eq(~r_curr_buf), ] # On restart, reset addresses and Sequential Memory readers, go to not # ready with m.If(self.restart): m.d.sync += [ r_addr.eq(0), last_full.eq(0), self.r_ready.eq(0), r_curr_buf.eq(0), ]
def synchronize(m, signal, *, output=None, o_domain='sync', stages=2): """ Convenience function. Synchronizes a signal, or equivalent collection. Parameters: input -- The signal to be synchronized. output -- The signal to output the result of the synchronization to, or None to have one created for you. domain -- The name of the domain to be synchronized to. stages -- The depth (in FFs) of the synchronization chain. Longer incurs more delay. Must be >= 2 to avoid metastability. Returns: record -- The post-synchronization signal. Will be equivalent to the `output` record if provided, or a new, created signal otherwise. """ # Quick function to create a synchronizer with our domain and stages. def create_synchronizer(signal, output): return FFSynchronizer(signal, output, o_domain=o_domain, stages=stages) if output is None: if isinstance(signal, Signal): output = Signal.like(signal) else: output = Record.like(signal) # If the object knows how to synchronize itself, let it. if hasattr(signal, '_synchronize_'): signal._synchronize_(m, output, o_domain=o_domain, stages=stages) return output # Trivial case: if this element doesn't have a layout, # we can just synchronize it directly. if not hasattr(signal, 'layout'): m.submodules += create_synchronizer(signal, output) return output # Otherwise, we'll need to make sure we only synchronize # elements with non-output directions. for name, layout, direction in signal.layout: # If this is a record itself, we'll need to recurse. if isinstance(signal[name], (Record, Pin)): synchronize(m, signal[name], output=output[name], o_domain=o_domain, stages=stages) continue # Skip any output elements, as they're already # in our clock domain, and we don't want to drive them. if (direction == DIR_FANOUT) or (hasattr(signal[name], 'o') and ~hasattr(signal[name], 'i')): m.d.comb += signal[name].eq(output[name]) continue m.submodules += create_synchronizer(signal[name], output[name]) return output
def elab(self, m): group = Signal(range(4)) repeat_count = Signal.like(self.repeats) mem = Array([Signal(14, name=f"mem{i}") for i in range(4)]) with m.If(repeat_count == 0): # On first repeat - pass through next and addr and record addresses m.d.comb += self.gen_next.eq(self.next) m.d.comb += self.addr.eq(self.gen_addr) with m.If(self.next): m.d.sync += mem[group].eq(self.gen_addr) with m.Else(): # Subsequently use recorded data m.d.comb += self.addr.eq(mem[group]) # Update group and repeat_count on next with m.If(self.next): m.d.sync += group.eq(group + 1) with m.If(group == 3): m.d.sync += repeat_count.eq(repeat_count + 1) with m.If(repeat_count + 1 == self.repeats): m.d.sync += repeat_count.eq(0) with m.If(self.start): m.d.sync += repeat_count.eq(0) m.d.sync += group.eq(0)
def delay(m, input_, cycles): result = [input_] for i in range(cycles): delayed = Signal.like(input_, name=f"{input_.name}_d{i+1}") m.d.sync += delayed.eq(result[-1]) result.append(delayed) return result
def elab(self, m: Module): buffering = Signal() # True if there is a value being buffered buffered_value = Signal.like(Value.cast(self.input.payload)) # Pipe valid and ready back and forth m.d.comb += [ self.input.ready.eq(~buffering | self.output.ready), self.output.valid.eq(buffering | self.input.valid), self.output.payload.eq( Mux(buffering, buffered_value, self.input.payload)) ] # Buffer when have incoming value but cannot output just now with m.If(~buffering & ~self.output.ready & self.input.valid): m.d.sync += buffering.eq(True) m.d.sync += buffered_value.eq(self.input.payload) # Handle cases when transfering out from buffer with m.If(buffering & self.output.ready): with m.If(self.input.valid): m.d.sync += buffered_value.eq(self.input.payload) with m.Else(): m.d.sync += buffering.eq(False) # Reset all state with m.If(self.reset): m.d.sync += buffering.eq(False) m.d.sync += buffered_value.eq(0)
def elaborate(self, platform): m = Module() with m.If(self.pending.w_stb): m.d.sync += self.pending.r_data.eq(self.pending.r_data & ~self.pending.w_data) with m.If(self.enable.w_stb): m.d.sync += self.enable.r_data.eq(self.enable.w_data) for i, event in enumerate(self._events): m.d.sync += self.status.r_data[i].eq(event.stb) if event.mode in ("rise", "fall"): event_stb_r = Signal.like(event.stb, name_suffix="_r") m.d.sync += event_stb_r.eq(event.stb) event_trigger = Signal(name="{}_trigger".format(event.name)) if event.mode == "level": m.d.comb += event_trigger.eq(event.stb) elif event.mode == "rise": m.d.comb += event_trigger.eq(~event_stb_r & event.stb) elif event.mode == "fall": m.d.comb += event_trigger.eq(event_stb_r & ~event.stb) else: assert False # :nocov: with m.If(event_trigger): m.d.sync += self.pending.r_data[i].eq(1) m.d.comb += self.irq.eq( (self.pending.r_data & self.enable.r_data).any()) return m
def __init__(self, n, a_shape, b_shape, accumulator_shape): self._n = n self._a_shape = a_shape self._b_shape = b_shape self._accumulator_shape = accumulator_shape self.input_a = Signal(unsigned(n * a_shape.width)) self.output_a = Signal.like(self.input_a) self.input_b = Signal(unsigned(n * b_shape.width)) self.output_b = Signal.like(self.input_b) self.input_first = Signal() self.output_first = Signal() self.input_last = Signal() self.output_last = Signal() self.output_accumulator = Signal(accumulator_shape) self.output_accumulator_new = Signal()
def add_register(self, address, *, value_signal=None, size=None, name=None, read_strobe=None, write_strobe=None, reset=0): """ Adds a standard, memory-backed register. Parameters: address -- the register's address, as a big-endian integer value_signal -- the signal that will store the register's value; if omitted a storage register will be created automatically size -- if value_signal isn't provided, this sets the size of the created register reset -- if value_signal isn't provided, this sets the reset value of the created register read_strobe -- a Signal to be asserted when the register is read; ignored if not provided write_strobe -- a Signal to be asserted when the register is written; ignored if not provided Returns: value_signal -- a signal that stores the register's value; which may be the value_signal arg, or may be a signal created during execution """ self._ensure_register_is_unused(address) # Generate a name for the register, if we don't already have one. name = name if name else "register_{:x}".format(address) # Generate a backing store for the register, if we don't already have one. if value_signal is None: size = self.register_size if (size is None) else size value_signal = Signal(size, name=name, reset=reset) # If we don't have a write strobe signal, create an internal one. if write_strobe is None: write_strobe = Signal(name=name + "_write_strobe") # Create our register-value-input and our write strobe. write_value = Signal.like(value_signal, name=name + "_write_value") # Create a generator for a the fragments that will manage the register's memory. def _elaborate_memory_register(m): with m.If(write_strobe): m.d.sync += value_signal.eq(write_value) # Add the register to our collection. self.registers[address] = { 'read': value_signal, 'write_signal': write_value, 'write_strobe': write_strobe, 'read_strobe': read_strobe, 'elaborate': _elaborate_memory_register, } return value_signal
def control(self, m): release_counter = Signal.like(self.release.payload) with m.If(release_counter > 0): m.d.comb += self.input.ready.eq(self.output.ready) m.d.comb += self.output.valid.eq(self.input.valid) with m.If(self.output.is_transferring()): m.d.sync += release_counter.eq(release_counter - 1) with m.Else(): m.d.comb += self.release.ready.eq(1) with m.If(self.release.is_transferring()): m.d.sync += release_counter.eq(self.release.payload)
def elab(self, m): count = Signal.like(self.max) last_count = self.max - 1 next_count = Mux(count == last_count, 0, count + 1) with m.If(self.en): m.d.sync += count.eq(next_count) m.d.comb += self.done.eq(count == last_count) with m.If(self.restart): m.d.sync += count.eq(0)
def build_multipliers(self, m, accumulator): # Pipeline cycle 0: calculate products products = [] for i in range(self._n): a_bits = self.input_a.word_select(i, self._a_shape.width) b_bits = self.input_b.word_select(i, self._b_shape.width) a = Signal(self._a_shape, name=f"a_{i}") b = Signal(self._b_shape, name=f"b_{i}") m.d.comb += [ a.eq(a_bits), b.eq(b_bits), ] ab = Signal.like(a * b) m.d.sync += ab.eq(a * b) products.append(ab) # Pipeline cycle 1: accumulate product_sum = Signal.like(tree_sum(products)) m.d.comb += product_sum.eq(tree_sum(products)) first_delayed = delay(m, self.input_first, 1)[-1] base = Mux(first_delayed, 0, accumulator) m.d.sync += accumulator.eq(base + product_sum)
def elaborate(self, platform): m = Module() ac = Signal(self.width+1) ac_next = Signal.like(ac) temp = Signal.like(ac) q1 = Signal(self.width) q1_next = Signal.like(q1) i = Signal(range(self.width)) # combinatorial with m.If(ac >= self.y): m.d.comb += [temp.eq(ac-self.y), Cat(q1_next, ac_next).eq( Cat(1, q1, temp[0:self.width-1]))] with m.Else(): m.d.comb += [Cat(q1_next, ac_next).eq(Cat(q1, ac) << 1)] # synchronized with m.If(self.start): m.d.sync += [self.valid.eq(0), i.eq(0)] with m.If(self.y == 0): m.d.sync += [self.busy.eq(0), self.dbz.eq(1)] with m.Else(): m.d.sync += [self.busy.eq(1), self.dbz.eq(0), Cat(q1, ac).eq(Cat(Const(0, 1), self.x, Const(0, self.width)))] with m.Elif(self.busy): with m.If(i == self.width-1): m.d.sync += [self.busy.eq(0), self.valid.eq(1), i.eq(0), self.q.eq(q1_next), self.r.eq(ac_next >> 1)] with m.Else(): m.d.sync += [i.eq(i+1), ac.eq(ac_next), q1.eq(q1_next)] return m
def elab(self, m): # Current r_addr is the address being presented to memory this cycle # By default current address is last address, but this can be # modied by the reatart or next signals last_addr = Signal.like(self.r_addr) m.d.sync += last_addr.eq(self.r_addr) m.d.comb += self.r_addr.eq(last_addr) # Respond to inputs with m.If(self.restart): m.d.comb += self.r_addr.eq(0) with m.Elif(self.next): m.d.comb += self.r_addr.eq( Mux(last_addr >= self.limit - 1, 0, last_addr + 1))
def __init__(self, *, endpoint_number, max_packet_size): self._endpoint_number = endpoint_number self._max_packet_size = max_packet_size # # I/O Port # self.interface = EndpointInterface() self.bytes_in_frame = Signal(range(0, self._MAX_FRAME_DATA + 1)) self.address = Signal(range(0, self._MAX_FRAME_DATA)) self.next_address = Signal.like(self.address) self.value = Signal(8)
def elab(self, m): areg = Signal.like(self.a) breg = Signal.like(self.b) ab = Signal(signed(64)) overflow = Signal() # for some reason negative nudge is not used nudge = 1 << 30 # cycle 0, register a and b m.d.sync += [ areg.eq(self.a), breg.eq(self.b), ] # cycle 1, decide if this is an overflow and multiply m.d.sync += [ overflow.eq((areg == INT32_MIN) & (breg == INT32_MIN)), ab.eq(areg * breg), ] # cycle 2, apply nudge determine result m.d.sync += [ self.result.eq(Mux(overflow, INT32_MAX, (ab + nudge)[31:])), ]
def elab(self, m): m.d.sync += self.finished.eq(0) m.d.comb += [ self.stream_in.ready.eq(self.running), self.stream_out.valid.eq(self.stream_in.is_transferring()), self.stream_out.payload.eq(self.stream_in.payload), ] counter = Signal.like(self.num_allowed) with m.If(self.start): m.d.sync += counter.eq(self.num_allowed) with m.If(self.stream_in.is_transferring()): m.d.sync += counter.eq(counter - 1) with m.If(counter == 1): m.d.sync += self.finished.eq(1) m.d.comb += self.running.eq(counter != 0)
def delay(m, signal, cycles): """Delays a signal by a number cycles. Parameters: m: Module The module to work in. signal: Signal(?) The signal to delay cycles: int The number of cycles to delay """ name = signal.name for i in range(cycles): delayed = Signal.like(signal, name=f"{name}_delay_{i}") m.d.sync += delayed.eq(signal) signal = delayed return signal
def delay(m, signal, interval, *, out=None): """ Creates a delayed copy of a given I/O signal. Currently only works at the FPGA's I/O boundary, and only on ECP5s. Parameters: signal -- The signal to be delayed. Must be either an I/O signal connected directly to a platform resource. delay -- Delay, in arbitrary units. These units will vary from unit to unit, but seem to be around 300ps on most ECP5 FPGAs. On ECP5s, maxes out at 127. out -- The signal to received the delayed signal; or None ot have a signal created for you. Returns: delayed -- The delayed signal. Will be equivalent to 'out' if provided; or a new signal otherwise. """ # If we're not being passed our output signal, create one. if out is None: out = Signal.like(signal) # If we have more than one signal, call this function on each # of the subsignals. if len(signal) > 1: # If we have a vector of signals, but a integer delay, # convert that integer to a vector of same-valued delays. if isinstance(interval, int): interval = [interval] * len(signal) return Cat( delay(m, s, d, out=o) for s, d, o in zip(signal, interval, out)) # # Base case: create a delayed version of the relevant signal. # m.submodules += Instance("DELAYG", i_A=signal, o_Z=out, p_DEL_VALUE=interval) return out
def __init__(self): # # I/O port. # self.tx_data = Signal(8) self.tx_valid = Signal() self.tx_ready = Signal() self.op_mode = Signal(2) self.bus_idle = Signal() self.ulpi_out_req = Signal() self.ulpi_data_out = Signal.like(self.tx_data) self.ulpi_nxt = Signal() self.ulpi_stp = Signal() self.busy = Signal()
def elaborate(self, platform): m = Module() beta = self.beta temp = Signal(signed(self.totalbits * 2)) n = len(self.coeff) j = Signal(range(1, n)) k = Signal.like(j) with m.FSM(reset='INIT') as algo: with m.State('INIT'): m.d.sync += self.done.eq(0) for i in range(n): m.d.sync += beta[i].eq(self.coeff[i]) m.d.sync += [k.eq(0), j.eq(1)] m.next = 'UPDATE' with m.FSM('UPDATE'): m.d.sync += temp.eq(beta[k] * (1 - self.t) + beta[k + 1] * self.t) m.next = 'MULTIPLICATIONFIX' # Fixed point arithmetic need fix # see multiplication as https://vha3.github.io/FixedPoint/FixedPoint.html with m.FSM('MULTIPLICATIONFIX'): m.d.sync += beta[k].eq( temp[self.fractionalbits:self.fractionalbits + self.totalbits]) with m.If(k != n - j): m.d.sync += k.eq(k + 1) m.next = 'UPDATE' with m.Else(): with m.If(j != n): m.d.sync += j.eq(j + 1) m.d.sync += k.eq(0) m.next = 'UPDATE' with m.Else(): m.next = 'FINISH' with m.FSM('FINISH'): m.d.sync += self.done.eq(1) m.next = 'FINISH' return m
def __init__(self, command_size=8, word_size=32): self.command_size = command_size self.word_size = word_size # # I/O port. # # SPI self.spi = SPIBus() # Command I/O. self.command = Signal(self.command_size) self.command_ready = Signal() # Data I/O self.word_received = Signal(self.word_size) self.word_to_send = Signal.like(self.word_received) self.word_complete = Signal() # Status self.idle = Signal() self.stalled = Signal()
def elab(self, m): captured = Signal.like(self.input) with m.If(self.capture): m.d.sync += captured.eq(self.input) m.d.comb += self.output.eq(Mux(self.capture, self.input, captured))
def __init__(self, inp): self.capture = Signal() self.input = inp self.output = Signal.like(inp)
def elaborate(self, platform): m = Module() # Grab signals that detect when we should shift in and out. sample_edge, output_edge = self.spi_edge_detectors(m) # We'll use separate buffers for transmit and receive, # as this makes the code a little more readable. bit_count = Signal(range(0, self.word_size), reset=0) current_tx = Signal.like(self.word_out) current_rx = Signal.like(self.word_in) # Signal that tracks if this is our first bit of the word. is_first_bit = Signal() # A word is ready one cycle after we complete a transaction # (and latch in the next word to be sent). with m.If(self.word_accepted): m.d.sync += [self.word_in.eq(current_rx), self.word_complete.eq(1)] with m.Else(): m.d.sync += self.word_complete.eq(0) # De-assert our control signals unless explicitly asserted. m.d.sync += [ self.word_accepted.eq(0), ] # If the chip is selected, process our I/O: chip_selected = self.spi.cs if not self.cs_idles_high else ~self.spi.cs with m.If(chip_selected): # Shift in data on each sample edge. with m.If(sample_edge): m.d.sync += [bit_count.eq(bit_count + 1), is_first_bit.eq(0)] if self.msb_first: m.d.sync += current_rx.eq( Cat(self.spi.sdi, current_rx[:-1])) else: m.d.sync += current_rx.eq(Cat(current_rx[1:], self.spi.sdi)) # If we're just completing a word, handle I/O. with m.If(bit_count + 1 == self.word_size): m.d.sync += [ self.word_accepted.eq(1), current_tx.eq(self.word_out) ] # Shift out data on each output edge. with m.If(output_edge): if self.msb_first: m.d.sync += Cat(current_tx[1:], self.spi.sdo).eq(current_tx) else: m.d.sync += Cat(self.spi.sdo, current_tx[:-1]).eq(current_tx) with m.Else(): m.d.sync += [ current_tx.eq(self.word_out), bit_count.eq(0), is_first_bit.eq(1) ] return m
def elaborate(self, platform): m = Module() spi = self.spi sample_edge = Fell(spi.sck, domain="sync") # Bit counter: counts the number of bits received. max_bit_count = max(self.word_size, self.command_size) bit_count = Signal(range(0, max_bit_count + 1)) # Shift registers for our command and data. current_command = Signal.like(self.command) current_word = Signal.like(self.word_received) # De-assert our control signals unless explicitly asserted. m.d.sync += [self.command_ready.eq(0), self.word_complete.eq(0)] with m.FSM() as fsm: m.d.comb += [ self.idle.eq(fsm.ongoing('IDLE')), self.stalled.eq(fsm.ongoing('STALL')), ] # STALL: entered when we can't accept new bits -- either when # CS starts asserted, or when we've received more data than expected. with m.State("STALL"): # Wait for CS to clear. with m.If(~spi.cs): m.next = 'IDLE' # We ignore all data until chip select is asserted, as that data Isn't For Us (TM). # We'll spin and do nothing until the bus-master addresses us. with m.State('IDLE'): m.d.sync += bit_count.eq(0) with m.If(spi.cs): m.next = 'RECEIVE_COMMAND' # Once CS is low, we'll shift in our command. with m.State('RECEIVE_COMMAND'): # If CS is de-asserted early; our transaction is being aborted. with m.If(~spi.cs): m.next = 'IDLE' # Continue shifting in data until we have a full command. with m.If(bit_count < self.command_size): with m.If(sample_edge): m.d.sync += [ bit_count.eq(bit_count + 1), current_command.eq( Cat(spi.sdi, current_command[:-1])) ] # ... and then pass that command out to our controller. with m.Else(): m.d.sync += [ bit_count.eq(0), self.command_ready.eq(1), self.command.eq(current_command) ] m.next = 'PROCESSING' # Give our controller a wait state to prepare any response they might want to... with m.State('PROCESSING'): m.next = 'LATCH_OUTPUT' # ... and then latch in the response to transmit. with m.State('LATCH_OUTPUT'): m.d.sync += current_word.eq(self.word_to_send) m.next = 'SHIFT_DATA' # Finally, exchange data. with m.State('SHIFT_DATA'): # If CS is de-asserted early; our transaction is being aborted. with m.If(~spi.cs): m.next = 'IDLE' m.d.sync += spi.sdo.eq(current_word[-1]) # Continue shifting data until we have a full word. with m.If(bit_count < self.word_size): with m.If(sample_edge): m.d.sync += [ bit_count.eq(bit_count + 1), current_word.eq(Cat(spi.sdi, current_word[:-1])) ] # ... and then output that word on our bus. with m.Else(): m.d.sync += [ bit_count.eq(0), self.word_complete.eq(1), self.word_received.eq(current_word) ] # Stay in the stall state until CS is de-asserted. m.next = 'STALL' return m