User Tools

Site Tools


usersguide:devguide

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
usersguide:devguide [2022/07/14 22:54]
dpisuperadmin
usersguide:devguide [2022/07/14 23:40] (current)
dpisuperadmin
Line 1: Line 1:
-====== the Peripheral Control Project Developer's Guide ======+====== User's Guide for Software Defined Peripherals ====== 
  
 ===== Introduction ===== ===== Introduction =====
Line 5: Line 6:
   * The Peripheral Control Core Wishbone bus   * The Peripheral Control Core Wishbone bus
   * Clone an Existing Peripheral   * Clone an Existing Peripheral
-  * design tips for a pccore peripheral +  * Design Tips for a New Peripheral Control Peripheral 
-  * debug your peripheral with iverilog +  * Debug Your Peripheral with Iverilog 
 +  * How to Add a New Peripheral Driver Module
  
 ==== The Peripheral Control Core Wishbone Bus ==== ==== The Peripheral Control Core Wishbone Bus ====
Line 46: Line 47:
 During a bus write cycle the peripheral latches DAT_I into the selected register. During a read bus cycle the peripheral ignore DAT_I and places the requested data on DAT_O. During a bus write cycle the peripheral latches DAT_I into the selected register. During a read bus cycle the peripheral ignore DAT_I and places the requested data on DAT_O.
  
-The Verilog code fragment below shows a typical peripheral interface definition.  "Clocks" are system available strobes that occur every 100ns, 1.0us, 10us, 100us, 1.0ms, 10ms, 100ms, and 1 second.  The four inout pins go to the FPGA pins.  Some peripherals have eight instead of four FPGA pins. +The Verilog code fragment below shows a typical peripheral interface definition.  "Clocks" are system available strobes that occur every 10.0ns, 100ns, 1.0us, 10us, 100us, 1.0ms, 10ms, 100ms, and 1 second.  The four inout pins go to the FPGA pins.  Some peripherals have eight instead of four FPGA pins. 
  
-  module dp_peri(CLK_I,WE_I,TGA_I,STB_I,ADR_I,STALL_O,ACK_O,DAT_I,DAT_O,clocks,pins);+  module pc_peri(CLK_I,WE_I,TGA_I,STB_I,ADR_I,STALL_O,ACK_O,DAT_I,DAT_O,clocks,pins);
       input  CLK_I;            // system clock       input  CLK_I;            // system clock
       input  WE_I;             // direction. Read-from-peri==0; Write-to-peri==1       input  WE_I;             // direction. Read-from-peri==0; Write-to-peri==1
Line 86: Line 87:
     # start pcdaemon and test Baseboard LEDs     # start pcdaemon and test Baseboard LEDs
     # (use sudo for the following if not in dialout group)       # (use sudo for the following if not in dialout group)  
 +    stty -opost  < /dev/ttyUSB0
     cat /usr/local/lib/pccore.bin > /dev/ttyUSB0     cat /usr/local/lib/pccore.bin > /dev/ttyUSB0
     sudo pcdaemon -efdv3 -s /dev/ttyUSB0     sudo pcdaemon -efdv3 -s /dev/ttyUSB0
Line 102: Line 104:
 Next is the myperi Linux shared object driver.  Move to the pcdaemon/fpga-drivers directory and copy the gpio4 directory to myperi. Next is the myperi Linux shared object driver.  Move to the pcdaemon/fpga-drivers directory and copy the gpio4 directory to myperi.
     cd pcdaemon     cd pcdaemon
-    cp -r fpga-drivers/fpgpio4 fpga-drivers/myperi+    cp -r fpga-drivers/gpio4 fpga-drivers/myperi
 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 fpga-drivers/myperi/gpio4.c fpga-drivers/myperi/myperi.c     mv fpga-drivers/myperi/gpio4.c fpga-drivers/myperi/myperi.c
-    vi myperi/Makefile+    vi fpga-drivers/myperi/Makefile
 While not strictly required, this is a good time to edit myperi.c and change the name of the peripheral.  The line with the peripheral name should now look something like: While not strictly required, this is a good time to edit myperi.c and change the name of the peripheral.  The line with the peripheral name should now look something like:
     pslot->name = "myperi";     pslot->name = "myperi";
Line 113: Line 115:
     cp ../pccore/src/drivlist.h include     cp ../pccore/src/drivlist.h include
  
-Build, install, and run dpdaemon as you did earlier.  Be sure to kill any running instances of dpdaemon before starting a new instance.  Use sudo to run dpdaemon or add yourself to the dialout group. +Build, install, and run pcdaemon as you did earlier.  Be sure to kill any running instances of pcdaemon before starting a new instance.  Use sudo to run pcdaemon or add yourself to the dialout group. 
-    cd dpdaemon+    cd pcdaemon
     make     make
     sudo make install     sudo make install
-    dpdaemon -/usr/local/lib/DPCore.bin+    stty -opost  < /dev/ttyUSB0 
 +    cat /usr/local/lib/pccore.bin > /dev/ttyUSB0 
 +    sudo pcdaemon -efdv3 -s /dev/ttyUSB0
     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 backup.
  
-==== Design Tips for a DPCore Peripheral ====+==== Design Tips for a New Peripheral Control 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.
  
-Your Verilog design actually starts with the driver and its API.  Try to design the resources in the API to match how you view the peripheral at a high level.  Your design goal for the driver is to put as much logic into it as possible so that the FPGA part of the peripheral can be as small and simple as possible.  Once you've got a view of what the driver and Verilog each do, you can define the registers that link the driver to the FPGA logic.  It is important to document the meaning, limits, and suggested use of the registers at the top of your Verilog file.  This will help you maintain the code when you come back to it months or years later.+Your Verilog design actually starts with the driver and its API.  Try to design the resources in the API to match how your view of the peripheral at a high level.  Your design goal is to put as much logic into the driver as possible so that the FPGA part of the peripheral can be as small and as simple as possible.  Once you've got a view of what the driver and Verilog each do, you can define the registers that link the driver to the FPGA logic.  It is important to document the meaning, limits, and suggested use of the registers at the top of your Verilog file.  This will help you maintain the code when you come back to it months or years later.
  
-The module declaration for most DPCore peripherals look the same.+The module declaration for most Peripheral Control peripherals look the same.
     module myperi(CLK_I,WE_I,TGA_I,STB_I,ADR_I,STALL_O,ACK_O,DAT_I,DAT_O,clocks,pins);     module myperi(CLK_I,WE_I,TGA_I,STB_I,ADR_I,STALL_O,ACK_O,DAT_I,DAT_O,clocks,pins);
         input  CLK_I;         // system clock         input  CLK_I;         // system clock
Line 141: Line 145:
         inout  [3:0] pins;    // Lines out to FPGA pins         inout  [3:0] pins;    // Lines out to FPGA pins
  
-After the module declaration you'll want to add wires and registers specific to your peripheral.  All DPCore Wishbone peripherals implement a Mealy-Moore state machine ([[https://en.wikipedia.org/wiki/Moore_machine]]).  When you write your Verilog be aware that you are implementing a state machine.  The absolute best thing you can do for your future self or others reading your code is to describe in some detail the meaning of the registers you use and how they help implement your state machine.  The design of your peripheral is all about the state machine it implements.+After the module declaration you'll want to add wires and registers specific to your peripheral.  All pccore Wishbone peripherals implement a Mealy-Moore state machine ([[https://en.wikipedia.org/wiki/Moore_machine]]).  When you write your Verilog be aware that you are implementing a state machine.  The absolute best thing you can do for your future self or others reading your code is to describe in some detail the meaning of the registers you use and how they help implement your state machine.  The design of your peripheral is all about the state machine it implements.
  
 Timers and timing are common in peripherals.  If your state machine is partly based on timing you might expect code something like the following: Timers and timing are common in peripherals.  If your state machine is partly based on timing you might expect code something like the following:
Line 163: Line 167:
     assign pin[0] = ((mystate == `MYSTATE_A) & (polltmr == 0));     assign pin[0] = ((mystate == `MYSTATE_A) & (polltmr == 0));
  
-**Auto Send**: The DPCore bus controller continuously polls each peripheral in turn to ask if the peripheral has data for the host.  If the peripheral has data to send the bus controller builds a read request request packet, performs the bus read cycles, and sends the data to the host.  This feature is called "auto send" and removes the need for an interrupt line to the host.+**Auto Send**: The pccore bus controller continuously polls each peripheral in turn to ask if the peripheral has data for the host.  If the peripheral has data to send the bus controller builds a read request request packet, performs the bus read cycles, and sends the data to the host.  This feature is called "auto send" and removes the need for an interrupt line to the host.
  
 The bus controller uses the Wishbone line TGA_I to select a normal read/write cycle or a auto send poll.  The code below shows how an internal flag, dataready, triggers an auto send packet.   The bus controller uses the Wishbone line TGA_I to select a normal read/write cycle or a auto send poll.  The code below shows how an internal flag, dataready, triggers an auto send packet.  
Line 176: Line 180:
 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.  This section gives sample code and a few tips for debugging you peripheral using 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.  This section gives sample code and a few tips for debugging you peripheral using iverilog.
  
-You may recall from the counter example above that you can think of a test bench as a circuit board onto which you plug your new peripheral.  Inputs to your peripheral are outputs from the test bench.  As with all Verilog, it is best to start with an explanation of how the circuit works+You may recall from the counter example in the Verilog tutorial article that you can think of a test bench as a circuit board onto which you plug your new peripheral.  Inputs to your peripheral are outputs from the test bench.  As with all Verilog, it is best to start with an explanation of how the circuit works
  
     /////////////////////////////////////////////////////////////////////////     /////////////////////////////////////////////////////////////////////////
Line 212: Line 216:
     reg    [7:0] DAT_I;      // Data INto the peripheral;     reg    [7:0] DAT_I;      // Data INto the peripheral;
     wire   [7:0] DAT_O;      // Data OUT from the peripheral, = DAT_I if not us.     wire   [7:0] DAT_O;      // Data OUT from the peripheral, = DAT_I if not us.
-    reg    [7:0] clocks;     // Array of clock pulses from 100ns to 1 second+    reg    [8:0] clocks;     // Array of clock pulses from 100ns to 1 second
     wire   [7:0] pins;       // Pins to HC04 modules.  Strobe is LSB     wire   [7:0] pins;       // Pins to HC04 modules.  Strobe is LSB
     reg    [6:0] echo;       // echo inputs from the SR04 sensors     reg    [6:0] echo;       // echo inputs from the SR04 sensors
Line 291: Line 295:
 The code in this section has been take in part from the sr04 test bench.  Hopefully you will not have too much difficulty modifying it for your peripheral. The code in this section has been take in part from the sr04 test bench.  Hopefully you will not have too much difficulty modifying it for your peripheral.
  
- 
- 
-/* 
-===== How the DPCore Build System Works ===== 
- 
-{{ :usersguide:pc_interconnect.png?400 |}} 
-*/ 
  
  
Line 306: Line 303:
 We use the term "driver" but do not confuse these with real Linux kernel drivers.  Driver is the right concept but technically our drivers are loadable plug-in modules implemented as shared-object files.  Our existing drivers all use C but you can use any language that can produce a shared-object file.  C, C++, and Rust are all good choices. We use the term "driver" but do not confuse these with real Linux kernel drivers.  Driver is the right concept but technically our drivers are loadable plug-in modules implemented as shared-object files.  Our existing drivers all use C but you can use any language that can produce a shared-object file.  C, C++, and Rust are all good choices.
  
-The code structure of drivers is fairly consistent from one driver to the next.  This make the documentation describing it all the more important.  Your file header block should start with copyright and license information.  Since the driver connects the dpset/dpget API to the registers you should include a description of the API as if you were describing to someone who had never seen it before.  This is where you answer the reader's question of "what does it do?" Next describe the registers and the meaning, if appropriate, of all of the bits in the registers.  The final piece is a description of how the API values relate to the register values.  The API-to-register documentation will make your driver much easier to maintain when you come back to it later.+The code structure of drivers is fairly consistent from one driver to the next.  This make your documentation describing your module all the more important.  Your file header block should start with copyright and license information.  Since the driver connects the pcset/pcget API to the registers you should include a description of the API as if you were describing to someone who had never seen it before.  This is where you answer the reader's question of "what does it do?" Next describe the registers and the meaning, if appropriate, of all of the bits in the registers.  The final piece is a description of how the API values relate to the register values.  The API-to-register documentation will make your driver much easier to maintain when you come back to it later.
  
-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(), which is executed when the module is attached to the daemon, usercmd() which is a callback invoked for the API commands dpsetdpget, and dpcat, and packet_hdlr() which is a callback that is executed when a packet arrives from the FPGA.+Peripheral Control drivers are event driven and 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(), which is executed when the module is attached to the daemon, usercmd() which is a callback invoked for the API commands pcsetpcget, and pccat, and packet_hdlr() which is a callback that is executed when a packet arrives from the FPGA.
  
  
  
 ==== Initialize() ==== ==== Initialize() ====
-To understand how to load a driver into dpdaemon you should, perhaps, have some understanding of how dpdaemon works.+To understand how to load a driver into pcdaemon you should, perhaps, have some understanding of how pcdaemon works.
  
-The core of dpdaemon is a list of slots.  Each slot has a SLOT structure (includes/daemon.h) which has the information needed to manage the peripheral in that slot.  SLOT has the number of the slot, the name of the shared object file, and an array of resources (RSC in includes/daemon.h) for the peripheral. //Resources//, you may recall, is the generic term given to the attributes and data endpoints of the peripheral. +The core of pcdaemon is a list of slots.  Each slot has a SLOT structure (include/daemon.h) which has the information needed to manage the peripheral in that slot.  SLOT has the number of the slot, the name of the shared object file, and an array of resources (RSC in includes/daemon.h) for the peripheral. //Resources//, you may recall, is the generic term given to the attributes and data endpoints of the peripheral. 
  
-Peripheral #0 in the FPGA binary is the //enumerator//. This  is just a copy of the perilist configuration file used to build the FPGA binary.  When dpdaemon starts it loads the enumerator driver and reads the list of peripherals in the FPGA. It then loops through the list trying to load the shared object driver for each peripheral.  When the driver is loaded dpdaemon looks up and calls the Initialize() routine in the driver. (Look for dlsym() in daemon/ui.c to see how this works.)  The goal of Initialize() is to give dpdaemon (i.e. the SLOT structure) everything it needs to manage the peripheral.+Peripheral #0 in the FPGA binary is the //enumerator//. This  is just a copy of the perilist configuration file used to build the FPGA binary.  When pcdaemon starts it loads the enumerator driver and reads the list of peripherals in the FPGA. It then loops through the list trying to load the shared object driver for each peripheral.  When the driver is loaded pcdaemon looks up and calls the Initialize() routine in the driver. (Look for dlsym() in daemon/ui.c to see how this works.)  The goal of Initialize() is to give pcdaemon (i.e. the SLOT structure) everything it needs to manage the peripheral.  The enumerator is usually overloaded with a board specific driver.  The board file lets you access buttons, LEDs, or other features unique to the board.
  
-Dpdaemon can have multiple instances of the same peripheral.  This implies that an instance's internal state must be kept separate from the internal state of all other instances.  To do this you should create a structure or object that holds your peripheral internal state.  For example, the gpio4 peripheral keeps the following state information:+Pcdaemon can have multiple instances of the same peripheral.  This implies that an instance's internal state must be kept separate from the internal state of all other instances.  To do this you should create a structure or object that holds your peripheral internal state.  For example, the gpio4 peripheral keeps the following state information:
         // All state info for an instance of an gpio4         // All state info for an instance of an gpio4
     typedef struct     typedef struct
Line 338: Line 335:
     if (pctx == (MYPERIDEV *) 0) {     if (pctx == (MYPERIDEV *) 0) {
         // Malloc failure this early?         // Malloc failure this early?
-        dplog("memory allocation failure in myperi initialization");+        pclog("memory allocation failure in myperi initialization");
         return (-1);         return (-1);
     }     }
Line 378: Line 375:
 The usercmd() routine is where you convert your API calls to read and write resources into packets of register reads and writes.   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.  The daemon listens on the socket and accepts connections from application programs.  The application program sends lines of text in the form  +The interface to pcdaemon is a TCP socket.  The daemon listens on the socket and accepts connections from application programs.  The application program sends lines of text in the form  
-    [dpset|dpget|dpcat] [peri_name|slot_id] resource_name [resource_values] +    [pcset|pcget|pccat] [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 checks for a valid peripheral name or slot ID, checks for a valid resource name, and verifies that the command (get/set/cat) is appropriate for the resource.  If everything is valid, the daemon calls your get/set callback.+The daemon parses lines of input and rejects lines that do not match the above format.  The daemon checks for a valid peripheral name or slot ID, checks for a valid resource name, and verifies that the command (get/set/cat) is appropriate for the resource.  If everything is valid, the daemon calls your get/set callback.
  
-The daemon passs a lot of information into your callback, including the command (DPGETDPSET, or DPCAT), the resource index you set in Initialize(), and the string of the new value,  There can be many instances of your peripheral, so the callback includes a SLOT pointer from which you can the the instance's private data structre.  Your response the the application that issued the command should be a newline terminated line of ASCII text.  The text goes into the 'buf' parameter and before returning you set *plen to the number of characters you put in buf.  You should be able to use the following exactly as it for your usercmd() callback.+The daemon passes a lot of information into your callback, including the command (PCGETPCSET, or PCCAT), the resource index you set in Initialize(), and the string of the new value,  There can be many instances of your peripheral, so the callback includes a SLOT pointer from which you can the the instance's private data structre.  Your response the the application that issued the command should be a newline terminated line of ASCII text.  The text goes into the 'buf' parameter and before returning you set *plen to the number of characters you put in buf.  You should be able to use the following exactly as it for your usercmd() callback.
     static void usercmd(     static void usercmd(
-        int      cmd,        //==DPGET if a read, ==DPSET on write+        int      cmd,        //==PCGET if a read, ==PCSET on write
         int      rscid,      // ID of resource being accessed         int      rscid,      // ID of resource being accessed
         char    *val,        // new value for the resource         char    *val,        // new value for the resource
Line 398: Line 395:
 Your code now needs to switch based on the resource and command.  A switch() statement works as does a string of if()/else if() statement.  Use your preferred coding style.  Long or complex calculations based on the user input might be moved to a separate routine to keep usercmd() simple and readable.  Your code might look something like the following: Your code now needs to switch based on the resource and command.  A switch() statement works as does a string of if()/else if() statement.  Use your preferred coding style.  Long or complex calculations based on the user input might be moved to a separate routine to keep usercmd() simple and readable.  Your code might look something like the following:
  
-    if ((cmd == DPGET) && (rcsid == RSC_MYRSC1)) {+    if ((cmd == PCGET) && (rcsid == RSC_MYRSC1)) {
         ret = snprintf(buf, *plen, "%1x\n", pctx->intr);         ret = snprintf(buf, *plen, "%1x\n", pctx->intr);
         *plen = ret;  // (errors are handled in calling routine)         *plen = ret;  // (errors are handled in calling routine)
         return;         return;
     }     }
-    else if ((cmd == DPSET) && (rcsid == RSC_MYRSC1)) {+    else if ((cmd == PCSET) && (rcsid == RSC_MYRSC1)) {
         ret = sscanf(val, "%x", &newrsc1);         ret = sscanf(val, "%x", &newrsc1);
         if ((ret != 1) || (newrsc1 < 0) || (newrsc1 > 0xf)) {         if ((ret != 1) || (newrsc1 < 0) || (newrsc1 > 0xf)) {
Line 417: Line 414:
         newrsc2 = getrsc2(val);         newrsc2 = getrsc2(val);
     }     }
-    else if ((cmd == DPCAT) && (rcsid == RSC_MYRSC3)) {+    else if ((cmd == PCCAT) && (rcsid == RSC_MYRSC3)) {
     .....     .....
     }     }
Line 424: Line 421:
  
 ==== Sending Packets to the FPGA ==== ==== Sending Packets to the FPGA ====
-The daemon and DPCore communicate using a packet based protocol which is defined in include/fpga.h.  You build a packet by setting the command, specifying the slot number, the register address, and the number of bytes in the data part of the packet.  Your code to build a packet might appear as follows:+The daemon and pccore communicate using a packet based protocol which is defined in include/fpga.h.  You build a packet by setting the command, specifying the slot number, the register address, and the number of bytes in the data part of the packet.  Your code to build a packet might appear as follows:
     static void sendconfigtofpga(     static void sendconfigtofpga(
         MYPERIDEV *pctx,   // This peripheral's context         MYPERIDEV *pctx,   // This peripheral's context
Line 441: Line 438:
         // Write the values for the pins, direction, and interrupt mask         // Write the values for the pins, direction, and interrupt mask
         // down to the card.         // down to the card.
-        pkt.cmd = DP_CMD_OP_WRITE DP_CMD_AUTOINC;+        pkt.cmd = PC_CMD_OP_WRITE PC_CMD_AUTOINC;
         pkt.core = pmycore->core_id;         pkt.core = pmycore->core_id;
         pkt.reg = MYPERI_REG_RSC1;   // the first reg of the three         pkt.reg = MYPERI_REG_RSC1;   // the first reg of the three
Line 448: Line 445:
         pkt.data[2] = pctx->rsc3;         pkt.data[2] = pctx->rsc3;
         pkt.count = 3;         pkt.count = 3;
-        txret = dpi_tx_pkt(pmycore, &pkt, 4 + pkt.count); // 4 header + data+        txret = pc_tx_pkt(pmycore, &pkt, 4 + pkt.count); // 4 header + data
  
  
 Some peripherals use a FIFO as a data endpoint.  In this case you would want to write all the bytes to one register.  Other peripherals have a string of registers that should be written secuentially.  This is referred to as "autoincrement" or "no autoincrement" Autoincrement can apply to both reading and writing registers so the four possibilities for the command are:  Some peripherals use a FIFO as a data endpoint.  In this case you would want to write all the bytes to one register.  Other peripherals have a string of registers that should be written secuentially.  This is referred to as "autoincrement" or "no autoincrement" Autoincrement can apply to both reading and writing registers so the four possibilities for the command are: 
-    pkt.cmd = DP_CMD_OP_WRITE DP_CMD_AUTOINC+    pkt.cmd = PC_CMD_OP_WRITE PC_CMD_AUTOINC
-    pkt.cmd = DP_CMD_OP_WRITE DP_CMD_NOAUTOINC+    pkt.cmd = PC_CMD_OP_WRITE PC_CMD_NOAUTOINC
-    pkt.cmd = DP_CMD_OP_READ  DP_CMD_AUTOINC+    pkt.cmd = PC_CMD_OP_READ  PC_CMD_AUTOINC
-    pkt.cmd = DP_CMD_OP_READ  DP_CMD_NOAUTOINC;+    pkt.cmd = PC_CMD_OP_READ  PC_CMD_NOAUTOINC;
  
-The routine to send a packet to the FPGA is dpi_tx_pkt().  You give it the peripheral address, the packet to send, and the total number of byte in the packet.  Dpi_tx_pkd() returns a success or failure indication.  You can use this to warn the user or to schedule another attempt.  Generally, something is seriously wrong if dpi_tx_pkt returns an error.+The routine to send a packet to the FPGA is pc_tx_pkt().  You give it the peripheral address, the packet to send, and the total number of byte in the packet.  Pc_tx_pkt() returns a success or failure indication.  You can use this to warn the user or to schedule another attempt.  Generally, something is seriously wrong if pci_tx_pkt() returns an error.
  
  
Line 463: Line 460:
 When you initialized your peripheral instance you specified a packet receive callback.  Your callback should be able to handle three types of packets from the FPGA.  The first is an acknowledgement for a packet you sent.  Use this packet to stop the timeout timers if you haveset one.  Otherwise the acknowledgement can be ignored. When you initialized your peripheral instance you specified a packet receive callback.  Your callback should be able to handle three types of packets from the FPGA.  The first is an acknowledgement for a packet you sent.  Use this packet to stop the timeout timers if you haveset one.  Otherwise the acknowledgement can be ignored.
  
-The second kind of packet is a read response.  Validate the packet and then read and format the packet data to send to the application.  Data to the application must be formatted as an ASCII string terminated by a newline.  When an application gives a DP_GET command the daemon marks the TCP connection as waiting for data from your peripheral.  You send data back to the application using a call to send_ui().+The second kind of packet is a read response.  Validate the packet and then read and format the packet data to send to the application.  Data to the application must be formatted as an ASCII string terminated by a newline.  When an application gives a PC_GET command the daemon marks the TCP connection as waiting for data from your peripheral.  You send data back to the application using a call to send_ui().
  
-The third kind of packet is an autosend packet.  Recall that the FPGA does not have a interrupt line to the CPU and instead can automatically send packets up to the host.  The autosend packet is similar in structure to a read response packet.  The difference is the high bit of the ''cmd'' byte.  In a read response the bit is set and in an autosend packet the bit is cleared.  Autosend data is most often used with resources that support the DP_CAT command.  The publish subscribe system in dpdaemon allows multiple TCP connections to subscribe to the same resource.   The routine to publish autosend data is the bcst_ui() routine.  Your code for read responses and autosend data might look like:+The third kind of packet is an autosend packet.  Recall that the FPGA does not have a interrupt line to the CPU and instead can automatically send packets up to the host.  The autosend packet is similar in structure to a read response packet.  The difference is the high bit of the ''cmd'' byte.  In a read response the bit is set and in an autosend packet the bit is cleared.  Autosend data is most often used with resources that support the PC_CAT command.  The publish subscribe system in dpdaemon allows multiple TCP connections to subscribe to the same resource.   The routine to publish autosend data is the bcst_ui() routine.  Your code for read responses and autosend data might look like:
  
         // If a read response from a user dpget command, send value to UI         // If a read response from a user dpget command, send value to UI
-        if ((pkt->cmd & DP_CMD_AUTO_MASK) != DP_CMD_AUTO_DATA) {+        if ((pkt->cmd & PC_CMD_AUTO_MASK) != PC_CMD_AUTO_DATA) {
             pinlen = sprintf(pinstr, "%1x\n", (pkt->data[0] & 0x0f));             pinlen = sprintf(pinstr, "%1x\n", (pkt->data[0] & 0x0f));
             send_ui(pinstr, pinlen, prsc->uilock);             send_ui(pinstr, pinlen, prsc->uilock);
Line 493: Line 490:
  
 ==== Non-FPGA Based Peripherals ==== ==== 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.  This section describes how you can use the dpdaemon and its API for non-FPGA based peripherals.  Let's start with an example of how it works.+If you have built an application using pcdaemon then you might appreciate the clean, simple, publish-subscribe API that it offers.  This section describes how you can use the pcdaemon and its API for non-FPGA based peripherals.  Let's start with an example of how it works.
  
-Dpdaemon comes with several examples of non-FPGA peripherals.  The first one to test is the 'hello_world' demo.  Start dpdaemon with any DPCore binary you have available.  Then at a command prompt enter: +Pcdaemon comes with several examples of non-FPGA peripherals.  The first one to test is the 'hello_world' demo.  Start pcdaemon with any pccore binary you have available.  Then at a command prompt enter: 
-    dploadso hellodemo.so +    pcloadso hellodemo.so 
-    dplist +    pclist 
-    dplist hellodemo +    pclist hellodemo 
-You should see the new peripheral listed in slot #11.  The help text displays the reqsources available to the peripheral.  Test it with the commands: +You should see the new peripheral listed in last slot.  The help text displays the resources available to the peripheral.  Test it with the commands: 
-    dpget hellodemo messagetext +    pcget hellodemo messagetext 
-    dpset hellodemo messagetext "Hello, again!" +    pcset hellodemo messagetext "Hello, again!" 
-    dpset hellodemo period 5 +    pcset hellodemo period 5 
-    dpcat hellodemo message+    pccat 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.  One difference is that non-FPGA based peripherals do not need a packet handler.  However they may need the ability to respond to data arriving from a file descriptor.  Working code for this is in the gamepad driver.  If you have as device or socket that you want to use as a data source you can add a callback for your file descriptor with a call to add_fd().  An example taken from the gamepad driver Initialize routine is shown below: The structure of non-FPGA based drivers is almost identical to FPGA based ones.  You will still need the Initialize() and usercmd() routines.  One difference is that non-FPGA based peripherals do not need a packet handler.  However they may need the ability to respond to data arriving from a file descriptor.  Working code for this is in the gamepad driver.  If you have as device or socket that you want to use as a data source you can add a callback for your file descriptor with a call to add_fd().  An example taken from the gamepad driver Initialize routine is shown below:
Line 515: Line 512:
     pctx->gpfd = open(pctx->device, (O_RDONLY | O_NONBLOCK));     pctx->gpfd = open(pctx->device, (O_RDONLY | O_NONBLOCK));
     if (pctx->gpfd != -1) {     if (pctx->gpfd != -1) {
-        add_fd(pctx->gpfd, DP_READ, getevents, (void *) pctx);+        add_fd(pctx->gpfd,PC_READ, getevents, (void *) pctx);
     }     }
  
Line 524: Line 521:
     {     {
  
-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.  As mentioned above, dpdaemon load the driver for the "enumerator" peripheral and then the enumerator driver loads drivers for the peripherals found in the list from the FPGA.  You can easily make dpdaemon entirely ////non-FPGA//// based by a small change in main() of daemon/main.c.+You can think of pcdaemon 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.  As mentioned above, pcdaemon loads the driver for the "enumerator" peripheral and then the enumerator driver loads drivers for the peripherals found in the list from the FPGA.  You can easily make dpdaemon entirely ////non-FPGA//// based by a small change in main() of daemon/main.c.
     // Add drivers here to always have them when the program starts     // Add drivers here to always have them when the program starts
     // The first loaded is in slot 0, the next in slot 1, ...     // The first loaded is in slot 0, the next in slot 1, ...
usersguide/devguide.1657839288.txt.gz · Last modified: 2022/07/14 22:54 by dpisuperadmin