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

Add Cynthion Gateware Tutorials #197

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

antoinevg
Copy link
Member

@antoinevg antoinevg commented Oct 22, 2024

This PR adds three two five Gateware tutorials of increasing complexity to the documentation:

  1. Gateware Blinky - Ye olde blinky. Includes instructions for installing the FPGA toolchain and uploading bitstreams.
  2. USB Gateware: Part 1 - Enumeration
  3. USB Gateware: Part 2 - WCID Descriptors
  4. USB Gateware: Part 3 - Control Transfers
  5. USB Gateware: Part 4 - Bulk Transfers

NOTE: In the interests of releasing some tutorials sooner rather than later I'll be adding the USB Audio Class Device tutorial in a separate PR.


Rendered: https://cynthion--197.org.readthedocs.build/en/197/

Depends on:

@antoinevg antoinevg force-pushed the antoinevg/gateware-tutorials branch from ae6a2c3 to f06f505 Compare October 29, 2024 13:32
@antoinevg antoinevg force-pushed the antoinevg/gateware-tutorials branch 4 times, most recently from dacfe7f to 743597d Compare November 5, 2024 15:40
@antoinevg antoinevg force-pushed the antoinevg/gateware-tutorials branch 12 times, most recently from 96ea10f to ccef718 Compare December 5, 2024 08:33
@antoinevg antoinevg force-pushed the antoinevg/gateware-tutorials branch from ccef718 to 2361f74 Compare December 10, 2024 12:22
@antoinevg antoinevg force-pushed the antoinevg/gateware-tutorials branch from 2361f74 to c67e981 Compare December 10, 2024 13:18
@antoinevg antoinevg marked this pull request as ready for review December 10, 2024 13:34
@mossmann mossmann requested a review from mndza December 10, 2024 17:06
descriptors = self.create_standard_descriptors()
control_endpoint = usb.add_standard_control_endpoint(
descriptors,
avoid_blockram=True # allow dynamic string descriptors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not really the reason. The avoid_blockram option is enabled because the blockram handler does not support non-contiguous indices for string descriptors, and the Microsoft one needs to be at 0xee. We considered implementing an internal mapping to handle these cases.

Comment on lines +122 to +166
# create a memory we will use as a data source/sink for our bulk endpoints
m.submodules.ram = ram = Memory(
width = 8,
depth = MAX_PACKET_SIZE,
init = [0] * MAX_PACKET_SIZE
)
w_port = ram.write_port(domain="usb")
r_port = ram.read_port(domain="usb")

# set the write_active status to the write port's enable status
m.d.comb += self.write_active.eq(w_port.en)

# shortcuts
stream_out = self.stream_out
stream_in = self.stream_in

# - EP 0x01 OUT logic ------------------------------------------------

# let the stream know we're always ready to start reading
m.d.comb += stream_out.ready.eq(1)

# wire the payload from the host up to our memory write port
m.d.comb += w_port.data.eq(stream_out.payload)

# read each byte coming in on the stream and write it to memory
with m.If(stream_out.valid & stream_out.ready):
m.d.comb += w_port.en.eq(1)
m.d.usb += w_port.addr.eq(w_port.addr + 1);
with m.Else():
m.d.comb += w_port.en.eq(0)
m.d.usb += w_port.addr.eq(0)

# - EP 0x82 IN logic -------------------------------------------------

# wire the payload to the host up to our memory read port
m.d.comb += stream_in.payload.eq(r_port.data)

# when the stream is ready and the write port is not active,
# read each byte from memory and write it out to the stream
with m.If(stream_in.ready & ~w_port.en):
m.d.usb += stream_in.valid.eq(1)
m.d.usb += r_port.addr.eq(r_port.addr + 1)
with m.Else():
m.d.usb += stream_in.valid.eq(0)
m.d.usb += r_port.addr.eq(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest a simpler example here with a SyncFIFO. Note it also blocks the stream when the FIFO is full:

m.submodules.mem = mem = DomainRenamer("usb")(fifo.SyncFIFO(width=8, depth=MAX_PACKET_SIZE))

...

m.d.comb += mem.w_data.eq(stream_out.payload)
m.d.comb += mem.w_en.eq(stream_out.valid)
m.d.comb += stream_out.ready.eq(mem.w_rdy)

m.d.comb += stream_in.payload.eq(mem.r_data)
m.d.comb += stream_in.valid.eq(mem.r_rdy)
m.d.comb += mem.r_en.eq(stream_in.ready)

Since Amaranth HDL v0.5.0, you can do this instead:

wiring.connect(m, stream_out, mem.w_stream)
wiring.connect(m, mem.r_stream, stream_in)

half_freq: int = int(60e6 // 2)
timer: Signal(25) = Signal(range(half_freq + 1))

with m.If(timer == half_freq):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the correct thing would be to check for timer == half_freq - 1. This is adding an extra cycle so the blinking period is not 500 ms (but really close!). For that reason, timer could just be Signal(range(half_freq)).


Once our endpoint descriptors have been added to our device configuration we will need some gateware that will be able to respond to USB requests from the host and allow us to receive and transmit data.

LUNA provides the ``USBStreamOutEndpoint`` and ``USBStreamInEndpoint`` modules which conform to the `Amaranth Data streams <https://amaranth-lang.org/docs/amaranth/latest/stdlib/stream.html>`__ interface. Simply put, streams provide a uniform mechanism for unidirectional exchange of arbitrary data between gateware modules.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which conform to the Amaranth Data streams <https://amaranth-lang.org/docs/amaranth/latest/stdlib/stream.html>__ interface

This is still not true, right?

Copy link
Contributor

@mndza mndza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! 😃 Added some comments/suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants