| |
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 ENABLE_WATCHDOG
#undef DEBUGGER /* no debugger */
#define SERIAL_BAUD_DIVISOR 51 /* 9600 @ 8Mhz */
//#define SHOW_UDP_REQUESTS
#define UDP_CHECKSUM_DONE
#define UDP_CMD_PORT 99
// HTML and images
udpdata.htm // base page
// CGI routines
sertest.cgi // iu00
// Assembly language
//------------------ 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 it.
;
; 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's URL,
; then return any data the serial port responds with as part of
; a Web page. (See CGI routine "sertest".) This has nothing
; to do with the "UDP part" of the project.
;--------------------------------------------------------------
#include "tcpip.inc"
#include "mempkt.asm"
.dseg
client_ip: .byte 4
client_port: .byte 2
udp_byte_count: .byte 2
udp_pkt_seq: .byte 2 ; packet sequence # 0...N
packet_header: .byte UDP_DATA ; ether/ip/udp headers
data_pkt_count: .byte 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
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.eseg
#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
#define SER_TMO 1500
;+
; **-sertest-serial I/O testor.
;-
#define SD_ADDR buf
#define SD_LEN buf+2
#define SD_CHAR buf+4
#define SD_TEMP buf+6
#define SD_LAST buf+8
#define SD_TMO SD_CHAR
sertest:
ppushn putcok,1 ; save putchar enable state
pmovbi putcok,0xff ; putchar is now OK
pprint "HTTP/1.0 200\nContent-type: text/html\n\n"
putchar_serial ; switch putchar to serial port
serial_binary ; put serial port in binary mode
pmovwi SD_ADDR,[data_addr] ; get data address
paddwi SD_ADDR,10 ; GET /iuNN?xxxxxxxxxxx
; 01234567890
pmovwi SD_LEN,[data_len]
psubwi SD_LEN,10 ; bias length for GET /...? part
;
; note there is not a lot of checking on the format of the URL. the first
; occurrence of '=' is interpreted as the start of the stuff to transmit
; to the serial port, and that continues until either '&' or ' ' is
; encountered.
;
pmovwi SD_LAST,0 ; clear last character (both bytes!)
find_equal_next:
pr2s SD_CHAR,[SD_ADDR],1 ; scan for '=' in Ethernet packet
pincw SD_ADDR
pdecw SD_LEN
pcmpbi SD_CHAR,'='
pjumpeq serdev_next
pcmpwi SD_LEN,0
pjumplo serial_done
pjumpne find_equal_next
pjump serial_done
;
; send stuff after "=" to serial port with %XX expansion until ' '.
;
serdev_next:
pwdr ; reset hardware watchdog timer
pr2s SD_CHAR,[SD_ADDR],1 ; get next byte
pcmpbi SD_CHAR,' ' ; the end?
pjumpeq serial_done ; yes...we found a ' '
pcmpbi SD_CHAR,'+' ; map '+' -> ' '
pjumpne not_plus
pmovbi SD_CHAR,' '
pjump output_next_char
not_plus:
pcmpbi SD_CHAR,'%' ; check for HTTP quoting char
pjumpne output_next_char
;
; get next two chars from request.
;
pincw SD_ADDR ; skip '%'
pr2s SD_CHAR,[SD_ADDR],2 ; get XX of %XX
paddwi SD_ADDR,1 ; skip past it
psubwi SD_LEN,2 ; discount in length
;
; hex conversion of two chars in SD_CHAR, SD_CHAR+1.
;
pmovb SD_TEMP,SD_CHAR ; get first char
pandwi SD_TEMP,0xff
psubwi SD_TEMP,'0' ; convert to binary (maybe)
pcmpwi SD_TEMP,10 ; see if valid hex digit
pjumplo do_upper_digit ; yep - do upper one
pandwi SD_TEMP,0xf ; 1-6
paddwi SD_TEMP,9 ; turn 1-6 into 10-15
do_upper_digit:
pmovb SD_CHAR,SD_CHAR+1 ; get second digit
pandwi SD_CHAR,0xff ; isolate it
psubwi SD_CHAR,'0' ; convert to binary (maybe)
pcmpwi SD_CHAR,10 ; see if valid hex digit
pjumplo merge_digits ; yep - merge the two
pandwi SD_CHAR,0xf
paddwi SD_CHAR,9
merge_digits:
pshnw SD_TEMP,4 ; shift top bits into place
paddwi SD_CHAR,[SD_TEMP] ; SD_CHAR now has char
output_next_char:
pandwi SD_CHAR,0xff ; isolate it
pcmpwi SD_CHAR,0x5c ; back-slash?
pjumpeq skip_output ; yes...don't output
pcmpwi SD_LAST,0x5c ; last character a back-slash?
pjumpne output_char_now ; no...proceed as normal
pcmpwi SD_CHAR,'r' ; got a \r?
pjumpne not_CR ; no...skip ahead
pmovbi SD_CHAR,0x0d ; yes...change it to CR
not_CR:
pcmpwi SD_CHAR,'n' ; got \n?
pjumpne not_LF ; no...
pmovbi SD_CHAR,0x0a ; yes...change to LF
not_LF:
output_char_now:
pputcb SD_CHAR
skip_output:
pmovbi SD_LAST,[SD_CHAR] ; save last character
pincw SD_ADDR ; adjust pointer
pdecw SD_LEN ; see if we're done
pjumplo serial_done
pjumpne serdev_next
serial_done:
pmovwi SD_TMO,SER_TMO ; select the timeout period
putchar_net ; switch putchar back to 'net
pcall serdev_copynet ; copy serial port input to net
ppopn putcok,1 ; restore putchar enable state
pret
;
; loop reading chars from the serial buffer and writing to 'net until
; LF encountered (or timeout)
;
; SD_TMO (word) = timeout loop count.
;
.cseg
serdev_nextchar:
pser_getc SD_CHAR ; get a char into buf
pjumpeq serdev_empty ; no more chars!
pputcb SD_CHAR ; put char to the net
pcmpbi SD_CHAR,0x0a ; check for LF
pjumpeq serdev_copydone
serdev_copynet:
pmovwi SD_TEMP,[SD_TMO] ; reset timeout
pwdr ; reset hardware watchdog timer too
pjump serdev_nextchar ; go get another one
serdev_empty:
psubwi SD_TEMP,1 ; decrement timeout
pjumpne serdev_nextchar ; not yet timed out
serdev_copydone:
pret
Back
|
|
|