3 The “Hello, world” test
3.1 The goal
Xillybus is a tool that is intended as a building block in a logic design. The best way to learn about Xillybus’ capabilities is hence to integrate it with your own user application logic. The demo bundle’s purpose is to be a starting point for working with Xillybus.
Therefore, the simplest possible application is implemented in the demo bundle: A loopback between two device files. This is achieved by connecting both sides of a FIFO to the Xillybus IP Core in the FPGA. As a result, when the host writes data to one device file, the FPGA returns the same data to the host through another device file.
The next few sections below explain how to test this simple functionality. This test is a simple method to verify that Xillybus operates correctly: The IP Core in the FPGA works as expected, the host detects the PCIe peripheral correctly, and the driver is installed properly. On top of that, this test is also an opportunity to learn how Xillybus works by making small modifications to the logic design in the FPGA.
As a first step, it’s recommended to make simple experiments with the demo bundle in order to understand how the logic in the FPGA and the device files work together. This alone often clarifies how to use Xillybus for your own application’s needs.
Aside from the loopback that is mentioned above, the demo bundle also implements a RAM and an additional loopback. This additional loopback is discussed briefly below. Regarding the RAM, it demonstrates how to access a memory array or registers. More information about this in section 3.4.
3.2 Preparations
The “Hello, world” test consists of running simple command-line programs, using Command Prompt windows.
As a first step, download the Xillybus package for Windows. It is a zip file that is available on the same web page that offers the driver. This zip file contains source code of these programs as well as executable binaries that are ready for running.
The easiest way is to use the executable binaries in order to carry out the “Hello world” test. However, it’s also possible to perform a compilation of these programs, as detailed in section 4.2.
Another possibility is to create a working environment that resembles Linux, as explained in section 4.3. If this possibility is chosen, follow the instructions for the “Hello world” test in the Getting started with Xillybus on a Linux host guide.
3.3 The trivial loopback test
A simple example of a loopback test with two Command Prompt windows is shown next.
Open a regular Command Prompt window and change directory to the “precompiled-demoapps” subdirectory of the Xillybus package for Windows. In order to use the results of your own compilation (as shown in section 4.2), change directory to XP32_DEBUG instead.
Type the following in this Command Prompt window:
> streamread \\.\xillybus_read_8
This makes the “streamread” program print out everything it reads from the xillybus_read_8 device file. Nothing is expected to happen at this stage.
Note that the backslashes are not duplicated. If this had been done with Cygwin (and not in a plain Command Prompt window), it would be \\\\.\\xillybus_read_8 instead.
Now open another Command Prompt window. Change to the same directory as the first Command Prompt, and type:
> streamwrite \\.\xillybus_write_8
This command sends anything that is typed in the second window to the device file (i.e. \\.\xillybus_write_8).
Type some text on the second Command Prompt window, and press ENTER. The same text will appear in the first Command Prompt window. Note that nothing is sent until ENTER is pressed. This is in accordance with the expected behavior of standard input.
If an error message was received while attempting these two commands, the following actions are suggested:
-
Check for typos.
-
Verify that the driver is installed and that the FPGA is detected as a Xillybus device: Open the Device Manager and compare with the last image in section 2.1.
-
Check for errors in the Event Viewer, as explained in section 2.3.
-
Ensure that the device files have been created, as explained in section 2.2 (search for xillybus_read_8 and xillybus_write_8).
Note that the FIFOs inside the FPGA are not at risk for overflow nor underflow: The core respects the ’full’ and ’empty’ signals inside the FPGA. When necessary, the Xillybus driver forces the computer program to wait until the FIFO is ready for I/O. This is called blocking, which means forcing the user space program to sleep.
Also note streamwrite works on each row separately, and doesn’t send anything to the FPGA before ENTER has been pressed. This is unlike the program with the same name for Linux.
There is another pair of device files that have a loopback between them: xillybus_read_32 and xillybus_write_32. These device files work with a 32-bit word, and this is also true for the FIFO inside the FPGA. The “hello world” test with these device files will therefore result in similar behavior, with one difference: All I/O is carried out in groups of 4 bytes. Therefore, when the input hasn’t reached a boundary of 4 bytes, the last bytes from the input will remain untransmitted.
3.4 Memory interface
The memread and memwrite programs are more interesting, because they demonstrate how to access memory on the FPGA. This is achieved by making function calls to _lseek() on the device file. There is a section in Xillybus host application programming guide for Windows that explains this API in relation to Xillybus’ device files.
Note that in the demo bundle, only xillybus_mem_8 allows seeking. This device file is also the only one that can be opened for both read and for write.
In this section, a tool named “hexdump” is used to display the content of the FPGA’s RAM. This tool can be found in the “unixutils” subdirectory of the Xillybus package for Windows. Alternatively, section 4.3 suggests other options for obtaining this tool.
Before writing to the memory, the existing situation can be observed by using hexdump.
> hexdump -C -v -n 32 \\.\xillybus_mem_8 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020
This output is the first 32 bytes in the memory array: hexdump opened xillybus_mem_8 and read 32 bytes from this device file. When a file that allows _lseek() is opened, the initial position is always zero. Hence the output consists of the data in the memory array, from position 0 to position 31.
It’s possible that your output will be different: This output reflects the FPGA’s RAM, which may contain other values. In particular, these values may be different from zero as a result of previous experiments with the RAM.
A few words about hexdump’s flags: The format of the output that is shown above is the result of “-C” and “-v”. “-n 32” means to show first 32 bytes only. The memory array is just 32 bytes long, so it’s pointless to read more than so.
memwrite can be used to change a value in the array. For example, the value at address 3 is changed to 170 (0xaa in hex format) with this command:
> memwrite \\.\xillybus_mem_8 3 170
In order to verify that the command worked, it’s possible to repeat the hexdump command from above:
> hexdump -C -v -n 32 \\.\xillybus_mem_8 00000000 00 00 00 aa 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020
So evidently, the command worked.
In memwrite.c, the important part is where it says “_lseek(fd, address, SEEK_SET)”. This function call changes the position of the device file. Consequently, this changes the address of the array’s element that is accessed inside the FPGA. The subsequent read operation or write operation starts from this position. Each such access increments the position according to the number of bytes that were transferred.
A device file that allows seeking is also useful for easily sending configuration commands to the FPGA. This is done by putting registers instead of the memory array in the FPGA. This is an example of a register that receives a new value in response to a write operation to address 2:
reg [7:0] my_register;
always @(posedge bus_clk)
if (user_w_mem_8_wren && (user_mem_8_addr == 2))
my_register <= user_w_mem_8_data;
Likewise, it’s possible to read back the values of registers by using a “case” statement in the FPGA’s logic.
