Documentation/driver-api/xillybus.rst GitHub 원문 ↗

Linux 6.18.37 · Driver API

Xillybus driver for generic FPGA interface

Xillybus FPGA IP 코어, 문자 장치 파이프, 동기·비동기 DMA 스트리밍, IDT probing과 버퍼 관리 구조를 설명하는 한국어 전문 번역입니다.

Source pathDocumentation/driver-api/xillybus.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

xillybus.rst:1-379

Xillybus는 프로젝트별 FPGA 데이터 경로를 Linux 문자 장치로 연결하는 IP 코어와 범용 드라이버입니다. IP 코어의 IDT가 파이프 수·방향·동기화·데이터 폭·DMA 버퍼 속성을 기술하고, 드라이버는 이를 읽어 `/dev/xillybus_*` 장치와 DMA 채널을 자동 구성합니다.

사용자는 일반 `read()`·`write()`와 `poll()`·`select()`를 사용하며, 동기 파이프는 호출 단위 전달 완료를 보장하고 비동기 파이프는 DMA 버퍼링으로 연속 스트림을 처리합니다. 부분 버퍼 timeout, autoflush, granularity leftover 처리가 파이프와 유사한 동작을 유지합니다.

안전성 측면에서 드라이버는 FPGA 장치 레지스터를 읽지 않고 FPGA가 DMA로 기록한 채널 0 메시지만 소비합니다. 따라서 실행 중 FPGA가 재프로그램되어 PCIe 장치가 사라져도 위험한 MMIO read를 피합니다.

Xillybus 전체 데이터 경로
FPGA user logic reads or writes a hardware FIFOXillybus IP core moves data through DMA buffersChannel 0 carries release and nonempty messagesLinux driver manages flow control and partial buffersUser space accesses `/dev/xillybus_*` as ordinary pipes

FPGA FIFO와 사용자 공간 파일 I/O 사이를 IDT 기반 채널과 DMA 버퍼가 연결합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 ==========================================
2 Xillybus driver for generic FPGA interface
3 ==========================================
4
5 :Author: Eli Billauer, Xillybus Ltd. (http://xillybus.com)
6 :Email: eli.billauer@gmail.com or as advertised on Xillybus' site.
7
8 .. Contents:
9
10 - Introduction
11 -- Background
12 -- Xillybus Overview
13
14 - Usage
15 -- User interface
16 -- Synchronization
17 -- Seekable pipes
18
19 - Internals
20 -- Source code organization
21 -- Pipe attributes
22 -- Host never reads from the FPGA
23 -- Channels, pipes, and the message channel
24 -- Data streaming
25 -- Data granularity
26 -- Probing
27 -- Buffer allocation
28 -- The "nonempty" message (supporting poll)
29
30
31 Introduction
32 ============
33
34 Background
35 ----------
36
37 An FPGA (Field Programmable Gate Array) is a piece of logic hardware, which
38 can be programmed to become virtually anything that is usually found as a
39 dedicated chipset: For instance, a display adapter, network interface card,
40 or even a processor with its peripherals. FPGAs are the LEGO of hardware:
41 Based upon certain building blocks, you make your own toys the way you like
42 them. It's usually pointless to reimplement something that is already
43 available on the market as a chipset, so FPGAs are mostly used when some
44 special functionality is needed, and the production volume is relatively low
45 (hence not justifying the development of an ASIC).
46
47 The challenge with FPGAs is that everything is implemented at a very low
48 level, even lower than assembly language. In order to allow FPGA designers to
49 focus on their specific project, and not reinvent the wheel over and over
50 again, pre-designed building blocks, IP cores, are often used. These are the
51 FPGA parallels of library functions. IP cores may implement certain
52 mathematical functions, a functional unit (e.g. a USB interface), an entire
53 processor (e.g. ARM) or anything that might come handy. Think of them as a
54 building block, with electrical wires dangling on the sides for connection to
55 other blocks.
56
57 One of the daunting tasks in FPGA design is communicating with a fullblown
58 operating system (actually, with the processor running it): Implementing the
59 low-level bus protocol and the somewhat higher-level interface with the host
60 (registers, interrupts, DMA etc.) is a project in itself. When the FPGA's
61 function is a well-known one (e.g. a video adapter card, or a NIC), it can
62 make sense to design the FPGA's interface logic specifically for the project.
63 A special driver is then written to present the FPGA as a well-known interface
64 to the kernel and/or user space. In that case, there is no reason to treat the
65 FPGA differently than any device on the bus.
66
67 It's however common that the desired data communication doesn't fit any well-
68 known peripheral function. Also, the effort of designing an elegant
69 abstraction for the data exchange is often considered too big. In those cases,
70 a quicker and possibly less elegant solution is sought: The driver is
71 effectively written as a user space program, leaving the kernel space part
72 with just elementary data transport. This still requires designing some
73 interface logic for the FPGA, and write a simple ad-hoc driver for the kernel.
74
75 Xillybus Overview
76 -----------------
77
78 Xillybus is an IP core and a Linux driver. Together, they form a kit for
79 elementary data transport between an FPGA and the host, providing pipe-like
80 data streams with a straightforward user interface. It's intended as a low-
81 effort solution for mixed FPGA-host projects, for which it makes sense to
82 have the project-specific part of the driver running in a user-space program.
83
84 Since the communication requirements may vary significantly from one FPGA
85 project to another (the number of data pipes needed in each direction and
86 their attributes), there isn't one specific chunk of logic being the Xillybus
87 IP core. Rather, the IP core is configured and built based upon a
88 specification given by its end user.
89
90 Xillybus presents independent data streams, which resemble pipes or TCP/IP
91 communication to the user. At the host side, a character device file is used
92 just like any pipe file. On the FPGA side, hardware FIFOs are used to stream
93 the data. This is contrary to a common method of communicating through fixed-
94 sized buffers (even though such buffers are used by Xillybus under the hood).
95 There may be more than a hundred of these streams on a single IP core, but
96 also no more than one, depending on the configuration.
97
98 In order to ease the deployment of the Xillybus IP core, it contains a simple
99 data structure which completely defines the core's configuration. The Linux
100 driver fetches this data structure during its initialization process, and sets
101 up the DMA buffers and character devices accordingly. As a result, a single
102 driver is used to work out of the box with any Xillybus IP core.
103
104 The data structure just mentioned should not be confused with PCI's
105 configuration space or the Flattened Device Tree.
106
107 Usage
108 =====
109
110 User interface
111 --------------
112
113 On the host, all interface with Xillybus is done through /dev/xillybus_*
114 device files, which are generated automatically as the drivers loads. The
115 names of these files depend on the IP core that is loaded in the FPGA (see
116 Probing below). To communicate with the FPGA, open the device file that
117 corresponds to the hardware FIFO you want to send data or receive data from,
118 and use plain write() or read() calls, just like with a regular pipe. In
119 particular, it makes perfect sense to go::
120
121 $ cat mydata > /dev/xillybus_thisfifo
122
123 $ cat /dev/xillybus_thatfifo > hisdata
124
125 possibly pressing CTRL-C as some stage, even though the xillybus_* pipes have
126 the capability to send an EOF (but may not use it).
127
128 The driver and hardware are designed to behave sensibly as pipes, including:
129
130 * Supporting non-blocking I/O (by setting O_NONBLOCK on open() ).
131
132 * Supporting poll() and select().
133
134 * Being bandwidth efficient under load (using DMA) but also handle small
135 pieces of data sent across (like TCP/IP) by autoflushing.
136
137 A device file can be read only, write only or bidirectional. Bidirectional
138 device files are treated like two independent pipes (except for sharing a
139 "channel" structure in the implementation code).
140
141 Synchronization
142 ---------------
143
144 Xillybus pipes are configured (on the IP core) to be either synchronous or
145 asynchronous. For a synchronous pipe, write() returns successfully only after
146 some data has been submitted and acknowledged by the FPGA. This slows down
147 bulk data transfers, and is nearly impossible for use with streams that
148 require data at a constant rate: There is no data transmitted to the FPGA
149 between write() calls, in particular when the process loses the CPU.
150
151 When a pipe is configured asynchronous, write() returns if there was enough
152 room in the buffers to store any of the data in the buffers.
153
154 For FPGA to host pipes, asynchronous pipes allow data transfer from the FPGA
155 as soon as the respective device file is opened, regardless of if the data
156 has been requested by a read() call. On synchronous pipes, only the amount
157 of data requested by a read() call is transmitted.
158
159 In summary, for synchronous pipes, data between the host and FPGA is
160 transmitted only to satisfy the read() or write() call currently handled
161 by the driver, and those calls wait for the transmission to complete before
162 returning.
163
164 Note that the synchronization attribute has nothing to do with the possibility
165 that read() or write() completes less bytes than requested. There is a
166 separate configuration flag ("allowpartial") that determines whether such a
167 partial completion is allowed.
168
169 Seekable pipes
170 --------------
171
172 A synchronous pipe can be configured to have the stream's position exposed
173 to the user logic at the FPGA. Such a pipe is also seekable on the host API.
174 With this feature, a memory or register interface can be attached on the
175 FPGA side to the seekable stream. Reading or writing to a certain address in
176 the attached memory is done by seeking to the desired address, and calling
177 read() or write() as required.
178
179
180 Internals
181 =========
182
183 Source code organization
184 ------------------------
185
186 The Xillybus driver consists of a core module, xillybus_core.c, and modules
187 that depend on the specific bus interface (xillybus_of.c and xillybus_pcie.c).
188
189 The bus specific modules are those probed when a suitable device is found by
190 the kernel. Since the DMA mapping and synchronization functions, which are bus
191 dependent by their nature, are used by the core module, a
192 xilly_endpoint_hardware structure is passed to the core module on
193 initialization. This structure is populated with pointers to wrapper functions
194 which execute the DMA-related operations on the bus.
195
196 Pipe attributes
197 ---------------
198
199 Each pipe has a number of attributes which are set when the FPGA component
200 (IP core) is built. They are fetched from the IDT (the data structure which
201 defines the core's configuration, see Probing below) by xilly_setupchannels()
202 in xillybus_core.c as follows:
203
204 * is_writebuf: The pipe's direction. A non-zero value means it's an FPGA to
205 host pipe (the FPGA "writes").
206
207 * channelnum: The pipe's identification number in communication between the
208 host and FPGA.
209
210 * format: The underlying data width. See Data Granularity below.
211
212 * allowpartial: A non-zero value means that a read() or write() (whichever
213 applies) may return with less than the requested number of bytes. The common
214 choice is a non-zero value, to match standard UNIX behavior.
215
216 * synchronous: A non-zero value means that the pipe is synchronous. See
217 Synchronization above.
218
219 * bufsize: Each DMA buffer's size. Always a power of two.
220
221 * bufnum: The number of buffers allocated for this pipe. Always a power of two.
222
223 * exclusive_open: A non-zero value forces exclusive opening of the associated
224 device file. If the device file is bidirectional, and already opened only in
225 one direction, the opposite direction may be opened once.
226
227 * seekable: A non-zero value indicates that the pipe is seekable. See
228 Seekable pipes above.
229
230 * supports_nonempty: A non-zero value (which is typical) indicates that the
231 hardware will send the messages that are necessary to support select() and
232 poll() for this pipe.
233
234 Host never reads from the FPGA
235 ------------------------------
236
237 Even though PCI Express is hotpluggable in general, a typical motherboard
238 doesn't expect a card to go away all of the sudden. But since the PCIe card
239 is based upon reprogrammable logic, a sudden disappearance from the bus is
240 quite likely as a result of an accidental reprogramming of the FPGA while the
241 host is up. In practice, nothing happens immediately in such a situation. But
242 if the host attempts to read from an address that is mapped to the PCI Express
243 device, that leads to an immediate freeze of the system on some motherboards,
244 even though the PCIe standard requires a graceful recovery.
245
246 In order to avoid these freezes, the Xillybus driver refrains completely from
247 reading from the device's register space. All communication from the FPGA to
248 the host is done through DMA. In particular, the Interrupt Service Routine
249 doesn't follow the common practice of checking a status register when it's
250 invoked. Rather, the FPGA prepares a small buffer which contains short
251 messages, which inform the host what the interrupt was about.
252
253 This mechanism is used on non-PCIe buses as well for the sake of uniformity.
254
255
256 Channels, pipes, and the message channel
257 ----------------------------------------
258
259 Each of the (possibly bidirectional) pipes presented to the user is allocated
260 a data channel between the FPGA and the host. The distinction between channels
261 and pipes is necessary only because of channel 0, which is used for interrupt-
262 related messages from the FPGA, and has no pipe attached to it.
263
264 Data streaming
265 --------------
266
267 Even though a non-segmented data stream is presented to the user at both
268 sides, the implementation relies on a set of DMA buffers which is allocated
269 for each channel. For the sake of illustration, let's take the FPGA to host
270 direction: As data streams into the respective channel's interface in the
271 FPGA, the Xillybus IP core writes it to one of the DMA buffers. When the
272 buffer is full, the FPGA informs the host about that (appending a
273 XILLYMSG_OPCODE_RELEASEBUF message channel 0 and sending an interrupt if
274 necessary). The host responds by making the data available for reading through
275 the character device. When all data has been read, the host writes on the
276 FPGA's buffer control register, allowing the buffer's overwriting. Flow
277 control mechanisms exist on both sides to prevent underflows and overflows.
278
279 This is not good enough for creating a TCP/IP-like stream: If the data flow
280 stops momentarily before a DMA buffer is filled, the intuitive expectation is
281 that the partial data in buffer will arrive anyhow, despite the buffer not
282 being completed. This is implemented by adding a field in the
283 XILLYMSG_OPCODE_RELEASEBUF message, through which the FPGA informs not just
284 which buffer is submitted, but how much data it contains.
285
286 But the FPGA will submit a partially filled buffer only if directed to do so
287 by the host. This situation occurs when the read() method has been blocking
288 for XILLY_RX_TIMEOUT jiffies (currently 10 ms), after which the host commands
289 the FPGA to submit a DMA buffer as soon as it can. This timeout mechanism
290 balances between bus bandwidth efficiency (preventing a lot of partially
291 filled buffers being sent) and a latency held fairly low for tails of data.
292
293 A similar setting is used in the host to FPGA direction. The handling of
294 partial DMA buffers is somewhat different, though. The user can tell the
295 driver to submit all data it has in the buffers to the FPGA, by issuing a
296 write() with the byte count set to zero. This is similar to a flush request,
297 but it doesn't block. There is also an autoflushing mechanism, which triggers
298 an equivalent flush roughly XILLY_RX_TIMEOUT jiffies after the last write().
299 This allows the user to be oblivious about the underlying buffering mechanism
300 and yet enjoy a stream-like interface.
301
302 Note that the issue of partial buffer flushing is irrelevant for pipes having
303 the "synchronous" attribute nonzero, since synchronous pipes don't allow data
304 to lay around in the DMA buffers between read() and write() anyhow.
305
306 Data granularity
307 ----------------
308
309 The data arrives or is sent at the FPGA as 8, 16 or 32 bit wide words, as
310 configured by the "format" attribute. Whenever possible, the driver attempts
311 to hide this when the pipe is accessed differently from its natural alignment.
312 For example, reading single bytes from a pipe with 32 bit granularity works
313 with no issues. Writing single bytes to pipes with 16 or 32 bit granularity
314 will also work, but the driver can't send partially completed words to the
315 FPGA, so the transmission of up to one word may be held until it's fully
316 occupied with user data.
317
318 This somewhat complicates the handling of host to FPGA streams, because
319 when a buffer is flushed, it may contain up to 3 bytes don't form a word in
320 the FPGA, and hence can't be sent. To prevent loss of data, these leftover
321 bytes need to be moved to the next buffer. The parts in xillybus_core.c
322 that mention "leftovers" in some way are related to this complication.
323
324 Probing
325 -------
326
327 As mentioned earlier, the number of pipes that are created when the driver
328 loads and their attributes depend on the Xillybus IP core in the FPGA. During
329 the driver's initialization, a blob containing configuration info, the
330 Interface Description Table (IDT), is sent from the FPGA to the host. The
331 bootstrap process is done in three phases:
332
333 1. Acquire the length of the IDT, so a buffer can be allocated for it. This
334 is done by sending a quiesce command to the device, since the acknowledge
335 for this command contains the IDT's buffer length.
336
337 2. Acquire the IDT itself.
338
339 3. Create the interfaces according to the IDT.
340
341 Buffer allocation
342 -----------------
343
344 In order to simplify the logic that prevents illegal boundary crossings of
345 PCIe packets, the following rule applies: If a buffer is smaller than 4kB,
346 it must not cross a 4kB boundary. Otherwise, it must be 4kB aligned. The
347 xilly_setupchannels() functions allocates these buffers by requesting whole
348 pages from the kernel, and diving them into DMA buffers as necessary. Since
349 all buffers' sizes are powers of two, it's possible to pack any set of such
350 buffers, with a maximal waste of one page of memory.
351
352 All buffers are allocated when the driver is loaded. This is necessary,
353 since large continuous physical memory segments are sometimes requested,
354 which are more likely to be available when the system is freshly booted.
355
356 The allocation of buffer memory takes place in the same order they appear in
357 the IDT. The driver relies on a rule that the pipes are sorted with decreasing
358 buffer size in the IDT. If a requested buffer is larger or equal to a page,
359 the necessary number of pages is requested from the kernel, and these are
360 used for this buffer. If the requested buffer is smaller than a page, one
361 single page is requested from the kernel, and that page is partially used.
362 Or, if there already is a partially used page at hand, the buffer is packed
363 into that page. It can be shown that all pages requested from the kernel
364 (except possibly for the last) are 100% utilized this way.
365
366 The "nonempty" message (supporting poll)
367 ----------------------------------------
368
369 In order to support the "poll" method (and hence select() ), there is a small
370 catch regarding the FPGA to host direction: The FPGA may have filled a DMA
371 buffer with some data, but not submitted that buffer. If the host waited for
372 the buffer's submission by the FPGA, there would be a possibility that the
373 FPGA side has sent data, but a select() call would still block, because the
374 host has not received any notification about this. This is solved with
375 XILLYMSG_OPCODE_NONEMPTY messages sent by the FPGA when a channel goes from
376 completely empty to containing some data.
377
378 These messages are used only to support poll() and select(). The IP core can
379 be configured not to send them for a slight reduction of bandwidth.
380

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Xillybus 문서 개요와 목차

1-30

이 문서는 Eli Billauer가 작성한 범용 FPGA 인터페이스용 Xillybus 드라이버 설명서입니다. Xillybus IP 코어와 Linux 드라이버가 어떤 문제를 해결하는지, 사용자 공간에서 파이프처럼 쓰는 방법과 내부 DMA 구현을 함께 다룹니다.

문서는 도입부에서 FPGA와 Xillybus의 배경을 설명하고, 사용법에서는 사용자 인터페이스·동기화·탐색 가능한 파이프를 다룹니다. 내부 구조에서는 소스 구성, 파이프 속성, 안전한 장치 통신, 채널과 메시지, 스트리밍, 데이터 단위, probing, 버퍼 할당, `poll()` 지원을 차례로 설명합니다.

Xillybus 문서 구성
부분핵심 내용
IntroductionFPGA 배경과 Xillybus 개요
Usage문자 장치, 동기·비동기 I/O, seekable pipe
InternalsDMA 채널, 메시지, probing, 버퍼와 poll 지원

==========================================
Xillybus driver for generic FPGA interface
==========================================

:Author: Eli Billauer, Xillybus Ltd. (http://xillybus.com)
:Email:  eli.billauer@gmail.com or as advertised on Xillybus' site.

.. Contents:

 - Introduction
  -- Background
  -- Xillybus Overview

 - Usage
  -- User interface
  -- Synchronization
  -- Seekable pipes

 - Internals
  -- Source code organization
  -- Pipe attributes
  -- Host never reads from the FPGA
  -- Channels, pipes, and the message channel
  -- Data streaming
  -- Data granularity
  -- Probing
  -- Buffer allocation
  -- The "nonempty" message (supporting poll)

FPGA 개발과 호스트 통신의 배경

31-74

FPGA(Field Programmable Gate Array)는 전용 칩셋에서 보통 제공하는 기능을 프로그래밍으로 구현할 수 있는 논리 하드웨어입니다. 디스플레이 어댑터, 네트워크 인터페이스, 주변장치를 포함한 프로세서까지 만들 수 있으며, 정해진 블록을 조합해 원하는 하드웨어를 만든다는 점에서 하드웨어의 LEGO에 비유됩니다.

시장에 이미 있는 칩을 FPGA로 다시 구현하는 일은 대개 의미가 없으므로, FPGA는 특수 기능이 필요하고 생산량이 ASIC 개발비를 정당화할 만큼 크지 않은 경우에 주로 사용됩니다. 하지만 FPGA의 구현 수준은 어셈블리 언어보다도 낮기 때문에 설계자가 매번 기반 기능을 새로 만들지 않도록 미리 설계된 IP 코어를 사용합니다.

IP 코어는 소프트웨어 라이브러리 함수에 대응하는 FPGA용 구성 요소입니다. 수학 함수, USB 같은 기능 단위, ARM과 같은 전체 프로세서 등 필요한 거의 모든 기능을 제공하며, 다른 블록과 연결할 전기 신호선을 가진 빌딩 블록처럼 동작합니다.

FPGA와 완전한 운영체제를 실행하는 호스트 프로세서 사이의 통신은 어려운 과제입니다. 저수준 버스 프로토콜뿐 아니라 레지스터, 인터럽트, DMA 같은 상위 인터페이스도 구현해야 합니다. 비디오 카드나 NIC처럼 기능이 잘 알려져 있으면 전용 인터페이스와 커널 드라이버를 설계할 수 있지만, 데이터 교환이 기존 주변장치 모델에 맞지 않으면 그 추상화 비용이 지나치게 큽니다.

그런 프로젝트에서는 커널 부분을 기본 데이터 운반에 한정하고 프로젝트별 드라이버 로직을 사용자 공간 프로그램으로 구현하는 빠른 접근을 택할 수 있습니다. 그래도 FPGA 측 인터페이스 로직과 간단한 커널 드라이버가 필요하며, Xillybus가 이 공통 부분을 제공합니다.

FPGA 호스트 인터페이스 선택
Define FPGA-specific functionUse a standard device model when one fitsOtherwise keep kernel logic as elementary transportRun project-specific behavior in user spaceReuse Xillybus IP core and Linux driver

장치 기능이 표준 모델에 맞는지에 따라 전용 커널 드라이버와 범용 데이터 운반 계층 중 하나를 선택합니다.

Introduction
============

Background
----------

An FPGA (Field Programmable Gate Array) is a piece of logic hardware, which
can be programmed to become virtually anything that is usually found as a
dedicated chipset: For instance, a display adapter, network interface card,
or even a processor with its peripherals. FPGAs are the LEGO of hardware:
Based upon certain building blocks, you make your own toys the way you like
them. It's usually pointless to reimplement something that is already
available on the market as a chipset, so FPGAs are mostly used when some
special functionality is needed, and the production volume is relatively low
(hence not justifying the development of an ASIC).

The challenge with FPGAs is that everything is implemented at a very low
level, even lower than assembly language. In order to allow FPGA designers to
focus on their specific project, and not reinvent the wheel over and over
again, pre-designed building blocks, IP cores, are often used. These are the
FPGA parallels of library functions. IP cores may implement certain
mathematical functions, a functional unit (e.g. a USB interface), an entire
processor (e.g. ARM) or anything that might come handy. Think of them as a
building block, with electrical wires dangling on the sides for connection to
other blocks.

One of the daunting tasks in FPGA design is communicating with a fullblown
operating system (actually, with the processor running it): Implementing the
low-level bus protocol and the somewhat higher-level interface with the host
(registers, interrupts, DMA etc.) is a project in itself. When the FPGA's
function is a well-known one (e.g. a video adapter card, or a NIC), it can
make sense to design the FPGA's interface logic specifically for the project.
A special driver is then written to present the FPGA as a well-known interface
to the kernel and/or user space. In that case, there is no reason to treat the
FPGA differently than any device on the bus.

It's however common that the desired data communication doesn't fit any well-
known peripheral function. Also, the effort of designing an elegant
abstraction for the data exchange is often considered too big. In those cases,
a quicker and possibly less elegant solution is sought: The driver is
effectively written as a user space program, leaving the kernel space part
with just elementary data transport. This still requires designing some
interface logic for the FPGA, and write a simple ad-hoc driver for the kernel.

Xillybus IP 코어와 범용 Linux 드라이버

75-106

Xillybus는 IP 코어와 Linux 드라이버로 구성됩니다. 둘을 함께 사용하면 FPGA와 호스트 사이에 파이프 형태의 데이터 스트림을 만들 수 있으며, 프로젝트 고유 로직을 사용자 공간에 두는 혼합 FPGA·호스트 프로젝트에서 적은 노력으로 통신 계층을 구축할 수 있습니다.

필요한 데이터 파이프의 수, 방향, 속성이 프로젝트마다 크게 다르므로 Xillybus IP 코어는 하나의 고정된 논리 덩어리가 아닙니다. 최종 사용자가 제시한 명세에 따라 IP 코어를 구성하고 빌드합니다.

호스트에서는 독립 스트림을 문자 장치 파일로 사용하고 FPGA에서는 하드웨어 FIFO를 통해 데이터를 흘립니다. 사용자에게는 파이프나 TCP/IP 연결처럼 보이지만 내부적으로는 고정 크기 DMA 버퍼를 사용합니다. 구성에 따라 스트림은 하나뿐일 수도 있고 한 IP 코어에 백 개가 넘을 수도 있습니다.

IP 코어에는 코어 구성을 완전히 기술하는 간단한 데이터 구조가 포함됩니다. Linux 드라이버는 초기화 중 이 구조를 가져와 DMA 버퍼와 문자 장치를 자동으로 설정하므로 하나의 드라이버가 서로 다른 모든 Xillybus IP 코어와 별도 수정 없이 동작합니다. 이 구조는 PCI configuration space나 Flattened Device Tree와는 다른 Xillybus 전용 기술 정보입니다.

Xillybus 구성에서 장치 파일까지
Describe required pipes and attributesBuild the configured Xillybus IP coreDriver fetches the core configurationAllocate DMA buffers and channel stateCreate matching `/dev/xillybus_*` files

FPGA 코어가 제공하는 구성 정보가 Linux의 DMA 채널과 문자 장치로 변환됩니다.

Xillybus Overview
-----------------

Xillybus is an IP core and a Linux driver. Together, they form a kit for
elementary data transport between an FPGA and the host, providing pipe-like
data streams with a straightforward user interface. It's intended as a low-
effort solution for mixed FPGA-host projects, for which it makes sense to
have the project-specific part of the driver running in a user-space program.

Since the communication requirements may vary significantly from one FPGA
project to another (the number of data pipes needed in each direction and
their attributes), there isn't one specific chunk of logic being the Xillybus
IP core. Rather, the IP core is configured and built based upon a
specification given by its end user.

Xillybus presents independent data streams, which resemble pipes or TCP/IP
communication to the user. At the host side, a character device file is used
just like any pipe file. On the FPGA side, hardware FIFOs are used to stream
the data. This is contrary to a common method of communicating through fixed-
sized buffers (even though such buffers are used by Xillybus under the hood).
There may be more than a hundred of these streams on a single IP core, but
also no more than one, depending on the configuration.

In order to ease the deployment of the Xillybus IP core, it contains a simple
data structure which completely defines the core's configuration. The Linux
driver fetches this data structure during its initialization process, and sets
up the DMA buffers and character devices accordingly. As a result, a single
driver is used to work out of the box with any Xillybus IP core.

The data structure just mentioned should not be confused with PCI's
configuration space or the Flattened Device Tree.

문자 장치 기반 사용자 인터페이스

107-140

호스트의 모든 Xillybus 인터페이스는 드라이버가 로드될 때 자동 생성되는 `/dev/xillybus_*` 장치 파일을 통해 제공됩니다. 파일 이름은 FPGA에 로드된 IP 코어의 구성에 따라 달라집니다.

FPGA의 특정 하드웨어 FIFO와 통신하려면 대응하는 장치 파일을 열고 일반 파이프와 같은 `write()` 또는 `read()` 호출을 사용합니다. 따라서 `cat mydata > /dev/xillybus_thisfifo`로 FPGA에 데이터를 보내거나 `cat /dev/xillybus_thatfifo > hisdata`로 데이터를 받을 수 있습니다. 파이프가 EOF를 보낼 수 있더라도 사용하지 않는 구성에서는 `CTRL-C`로 명령을 끝내는 것도 자연스럽습니다.

드라이버와 하드웨어는 파이프다운 동작을 제공합니다. `open()` 시 `O_NONBLOCK`을 설정한 non-blocking I/O와 `poll()`·`select()`를 지원합니다. 부하가 클 때는 DMA로 대역폭을 효율적으로 사용하면서도 작은 데이터 조각은 자동 flush하여 TCP/IP와 비슷한 지연 특성을 냅니다.

장치 파일은 읽기 전용, 쓰기 전용 또는 양방향일 수 있습니다. 양방향 장치는 구현에서 하나의 channel 구조를 공유하지만, 사용자 관점에서는 서로 독립된 두 파이프로 취급됩니다.

Xillybus 사용자 공간 동작
기능동작
데이터 송신문자 장치에 일반 `write()` 또는 shell redirection 사용
데이터 수신문자 장치에서 일반 `read()` 또는 `cat` 사용
대기 방식Blocking과 `O_NONBLOCK` 모두 지원
준비 상태`poll()`과 `select()` 지원
성능대량 데이터는 DMA, 작은 조각은 autoflush

Usage
=====

User interface
--------------

On the host, all interface with Xillybus is done through /dev/xillybus_*
device files, which are generated automatically as the drivers loads. The
names of these files depend on the IP core that is loaded in the FPGA (see
Probing below). To communicate with the FPGA, open the device file that
corresponds to the hardware FIFO you want to send data or receive data from,
and use plain write() or read() calls, just like with a regular pipe. In
particular, it makes perfect sense to go::

        $ cat mydata > /dev/xillybus_thisfifo

        $ cat /dev/xillybus_thatfifo > hisdata

possibly pressing CTRL-C as some stage, even though the xillybus_* pipes have
the capability to send an EOF (but may not use it).

The driver and hardware are designed to behave sensibly as pipes, including:

* Supporting non-blocking I/O (by setting O_NONBLOCK on open() ).

* Supporting poll() and select().

* Being bandwidth efficient under load (using DMA) but also handle small
  pieces of data sent across (like TCP/IP) by autoflushing.

A device file can be read only, write only or bidirectional. Bidirectional
device files are treated like two independent pipes (except for sharing a
"channel" structure in the implementation code).

동기 파이프와 비동기 파이프

141-168

Xillybus 파이프는 IP 코어를 만들 때 synchronous 또는 asynchronous로 구성합니다. 동기 파이프의 `write()`는 일부 데이터가 FPGA에 전달되고 확인될 때만 성공으로 반환합니다. 호출 사이에는, 특히 프로세스가 CPU를 잃는 동안 FPGA로 데이터가 전송되지 않으므로 대량 전송이 느려지고 일정한 데이터율이 필요한 스트림에는 거의 사용할 수 없습니다.

비동기 파이프의 `write()`는 버퍼에 데이터를 저장할 공간이 충분하면 반환합니다. FPGA에서 호스트로 향하는 비동기 파이프는 장치 파일이 열린 직후 `read()` 요청 여부와 관계없이 전송할 수 있지만, 동기 파이프는 `read()`가 요청한 양만 전송합니다.

따라서 동기 파이프에서는 현재 드라이버가 처리하는 `read()` 또는 `write()`를 만족시키기 위해서만 호스트와 FPGA 사이의 데이터가 이동하고, 해당 호출은 전송 완료까지 기다립니다.

synchronization 속성과 요청한 바이트보다 적게 처리할 수 있는지는 별개입니다. 부분 완료 허용 여부는 별도의 `allowpartial` 구성 플래그가 결정합니다.

동기화 모드 비교
구분SynchronousAsynchronous
`write()` 반환FPGA 전달과 확인 뒤 반환버퍼 저장 공간이 있으면 반환
FPGA→host 전송`read()`가 요청한 양만 전송파일이 열리면 미리 전송 가능
적합한 용도호출 단위 확인이 필요한 제어대량·연속 스트리밍
부분 완료`allowpartial`이 별도로 결정`allowpartial`이 별도로 결정

Synchronization
---------------

Xillybus pipes are configured (on the IP core) to be either synchronous or
asynchronous. For a synchronous pipe, write() returns successfully only after
some data has been submitted and acknowledged by the FPGA. This slows down
bulk data transfers, and is nearly impossible for use with streams that
require data at a constant rate: There is no data transmitted to the FPGA
between write() calls, in particular when the process loses the CPU.

When a pipe is configured asynchronous, write() returns if there was enough
room in the buffers to store any of the data in the buffers.

For FPGA to host pipes, asynchronous pipes allow data transfer from the FPGA
as soon as the respective device file is opened, regardless of if the data
has been requested by a read() call. On synchronous pipes, only the amount
of data requested by a read() call is transmitted.

In summary, for synchronous pipes, data between the host and FPGA is
transmitted only to satisfy the read() or write() call currently handled
by the driver, and those calls wait for the transmission to complete before
returning.

Note that the synchronization attribute has nothing to do with the possibility
that read() or write() completes less bytes than requested. There is a
separate configuration flag ("allowpartial") that determines whether such a
partial completion is allowed.

탐색 가능한 동기 파이프

169-179

동기 파이프는 스트림의 위치를 FPGA 사용자 로직에 노출하도록 구성할 수 있습니다. 이 기능을 켜면 호스트 API에서도 해당 파이프를 seek할 수 있습니다.

FPGA 측에서 메모리 또는 레지스터 인터페이스를 seekable stream에 연결하면, 호스트는 원하는 주소로 seek한 뒤 `read()` 또는 `write()`를 호출하여 연결된 메모리의 특정 주소를 읽거나 쓸 수 있습니다.

Seekable pipe 접근
Configure a synchronous pipe as seekableAttach FPGA memory or register interfaceSeek the host device file to an addressCall `read()` or `write()`Access the corresponding FPGA-side location

파일 위치가 FPGA 측 메모리 또는 레지스터 주소로 전달됩니다.

Seekable pipes
--------------

A synchronous pipe can be configured to have the stream's position exposed
to the user logic at the FPGA. Such a pipe is also seekable on the host API.
With this feature, a memory or register interface can be attached on the
FPGA side to the seekable stream. Reading or writing to a certain address in
the attached memory is done by seeking to the desired address, and calling
read() or write() as required.

코어 모듈과 버스별 모듈

180-195

Xillybus 드라이버는 공통 코어인 `xillybus_core.c`와 버스 인터페이스에 의존하는 `xillybus_of.c`, `xillybus_pcie.c` 모듈로 구성됩니다.

커널이 적합한 장치를 찾으면 버스별 모듈이 probe됩니다. DMA mapping과 synchronization 함수는 버스에 따라 달라지므로, 초기화할 때 `xilly_endpoint_hardware` 구조체를 코어에 전달합니다.

`xilly_endpoint_hardware`에는 버스에서 실제 DMA 관련 작업을 수행하는 wrapper 함수 포인터가 들어 있습니다. 이 방식으로 코어의 스트림·채널 로직과 OF 또는 PCIe의 DMA 세부 구현을 분리합니다.

Xillybus 소스 계층
Kernel probes OF or PCIe moduleBus module prepares `xilly_endpoint_hardware`Wrapper callbacks implement bus DMA operations`xillybus_core.c` initializes common channelsCharacter devices expose the streams

버스별 probe가 DMA 연산 집합을 제공하고 공통 코어가 나머지 프로토콜을 처리합니다.

Internals
=========

Source code organization
------------------------

The Xillybus driver consists of a core module, xillybus_core.c, and modules
that depend on the specific bus interface (xillybus_of.c and xillybus_pcie.c).

The bus specific modules are those probed when a suitable device is found by
the kernel. Since the DMA mapping and synchronization functions, which are bus
dependent by their nature, are used by the core module, a
xilly_endpoint_hardware structure is passed to the core module on
initialization. This structure is populated with pointers to wrapper functions
which execute the DMA-related operations on the bus.

IDT에서 읽는 파이프 속성

196-233

각 파이프의 속성은 FPGA IP 코어를 빌드할 때 정해집니다. 드라이버의 `xilly_setupchannels()`는 코어 구성을 기술하는 IDT에서 이 값을 읽어 `xillybus_core.c`의 채널을 설정합니다.

`is_writebuf`는 FPGA 관점의 방향을 나타내며 0이 아니면 FPGA가 쓰는 FPGA→host 파이프입니다. `channelnum`은 호스트와 FPGA 사이에서 파이프를 식별하는 채널 번호이고, `format`은 기본 데이터 폭을 지정합니다.

`allowpartial`이 0이 아니면 해당 방향의 `read()` 또는 `write()`가 요청량보다 적은 바이트를 처리하고 반환할 수 있습니다. 일반 UNIX 동작과 맞추기 위해 보통 이를 허용합니다. `synchronous`는 동기 파이프 여부를 지정합니다.

`bufsize`는 DMA 버퍼 하나의 크기, `bufnum`은 파이프에 할당하는 버퍼 수이며 둘 다 항상 2의 거듭제곱입니다. `exclusive_open`은 연결된 장치 파일의 배타적 open을 강제합니다. 양방향 파일이 한 방향으로만 열려 있다면 반대 방향은 한 번 열 수 있습니다.

`seekable`은 파이프가 위치 기반 접근을 제공하는지 나타냅니다. 일반적으로 0이 아닌 `supports_nonempty`는 이 파이프에서 `select()`와 `poll()`을 지원하는 데 필요한 하드웨어 메시지를 보낼 수 있음을 뜻합니다.

파이프 속성
속성의미
`is_writebuf`0이 아니면 FPGA→host 방향
`channelnum`호스트·FPGA 통신 채널 번호
`format`8·16·32비트 기본 데이터 폭
`allowpartial`부분 `read()`·`write()` 완료 허용
`synchronous`동기 파이프 선택
`bufsize` / `bufnum`DMA 버퍼 크기와 개수
`exclusive_open`장치 파일 배타적 open
`seekable`위치 기반 접근 허용
`supports_nonempty``poll()`·`select()`용 메시지 지원

Pipe attributes
---------------

Each pipe has a number of attributes which are set when the FPGA component
(IP core) is built. They are fetched from the IDT (the data structure which
defines the core's configuration, see Probing below) by xilly_setupchannels()
in xillybus_core.c as follows:

* is_writebuf: The pipe's direction. A non-zero value means it's an FPGA to
  host pipe (the FPGA "writes").

* channelnum: The pipe's identification number in communication between the
  host and FPGA.

* format: The underlying data width. See Data Granularity below.

* allowpartial: A non-zero value means that a read() or write() (whichever
  applies) may return with less than the requested number of bytes. The common
  choice is a non-zero value, to match standard UNIX behavior.

* synchronous: A non-zero value means that the pipe is synchronous. See
  Synchronization above.

* bufsize: Each DMA buffer's size. Always a power of two.

* bufnum: The number of buffers allocated for this pipe. Always a power of two.

* exclusive_open: A non-zero value forces exclusive opening of the associated
  device file. If the device file is bidirectional, and already opened only in
  one direction, the opposite direction may be opened once.

* seekable: A non-zero value indicates that the pipe is seekable. See
  Seekable pipes above.

* supports_nonempty: A non-zero value (which is typical) indicates that the
  hardware will send the messages that are necessary to support select() and
  poll() for this pipe.

장치 레지스터를 읽지 않는 안전 설계와 채널 0

234-263

PCI Express는 규격상 hotplug를 지원하지만 일반적인 메인보드는 카드가 갑자기 사라지는 상황을 기대하지 않습니다. 재프로그램 가능한 FPGA 기반 PCIe 카드는 호스트가 동작하는 동안 실수로 재프로그램되면 버스에서 사라질 수 있습니다.

이때 즉시 아무 일도 일어나지 않을 수 있지만 호스트가 사라진 PCIe 장치에 매핑된 주소를 읽으면, 표준이 정상 복구를 요구함에도 일부 메인보드에서는 시스템이 곧바로 멈춥니다.

이 정지를 피하기 위해 Xillybus 드라이버는 장치 레지스터 공간을 전혀 읽지 않습니다. FPGA에서 호스트로 오는 모든 통신은 DMA로 수행합니다. 인터럽트 서비스 루틴도 상태 레지스터를 읽지 않고, FPGA가 준비한 작은 메시지 버퍼에서 인터럽트 이유를 확인합니다. 일관성을 위해 PCIe가 아닌 버스에도 같은 방식을 사용합니다.

사용자에게 보이는 각 파이프는 양방향 여부와 관계없이 FPGA와 호스트 사이의 데이터 채널 하나를 할당받습니다. 채널과 파이프를 구분하는 특별한 이유는 채널 0입니다. 채널 0은 FPGA의 인터럽트 관련 메시지에만 사용되며 연결된 사용자 파이프가 없습니다.

호스트가 FPGA를 읽지 않는 메시지 경로
FPGA prepares a short message in host memoryFPGA raises an interrupt when necessaryISR consumes the DMA-written messageNo device status register is readChannel 0 carries control and interrupt messages

FPGA가 DMA 메시지와 인터럽트를 통해 상태를 밀어 넣어 사라진 PCIe 장치의 MMIO read 위험을 없앱니다.

Host never reads from the FPGA
------------------------------

Even though PCI Express is hotpluggable in general, a typical motherboard
doesn't expect a card to go away all of the sudden. But since the PCIe card
is based upon reprogrammable logic, a sudden disappearance from the bus is
quite likely as a result of an accidental reprogramming of the FPGA while the
host is up. In practice, nothing happens immediately in such a situation. But
if the host attempts to read from an address that is mapped to the PCI Express
device, that leads to an immediate freeze of the system on some motherboards,
even though the PCIe standard requires a graceful recovery.

In order to avoid these freezes, the Xillybus driver refrains completely from
reading from the device's register space. All communication from the FPGA to
the host is done through DMA. In particular, the Interrupt Service Routine
doesn't follow the common practice of checking a status register when it's
invoked. Rather, the FPGA prepares a small buffer which contains short
messages, which inform the host what the interrupt was about.

This mechanism is used on non-PCIe buses as well for the sake of uniformity.


Channels, pipes, and the message channel
----------------------------------------

Each of the (possibly bidirectional) pipes presented to the user is allocated
a data channel between the FPGA and the host. The distinction between channels
and pipes is necessary only because of channel 0, which is used for interrupt-
related messages from the FPGA, and has no pipe attached to it.

DMA 버퍼 기반의 연속 데이터 스트리밍

264-305

양쪽 사용자에게는 분할되지 않은 연속 스트림이 보이지만 구현은 채널마다 할당한 DMA 버퍼 집합을 사용합니다. FPGA→host 방향에서는 채널 인터페이스로 들어온 데이터를 IP 코어가 DMA 버퍼 하나에 기록합니다.

버퍼가 가득 차면 FPGA는 채널 0에 `XILLYMSG_OPCODE_RELEASEBUF` 메시지를 추가하고 필요하면 인터럽트를 보내 호스트에 알립니다. 호스트는 문자 장치에서 해당 데이터를 읽을 수 있게 하고, 데이터가 모두 소비되면 FPGA의 buffer control register에 써서 버퍼를 다시 덮어쓸 수 있도록 합니다. 양쪽 flow control이 underflow와 overflow를 막습니다.

가득 찬 버퍼만 제출하면 TCP/IP 같은 스트림을 만들 수 없습니다. 버퍼가 다 차기 전에 데이터 흐름이 잠시 멈추더라도 이미 들어온 일부 데이터는 도착해야 합니다. 그래서 `XILLYMSG_OPCODE_RELEASEBUF` 메시지는 버퍼 번호뿐 아니라 실제 데이터 길이도 전달합니다.

FPGA는 호스트가 지시한 경우에만 부분 버퍼를 제출합니다. `read()`가 `XILLY_RX_TIMEOUT` jiffies, 현재 약 10ms 동안 block되면 호스트가 가능한 즉시 DMA 버퍼를 제출하라고 명령합니다. 이 timeout은 부분 버퍼가 너무 자주 전송되어 대역폭이 낭비되는 것을 막으면서 데이터 꼬리의 지연을 낮게 유지합니다.

Host→FPGA 방향도 비슷하지만 부분 버퍼 처리는 다릅니다. 사용자는 바이트 수가 0인 `write()`로 현재 버퍼의 모든 데이터를 FPGA에 제출하도록 요청할 수 있습니다. 이는 block하지 않는 flush와 비슷합니다. 마지막 `write()` 뒤 약 `XILLY_RX_TIMEOUT`이 지나면 같은 효과를 내는 autoflush도 작동하므로 사용자는 내부 버퍼링을 몰라도 스트림처럼 사용할 수 있습니다.

`synchronous` 속성이 0이 아닌 파이프는 `read()`와 `write()` 호출 사이에 데이터를 DMA 버퍼에 남겨 두지 않으므로 부분 버퍼 flush 문제가 적용되지 않습니다.

FPGA에서 호스트로 흐르는 버퍼 생명주기
FPGA fills a channel DMA bufferSend `XILLYMSG_OPCODE_RELEASEBUF` with used lengthHost exposes bytes through the character deviceUser consumes the available stream dataHost releases the buffer for FPGA overwriteTimeout requests early submission of a partial tail

부분 버퍼 길이와 timeout을 함께 사용해 대역폭과 지연을 조절합니다.

Data streaming
--------------

Even though a non-segmented data stream is presented to the user at both
sides, the implementation relies on a set of DMA buffers which is allocated
for each channel. For the sake of illustration, let's take the FPGA to host
direction: As data streams into the respective channel's interface in the
FPGA, the Xillybus IP core writes it to one of the DMA buffers. When the
buffer is full, the FPGA informs the host about that (appending a
XILLYMSG_OPCODE_RELEASEBUF message channel 0 and sending an interrupt if
necessary). The host responds by making the data available for reading through
the character device. When all data has been read, the host writes on the
FPGA's buffer control register, allowing the buffer's overwriting. Flow
control mechanisms exist on both sides to prevent underflows and overflows.

This is not good enough for creating a TCP/IP-like stream: If the data flow
stops momentarily before a DMA buffer is filled, the intuitive expectation is
that the partial data in buffer will arrive anyhow, despite the buffer not
being completed. This is implemented by adding a field in the
XILLYMSG_OPCODE_RELEASEBUF message, through which the FPGA informs not just
which buffer is submitted, but how much data it contains.

But the FPGA will submit a partially filled buffer only if directed to do so
by the host. This situation occurs when the read() method has been blocking
for XILLY_RX_TIMEOUT jiffies (currently 10 ms), after which the host commands
the FPGA to submit a DMA buffer as soon as it can. This timeout mechanism
balances between bus bandwidth efficiency (preventing a lot of partially
filled buffers being sent) and a latency held fairly low for tails of data.

A similar setting is used in the host to FPGA direction. The handling of
partial DMA buffers is somewhat different, though. The user can tell the
driver to submit all data it has in the buffers to the FPGA, by issuing a
write() with the byte count set to zero. This is similar to a flush request,
but it doesn't block. There is also an autoflushing mechanism, which triggers
an equivalent flush roughly XILLY_RX_TIMEOUT jiffies after the last write().
This allows the user to be oblivious about the underlying buffering mechanism
and yet enjoy a stream-like interface.

Note that the issue of partial buffer flushing is irrelevant for pipes having
the "synchronous" attribute nonzero, since synchronous pipes don't allow data
to lay around in the DMA buffers between read() and write() anyhow.

8·16·32비트 데이터 단위와 leftover 처리

306-323

FPGA에서 데이터는 `format` 속성에 따라 8, 16 또는 32비트 폭의 word로 도착하거나 전송됩니다. 드라이버는 사용자가 자연 정렬과 다른 단위로 파이프에 접근해도 가능한 한 이 차이를 숨깁니다.

예를 들어 32비트 granularity 파이프에서 한 바이트씩 읽을 수 있습니다. 16·32비트 파이프에 한 바이트씩 쓰는 것도 가능하지만, 드라이버는 완성되지 않은 word를 FPGA로 보낼 수 없으므로 최대 한 word의 전송을 사용자 데이터가 모두 채울 때까지 보류할 수 있습니다.

이 제약 때문에 host→FPGA 스트림의 flush가 복잡해집니다. flush한 버퍼 끝에는 FPGA word를 이루지 못하는 최대 3바이트가 남을 수 있으며, 데이터 손실을 막으려면 이를 다음 버퍼로 옮겨야 합니다. `xillybus_core.c`에서 `leftovers`를 언급하는 코드는 이 처리를 구현합니다.

Granularity와 보류 가능한 꼬리
`format` 데이터 폭자연 단위완성되지 않을 수 있는 꼬리
8 bit1 byte없음
16 bit2 bytes최대 1 byte
32 bit4 bytes최대 3 bytes

Data granularity
----------------

The data arrives or is sent at the FPGA as 8, 16 or 32 bit wide words, as
configured by the "format" attribute. Whenever possible, the driver attempts
to hide this when the pipe is accessed differently from its natural alignment.
For example, reading single bytes from a pipe with 32 bit granularity works
with no issues. Writing single bytes to pipes with 16 or 32 bit granularity
will also work, but the driver can't send partially completed words to the
FPGA, so the transmission of up to one word may be held until it's fully
occupied with user data.

This somewhat complicates the handling of host to FPGA streams, because
when a buffer is flushed, it may contain up to 3 bytes don't form a word in
the FPGA, and hence can't be sent. To prevent loss of data, these leftover
bytes need to be moved to the next buffer. The parts in xillybus_core.c
that mention "leftovers" in some way are related to this complication.

Interface Description Table을 이용한 probing

324-340

드라이버가 로드할 때 만드는 파이프의 수와 속성은 FPGA의 Xillybus IP 코어 구성에 따라 달라집니다. 초기화 중 FPGA는 구성 정보를 담은 blob인 Interface Description Table(IDT)을 호스트에 보냅니다.

Bootstrap은 세 단계로 수행됩니다. 먼저 장치에 quiesce 명령을 보내고 그 응답에 포함된 IDT 버퍼 길이를 얻어 메모리를 할당합니다. 다음으로 IDT 자체를 가져오고, 마지막으로 IDT가 기술한 파이프와 인터페이스를 생성합니다.

Xillybus bootstrap
Send quiesce commandRead IDT length from the acknowledgementAllocate a host bufferAcquire the complete IDT blobCreate channels and device interfaces from IDT

고정 드라이버가 FPGA별 IDT를 읽어 런타임 인터페이스를 만듭니다.

Probing
-------

As mentioned earlier, the number of pipes that are created when the driver
loads and their attributes depend on the Xillybus IP core in the FPGA. During
the driver's initialization, a blob containing configuration info, the
Interface Description Table (IDT), is sent from the FPGA to the host. The
bootstrap process is done in three phases:

1. Acquire the length of the IDT, so a buffer can be allocated for it. This
   is done by sending a quiesce command to the device, since the acknowledge
   for this command contains the IDT's buffer length.

2. Acquire the IDT itself.

3. Create the interfaces according to the IDT.

PCIe 경계 규칙과 DMA 버퍼 할당

341-365

PCIe packet이 허용되지 않은 경계를 넘지 않도록 버퍼 할당에는 규칙이 적용됩니다. 버퍼가 4kB보다 작으면 4kB 경계를 넘어서는 안 되고, 4kB 이상이면 시작 주소가 4kB에 정렬되어야 합니다.

`xilly_setupchannels()`는 커널에 전체 page를 요청한 뒤 필요에 따라 DMA 버퍼로 나눕니다. 모든 버퍼 크기가 2의 거듭제곱이므로 어떤 버퍼 집합도 최대 한 page만 낭비하면서 배치할 수 있습니다.

모든 버퍼는 드라이버가 로드될 때 할당합니다. 큰 연속 물리 메모리 구간이 필요할 수 있고, 이런 구간은 시스템이 막 부팅된 시점에 확보할 가능성이 더 높기 때문입니다.

버퍼는 IDT에 나타난 순서로 할당하며, IDT의 파이프가 버퍼 크기 내림차순으로 정렬된다는 규칙에 의존합니다. page 이상인 버퍼는 필요한 page 수를 통째로 요청합니다. page보다 작으면 새 page 일부를 사용하거나 이미 부분 사용 중인 page의 남은 공간에 넣습니다. 이 배치에서는 마지막 page를 제외한 모든 요청 page를 100% 사용할 수 있습니다.

DMA 버퍼 packing
Read buffers in decreasing-size IDT orderAllocate whole aligned pages for buffers at least 4kBOpen one page for sub-page buffersPack smaller buffers without crossing 4kBLeave at most the final page partially unused

크기 내림차순과 2의 거듭제곱 크기를 이용해 PCIe 경계를 지키면서 page 낭비를 제한합니다.

Buffer allocation
-----------------

In order to simplify the logic that prevents illegal boundary crossings of
PCIe packets, the following rule applies: If a buffer is smaller than 4kB,
it must not cross a 4kB boundary. Otherwise, it must be 4kB aligned. The
xilly_setupchannels() functions allocates these buffers by requesting whole
pages from the kernel, and diving them into DMA buffers as necessary. Since
all buffers' sizes are powers of two, it's possible to pack any set of such
buffers, with a maximal waste of one page of memory.

All buffers are allocated when the driver is loaded. This is necessary,
since large continuous physical memory segments are sometimes requested,
which are more likely to be available when the system is freshly booted.

The allocation of buffer memory takes place in the same order they appear in
the IDT. The driver relies on a rule that the pipes are sorted with decreasing
buffer size in the IDT. If a requested buffer is larger or equal to a page,
the necessary number of pages is requested from the kernel, and these are
used for this buffer. If the requested buffer is smaller than a page, one
single page is requested from the kernel, and that page is partially used.
Or, if there already is a partially used page at hand, the buffer is packed
into that page. It can be shown that all pages requested from the kernel
(except possibly for the last) are 100% utilized this way.

poll과 select를 위한 nonempty 메시지

366-379

FPGA→host 방향에서 `poll()`과 `select()`를 정확히 지원하려면 별도 알림이 필요합니다. FPGA가 DMA 버퍼에 일부 데이터를 기록했지만 아직 버퍼를 제출하지 않았을 수 있기 때문입니다.

호스트가 버퍼 제출만 기다린다면 FPGA가 이미 데이터를 보냈는데도 알림을 받지 못해 `select()`가 계속 block될 수 있습니다. Xillybus는 채널이 완전히 빈 상태에서 데이터가 있는 상태로 바뀔 때 FPGA가 `XILLYMSG_OPCODE_NONEMPTY` 메시지를 보내 이 문제를 해결합니다.

이 메시지는 오직 `poll()`과 `select()`의 준비 상태를 지원하는 데 사용됩니다. 약간의 대역폭을 절약해야 하고 해당 기능이 필요 없다면 IP 코어를 nonempty 메시지를 보내지 않도록 구성할 수 있습니다.

Nonempty 상태 알림
Channel begins completely emptyFPGA writes the first data into a DMA bufferFPGA sends `XILLYMSG_OPCODE_NONEMPTY`Host wakes `poll()` or `select()` waitersNormal buffer release later transfers the bytes

부분 DMA 버퍼가 제출되기 전에도 호스트가 읽기 가능 상태를 알 수 있게 합니다.

The "nonempty" message (supporting poll)
----------------------------------------

In order to support the "poll" method (and hence select() ), there is a small
catch regarding the FPGA to host direction: The FPGA may have filled a DMA
buffer with some data, but not submitted that buffer. If the host waited for
the buffer's submission by the FPGA, there would be a possibility that the
FPGA side has sent data, but a select() call would still block, because the
host has not received any notification about this. This is solved with
XILLYMSG_OPCODE_NONEMPTY messages sent by the FPGA when a channel goes from
completely empty to containing some data.

These messages are used only to support poll() and select(). The IP core can
be configured not to send them for a slight reduction of bandwidth.