Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gateware: ApolloAdvertiser generates rising edges now #73

Merged
merged 1 commit into from
Jun 6, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 24 additions & 30 deletions apollo_fpga/gateware/advertiser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,54 +7,48 @@
""" Controllers for communicating with Apollo through the FPGA_ADV pin """

from amaranth import Elaboratable, Module, Signal, Mux
from amaranth_stdio.serial import AsyncSerialTX

from luna.gateware.usb.usb2.request import USBRequestHandler
from usb_protocol.types import USBRequestType, USBRequestRecipient


class ApolloAdvertiser(Elaboratable):
""" Gateware that implements a periodic announcement to Apollo using the FPGA_ADV pin.
""" Gateware that implements an announcement signal for Apollo using the FPGA_ADV pin.

Currently it is used to tell Apollo that the gateware wants to use the CONTROL port.
Used to tell Apollo that the gateware wants to use the CONTROL port.
Apollo will keep the port switch connected to the FPGA after a reset as long as this
message is received periodically.
Once the port is lost, Apollo will ignore further messages until a specific vendor
request is called.
signal is being received and the port takeover is allowed.

I/O ports:
I: stop -- Advertisement messages are stopped if this line is asserted.
I: stop -- Advertisement signal is stopped if this line is asserted.
"""
def __init__(self):
self.stop = Signal()
def __init__(self, pad=None, clk_freq_hz=None):
self.pad = pad
self.clk_freq_hz = clk_freq_hz
self.stop = Signal()

def default_request_handler(self, if_number):
return ApolloAdvertiserRequestHandler(if_number, self.stop)

def elaborate(self, platform):
m = Module()

clk_freq = platform.DEFAULT_CLOCK_FREQUENCIES_MHZ["sync"] * 1e6

# Communication is done with a serial transmitter (unidirectional)
baudrate = 9600
divisor = int(clk_freq // baudrate)
fpga_adv = AsyncSerialTX(divisor=divisor, data_bits=8, parity="even")
m.submodules += fpga_adv

# Counter with 50ms period
period = int(clk_freq * 50e-3)
timer = Signal(range(period))
m.d.sync += timer.eq(Mux(timer == period-1, 0, timer+1))

# Trigger announcement when the counter overflows
m.d.comb += [
fpga_adv.data .eq(ord('A')),
fpga_adv.ack .eq((timer == 0) & ~self.stop),
]

# Drive the FPGA_ADV pin with the serial transmitter
m.d.comb += platform.request("int").o.eq(fpga_adv.o)
# Handle default values.
if self.pad is None:
self.pad = platform.request("int")
if self.clk_freq_hz is None:
self.clk_freq_hz = platform.DEFAULT_CLOCK_FREQUENCIES_MHZ["sync"] * 1e6

# Generate clock with 20ms period.
half_period = int(self.clk_freq_hz * 10e-3)
timer = Signal(range(half_period))
clk = Signal()
m.d.sync += timer.eq(Mux(timer == half_period-1, 0, timer+1))
with m.If((timer == 0) & (~self.stop)):
m.d.sync += clk.eq(~clk)

# Drive the FPGA_ADV pin with the generated clock signal.
m.d.comb += self.pad.o.eq(clk)

return m

Expand Down