Skip to content

Commit

Permalink
(WIP) Add an alternative setting using interrupt endpoints.
Browse files Browse the repository at this point in the history
To improve worst-case latency while still keeping guaranteed
delivery.
  • Loading branch information
whitequark committed Nov 10, 2019
1 parent e6ff3cb commit d6335e0
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 51 deletions.
96 changes: 67 additions & 29 deletions firmware/main.c
Expand Up @@ -53,37 +53,64 @@ usb_desc_device_qualifier_c usb_device_qualifier = {
usb_desc_interface_c usb_interface_0_disabled =
USB_INTERFACE(/*bInterfaceNumber=*/0, /*bAlternateSetting=*/0, /*bNumEndpoints=*/0,
/*iInterface=*/6);
usb_desc_interface_c usb_interface_0_double =
usb_desc_interface_c usb_interface_0_double_bulk =
USB_INTERFACE(/*bInterfaceNumber=*/0, /*bAlternateSetting=*/1, /*bNumEndpoints=*/2,
/*iInterface=*/7);
usb_desc_interface_c usb_interface_0_quad =
usb_desc_interface_c usb_interface_0_quad_bulk =
USB_INTERFACE(/*bInterfaceNumber=*/0, /*bAlternateSetting=*/1, /*bNumEndpoints=*/2,
/*iInterface=*/8);
usb_desc_interface_c usb_interface_0_double_interrupt =
USB_INTERFACE(/*bInterfaceNumber=*/0, /*bAlternateSetting=*/2, /*bNumEndpoints=*/2,
/*iInterface=*/9);
usb_desc_interface_c usb_interface_0_quad_interrupt =
USB_INTERFACE(/*bInterfaceNumber=*/0, /*bAlternateSetting=*/2, /*bNumEndpoints=*/2,
/*iInterface=*/10);

usb_desc_interface_c usb_interface_1_disabled =
USB_INTERFACE(/*bInterfaceNumber=*/1, /*bAlternateSetting=*/0, /*bNumEndpoints=*/0,
/*iInterface=*/6);
usb_desc_interface_c usb_interface_1_double =
usb_desc_interface_c usb_interface_1_double_bulk =
USB_INTERFACE(/*bInterfaceNumber=*/1, /*bAlternateSetting=*/1, /*bNumEndpoints=*/2,
/*iInterface=*/7);
usb_desc_interface_c usb_interface_1_double_interrupt =
USB_INTERFACE(/*bInterfaceNumber=*/1, /*bAlternateSetting=*/2, /*bNumEndpoints=*/2,
/*iInterface=*/9);

#define USB_BULK_ENDPOINT(bEndpointAddress_) \
#define USB_ENDPOINT(bEndpointAddress_, bmAttributes_, wMaxPacketSize_, bInterval_) \
{ \
.bLength = sizeof(struct usb_desc_endpoint), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = bEndpointAddress_, \
.bmAttributes = USB_XFER_BULK, \
.wMaxPacketSize = 512, \
.bInterval = 0, \
.bmAttributes = bmAttributes_, \
.wMaxPacketSize = wMaxPacketSize_, \
.bInterval = bInterval_, \
}

usb_desc_endpoint_c usb_endpoint_2_out =
USB_BULK_ENDPOINT(/*bEndpointAddress=*/2|USB_DIR_OUT);
usb_desc_endpoint_c usb_endpoint_4_out =
USB_BULK_ENDPOINT(/*bEndpointAddress=*/4|USB_DIR_OUT);
usb_desc_endpoint_c usb_endpoint_6_in =
USB_BULK_ENDPOINT(/*bEndpointAddress=*/6|USB_DIR_IN );
usb_desc_endpoint_c usb_endpoint_8_in =
USB_BULK_ENDPOINT(/*bEndpointAddress=*/8|USB_DIR_IN );
usb_desc_endpoint_c usb_endpoint_2_out_bulk =
USB_ENDPOINT(/*bEndpointAddress=*/2|USB_DIR_OUT, /*bmAttributes=*/USB_XFER_BULK,
/*wMaxPacketSize=*/512, /*bInterval=*/0);
usb_desc_endpoint_c usb_endpoint_4_out_bulk =
USB_ENDPOINT(/*bEndpointAddress=*/4|USB_DIR_OUT, /*bmAttributes=*/USB_XFER_BULK,
/*wMaxPacketSize=*/512, /*bInterval=*/0);
usb_desc_endpoint_c usb_endpoint_6_in_bulk =
USB_ENDPOINT(/*bEndpointAddress=*/6|USB_DIR_IN , /*bmAttributes=*/USB_XFER_BULK,
/*wMaxPacketSize=*/512, /*bInterval=*/0);
usb_desc_endpoint_c usb_endpoint_8_in_bulk =
USB_ENDPOINT(/*bEndpointAddress=*/8|USB_DIR_IN , /*bmAttributes=*/USB_XFER_BULK,
/*wMaxPacketSize=*/512, /*bInterval=*/0);

usb_desc_endpoint_c usb_endpoint_2_out_interrupt =
USB_ENDPOINT(/*bEndpointAddress=*/2|USB_DIR_OUT, /*bmAttributes=*/USB_XFER_INTERRUPT,
/*wMaxPacketSize=*/512|USB_TX_3_PER_MICROFRAME, /*bInterval=*/1);
usb_desc_endpoint_c usb_endpoint_4_out_interrupt =
USB_ENDPOINT(/*bEndpointAddress=*/4|USB_DIR_OUT, /*bmAttributes=*/USB_XFER_INTERRUPT,
/*wMaxPacketSize=*/512|USB_TX_3_PER_MICROFRAME, /*bInterval=*/1);
usb_desc_endpoint_c usb_endpoint_6_in_interrupt =
USB_ENDPOINT(/*bEndpointAddress=*/6|USB_DIR_IN , /*bmAttributes=*/USB_XFER_INTERRUPT,
/*wMaxPacketSize=*/512|USB_TX_3_PER_MICROFRAME, /*bInterval=*/1);
usb_desc_endpoint_c usb_endpoint_8_in_interrupt =
USB_ENDPOINT(/*bEndpointAddress=*/8|USB_DIR_IN , /*bmAttributes=*/USB_XFER_INTERRUPT,
/*wMaxPacketSize=*/512|USB_TX_3_PER_MICROFRAME, /*bInterval=*/1);

usb_configuration_c usb_config_2_pipes = {
{
Expand All @@ -96,14 +123,20 @@ usb_configuration_c usb_config_2_pipes = {
.bMaxPower = 250,
},
{
{ .interface = &usb_interface_0_disabled },
{ .interface = &usb_interface_0_double },
{ .endpoint = &usb_endpoint_2_out },
{ .endpoint = &usb_endpoint_6_in },
{ .interface = &usb_interface_1_disabled },
{ .interface = &usb_interface_1_double },
{ .endpoint = &usb_endpoint_4_out },
{ .endpoint = &usb_endpoint_8_in },
{ .interface = &usb_interface_0_disabled },
{ .interface = &usb_interface_0_double_bulk },
{ .endpoint = &usb_endpoint_2_out_bulk },
{ .endpoint = &usb_endpoint_6_in_bulk },
{ .interface = &usb_interface_0_double_interrupt },
{ .endpoint = &usb_endpoint_2_out_interrupt },
{ .endpoint = &usb_endpoint_6_in_interrupt },
{ .interface = &usb_interface_1_disabled },
{ .interface = &usb_interface_1_double_bulk },
{ .endpoint = &usb_endpoint_4_out_bulk },
{ .endpoint = &usb_endpoint_8_in_bulk },
{ .interface = &usb_interface_1_double_interrupt },
{ .endpoint = &usb_endpoint_4_out_interrupt },
{ .endpoint = &usb_endpoint_8_in_interrupt },
{ 0 }
}
};
Expand All @@ -119,10 +152,13 @@ usb_configuration_c usb_config_1_pipe = {
.bMaxPower = 250,
},
{
{ .interface = &usb_interface_0_disabled },
{ .interface = &usb_interface_0_quad },
{ .endpoint = &usb_endpoint_2_out },
{ .endpoint = &usb_endpoint_6_in },
{ .interface = &usb_interface_0_disabled },
{ .interface = &usb_interface_0_quad_bulk },
{ .endpoint = &usb_endpoint_2_out_bulk },
{ .endpoint = &usb_endpoint_6_in_bulk },
{ .interface = &usb_interface_0_quad_interrupt },
{ .endpoint = &usb_endpoint_2_out_interrupt },
{ .endpoint = &usb_endpoint_6_in_interrupt },
{ 0 }
}
};
Expand All @@ -146,8 +182,10 @@ usb_ascii_string_c usb_strings[] = {
[4] = "Pipe P at {4x512B EP2OUT/EP6IN}",
// Interfaces
[5] = "Disabled",
[6] = "Double-buffered 512B",
[7] = "Quad-buffered 512B",
[6] = "Double-buffered 512B BULK",
[7] = "Quad-buffered 512B BULK",
[8] = "Double-buffered 512B INTERRUPT",
[9] = "Quad-buffered 512B INTERRUPT",
};

usb_descriptor_set_c usb_descriptor_set = {
Expand Down
16 changes: 13 additions & 3 deletions software/glasgow/access/__init__.py
@@ -1,12 +1,22 @@
from abc import ABCMeta, abstractmethod
from enum import Enum
from nmigen.compat import *

from ..gateware.pads import Pads


__all__ = ["AccessArguments"]
__all__ += ["AccessMultiplexer", "AccessMultiplexerInterface"]
__all__ += ["AccessDemultiplexer", "AccessDemultiplexerInterface"]
__all__ = [
"AccessArguments",
"AccessMultiplexer", "AccessMultiplexerInterface",
"AccessDemultiplexer", "AccessDemultiplexerInterface",
"AccessHint"
]


class AccessHint(Enum):
DEFAULT = "default"
BANDWIDTH = "bandwidth"
LATENCY = "latency"


class AccessArguments(metaclass=ABCMeta):
Expand Down
50 changes: 34 additions & 16 deletions software/glasgow/access/direct/demultiplexer.py
Expand Up @@ -5,7 +5,7 @@
from ...support.logging import *
from ...support.chunked_fifo import *
from ...support.task_queue import *
from .. import AccessDemultiplexer, AccessDemultiplexerInterface
from .. import AccessHint, AccessDemultiplexer, AccessDemultiplexerInterface


# On Linux, the total amount of in-flight USB requests for the entire system is limited
Expand Down Expand Up @@ -137,10 +137,18 @@ async def claim_interface(self, applet, mux_interface, args, pull_low=set(), pul


class DirectDemultiplexerInterface(AccessDemultiplexerInterface):
def __init__(self, device, applet, mux_interface,
read_buffer_size=None, write_buffer_size=None):
def __init__(self, device, applet, mux_interface, *,
hint="default", read_buffer_size=None, write_buffer_size=None):
super().__init__(device, applet)

hint = AccessHint(hint)
if hint in (AccessHint.DEFAULT, AccessHint.BANDWIDTH):
transfer_type = usb1.TRANSFER_TYPE_BULK
elif hint == AccessHint.LATENCY:
transfer_type = usb1.TRANSFER_TYPE_INTERRUPT
else:
assert False

self._write_buffer_size = write_buffer_size
self._read_buffer_size = read_buffer_size
self._in_pushback = asyncio.Condition()
Expand All @@ -158,18 +166,28 @@ def __init__(self, device, applet, mux_interface,
assert self._pipe_num < len(interfaces)
interface = interfaces[self._pipe_num]

settings = list(interface.iterSettings())
setting = settings[1] # alt-setting 1 has the actual endpoints
for endpoint in setting.iterEndpoints():
address = endpoint.getAddress()
packet_size = endpoint.getMaxPacketSize()
if address & usb1.ENDPOINT_DIR_MASK == usb1.ENDPOINT_IN:
self._endpoint_in = address
self._in_packet_size = packet_size
if address & usb1.ENDPOINT_DIR_MASK == usb1.ENDPOINT_OUT:
self._endpoint_out = address
self._out_packet_size = packet_size
assert self._endpoint_in != None and self._endpoint_out != None
self._alt_setting = None
for setting in interface.iterSettings():
self._endpoint_in = None
self._endpoint_out = None
for endpoint in setting.iterEndpoints():
address = endpoint.getAddress()
attributes = endpoint.getAttributes()
packet_size = endpoint.getMaxPacketSize()
if (address & usb1.ENDPOINT_DIR_MASK == usb1.ENDPOINT_IN and
attributes & usb1.TRANSFER_TYPE_MASK == transfer_type):
self._endpoint_in = address
self._in_packet_size = packet_size
if (address & usb1.ENDPOINT_DIR_MASK == usb1.ENDPOINT_OUT and
attributes & usb1.TRANSFER_TYPE_MASK == transfer_type):
self._endpoint_out = address
self._out_packet_size = packet_size
if self._endpoint_in is not None and self._endpoint_out is not None:
self._alt_setting = setting.getAlternateSetting()
break
assert self._alt_setting is not None
self.logger.debug("FIFO: choosing alternate setting %d for access hint %s",
self._alt_setting, hint.name)

self._interface = self.device.usb_handle.claimInterface(self._pipe_num)
self._in_tasks = TaskQueue()
Expand All @@ -193,7 +211,7 @@ async def reset(self):
await self.device.write_register(self._addr_reset, 1)

self.logger.trace("FIFO: synchronizing buffers")
self.device.usb_handle.setInterfaceAltSetting(self._pipe_num, 1)
self.device.usb_handle.setInterfaceAltSetting(self._pipe_num, self._alt_setting)
self._in_buffer .clear()
self._out_buffer.clear()

Expand Down
3 changes: 2 additions & 1 deletion software/glasgow/applet/audio/yamaha_opx/__init__.py
Expand Up @@ -169,7 +169,7 @@ def __init__(self, pads, master_cyc):

###

self.submodules.clkgen = ClockGen(master_cyc)
self.submodules.clkgen = CEInserter()(ClockGen(master_cyc))
self.comb += self.stb_m.eq(self.clkgen.stb_r)

self.comb += [
Expand Down Expand Up @@ -245,6 +245,7 @@ def __init__(self, pads, in_fifo, out_fifo, sample_decoder_cls, channel_count,
# The code below assumes that the FSM clock is under ~50 MHz, which frees us from the need
# to explicitly satisfy setup/hold timings.
self.submodules.control_fsm = FSM()
self.comb += self.cpu_bus.clkgen.ce.eq(out_fifo.readable)
self.control_fsm.act("IDLE",
NextValue(cpu_bus.oe, 1),
If(out_fifo.readable,
Expand Down
6 changes: 5 additions & 1 deletion software/glasgow/applet/internal/benchmark/__init__.py
Expand Up @@ -127,13 +127,17 @@ def add_run_arguments(cls, parser, access):
parser.add_argument(
"-c", "--count", metavar="COUNT", type=int, default=1 << 23,
help="transfer COUNT bytes (default: %(default)s)")
parser.add_argument(
"--hint", metavar="HINT", choices=("bandwidth", "latency"), default="default",
help="optimize interface for HINT")

parser.add_argument(
dest="modes", metavar="MODE", type=str, nargs="*", choices=[[]] + cls.__all_modes,
help="run benchmark mode MODE (default: {})".format(" ".join(cls.__all_modes)))

async def run(self, device, args):
iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args=None)
iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args=None,
hint=args.hint)

golden = bytearray()
while len(golden) < args.count:
Expand Down
2 changes: 1 addition & 1 deletion vendor/libfx2

0 comments on commit d6335e0

Please sign in to comment.