This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Next revision Both sides next revision | ||
usersguide:devguide [2022/03/08 00:42] dpisuperadmin [How to Write a Wishbone Peripheral] |
usersguide:devguide [2022/07/14 22:54] dpisuperadmin |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== | + | ====== |
===== Introduction ===== | ===== Introduction ===== | ||
This document describes how create new software defined peripherals for the Baseboard4 using the Verilog hardware description language. To appreciate this document you should be comfortable with digital design and with the Verilog hardware description language. | This document describes how create new software defined peripherals for the Baseboard4 using the Verilog hardware description language. To appreciate this document you should be comfortable with digital design and with the Verilog hardware description language. | ||
- | * how to get started with Verilog, | + | * The Peripheral |
- | * how to create a new Wishbone peripheral, | + | * Clone an Existing Peripheral |
- | * how the DPCore host interface works, and | + | * design tips for a pccore |
- | * how to add a new peripherals into the build system. | + | |
- | + | ||
- | + | ||
- | ===== How to Get Started with Verilog ===== | + | |
- | In this section you will see how to use Verilog to build FPGA applications. | + | |
- | * "hello world" in Verilog | + | |
- | * use iverilog to test your Verilog circuit | + | |
- | * install the Xilinx compiler | + | |
- | * compile your design and test it on the Baseboard | + | |
- | + | ||
- | === "Hello World" in Verilog === | + | |
- | Most programming languages have a sample application that prints the phrase " | + | |
- | // Simple up counter for the Baseboard. | + | |
- | // Visible update rate is about 12 times per second | + | |
- | + | ||
- | module counter(CK12, | + | |
- | input | + | |
- | output | + | |
- | + | ||
- | reg [27:0] count; | + | |
- | + | ||
- | initial | + | |
- | begin | + | |
- | count = 0; | + | |
- | end | + | |
- | + | ||
- | always @(posedge CK12) | + | |
- | begin | + | |
- | count <= count + 28' | + | |
- | end | + | |
- | + | ||
- | assign LEDS = count[27: | + | |
- | + | ||
- | endmodule | + | |
- | + | ||
- | Have you ever seen a circuit board with gold or copper fingers for the connector? | + | |
- | + | ||
- | Think of this counter module as a circuit board with nine signal pins on its edge. In this case there is only one module, so the counter module is the top module. | + | |
- | module counter(CK12, | + | |
- | input | + | |
- | output | + | |
- | Clearly the code inside the module describes the digital circuitry on our imaginary circuit board. | + | |
- | + | ||
- | You already know that a register is just an array of flip-flops. | + | |
- | reg [27:0] count; | + | |
- | + | ||
- | You can tell the Verilog compiler what values to place in registers when the FPGA is loaded. | + | |
- | initial | + | |
- | begin | + | |
- | count = 0; | + | |
- | end | + | |
- | + | ||
- | Consider a flip-flop. | + | |
- | always @(posedge CK12) | + | |
- | begin | + | |
- | count <= count + 28' | + | |
- | end | + | |
- | The left hand side is the input to the count register and the right hand side is the output of count plus one. The output of an edge-triggered flip-flop is given a value only on the edge of its input clock. | + | |
- | + | ||
- | Assignment outside of a synchronous block is done with just an equal sign. This is called a // | + | |
- | assign LEDS = count[27: | + | |
- | sets the value of LEDS to the high eight bits of the counter. Just as you should not drive a wire with two different output, so Verilog wants just one output driving a wire or input. | + | |
- | assign outputA = inputX; | + | |
- | assign outputA = count + 1; | + | |
- | + | ||
- | If you have enjoyed this introduction you may want to get more information from one of the many books and on-line tutorials for Verilog. | + | |
- | While the compiler can flag many errors it can not identify logic errors in your design. | + | |
- | + | ||
- | === Test Your Verilog Design Using Iverilog === | + | |
- | The word " | + | |
- | + | ||
- | The simulation environment for a circuit is called a //test bench// | + | |
- | + | ||
- | Install iverilog and gtkwave on a Debian system with the command: | + | |
- | sudo apt-get install iverilog gtkwave | + | |
- | + | ||
- | Save the following as counter_tb.v | + | |
- | // iverilog test bench for the simple counter in counter.v | + | |
- | + | ||
- | `timescale 10ns/10ns | + | |
- | + | ||
- | module counter_tb; | + | |
- | // direction is relative to the DUT | + | |
- | reg clk; // 12.5 MHz system clock | + | |
- | wire [7:0] leds; // LEDs on Baseboard | + | |
- | + | ||
- | // Add the device under test | + | |
- | counter counter_dut(clk, | + | |
- | + | ||
- | // generate the clock | + | |
- | initial | + | |
- | always | + | |
- | + | ||
- | initial | + | |
- | begin | + | |
- | $dumpfile (" | + | |
- | $dumpvars (0, counter_tb); | + | |
- | + | ||
- | // 100 million steps of 10ns is one second | + | |
- | # | + | |
- | $finish; | + | |
- | end | + | |
- | endmodule | + | |
- | + | ||
- | Run the simulation, convert the output to a gtkwave format, and display the results with the commands: | + | |
- | iverilog -o counter_tb.vvp counter_tb.v counter.v | + | |
- | vvp counter_tb.vvp -lxt2 | + | |
- | gtkwave counter_tb.xt2 | + | |
- | + | ||
- | To view the LED waveforms click on " | + | |
- | {{ : | + | |
- | + | ||
- | === Install and Test the Xilinx Toolchain === | + | |
- | Once your simulation output is correct you are ready to compile and download your design to the FPGA. | + | |
- | This section describes how to install the Xilinx FPGA design tools, how to use the Xilinx command line tools to compile a Verilog design, and how to download the compiled code to the Baseboard. | + | |
- | + | ||
- | The Baseboard uses a Xilinx Spartan-3E and a USB interface for both downloads and a host interface. | + | |
- | + | ||
- | Xilinx provides a set of free design tools, ISE, which are part of their WebPACK download. | + | |
- | + | ||
- | Start by going to the Xilinx download site at: http:// | + | |
- | + | ||
- | Install the software by untarring the download file and running the " | + | |
- | + | ||
- | The installation will ask which products to install. | + | |
- | + | ||
- | Once the installation is complete you can add the Xilinx Verilog compiler toolchain to you path and verify that it can be found with the commands: | + | |
- | export PATH=$PATH:/ | + | |
- | which ise | + | |
- | + | ||
- | By default, ise opens a graphical integrated development environment. | + | |
- | + | ||
- | Before compiling your Verilog to an FPGA binary you need to tell the compiler how the wires in the Verilog module map to the physical FPGA pins. Xilinx uses a "user constraints file" (.ucf) for this. The minimum UCF file for your counter is shown below. | + | |
- | NET " | + | |
- | NET " | + | |
- | NET " | + | |
- | NET " | + | |
- | NET " | + | |
- | NET " | + | |
- | NET " | + | |
- | NET " | + | |
- | NET " | + | |
- | + | ||
- | The commands that Xilinx uses to compile Verilog for a SPartan3 can be hidden by a Makefile but you might be interested in the steps involved. | + | |
- | + | ||
- | The first command, xst, synthesizes the Verilog file into a hardware design that is saved as a netlist file with an .ngc extension. | + | |
- | echo "run -ifn counter.v -ifmt Verilog -ofn counter.ngc -p xc3s100e-4-vq100" | + | |
- | + | ||
- | You have to specify the input file, the input file format, the name of the output file and the exact type of FPGA. Xst generates several report files and directories, | + | |
- | + | ||
- | The ngdbuild command further decomposes the design into FPGA native elements such as flip-flops, gates, and RAM blocks. | + | |
- | + | ||
- | ngdbuild | + | |
- | + | ||
- | It is the ngdbuild command that first considers the pin location, loading, and timing requirements specified in the user constraints file, counter.ucf. | + | |
- | + | ||
- | The Xilinx map command converts the generic elements from the step above to the elements specific to the target FPGA. It also performs a design rules check on the overall design. The map command produces two files, a Physical Constraints File file and a Native Circuit Description file, that are used in subsequent commands. | + | |
- | + | ||
- | map -detail -pr b counter.ngd | + | |
- | + | ||
- | The map command produces quite a few reports. | + | |
- | + | ||
- | The place and route command (par) uses the Physical Constraints File and the Native Circuit Description to produce another Native Circuit Description file which contains the fully routed FPGA design. | + | |
- | + | ||
- | par counter.ncd parout.ncd counter.pcf | + | |
- | + | ||
- | Output processing starts with the bitgen program which converts the fully routed FPGA design into the pattern of configuration bits found in the FPGA after download. | + | |
- | + | ||
- | bitgen -g StartUpClk: | + | |
- | + | ||
- | The bitgen program lets you specify which clock pin to use during initialization and whether or not to generate a CRC checksum on the download image. | + | |
- | + | ||
- | promgen -w -p bin -o counter.bin -u 0 counter.bit | + | |
- | + | ||
- | The promgen program is a utility that converts bitstream files into various PROM file formats. | + | |
- | + | ||
- | All of the commands described above, including xst, ngdbuild, map, par, bitgen, and promgen have excellent PDF manuals in either the ISE/ | + | |
- | + | ||
- | echo "run -ifn counter.v -ifmt Verilog -ofn counter.ngc -p xc3s100e-4-vq100" | + | |
- | ngdbuild | + | |
- | map -detail -pr b counter.ngd | + | |
- | par counter.ncd parout.ncd counter.pcf | + | |
- | bitgen -g StartUpClk: | + | |
- | promgen -w -p bin -o counter.bin -u 0 counter.bit | + | |
- | + | ||
- | === Download Your Design to the Baseboard === | + | |
- | When the Baseboard powers up or after pressing the reset button the FPGA waits for an binary image from the serial port. Linux serial port drivers can suppress certain characters from an output stream. | + | |
- | sudo stty --file=/ | + | |
- | + | ||
- | Press the reset button and send the FPGA binary to the Baseboard with the command: | + | |
- | cat counter.bin > / | + | |
- | + | ||
- | If all has gone well you should see an up counter on the Baseboard LEDs. | + | |
- | + | ||
- | + | ||
- | ==== How to Write a Wishbone | + | |
- | In this section you will see how to build your own custom Verilog peripheral. | + | |
- | * the DPcore | + | |
- | * clone an existing peripheral and rebuild DPCore | + | |
- | * design tips for a DPCore | + | |
* debug your peripheral with iverilog | * debug your peripheral with iverilog | ||
- | === The DPCore | + | ==== The Peripheral Control Core Wishbone Bus ==== |
A Wishbone Bus is a synchronous, | A Wishbone Bus is a synchronous, | ||
- | In the case of DPcore, the Wishbone bus does not connect to a CPU but to an interface to a host computer. | + | In the case of pccore, the Wishbone bus does not connect to a CPU but to an interface to a host computer. |
{{ : | {{ : | ||
Line 222: | Line 22: | ||
{{ : | {{ : | ||
- | The diagram to the right shows the topology for PDcore. It shows two of the possible sixteen peripherals. | + | The diagram to the right shows the topology for pccore. It shows two of the possible sixteen peripherals. |
- | The paragraphs below describe the Wishbone bus as implemented for DPcore. We use _X to indicate both input (_I) and output (_O) signals. | + | The paragraphs below describe the Wishbone bus as implemented for pccore. We use _X to indicate both input (_I) and output (_O) signals. |
Peripheral Signal Names : | Peripheral Signal Names : | ||
Line 258: | Line 58: | ||
input [7:0] DAT_I; | input [7:0] DAT_I; | ||
output [7:0] DAT_O; | output [7:0] DAT_O; | ||
- | input [7:0] clocks; | + | input [8:0] clocks; |
inout [3:0] pins; // FPGA pins for this peripheral | inout [3:0] pins; // FPGA pins for this peripheral | ||
- | The DPcore | + | The pccore |
- | === Clone an Existing Peripheral === | + | ==== Clone an Existing Peripheral |
It should be no surprise that the easiest way to build a new peripherals is to base it on an existing one. This section shows you how to do this. | It should be no surprise that the easiest way to build a new peripherals is to base it on an existing one. This section shows you how to do this. | ||
- | Start with a working system built from source. Download the source code for DPcore | + | Start with a working system built from source. Download the source code for pccore |
- | | + | |
- | cd DPCore/src | + | tar -xf pccore.tgz |
- | # Edit perilist to set the peripherals | + | cd pccore/boards/ |
+ | # Edit perilist to set all peripherals | ||
vi perilist | vi perilist | ||
make | make | ||
- | sudo cp DPCore.bin / | + | sudo cp build/ |
+ | | ||
Expect several warnings about signals without loads. | Expect several warnings about signals without loads. | ||
- | | + | |
- | cd dpdaemon | + | tar -xf pcdaemon.tgz |
+ | cd pcdaemon | ||
make | make | ||
sudo make install | sudo make install | ||
- | # start dpdaemon | + | # start pcdaemon |
- | | + | |
- | /usr/local/ | + | cat / |
+ | | ||
+ | pcset out4 outval 5 | ||
- | With everything built from source, you can now start adding your own code. Move to the DPCore.src directory and copy gpio4.v to myperi.v, where you can replace " | + | With everything built from source, you can now start adding your own code. Move to the pccore/src directory and copy gpio4.v to myperi.v, where you can replace " |
- | Edit buildmain.c and clone the line for gpio4. | + | Edit drivlist.h and clone the line for gpio4. |
- | {" | + | {" |
becomes | becomes | ||
- | {" | + | {" |
- | {" | + | {" |
+ | Replace the " | ||
- | Edit perilist and replace | + | Edit boards/ |
- | Next is the myperi driver. | + | Next is the myperi |
- | cp -r gpio4 myperi | + | cd pcdaemon |
+ | cp -r fpga-drivers/ | ||
Change the name of the driver file and change the target name in the Makefile. | Change the name of the driver file and change the target name in the Makefile. | ||
- | mv myperi/ | + | mv fpga-drivers/ |
vi myperi/ | vi myperi/ | ||
While not strictly required, this is a good time to edit myperi.c and change the name of the peripheral. | While not strictly required, this is a good time to edit myperi.c and change the name of the peripheral. | ||
pslot-> | pslot-> | ||
+ | Edit the Makefile in fpga-drivers to add entries for make, install, clean, and remove. | ||
+ | vi fpga-drivers/ | ||
+ | The peripherals IDs must be kept in synch between the Verilog code and the daemon code. This is currently a manual process so copy the drivlist.h file you edited in the above steps to the pcdaemon/ | ||
+ | cp ../ | ||
- | Build, install, and run dpdaemon as you did earlier. | + | Build, install, and run dpdaemon as you did earlier. |
cd dpdaemon | cd dpdaemon | ||
make | make | ||
sudo make install | sudo make install | ||
- | dpdaemon -l / | + | dpdaemon -l / |
dplist | dplist | ||
If all has gone well the list of peripherals should now include your new peripheral name. This might a good time to do a git commit. | If all has gone well the list of peripherals should now include your new peripheral name. This might a good time to do a git commit. | ||
- | === Design Tips for a DPCore Peripheral === | + | ==== Design Tips for a DPCore Peripheral |
This guide can not give you specific advice about your new peripheral but we can give some tips for its design and coding. | This guide can not give you specific advice about your new peripheral but we can give some tips for its design and coding. | ||
Line 362: | Line 173: | ||
and is equal to 8 if it is a poll bus cycle and there is data ready for the host. The" | and is equal to 8 if it is a poll bus cycle and there is data ready for the host. The" | ||
- | === Debug Your Peripheral with Iverilog === | + | ==== Debug Your Peripheral with Iverilog |
Having done it both ways, this author can attest to the fact that it is //much// easier to debug a new peripheral using a simulator. | Having done it both ways, this author can attest to the fact that it is //much// easier to debug a new peripheral using a simulator. | ||
Line 479: | Line 290: | ||
The code in this section has been take in part from the sr04 test bench. | The code in this section has been take in part from the sr04 test bench. | ||
+ | |||
+ | |||
+ | |||
+ | /* | ||
+ | ===== How the DPCore Build System Works ===== | ||
+ | |||
+ | {{ : | ||
+ | */ | ||
+ | |||
+ | |||
+ | |||
+ | ===== How to Add a New Peripheral Driver Module ===== | ||
+ | The next step after adding and testing your Verilog peripheral is to write a driver for it. This section describes the common features of the drivers and offers some tips that might simplify your driver. | ||
+ | |||
+ | We use the term " | ||
+ | |||
+ | The code structure of drivers is fairly consistent from one driver to the next. This make the documentation describing it all the more important. | ||
+ | |||
+ | DPCore drivers are event driven and have to deal with three events: creation, an API command from the user, and arrival of a packet from the FPGA. These three events are handled by Initialize(), | ||
+ | |||
+ | |||
+ | |||
+ | ==== Initialize() ==== | ||
+ | To understand how to load a driver into dpdaemon you should, perhaps, have some understanding of how dpdaemon works. | ||
+ | |||
+ | The core of dpdaemon is a list of slots. | ||
+ | |||
+ | Peripheral #0 in the FPGA binary is the // | ||
+ | |||
+ | Dpdaemon can have multiple instances of the same peripheral. | ||
+ | // All state info for an instance of an gpio4 | ||
+ | typedef struct | ||
+ | { | ||
+ | void *pslot; | ||
+ | int pinval; | ||
+ | int dir; // pin direction (in=0, out=1) | ||
+ | int intr; // autosend on change (no=0, yes=1) | ||
+ | void *ptimer; | ||
+ | } GPIO4DEV; | ||
+ | |||
+ | The Initialize() routine is passed a pointer to its allocated SLOT structure (SLOT *pslot). | ||
+ | Allocate memory for your peripheral state information and attach it to the SLOT structure with: | ||
+ | MYPERIDEV *pctx; | ||
+ | | ||
+ | // Allocate memory for this peripheral | ||
+ | pctx = (MYPERIDEV *) malloc(sizeof(MYPERIDEV)); | ||
+ | if (pctx == (MYPERIDEV *) 0) { | ||
+ | // Malloc failure this early? | ||
+ | dplog(" | ||
+ | return (-1); | ||
+ | } | ||
+ | pslot-> | ||
+ | |||
+ | While not a hard requirement, | ||
+ | |||
+ | // Register this slot's packet callback (pcb). | ||
+ | // Set its name, description and help text. | ||
+ | (pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | |||
+ | The help text is stored in the readme.txt file and is converted to readme.h as part of the build process. | ||
+ | |||
+ | The Initialize() routine is where you set the name and properties of your resources. | ||
+ | // Add the handlers for the user visible resources | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | pslot-> | ||
+ | |||
+ | ==== usercmd() ==== | ||
+ | The usercmd() routine is where you convert your API calls to read and write resources into packets of register reads and writes. | ||
+ | |||
+ | The interface to dpdaemon is a TCP socket. | ||
+ | [dpset|dpget|dpcat] [peri_name|slot_id] resource_name [resource_values] | ||
+ | The daemon parses line of input and rejects lines that do not match the above format. | ||
+ | |||
+ | The daemon passs a lot of information into your callback, including the command (DPGET, DPSET, or DPCAT), the resource index you set in Initialize(), | ||
+ | static void usercmd( | ||
+ | int cmd, //==DPGET if a read, ==DPSET on write | ||
+ | int rscid, | ||
+ | char *val, // new value for the resource | ||
+ | SLOT *pslot, | ||
+ | int cn, // Index into UI table for requesting conn | ||
+ | int | ||
+ | char *buf) | ||
+ | { | ||
+ | |||
+ | Usually the first thing to do is get the "local context" | ||
+ | pctx = (MYPERIDEV *) pslot-> | ||
+ | |||
+ | Your code now needs to switch based on the resource and command. | ||
+ | |||
+ | if ((cmd == DPGET) && (rcsid == RSC_MYRSC1)) { | ||
+ | ret = snprintf(buf, | ||
+ | *plen = ret; // (errors are handled in calling routine) | ||
+ | return; | ||
+ | } | ||
+ | else if ((cmd == DPSET) && (rcsid == RSC_MYRSC1)) { | ||
+ | ret = sscanf(val, " | ||
+ | if ((ret != 1) || (newrsc1 < 0) || (newrsc1 > 0xf)) { | ||
+ | ret = snprintf(buf, | ||
+ | *plen = ret; | ||
+ | return; | ||
+ | } | ||
+ | pctx-> | ||
+ | sendconfigtofpga(pctx, | ||
+ | } | ||
+ | else if ((cmd == DPSET) && (rcsid == RSC_MYRSC2)) { | ||
+ | // Do a long or complex calculation in another routine | ||
+ | newrsc2 = getrsc2(val); | ||
+ | } | ||
+ | else if ((cmd == DPCAT) && (rcsid == RSC_MYRSC3)) { | ||
+ | ..... | ||
+ | } | ||
+ | |||
+ | The above code shows how to respond to resource values that are out of range or otherwise invalid. | ||
+ | |||
+ | ==== Sending Packets to the FPGA ==== | ||
+ | The daemon and DPCore communicate using a packet based protocol which is defined in include/ | ||
+ | static void sendconfigtofpga( | ||
+ | MYPERIDEV *pctx, | ||
+ | int | ||
+ | char *buf) // where to store user visible error messages | ||
+ | { | ||
+ | DP_PKT | ||
+ | SLOT *pslot; | ||
+ | CORE *pmycore; | ||
+ | int txret; | ||
+ | int ret; // generic return value | ||
+ | | ||
+ | pslot = pctx-> | ||
+ | pmycore = pslot-> | ||
+ | | ||
+ | // Write the values for the pins, direction, and interrupt mask | ||
+ | // down to the card. | ||
+ | pkt.cmd = DP_CMD_OP_WRITE | DP_CMD_AUTOINC; | ||
+ | pkt.core = pmycore-> | ||
+ | pkt.reg = MYPERI_REG_RSC1; | ||
+ | pkt.data[0] = pctx-> | ||
+ | pkt.data[1] = pctx-> | ||
+ | pkt.data[2] = pctx-> | ||
+ | pkt.count = 3; | ||
+ | txret = dpi_tx_pkt(pmycore, | ||
+ | |||
+ | |||
+ | Some peripherals use a FIFO as a data endpoint. | ||
+ | pkt.cmd = DP_CMD_OP_WRITE | DP_CMD_AUTOINC; | ||
+ | pkt.cmd = DP_CMD_OP_WRITE | DP_CMD_NOAUTOINC; | ||
+ | pkt.cmd = DP_CMD_OP_READ | ||
+ | pkt.cmd = DP_CMD_OP_READ | ||
+ | |||
+ | The routine to send a packet to the FPGA is dpi_tx_pkt(). | ||
+ | |||
+ | |||
+ | ==== Handling Packets from the FPGA ==== | ||
+ | When you initialized your peripheral instance you specified a packet receive callback. | ||
+ | |||
+ | The second kind of packet is a read response. | ||
+ | |||
+ | The third kind of packet is an autosend packet. | ||
+ | |||
+ | // If a read response from a user dpget command, send value to UI | ||
+ | if ((pkt-> | ||
+ | pinlen = sprintf(pinstr, | ||
+ | send_ui(pinstr, | ||
+ | prompt(prsc-> | ||
+ | | ||
+ | // Response sent so clear the lock | ||
+ | prsc-> | ||
+ | del_timer(pctx-> | ||
+ | pctx-> | ||
+ | return; | ||
+ | } | ||
+ | | ||
+ | // Process of elimination makes this an autosend packet. | ||
+ | // Broadcast it if any UI are monitoring it. | ||
+ | if (prsc-> | ||
+ | pinlen = sprintf(pinstr, | ||
+ | // bkey will return cleared if UIs are no longer monitoring us | ||
+ | bcst_ui(pinstr, | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | You can see some of the internal working of the daemon in the above code. The uilock tied to a resourse tells your driver that it is in a state of waiting for a read response from the FPGA. The resource ' | ||
+ | |||
+ | |||
+ | ==== Non-FPGA Based Peripherals ==== | ||
+ | If you have ever built an application using dpdaemon then you have most likely come to appreciate the clean, simple, publish-subscribe API that it offers. | ||
+ | |||
+ | Dpdaemon comes with several examples of non-FPGA peripherals. | ||
+ | dploadso hellodemo.so | ||
+ | dplist | ||
+ | dplist hellodemo | ||
+ | You should see the new peripheral listed in slot #11. The help text displays the reqsources available to the peripheral. | ||
+ | dpget hellodemo messagetext | ||
+ | dpset hellodemo messagetext " | ||
+ | dpset hellodemo period 5 | ||
+ | dpcat hellodemo message | ||
+ | |||
+ | The structure of non-FPGA based drivers is almost identical to FPGA based ones. You will still need the Initialize() and usercmd() routines. | ||
+ | // Init our GAMEPAD structure | ||
+ | pctx-> | ||
+ | pctx-> | ||
+ | pctx-> | ||
+ | pctx-> | ||
+ | (void) strncpy(pctx-> | ||
+ | // now open and register the gamepad device | ||
+ | pctx-> | ||
+ | if (pctx-> | ||
+ | add_fd(pctx-> | ||
+ | } | ||
+ | |||
+ | In the above case the callback getevents() is called when the file descriptor is readable. | ||
+ | static void getevents( | ||
+ | int | ||
+ | void | ||
+ | { | ||
+ | |||
+ | You can think of dpdaemon as having two parts, the daemon part and the FPGA part. The FPGA part is actually started as if it were a non-FPGA driver. | ||
+ | // Add drivers here to always have them when the program starts | ||
+ | // The first loaded is in slot 0, the next in slot 1, ... | ||
+ | (void) add_so(" | ||
+ | //(void) add_so(" | ||
+ | |||
+ | |||
+ | To better understand this you might want to comment our the enumerator and add tts and gamepad to main.c and see how the resulting system is all non-FPGA peripherals. | ||
+ | |||