def elaborate(self, platform): """ Generate the Blinky tester. """ m = Module() # Grab our I/O connectors. leds = [ platform.request_optional("led", i, default=NullPin()).o for i in range(0, 8) ] user_io = [ platform.request_optional("user_io", i, default=NullPin()).o for i in range(0, 8) ] # Clock divider / counter. counter = Signal(28) m.d.sync += counter.eq(counter + 1) # Attach the LEDs and User I/O to the MSBs of our counter. m.d.comb += Cat(leds).eq(counter[-7:-1]) m.d.comb += Cat(user_io).eq(counter[7:21]) # Return our elaborated module. return m
def elab(self, m): # Unset valid on transfer (may be overrridden below) with m.If(self.output.ready): m.d.sync += self.output.valid.eq(0) # Set flag to remember that value is available flags = Array(Signal(name=f"flag_{i}") for i in range(8)) for i in range(8): with m.If(self.accumulator_new[i]): m.d.sync += flags[i].eq(1) # Calculate index of value to output index = Signal(range(8)) next_index = Signal(range(8)) with m.If(self.half_mode): # counts: 0, 1, 4, 5, 0 ... m.d.comb += next_index[1].eq(0) m.d.comb += Cat(next_index[0], next_index[2] ).eq(Cat(index[0], index[2]) + 1) with m.Else(): m.d.comb += next_index.eq(index + 1) # If value available this cycle, or previously # - output new value # - unset flag # - increment index with m.If(Array(self.accumulator_new)[index] | flags[index]): m.d.sync += [ self.output.payload.eq(Array(self.accumulator)[index]), self.output.valid.eq(1), flags[index].eq(0), index.eq(next_index), ]
def elaborate(self, platform): m = Module() m.submodules.bridge = self._bridge # Grab our LEDS... leds = Cat(platform.request("led", i) for i in range(6)) # ... and update them on each register write. with m.If(self._output.w_stb): m.d.sync += leds.eq(self._output.w_data) return m
def populate_ulpi_registers(self, m): """ Creates translator objects that map our control signals to ULPI registers. """ # Function control. function_control = Cat(self.xcvr_select, self.term_select, self.op_mode, Const(0), ~self.suspend, Const(0)) self.add_composite_register(m, 0x04, function_control, reset_value=0b01000001) # OTG control. otg_control = Cat( self.id_pullup, self.dp_pulldown, self.dm_pulldown, self.dischrg_vbus, self.chrg_vbus, Const(0), Const(0), self.use_external_vbus_indicator ) self.add_composite_register(m, 0x0A, otg_control, reset_value=0b00000110)
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 elaborate(self, platform): m = Module() m.submodules += self.ila # Generate our clock domains. clocking = LunaECP5DomainGenerator(clock_frequencies=CLOCK_FREQUENCIES) m.submodules.clocking = clocking # Clock divider / counter. m.d.fast += self.counter.eq(self.counter + 1) # Set our ILA to trigger each time the counter is at a random value. # This shows off our example a bit better than counting at zero. m.d.comb += self.ila.trigger.eq(self.counter == 7) # Grab our I/O connectors. leds = [platform.request("led", i, dir="o") for i in range(0, 6)] spi_bus = synchronize(m, platform.request('debug_spi'), o_domain='fast') # Attach the LEDs and User I/O to the MSBs of our counter. m.d.comb += Cat(leds).eq(self.counter[-7:-1]) # Connect our ILA up to our board's aux SPI. m.d.comb += self.ila.spi.connect(spi_bus) # Return our elaborated module. return m
def __init__(self, *, signals, sample_depth, domain="sync", sample_rate=60e6, samples_pretrigger=1): self.domain = domain self.signals = signals self.inputs = Cat(*signals) self.sample_width = len(self.inputs) self.sample_depth = sample_depth self.samples_pretrigger = samples_pretrigger self.sample_rate = sample_rate self.sample_period = 1 / sample_rate # # Create a backing store for our samples. # self.mem = Memory(width=self.sample_width, depth=sample_depth, name="ila_buffer") # # I/O port # self.trigger = Signal() self.sampling = Signal() self.complete = Signal() self.captured_sample_number = Signal(range(0, self.sample_depth)) self.captured_sample = Signal(self.sample_width)
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 trap(cause: Optional[Union[TrapCause, IrqCause]], interrupt=False): fetch_with_new_pc(Cat(Const(0, 2), self.csr_unit.mtvec.base)) if cause is None: return assert isinstance(cause, TrapCause) or isinstance(cause, IrqCause) e = exception_unit notifiers = e.irq_cause_map if interrupt else e.trap_cause_map m.d.comb += notifiers[cause].eq(1)
def elaborate(self, platform): m = Module() interface = self.interface setup = self.interface.setup # Grab a reference to the board's LEDs. leds = Cat(platform.request_optional("led", i, default=NullPin()).o for i in range(8)) # # Vendor request handlers. with m.If(setup.type == USBRequestType.VENDOR): with m.Switch(setup.request): # SET_LEDS request handler: handler that sets the board's LEDS # to a user provided value with m.Case(self.REQUEST_SET_LEDS): # If we have an active data byte, splat it onto the LEDs. # # For simplicity of this example, we'll accept any byte in # the packet; and not just the first one; each byte will # cause an update. This is fun; we can PWM the LEDs with # USB packets. :) with m.If(interface.rx.valid & interface.rx.next): m.d.usb += leds.eq(interface.rx.payload) # Once the receive is complete, respond with an ACK. with m.If(interface.rx_ready_for_response): m.d.comb += interface.handshakes_out.ack.eq(1) # If we reach the status stage, send a ZLP. with m.If(interface.status_requested): m.d.comb += self.send_zlp() with m.Case(): # # Stall unhandled requests. # with m.If(interface.status_requested | interface.data_requested): m.d.comb += interface.handshakes_out.stall.eq(1) return m
def elaborate(self, platform): m = Module() uart = platform.request("uart") clock_freq = int(60e6) char_freq = int(6e6) # Create our UART transmitter. transmitter = UARTTransmitter(divisor=int(clock_freq // 115200)) m.submodules.transmitter = transmitter stream = transmitter.stream # Create a counter that will let us transmit ten times per second. counter = Signal(range(0, char_freq)) with m.If(counter == (char_freq - 1)): m.d.sync += counter.eq(0) with m.Else(): m.d.sync += counter.eq(counter + 1) # Create a simple ROM with a message for ourselves... letters = Array(ord(i) for i in "Hello, world! \r\n") # ... and count through it whenever we send a letter. current_letter = Signal(range(0, len(letters))) with m.If(stream.ready): m.d.sync += current_letter.eq(current_letter + 1) # Hook everything up. m.d.comb += [ stream.payload.eq(letters[current_letter]), stream.valid.eq(counter == 0), uart.tx.o.eq(transmitter.tx), ] # If this platform has an output-enable control on its UART, drive it iff # we're actively driving a transmission. if hasattr(uart.tx, 'oe'): m.d.comb += uart.tx.oe.eq(transmitter.driving), # Turn on a single LED, just to show something's running. led = Cat(platform.request('led', i) for i in range(6)) m.d.comb += led.eq(~transmitter.tx) return m
def elaborate(self, platform): m = Module() m.submodules += self.ila # Grab a reference to our debug-SPI bus. board_spi = synchronize(m, platform.request("debug_spi")) # Clock divider / counter. m.d.sync += self.counter.eq(self.counter + 1) # Another example signal, for variety. m.d.sync += self.toggle.eq(~self.toggle) # Create an SPI bus for our ILA. ila_spi = SPIBus() m.d.comb += [ self.ila.spi .connect(ila_spi), # For sharing, we'll connect the _inverse_ of the primary # chip select to our ILA bus. This will allow us to send # ILA data when CS is un-asserted, and register data when # CS is asserted. ila_spi.cs .eq(~board_spi.cs) ] # Create a set of registers... spi_registers = SPIRegisterInterface() m.submodules.spi_registers = spi_registers # ... and an SPI bus for them. reg_spi = SPIBus() m.d.comb += [ spi_registers.spi .connect(reg_spi), reg_spi.cs .eq(board_spi.cs) ] # Multiplex our ILA and register SPI busses. m.submodules.mux = SPIMultiplexer([ila_spi, reg_spi]) m.d.comb += m.submodules.mux.shared_lines.connect(board_spi) # Add a simple ID register to demonstrate our registers. spi_registers.add_read_only_register(REGISTER_ID, read=0xDEADBEEF) # Create a simple SFR that will trigger an ILA capture when written, # and which will display our sample status read. spi_registers.add_sfr(REGISTER_ILA, read=self.ila.complete, write_strobe=self.ila.trigger ) # Attach the LEDs and User I/O to the MSBs of our counter. leds = [platform.request("led", i, dir="o") for i in range(0, 6)] m.d.comb += Cat(leds).eq(self.counter[-7:-1]) # Return our elaborated module. return m
def elab(self, m): register = Signal(32) m.d.comb += self.result.eq(register) with m.If(self.shift_en): calc = Signal(32) m.d.comb += [ calc.eq(Cat(register[8:], self.in_value)), self.result.eq(calc), ] m.d.sync += register.eq(calc)
def elaborate(self, platform): m = Module() # neat way of setting carry flag res_and_carry = Cat(self.res, self.carry) m.d.comb += res_and_carry.eq( Mux(self.sub, self.src1 - self.src2, self.src1 + self.src2)) with m.If(self.sub): with m.If((self.src1[-1] != self.src2[-1]) & (self.src1[-1] != self.res[-1])): m.d.comb += self.overflow.eq(1) with m.Else(): # add with m.If((self.src1[-1] == self.src2[-1]) & (self.src1[-1] != self.res[-1])): m.d.comb += self.overflow.eq(1) return m
def max_(word0, word1): result = [Signal(8, name=f"result{i}") for i in range(4)] bytes0 = [word0[i:i + 8] for i in range(0, 32, 8)] bytes1 = [word1[i:i + 8] for i in range(0, 32, 8)] for r, b0, b1 in zip(result, bytes0, bytes1): sb0 = Signal(signed(8)) m.d.comb += sb0.eq(b0) sb1 = Signal(signed(8)) m.d.comb += sb1.eq(b1) m.d.comb += r.eq(Mux(sb1 > sb0, b1, b0)) return Cat(*result)
def _split_samples(self, all_samples): """ Returns an iterator that iterates over each sample in the raw binary of samples. """ from apollo_fpga.support.bits import bits sample_width_bytes = self.ila.bytes_per_sample # Iterate over each sample, and yield its value as a bits object. for i in range(0, len(all_samples), sample_width_bytes): raw_sample = all_samples[i:i + sample_width_bytes] sample_length = len(Cat(self.ila.signals)) yield bits.from_bytes(raw_sample, length=sample_length, byteorder='little')
def elaborate(self, platform): m = Module() width = self._width # Instead of using a counter, we will use a sentinel bit in the shift # register to indicate when it is full. shift_reg = Signal(width + 1, reset=0b1) m.d.comb += self.o_data.eq(shift_reg[0:width]) m.d.usb_io += self.o_put.eq(shift_reg[width - 1] & ~shift_reg[width] & self.i_valid), with m.If(self.reset): m.d.usb_io += shift_reg.eq(1) with m.If(self.i_valid): with m.If(shift_reg[width]): m.d.usb_io += shift_reg.eq(Cat(self.i_data, Const(1))) with m.Else(): m.d.usb_io += shift_reg.eq(Cat(self.i_data, shift_reg[0:width])), return m
def elab(self, m): m.d.comb += [ self.done.eq(True), self.w_addr.eq(self.count[self.num_memories_bits:]), self.updated.eq(Cat(self.w_en).any() | self.restart), ] with m.If(self.restart): m.d.sync += self.count.eq(0) with m.Elif(self.start): m.d.comb += [ self.w_en[self.count[:self.num_memories_bits]].eq(1), self.w_data.eq(self.in0), ] m.d.sync += self.count.eq(self.count + 1)
def uart_gen_serial_record(platform: Platform, m: Module): if platform: serial = platform.request("uart") debug = platform.request("debug") m.d.comb += [ debug.eq(Cat( serial.tx, Const(0, 1), # GND )) ] else: serial = Record(Layout([("tx", 1)]), name="UART_SERIAL") self.serial = serial # TODO this is obfuscated, but we need those signals for simulation testbench return serial
def elaborate(self, platform): m = Module() board_spi = platform.request("debug_spi") # Create a set of registers, and expose them over SPI. spi_registers = SPIRegisterInterface(default_read_value=0x4C554E41) #default read = u'LUNA' m.submodules.spi_registers = spi_registers # Fill in some example registers. # (Register 0 is reserved for size autonegotiation). spi_registers.add_read_only_register(1, read=0xc001cafe) led_reg = spi_registers.add_register(2, size=6, name="leds") spi_registers.add_read_only_register(3, read=0xdeadbeef) # ... and tie our LED register to our LEDs. led_out = Cat([platform.request_optional("led", i, default=NullPin()).o for i in range(0, 8)]) m.d.comb += led_out.eq(led_reg) # Connect up our synchronized copies of the SPI registers. spi = synchronize(m, board_spi) m.d.comb += spi_registers.spi.connect(spi) return m
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 elab(self, m: Module): rp_data = [] for i in range(4): mem = Memory(depth=self._depth // 4, width=32) # Set up read port m.submodules[f'rp_{i}'] = rp = mem.read_port(transparent=False) rp_data.append(rp.data) m.d.comb += rp.addr.eq(self.read_addr) m.d.comb += rp.en.eq(~self.write_enable) # Set up write port m.submodules[f'wp_{i}'] = wp = mem.write_port() m.d.comb += wp.addr.eq(self.write_addr[2:]) m.d.comb += wp.data.eq(self.write_data) m.d.comb += wp.en.eq(self.write_enable & (i == self.write_addr[:2])) # Assemble all of the read port outputs into one, 128bit wide signal m.d.comb += self.read_data.eq(Cat(rp_data))
def elab(self, m): # This code covers 8 cases, determined by bits 1, 2 and 3 of self.addr. # First, bit 2 and 3 are used to select the appropriate ram_mux phase # and addresses in order to read the two words containing the required # data via channels 0 and 3 of the RAM Mux. Once the two words have been # retrieved, six bytes are selected from those two words based on the # value of bit 1 of self.addr. # Uses just two of the mux channels - 0 and 3 # For convenience, tie the unused addresses to zero m.d.comb += self.ram_mux_addr[1].eq(0) m.d.comb += self.ram_mux_addr[2].eq(0) # Calculate block addresses of the two words - second word may cross 16 # byte block boundary block = Signal(14) m.d.comb += block.eq(self.addr[4:]) m.d.comb += self.ram_mux_addr[0].eq(block) m.d.comb += self.ram_mux_addr[3].eq( Mux(self.ram_mux_phase == 3, block + 1, block)) # Use phase to select the two required words to channels 0 & 3 m.d.comb += self.ram_mux_phase.eq(self.addr[2:4]) # Select correct three half words when data is available, on cycle after # address received. byte_sel = Signal(1) m.d.sync += byte_sel.eq(self.addr[1]) d0 = self.ram_mux_data[0] d3 = self.ram_mux_data[3] dmix = Signal(32) m.d.comb += dmix.eq(Cat(d0[16:], d3[:16])) with m.If(byte_sel == 0): m.d.comb += self.data_out[0].eq(d0) m.d.sync += self.data_out[1].eq(dmix) with m.Else(): m.d.comb += self.data_out[0].eq(dmix) m.d.sync += self.data_out[1].eq(d3)
def elaborate(self, platform): m = Module() # Generate our domain clocks/resets. m.submodules.car = platform.clock_domain_generator() # Create our USB device interface... ulpi = platform.request(platform.default_usb_connection) m.submodules.usb = usb = USBDevice(bus=ulpi) # Add our standard control endpoint to the device. descriptors = self.create_descriptors() usb.add_standard_control_endpoint(descriptors) # Add a stream endpoint to our device. stream_ep = USBStreamOutEndpoint( endpoint_number=self.BULK_ENDPOINT_NUMBER, max_packet_size=self.MAX_BULK_PACKET_SIZE) usb.add_endpoint(stream_ep) leds = Cat( platform.request_optional("led", i, default=NullPin()) for i in range(6)) user_io = Cat( platform.request_optional("user_io", i, default=NullPin()) for i in range(4)) # Always stream our USB data directly onto our User I/O and LEDS. with m.If(stream_ep.stream.valid): m.d.usb += [ leds.eq(stream_ep.stream.payload), user_io.eq(stream_ep.stream.payload), ] # Always accept data as it comes in. m.d.comb += stream_ep.stream.ready.eq(1) # Connect our device as a high speed device by default. m.d.comb += [ usb.connect.eq(1), usb.full_speed_only.eq(1 if os.getenv('LUNA_FULL_ONLY') else 0), ] return m
def elab(self, m: Module): # Registers accumulate incoming values registers = [Signal(8, name="register{i}") for i in range(3)] waiting_to_send = Signal() # Handle accumulating values with m.FSM(reset="BYTE_0"): with m.State("BYTE_0"): m.d.comb += self.input.ready.eq(1) m.d.sync += registers[0].eq(self.input.payload) with m.If(self.input.is_transferring()): m.next = "BYTE_1" with m.State("BYTE_1"): m.d.comb += self.input.ready.eq(1) m.d.sync += registers[1].eq(self.input.payload) with m.If(self.input.is_transferring()): m.next = "BYTE_2" with m.State("BYTE_2"): m.d.comb += self.input.ready.eq(1) m.d.sync += registers[2].eq(self.input.payload) with m.If(self.input.is_transferring()): m.next = "BYTE_3" with m.State("BYTE_3"): m.d.comb += self.input.ready.eq(~waiting_to_send) with m.If(self.input.is_transferring()): m.d.sync += waiting_to_send.eq(1) m.d.sync += self.output.payload.eq( Cat(registers[0], registers[1], registers[2], self.input.payload)) m.next = "BYTE_0" # Handle output whenever ready m.d.comb += self.output.valid.eq(waiting_to_send) with m.If(self.output.is_transferring()): m.d.sync += waiting_to_send.eq(0)
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 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
def elaborate(self, platform): m = Module() # Generate our clock domains. clocking = LunaECP5DomainGenerator(clock_frequencies=CLOCK_FREQUENCIES) m.submodules.clocking = clocking registers = JTAGRegisterInterface(default_read_value=0xDEADBEEF) m.submodules.registers = registers # Simple applet ID register. registers.add_read_only_register(REGISTER_ID, read=0x54455354) # LED test register. led_reg = registers.add_register(REGISTER_LEDS, size=6, name="leds", reset=0b111111) led_out = Cat( [platform.request("led", i, dir="o") for i in range(0, 6)]) m.d.comb += led_out.eq(led_reg) # # Target power test register. # Note: these values assume you've populated the correct AP22814 for # your revision (AP22814As for rev0.2+, and AP22814Bs for rev0.1). # bits [1:0]: 0 = power off # 1 = provide A-port VBUS # 2 = pass through target VBUS # power_test_reg = Signal(3) power_test_write_strobe = Signal() power_test_write_value = Signal(2) registers.add_sfr(REGISTER_TARGET_POWER, read=power_test_reg, write_strobe=power_test_write_strobe, write_signal=power_test_write_value) # Store the values for our enable bits. with m.If(power_test_write_strobe): m.d.sync += power_test_reg[0:2].eq(power_test_write_value) # Decode the enable bits and control the two power supplies. power_a_port = platform.request("power_a_port") power_passthrough = platform.request("pass_through_vbus") with m.If(power_test_reg[0:2] == 1): m.d.comb += [power_a_port.eq(1), power_passthrough.eq(0)] with m.Elif(power_test_reg[0:2] == 2): m.d.comb += [power_a_port.eq(0), power_passthrough.eq(1)] with m.Else(): m.d.comb += [power_a_port.eq(0), power_passthrough.eq(0)] # # User IO GPIO registers. # # Data direction register. user_io_dir = registers.add_register(REGISTER_USER_IO_DIR, size=2) # Pin (input) state register. user_io_in = Signal(2) registers.add_sfr(REGISTER_USER_IO_IN, read=user_io_in) # Output value register. user_io_out = registers.add_register(REGISTER_USER_IO_OUT, size=2) # Grab and connect each of our user-I/O ports our GPIO registers. for i in range(2): pin = platform.request("user_io", i) m.d.comb += [ pin.oe.eq(user_io_dir[i]), user_io_in[i].eq(pin.i), pin.o.eq(user_io_out[i]) ] # # ULPI PHY windows # self.add_ulpi_registers(m, platform, ulpi_bus="target_phy", register_base=REGISTER_TARGET_ADDR) self.add_ulpi_registers(m, platform, ulpi_bus="host_phy", register_base=REGISTER_HOST_ADDR) self.add_ulpi_registers(m, platform, ulpi_bus="sideband_phy", register_base=REGISTER_SIDEBAND_ADDR) # # HyperRAM test connections. # ram_bus = platform.request('ram') psram = HyperRAMInterface(bus=ram_bus, **platform.ram_timings) m.submodules += psram psram_address_changed = Signal() psram_address = registers.add_register( REGISTER_RAM_REG_ADDR, write_strobe=psram_address_changed) registers.add_sfr(REGISTER_RAM_VALUE, read=psram.read_data) # Hook up our PSRAM. m.d.comb += [ ram_bus.reset.eq(0), psram.single_page.eq(0), psram.perform_write.eq(0), psram.register_space.eq(1), psram.final_word.eq(1), psram.start_transfer.eq(psram_address_changed), psram.address.eq(psram_address), ] return m