udpdata.pwp


// PicoWeb Project File

#define BANNER "\r\nPicoWeb UDP data acquisition tester.\r\n"

#define EEPROM_IP         /* use file "ip" for IP address */
#define NET_CONFIG_IP     /* allow IP address reconfiguration via net */
#define ENABLE_WATCHDOG   /* use Atmel watchdog timer hardware */
#undef DEBUGGER           /* no debugger...we're using serial port */

//#define CLOCK 8000000
#define CLOCK 7372000     /* processor clock rate (Hz) */
#define BAUD_RATE 19200   /* serial port baud rate */

//#define SHOW_UDP_REQUESTS

#define UDP_CHECKSUM_DONE

#define UDP_CMD_PORT 99

// HTML and images
udpdata.htm             // home page

// publix CGI routines
sertest.cgi

//------------------ included AVR assembly language follows ---------------
#avr_reset

    clr r17
    sts data_pkt_count,r17          ; reset packet counter

#avr_slow

#avr_fast
    rcall udp_loop_processing

#avr_asm

;--------------------------------------------------------------
; Project Notes:
; 
; This sample is not really set up to run.  It was used it to
; test UDP throughput to a PC using a special version of Perl
; which was hacked to do UDP transfers.  For various reasons,
; we cannot distribute this version of Perl.
; 
; The sample project takes a "trigger" UDP packet from the PC
; which tells the PicoWeb how much "dummy" data to send back
; using one or more UDP packets.  The response UDP packets
; are limited to 1024 bytes of "payload" data.  Therefore,
; if the PC sends a "trigger" packet requesting 16K bytes of
; data, then a total of 16 1024-byte UDP packets will be sent
; by the PicoWeb in quick succession.  Using this setup, we
; determined the maximum sustained rate at which we could move
; UDP data from the PicoWeb to a laptop PC (running Windows NT)
; with a PCMCIA Ethernet card was 239 Kbytes/sec.
; 
; As for a description of the various routines in this project:
; 
; The routine "import_udp" gets called each time a UDP packet
; is received by the PicoWeb.  This routine looks at the first
; two bytes of the incoming UDP packet and then saves this
; 16-bit number, the total amount of dummy UDP data to return.
; This routine then zeros the counter which keeps track of how
; much UDP data has been returned.  Also, this routine calls
; "set_client" to remember the IP address and port of the
; incoming UDP packet.  This information will be used to send
; back one or more response UDP data packets.
; 
; The routine "udp_loop_processing" is called each time through
; the PicoWeb "fast idle" loop.  It checks whether any UDP
; "response" data needs to be sent.  If so, it uses a routine
; called "movedata" (see pcode instruction "pf2x") to setup for
; UDP data transfer and then build up the UDP packet''s data.
; The routine "udp_loop_processing" then calls the routine
; "mem_udp_send_client" to complete the checksum the UDP packet
; and to send the newly formed packet out on the network.
; 
; The routine "set_client" records the IP address and port
; number from an incoming UDP packet for later use.
; 
; The routine "movedata" is called to setup for creating the
; data for the UDP packet.  UDP data is supplied two bytes
; at a time by a co-routine whose address is stored in the
; Z-register.  This co-routine is named "movedata_next" in this
; sample project.  Each time this "co-routine" is called it is
; expected to supply 16-bits of new UDP data in the Y-register
; (after first calling "f2x_checksum" to compute a running
; UDP checksum).
; 
; Note that this project also can send data to the serial port
; using "parameters" supplied as part of a GET request URL line
; The string after "C=" in the URL is send out the serial port.
; Any data returned by the serial port (within 1 s) is output to 
; the Web page.  (See CGI routine "sertest".)  This has nothing
; to do with the "UDP part" of the project.
;--------------------------------------------------------------

#include "tcpip.inc"
#include "mempkt.asm"

.section .bss

client_ip:          .skip 4
client_port:        .skip 2
udp_byte_count:     .skip 2
udp_pkt_seq:        .skip 2         ; packet sequence # 0...N
packet_header:      .skip UDP_DATA  ; ether/ip/udp headers
data_pkt_count:     .skip 2         ; incremented by 1 for every packet sent

.cseg

#define UDP_PKT_LEN     1024

#define MEM_ETH_DEST    packet_header+0
#define MEM_ETH_SRC     MEM_ETH_DEST+6
#define MEM_ETH_PT      MEM_ETH_SRC+6

#define MEM_IPH         MEM_ETH_PT+2
#define MEM_IP_HVTOS    MEM_IPH+0
#define MEM_IP_LEN      MEM_IPH+2
#define MEM_IP_ID       MEM_IPH+4
#define MEM_IP_FFO      MEM_IPH+6
#define MEM_IP_TTLP     MEM_IPH+8
#define MEM_IP_CHKSUM   MEM_IPH+10
#define MEM_IP_SRCADDR  MEM_IPH+12
#define MEM_IP_DSTADDR  MEM_IPH+16
#define MEM_IPD         MEM_IPH+20

#define MEM_UDP_HEADER  MEM_IPD+0
#define MEM_UDP_SP      MEM_UDP_HEADER+0
#define MEM_UDP_DP      MEM_UDP_HEADER+2
#define MEM_UDP_LEN     MEM_UDP_HEADER+4
#define MEM_UDP_CHKSUM  MEM_UDP_HEADER+6
#define MEM_UDP_DATA    MEM_UDP_HEADER+8
    
;+
; **-udp-udp receive handler.
;
; return with ret.
;-

#define UDP_FUNC    UDP_DATA

.global import_udp
import_udp:
    rcall pcode
    pr2s buf,UDP_DP,2           ; get destination port
    pcmpwi buf,BSWAP(UDP_CMD_PORT)          ; check if it matches
    pjumpne udp_bail

    pr2s udp_byte_count,UDP_DATA,2      ; byte count is first two bytes of data
    pmovwi udp_pkt_seq,0        ; zero sequence number
    pincw data_pkt_count
#if defined(SHOW_UDP_REQUESTS)
    ppushn putcok,1
    ppushn putc_b,1
    pmovbi putcok,1
    pmovbi putc_b,0
    pprintr "UDP src",IP_SRCADDR,4
    pprintr ":",UDP_DP,2
    pprintv " len ",[udp_byte_count]
    pcrlf
    ppopn putc_b,1
    ppopn putcok,1
#endif
    pcall set_client            ; remember who to send data to!
udp_bail:
    pend
    ret
;+
; **-udp_loop_processing-fast loop hook.
;
; this routine is called every time through the "fast loop".  it generates the
; next packet if there is more data to send.
;
; note:
;   this routine is called via "rcall", and must return with "ret".
;-

udp_loop_processing:
    rcall pcode
    pandwi udp_byte_count,0xffff            ; check remaining byte count
    pjumpeq udp_bail

#if defined(SHOW_UDP_REQUESTS)
    pprintv "pkt=",[udp_byte_count]
#endif
    pmovwi MEM_UDP_LEN,UDP_PKT_LEN+2        ; set length (+2 for sequence #)
    pf2x UDP_DATA,movedata,UDP_PKT_LEN+2    ; move the data
    pcall mem_udp_send_client               ; send it

    psubwi udp_byte_count,UDP_PKT_LEN       ; minus bytes just moved (excluding seq #)
    pjumplo udp_stop
    pjumpeq udp_stop
    pjump udp_bail                          ; more to send
udp_stop:
    pmovwi udp_byte_count,0                 ; zero out count
    pjump udp_bail
;+
; **-set_client-remember requestor as client for future autonomous sends.
;
; inputs:
;   current receive packet contains client info
;
; outputs:
;   appropriate info copied to memory packet buffer.
;
; caveats:
;   called via pcall/pret
;-

set_client:
    pr2s client_ip,IP_SRCADDR,4                 ; remember client''s IP
    pr2s client_port,UDP_SP,2                   ;  and port number
    pr2s MEM_ETH_DEST,ETH_SRC,6                 ; and client ether


    pmemcpy MEM_IP_SRCADDR,my_ip,4              ; source ip == me

    pmovwi MEM_UDP_SP,BSWAP(UDP_CMD_PORT)       ;  and port
    pmemcpy MEM_ETH_SRC,my_ether,6              ;   and ether
    
    pmovwi MEM_ETH_PT,0x0008                    ; set PT=IP

    pmovwi MEM_IP_HVTOS,0x0045                  ; 4500
    pmovwi MEM_IP_TTLP,0x1101           ; UDP proto and TTL=1
    pmovwi MEM_IP_FFO,0                 ; zero fragment stuff
    pret
;+
; **-movedata-move data to packet.
;
; inputs:
;   none
;
; outputs:
;   data moved
;
; notes:
;   demonstrates how to use pf2x with initial setup processing vs.
;   "during the loop" processing.
;-

movedata:
    movwi z,movedata_next
    clrw x                                  ; reset the checksum
    ldsw y,udp_pkt_seq                      ; get packet sequence #
    rcall moveword                          ; "move" it
    incw y                                  ; advance sequence #
    stsw udp_pkt_seq,y                      ; store it back
    lds yl,data_pkt_count                   ; get start of sequence
    mov yh,yl
    inc yh
    
    ret
movedata_next:
    rcall moveword
    inc yl
    inc yl
    inc yh
    inc yh
    ret
moveword:
    mov i0,yl
    mov i1,yh					; i0/i1 = next word of data

    f2x_checksum y,r10				; accumulate checksum - r10 destroyed

    ret
;+
; **-mem_udp_send-send a UDP datagram from ram.
;
; inputs:
;   transmit buffer:
;       MEM_UDP_DATA    = user data to be sent
;       MEM_UDP_LEN     = native byte order length of data to send
;
; checksum algorithm:
;
;   ip_chksum = 0 ;
;   ip_chksum = ~checksum(IPH,IPHLEN)
;   udp_chksum = 0
;   ph.src = ip.src
;   ph.dst = ip.dst
;   ph.mbzprot = 0|(ip.prot<<8)
;   ph.len = udp_len (UDP data only, not header)
;   ph_chksum = checksum(UDP_HDR,UDP_LEN+UDP_HEADER_LEN)
;   udp_chksum = ~checksum(PH_HDR,PH_HEADER_LEN)
;-

#define PH_HEADER  rcv_hdr+en_rbuf_nhdr
#define PH_SRCADDR PH_HEADER+0
#define PH_DSTADDR PH_HEADER+4
#define PH_MBZPROT PH_HEADER+8
#define PH_LEN     PH_HEADER+10
#define PH_CHKSUM  PH_HEADER+12
#define PH_HEADER_LEN      14

#define MOVW(x,y) pmovwi x,[y]

#define UDP_CHECKSUM_DONE

mem_udp_send_client:
    pmemcpy MEM_IP_DSTADDR,client_ip,4          ; destination = client
    pmovwi MEM_UDP_DP,[client_port]             ;  and client port

mem_udp_send:
#if defined(UDP_MEM_SEND_DEBUG)
    pprintv " MVCHK=",[chkacc]
    pclrw chkacc
    pmem_chksum MEM_UDP_DATA,[MEM_UDP_LEN]
    pprintv " UDPCHK=",[chkacc]
#endif

;
; compute UDP length and store in UDP_LEN field and PH_LEN field.
;
    MOVW(buf,MEM_UDP_LEN)              ; get length (native order)
    paddwi buf,UDP_HEADER_LEN           ; total length
    MOVW(MEM_UDP_LEN,swap buf)         ; set UDP length
;
; checksum UDP data, and store checksum (uncomplemented) in pseudoheader.
;
    pclrw MEM_UDP_CHKSUM                ; zero UDP checksum
#if defined(UDP_CHECKSUM_DONE)
    pmem_chksum MEM_UDP_HEADER,UDP_HEADER_LEN
#else
    pclrw chkacc                        ; reset accumulator
    pmem_chksum MEM_UDP_HEADER,[buf]    ; compute UDP checksum
#endif
    MOVW(PH_CHKSUM,chkacc)             ; store checksum in pseudoheader

    paddwi buf,IP_HEADER_LEN
    MOVW(MEM_IP_LEN,swap buf)          ; set IP length
;
; the IP header should be ready, zero its checksum, checksum it, and
; store complemented checksum back in header.
;
    pclrw chkacc
    pclrw MEM_IP_CHKSUM                 ; zero IP header checksum
    pmem_chksum MEM_IPH,IP_HEADER_LEN   ; checksum IP header
    pxorwi chkacc,-1                    ; complement the ip checksum
#if defined(LOG_MEM_CHKSUM)
    pprintv "IPCK ",[chkacc]
#endif
    MOVW(MEM_IP_CHKSUM,chkacc)          ; store in IP header
;
; finally, checksum the pseudoheader and store complemented checksum
; in UDP header.
;
    pmemcpy PH_SRCADDR,MEM_IP_SRCADDR,8 ; source/dest IP addrs
    pmovwi PH_MBZPROT,0x1100            ; UDP proto in PH
    MOVW(PH_LEN,MEM_UDP_LEN)            ; store net order length in ph

    pclrw chkacc
    pmem_chksum PH_HEADER,PH_HEADER_LEN ; checksum pseudoheader
    pxorwi chkacc,-1                    ; complement
    MOVW(MEM_UDP_CHKSUM,chkacc)         ; store in UDP header checksum
#if defined(LOG_MEM_CHKSUM)
    pprintv "PHCK ",[chkacc]
#endif
;
; compute total packet size and send.
;
    MOVW(buf,swap MEM_IP_LEN)           ; ip length total
    paddwi buf,6+6+2                    ; compute length with ether header

#if defined(UDP_CHECKSUM_DONE)
;
; if the checksum has already been computed, it also means the packet
; data is in the frame buffer, so just load the headers.
;
    ps2x 0,MEM_ETH_DEST,UDP_DATA        ; ether, ip, udp headers
#else
;
; load everything.
;
    ps2x 0,MEM_ETH_DEST,[buf]           ; copy packet to board
#endif

    xmit_frame [buf]                    ; out she goes

#if defined(LOG_MEM_CHKSUM)
    pprintv "PKLEN ",[buf]
#endif

    pret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;   serial device I/O ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

#define putchar_serial pmovbi putc_b,0
#define putchar_net    pmovbi putc_b,1
#define serial_binary  pser_mode 1
#define serial_normal  pser_mode 0

.eseg
;
;  sertest
;
;   Send command from URL parameter string to serial port and
;   output response back from serial port
;
sertest:
    putchar_serial                      ; switch putchar to serial port
    serial_binary                       ; put serial port in binary mode
    pcall serdev_print_url_cmd          ; send command from URL string
get_response:
    putchar_net                         ; switch putchar back to ''net
    pcall serdev_copynet                ; copy serial port input to net
    pret

;
; serdev_print_url_cmd
;
; print command string in URL after "C="
;
serdev_print_url_cmd:
    purlparm buf,"C="               ; search for command string
    pjumpne 1f                      ; exit if not found
    pprinturl buf,1                 ; output command string (after C=)
1:
    pret

;
; serdev_copynet
;
; loop reading chars from the serial buffer and writing to ''net until
; LF encountered (or timeout)
;
.cseg
serdev_copynet:
    psgetcto buf,PSGETCTO_MSECS(1000) ; get char from UART with 1s timeout
    pjumpeq serdev_copydone           ; no more chars!
    pcmpbi buf,0x0a                   ; check for LF (exit if found)
    pjumpeq serdev_copydone
    pputcb buf                        ; output character
    pjump serdev_copynet              ; back for more
serdev_copydone:
    pret

Back