3 Description of signals
3.1 Naming convention of FPGA signals
Except for the two global signals, bus_clk and quiesce, all signals follow a simple convention. For example, a write enable signal may have the name user_w_write_32_wren. This name is divided into four components:
-
The “user” prefix is common to all user interface signals.
-
The “w” part indicates that this signal belongs to a stream from the host to the FPGA (host “write”). Streams from the FPGA to the host have an “r” instead. Address signals don’t have this part, since they apply to both directions. Note that the host’s viewpoint is taken with regards to the choice of “w” or “r”.
-
The “write_32” strings appears in the related device file’s name: /dev/xillybus_write_32 or /dev/xillyusb_00_write_32, as applicable.
-
The suffix signifies the signal’s meaning.
In the remainder of this section, the device file name (the third component) is denoted {devfile} to avoid confusion.
Each signal name is followed by (IN) to indicate that the signal is an input to the IP core, or (OUT) when it’s an output from the IP core.
3.2 Signals for host to FPGA transmission
-
user_w_{devfile}_data (OUT) – This signal contains data during write cycles.
-
user_w_{devfile}_wren (OUT) – This signal is a write enable signal to the FIFO: It is high when there is valid data on the user_w_{devfile}_data signal that should be written to the FIFO (or any other logic that imitates a FIFO’s behavior).
-
user_w_{devfile}_full (IN) – This signal informs the IP core that no more data can be written.
Important: The ’full’ signal may change from low to high only on the clock cycle after a write cycle. All standard FIFOs behave like this, so this rule is relevant only if the IP core is connected directly with the application logic (i.e. without a FIFO in the middle).
The reason for this rule is that the Xillybus IP core treats a low ’full’ signal as a green light to start transferring data from the host. Failing to conform with this rule may cause sporadic writes that ignore the ’full’ condition.
A typical Verilog implementation of the ’full’ signal should be something like this:
always @(posedge bus_clk) if (ready_to_get_more_data) user_w_mydevice_full <= 0; // Turn low any time else if (user_w_mydevice_wren && { ... some condition ... } ) user_w_mydevice_full <= 1; // Only in conjunction with wrenThe same in VHDL:
process (bus_clk) begin if (bus_clk'event and bus_clk = '1') then if (ready_to_get_more_data = '1') then user_w_mydevice_full <= '0'; -- Turn low any time elsif (user_w_mydevice_wren = '1' and { some condition } ) user_w_mydevice_full <= '1'; -- Turn high only with wren end if; end if; end process; -
user_w_{devfile}_open (OUT) – This signal is high when the related device file on the host is open for write (if the file is open for read-only, when allowed, it will not change this signal). This signal may be used to reset the FIFO or other logic when the file is closed (used as an active low reset).
If a file is opened by multiple processes on the host (e.g. as a result of a call to the fork() function), this signal remains high until all processes have closed the file.
3.3 Signals for FPGA to host transmission
-
user_r_{devfile}_data (IN) – This signal contains data during read cycles. This signal is allowed to change only when a FIFO would have changed it. In other words, it may only change on a clock cycle after user_r_{devfile}_rden is high.
-
user_r_{devfile}_rden (OUT) – This signal is a read enable signal to the FIFO: When this signal is high, user_r_{devfile}_data must contain valid data on the following clock cycle.
-
user_r_{devfile}_empty (IN) – This signal informs the core that no more data can be read.
Important: The ’empty’ signal may change from low to high only on the clock cycle after a read cycle. All standard FIFOs behave like this, so this rule is relevant only if the IP core is connected directly with the application logic (i.e. without a FIFO in the middle).
The reason for this rule is that the Xillybus IP core treats a low ’empty’ signal as a green light to start transferring data to the host. Failing to conform with this rule may cause sporadic reads that ignore that the FIFO is empty.
A typical Verilog implementation of the ’empty’ signal should be something like this:
always @(posedge bus_clk) if (ready_to_give_more_data) user_r_mydevice_empty <= 0; // Turn low any time else if (user_r_mydevice_rden && { ... some condition ... } ) user_r_mydevice_empty <= 1; // Turn high only with rdenThe same in VHDL:
process (bus_clk) begin if (bus_clk'event and bus_clk = '1') then if (ready_to_give_more_data = '1') then user_r_mydevice_empty <= '0'; -- Turn low any time elsif (user_r_mydevice_rden = '1' and { some condition } ) user_r_mydevice_empty <= '1'; -- Turn high only with rden end if; end if; end process; -
user_r_{devfile}_eof (IN) – This signal tells the core to generate an end-of-file. The result of a high ’eof’ is also that the core will not read from the FIFO anymore (i.e. user_r_{devfile}_rden is kept low until the file is closed and reopened).
On the host, the application software will finish reading all data that the IP core has received before this signal became high. Only then will the host receive an EOF when it calls the read() function.
Note that the ’eof’ signal will not cause an EOF at the host immediately, if there is still data that has not been read by the application software. The delivery of the EOF on the host is made with common sense, i.e. after all data has been read by the host.
After the ’eof’ signal has been high, it doesn’t matter if it stays high afterwards or changes to low. The IP core remembers the EOF request until the file is closed. Regarding the ’empty’ signal, it doesn’t matter if it changes to high at the same time as ’eof’ changes to high. In fact, from the moment ’eof’ is high, the ’empty’ signal doesn’t matter at all, until the file is closed.
Similar to the ’empty’ signal, the ’eof’ signal is allowed to change to high only on a clock cycle after a read cycle. There is one exception however: When the ’empty’ signal is already high, the ’eof’ may be changed to high anytime. This exception can be used to cause the immediate termination of a read() function call on the host, if it sleeps while waiting for data.
Changing ’eof’ to high without conforming with this rule will generate an EOF, but it may not work accurately: Some data may be lost just before the EOF, or unrelated data may be added before the EOF, or even after the EOF (so the application software receives data after the EOF, which is illegal).
One possibility to ensure that ’eof’ conforms to this rule is to define ’eof’ as the output of a combinatorial function. In Verilog, it may be written like this:
assign user_r_mydevice_eof = user_r_mydevice_empty && [ ... ];
Or in VHDL:
user_r_mydevice_eof <= user_r_mydevice_empty and [ ... ];
With this method, the ’eof’ signal is always low when ’empty’ is low.
-
user_r_{devfile}_open (OUT) – This signal is high when the related device file on the host is open for read (if the file is open for write-only, when allowed, it will not change this signal). This signal may be used to reset the FIFO or other logic when the file is closed (used as an active low reset).
If a file is opened by multiple processes on the host (e.g. as a result of a call to the fork() function), this signal remains high until all processes have closed the file.
There is no direct connection between the ’eof’ signal and the ’open’ signal. The ’open’ signal will change to low when the file is closed on the host, regardless of ’eof’. However, note that application software typically responds to an EOF by closing the file. It’s therefore easy to mistakenly believe that there exists a connection between these signals.
3.4 Memory interface signals
A Xillybus device file can be configured to have an address signal. The application software assigns a value to this signal by using the standard API for seeking in a file (e.g. lseek() ). Also, an increment of the address occurs automatically on the FPGA as a result of read cycles and write cycles.
A standard block RAM is easily connected with the IP core. This is done by using the signals that have already been mentioned above with relation to FIFOs and the signals that are detailed below. The result is that the block RAM’s memory array is available to the host as a file: Read and write operations on the file result in read and write operations on the memory array. The host may access single memory elements or segments of the memory array: This depends on the length of the read or write operations.
It is also possible to implement an array of registers that behaves like a block RAM on the FPGA. By doing so, these registers become easily accessed by the host.
The ’empty’ and ’full’ signals can be used to slow down read and write operations for memories that require wait states, or when there is another reason to briefly delay operations.
A memory interface requires two additional signals:
-
user_{devfile}_addr (OUT) – This signal contains the address at the present time. When the read enable is high, a read operation from this address is required. When the write enable is high, a write operation to this address is required. Connecting this signal directly to a block RAM’s address input will work as expected. The width of this signal is configurable up to 32 bits.
The address’ value returns to zero after a read or write operation at the maximal address (depending on the width of the address signal). If the value of a function call to lseek() is out of range, only the LSBs are copied to this signal.
-
user_{devfile}_addr_update (OUT) – This signal is high during one clock cycle as a result of a function call to lseek() on the host. The ’update’ signal is high on the same clock cycle as the ’addr’ signal has its updated value.
The purpose of this signal is to give the application logic a chance to indicate that it needs time to prepare data for reading, as a result of the address’ update. This is done by changing the ’empty’ signal to high in response to such update.
For this purpose, there is one exception to the rule that ’empty’ can change to high only one clock cycle after a read cycle: It can also change to high on the clock cycle that is after ’update’ was high.
The following Verilog code is therefore correct:
always @(posedge bus_clk) if ( { ... memory is ready ... } ) user_r_mydevice_empty <= 0; else if ((user_mydevice_addr_update) && ( user_mydevice_addr > { ... some limit ...} )) user_r_mydevice_empty <= 1;And the same in VHDL:
process (bus_clk) begin if (bus_clk'event and bus_clk = '1') then if ( { ... memory is ready ... } ) then user_r_mydevice_empty <= '0'; elsif (user_mydevice_addr_update = '1' and user_mydevice_addr > { ... some limit ...} ) user_r_mydevice_empty <= '1'; end if; end if; end process;Note that since ’empty’ can change to low at any time, it’s reasonable to change ’empty’ to high as a result of every address update (regardless of the address), and then let the logic evaluate if ’empty’ can be changed back to low.
The ’full’ signal can also change to high in a similar manner, even though it’s not clear why this should be useful.
When the related device file on the host is closed, (i.e. when user_w_{devfile}_open and user_r_{devfile}_open are both low), the address is reset, and hence its value changes to zero. Note however that this is not considered an address update, i.e. user_{devfile}_addr_update remains low.
3.5 The quiesce signal
The quiesce signal is high when the host expects the IP core to be completely inactive (quiescent state). This is usually when:
-
The host has not yet loaded the driver, or the host has unloaded it.
-
On Windows: When the host is about to enter hibernation.
-
With XillyUSB: Also when the device isn’t connected to the computer at all.
The intention of this signal is to be used as a synchronous reset, however this signal is most likely not necessary: When the IP core is in an inactive state (i.e. quiescent state), all files are closed. Accordingly, the application logic can rely on the *_open signals alone as a reset signal. The ’quiesce’ signal can be used as a more global form of reset.
