OpenCores
URL https://opencores.org/ocsvn/openarty/openarty/trunk

Subversion Repositories openarty

[/] [openarty/] [trunk/] [sw/] [board/] [simple_ping.c] - Blame information for rev 52

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 52 dgisselq
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Filename:    simple-ping.c
4
//
5
// Project:     OpenArty, an entirely open SoC based upon the Arty platform
6
//
7
// Purpose:     To exercise the network port by ...
8
//
9
//      1. Pinging another system, at 1PPS
10
//      2. Replying to ARP requests
11
//      3. Replying to external 'pings' requests
12
//
13
//      To configure this for your network, you will need to adjust the
14
//      following constants within this file:
15
//
16
//      my_ip_addr
17
//              This is the (fixed) IP address of your Arty board.  The first
18
//              octet of the IP address is kept in the high order word.
19
//
20
//      my_mac_addr     (unsigned long, 64 bits)
21
//              This is the fixed MAC address of your Arty board.  The first
22
//              two octets appear in bits 47:32 (MSB #s are high), and the other
23
//              four in bits 31:0. Since the Arty PHY does not come with a
24
//              designated MAC address, I generated one for my PHY using
25
//              /dev/rand.  The key to this, though, is that the second nibble
26
//              (bits 8..12) in my_mac_addr[0] must be set to 4'h2 to reflect
27
//              this fact.
28
//
29
//      ping_ip_addr
30
//              This is the IP address of the computer you wish to ping.
31
//
32
//      my_ip_router
33
//              In case the computer you wish to ping is not your
34
//              router/gateway, and worse that it is not on your network, then
35
//              you will need to fill this value in with the IP address of a
36
//              gateway server that is accessable from this network.  Place
37
//              that IP address into this variable.
38
//              
39
//      my_ip_mask
40
//              The IP mask is used to determine what is on your subnet, versus
41
//              what needs to be sent to your router/gateway.  Set this mask
42
//              such that a '1' is placed in every network bit of your IP
43
//              address, and '0' in every host bit.  For me, I am using a
44
//              network of 192.168.10.x, where x is the computer on the network,
45
//              so I set this to 0xffffff00.
46
//
47
//
48
// Creator:     Dan Gisselquist, Ph.D.
49
//              Gisselquist Technology, LLC
50
//
51
////////////////////////////////////////////////////////////////////////////////
52
//
53
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
54
//
55
// This program is free software (firmware): you can redistribute it and/or
56
// modify it under the terms of  the GNU General Public License as published
57
// by the Free Software Foundation, either version 3 of the License, or (at
58
// your option) any later version.
59
//
60
// This program is distributed in the hope that it will be useful, but WITHOUT
61
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
62
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
63
// for more details.
64
//
65
// You should have received a copy of the GNU General Public License along
66
// with this program.  (It's in the $(ROOT)/doc directory, run make with no
67
// target there if the PDF file isn't present.)  If not, see
68
// <http://www.gnu.org/licenses/> for a copy.
69
//
70
// License:     GPL, v3, as defined and found on www.gnu.org,
71
//              http://www.gnu.org/licenses/gpl.html
72
//
73
//
74
////////////////////////////////////////////////////////////////////////////////
75
//
76
//
77
#include "artyboard.h"
78
#include "zipcpu.h"
79
#include "zipsys.h"
80
#define KTRAPID_SENDPKT 0
81
#include "etcnet.h"
82
#include "protoconst.h"
83
#include "ledcolors.h"
84
#include "ipcksum.h"
85
#include "arp.h"
86
 
87
#define sys     _sys
88
 
89
unsigned        pkts_received = 0, replies_received=0, arp_requests_received=0,
90
                arp_pkt_count =0, arp_pkt_invalid =0,
91
                arp_missed_ip = 0, arp_non_broadcast = 0,
92
                ip_pkts_received = 0, ip_pkts_invalid = 0,
93
                icmp_echo_requests=0, icmp_invalid= 0,
94
                ping_reply_address_not_found = 0, ping_replies_sent = 0,
95
                ping_reply_err = 0, user_tx_packets = 0,
96
                user_heartbeats = 0;
97
 
98
unsigned long ping_mac_addr;
99
 
100
// These two numbers will allow us to keep track of how many ping's we've
101
// sent and how many we've received the returns for.
102
unsigned        ping_tx_count = 0, ping_rx_count = 0;
103
 
104
// This is a cheater's approach to knowing what IP to ping: we pre-load the
105
// program with both the IP address to ping, as well as the MAC address
106
// associated with that IP address.  Future implementations will need to
107
// 1. Look up the MAC address of the device we wish to ping (assuming our
108
//      subnet), or
109
// 2. Later, check if the IP address is not on our subnet, and if not then
110
//      look up the MAC address of the router and use that MAC address when
111
//      sending (no change to the IP)
112
unsigned        ping_ip_addr  = IPADDR(192,168,10,1);
113
unsigned long   ping_mac_addr = 0;
114
 
115
// My network ID.  The 192.168.10 part comes from the fact that this is a
116
// local network.  The .22 (last octet) is due to the fact that this is
117
// an unused ID on my network.
118
unsigned        my_ip_addr  = DEFAULTIP;
119
// Something from /dev/rand
120
//      Only ... the second nibble must be two.  Hence we start with d(2)d8.
121
unsigned long   my_mac_addr = DEFAULTMAC, router_mac_addr = 0;
122
unsigned        my_ip_mask = LCLNETMASK,
123
                my_ip_router = DEFAULT_ROUTERIP;
124
 
125
unsigned        pkt_id = 0;
126
 
127
///////////
128
//
129
//
130
// User tasks and functions (excluding ARP)
131
//
132
//
133
///////////
134
 
135
 
136
//
137
// We'll give our user 64kW of global variables
138
//
139
#define USER_STACK_SIZE 4096
140
int     user_stack[USER_STACK_SIZE];
141
const int *user_sp = &user_stack[USER_STACK_SIZE];
142
 
143
 
144
void    uping_reply(unsigned ipaddr, unsigned *icmp_request) {
145
        unsigned        pkt[2048];
146
        unsigned long   hwaddr;
147
        int             maxsz = 2048;
148
 
149
        maxsz = 1<<((sys->io_enet.n_rxcmd>>24)&0x0f);
150
        if (maxsz > 2048)
151
                maxsz = 2048;
152
        int pktln = (icmp_request[0] & 0x0ffff)+8, pktlnw = (pktln+3)>>2;
153
 
154
        if (arp_lookup(ipaddr, &hwaddr)!=0) {
155
                // Couldn't find the address -- don't reply, but send an arp
156
                // request.
157
                //
158
                // ARP request is automatic when the address isn't found,
159
                // and done by the arp_lookup.
160
                ping_reply_address_not_found++;
161
        } else if ((pktlnw < maxsz)
162
                        &&((icmp_request[0]>>24)==0x045)) {
163
                int             id;
164
 
165
                pkt[0] = (unsigned)(hwaddr>>16);
166
                pkt[1] = ((unsigned)(hwaddr<<16))|ETHERTYPE_IP;
167
                pkt[2] = icmp_request[0] & 0xff00ffff;
168
                id = pkt_id + sys->io_b.i_tim.sub;
169
                pkt[3] = (id&0x0ffff)<<16; // no fragments
170
                pkt[4] = 0xff010000;//No flags,frag offset=0,ttl=0,proto=1(ICMP)
171
                pkt[5] = icmp_request[4];       // Swap sender and receiver
172
                pkt[6] = icmp_request[3];
173
                for(int k=7; k<pktlnw; k++)
174
                        pkt[k] = icmp_request[k-2];
175
                pkt[7] = 0;
176
 
177
                if ((pktln & 3)!=0)
178
                        pkt[pktlnw-1] &= ~((1<<((4-(pktln&3))<<3))-1);
179
 
180
                // Now, let's go fill in the IP and ICMP checksums
181
                pkt[4] |= ipcksum(5, &pkt[2]);
182
 
183
                pkt[7] &= 0xffff0000;
184
                pkt[7] |= ipcksum(pktlnw-7, &pkt[7]);
185
 
186
                ping_replies_sent++;
187
 
188
                syscall(KTRAPID_SENDPKT,0,(unsigned)pkt, pktln);
189
        } else
190
                ping_reply_err ++;
191
}
192
 
193
unsigned        rxpkt[2048];
194
void    user_task(void) {
195
        unsigned        rtc = sys->io_rtc.r_clock;
196
 
197
        while(1) {
198
                do {
199
                        unsigned long   mac;
200
 
201
                        // Rate limit our ARP searching to one Hz
202
                        rtc = sys->io_rtc.r_clock;
203
 
204
                        if (arp_lookup(ping_ip_addr, &ping_mac_addr) == 0)
205
                                arp_lookup(my_ip_router, &mac);
206
 
207
                        while(((sys->io_enet.n_rxcmd & ENET_RXAVAIL)==0)
208
                                        &&(sys->io_rtc.r_clock == rtc))
209
                                user_heartbeats++;
210
                } while((sys->io_enet.n_rxcmd & ENET_RXAVAIL)==0);
211
 
212
                // Okay, now we have a receive packet ... let's process it
213
                int     etype = sys->io_enet_rx[1] & 0x0ffff;
214
                unsigned *epayload; //  = (unsigned *)&sys->io_enet_rx[2];
215
                int     invalid = 0;
216
                int     ln, rxcmd = sys->io_enet.n_rxcmd;
217
 
218
                ln = sys->io_enet.n_rxcmd & 0x07ff;
219
                for(int k=0; k<(ln+3)>>2; k++)
220
                        rxpkt[k] = sys->io_enet_rx[k];
221
                epayload = &rxpkt[2];
222
                sys->io_enet.n_rxcmd = ENET_RXCLR|ENET_RXCLRERR;
223
 
224
                pkts_received++;
225
 
226
                if (etype == ETHERTYPE_IP) {
227
                        unsigned *ip = epayload,
228
                                *ippayload = &ip[(ip[0]>>24)&0x0f];
229
 
230
                        if (((ip[0]>>28)&15)!=4)
231
                                invalid = 1;
232
                        else if (ip[1] & 0x0bfff)
233
                                // Packet is fragmented--toss it out    
234
                                invalid = 1;
235
                        else if (ip[4] != my_ip_addr)
236
                                invalid = 1;
237
 
238
                        ip_pkts_received += (invalid^1);
239
                        ip_pkts_invalid  += (invalid);
240
 
241
                        unsigned ipproto = (ip[2]>>16)&0x0ff;
242
                        if((invalid==0)&&(ipproto == IPPROTO_ICMP)) {
243
                                unsigned icmp_type = ippayload[0]>>24;
244
                                if (icmp_type == ICMP_ECHOREPLY) {
245
                                        // We got our ping response
246
                                        sys->io_b.i_clrled[3] = LEDC_GREEN;
247
                                        sys->io_b.i_leds = 0x80;
248
                                        ping_rx_count++;
249
                                } else if (icmp_type == ICMP_ECHO) {
250
                                        // Someone is pinging us
251
                                        uping_reply(ip[3],ip);
252
                                        icmp_echo_requests++;
253
                                } else
254
                                        icmp_invalid++;
255
                        }
256
                } else if (etype == ETHERTYPE_ARP) {
257
                        arp_pkt_count++;
258
                        if (epayload[0] != 0x010800) {
259
                                invalid = 1;
260
                                arp_pkt_invalid++;
261
                        } else if ((epayload[1] == 0x06040001)
262
                                &&(rxcmd & ENET_RXBROADCAST)
263
                                &&(epayload[6] == my_ip_addr))
264
                                { // ARP Request
265
                                unsigned sha[2], // Senders HW address
266
                                        sip, // Senders IP address
267
                                        dip; // Desired IP address
268
                                sha[0] = epayload[2];
269
                                sha[1] = epayload[3]>>16;
270
                                sha[1] |= sha[0]<<16;
271
                                sha[0] >>= 16;
272
                                sip = (epayload[3]<<16)|(epayload[4]>>16);
273
                                arp_requests_received++;
274
                                send_arp_reply(sha[0], sha[1], sip);
275
                        } else if ((epayload[1] == 0x06040002) // Reply
276
                                &&((rxcmd & ENET_RXBROADCAST)==0)
277
                                &&(epayload[6] == my_ip_addr)) {
278
                                unsigned        sip;
279
                                unsigned long   sha;
280
 
281
                                sha = *(unsigned long *)(&epayload[2]);
282
                                sha >>= 16;
283
                                sip = (epayload[3]<<16)|(epayload[4]>>16);
284
                                if (sip == ping_ip_addr)
285
                                        ping_mac_addr = sha;
286
                                arp_table_add(sip, sha);
287
                        }
288
                }
289
        }
290
}
291
 
292
 
293
///////////
294
//
295
//
296
// Supervisor tasks and functions
297
//
298
//
299
///////////
300
 
301
 
302
void    send_ping(void) {
303
        unsigned *pkt;
304
 
305
        // If we don't know our destination MAC address yet, just return
306
        if (ping_mac_addr==0) {
307
                sys->io_b.i_clrled[1] = LEDC_YELLOW;
308
                return;
309
        }
310
 
311
        // If the network is busy transmitting, wait for it to finish
312
        if (sys->io_enet.n_txcmd & ENET_TXBUSY) {
313
                while(sys->io_enet.n_txcmd & ENET_TXBUSY)
314
                        ;
315
        }
316
 
317
        // Form a packet to transmit
318
        pkt = (unsigned *)&sys->io_enet_tx;
319
        pkt[0] = (ping_mac_addr>>16);
320
        pkt[1] = ((unsigned)(ping_mac_addr<<16))|ETHERTYPE_IP;
321
        pkt[2] = 0x4500001c;
322
        pkt_id += BIG_PRIME; // A BIG prime number
323
        pkt[3] = (pkt_id&0x0ffff)<<16;;
324
        pkt[4] = 0x80010000; // No flags, ragment offset = 0, ttl=0, proto=1(ICMP)
325
        pkt[5] =   my_ip_addr;
326
        pkt[6] = ping_ip_addr;
327
        // Ping payload: type = 0x08 (PING, the response will be zero)
328
        //      CODE = 0
329
        //      Checksum will be filled in later
330
        pkt[7] = 0x08000000;
331
        pkt_id += BIG_PRIME;
332
        pkt[8] = (pkt_id + BIG_PRIME);
333
 
334
        // Calculate the IP header checksum
335
        pkt[4] |= ipcksum(5, &pkt[2]);
336
 
337
        // Calculate the PING payload checksum
338
        pkt[7] &= 0xffff0000;
339
        pkt[7] |= ipcksum(2, &pkt[7]);
340
 
341
        // Finally, send the packet -- 9*4 = our total number of octets
342
        sys->io_enet.n_txcmd = ENET_TXCMD(9*4);
343
 
344
        ping_tx_count++;
345
}
346
 
347
 
348
int     heartbeats = 0, subbeats = 0, gbl_picv = 0;
349
int main(int argc, char **argv) {
350
        unsigned        user_context[16];
351
        int             lastpps;
352
 
353
        for(int i=0; i<16; i++)
354
                user_context[i] = 0;
355
        user_context[13] = (unsigned)user_sp;
356
        user_context[15] = (unsigned)user_task;
357
        restore_context(user_context);
358
 
359
        init_arp_table();
360
 
361
        for(int i=0; i<4; i++)
362
                sys->io_b.i_clrled[i] = LEDC_BRIGHTRED;
363
        sys->io_b.i_leds = 0x0ff;
364
 
365
        // Start up the network interface
366
        if ((sys->io_enet.n_txcmd & ENET_RESET)!=0)
367
                sys->io_enet.n_txcmd = 0; // Turn on all our features
368
        {
369
                volatile unsigned long *emac = (volatile unsigned long *)&sys->io_enet.n_mac;
370
                *emac = my_mac_addr;
371
        }
372
 
373
        // Turn off our right-hand LED, first part of startup is complete
374
        sys->io_b.i_leds = 0x010;
375
        // Turn our first CLR LED green as well
376
        sys->io_b.i_clrled[0] = LEDC_GREEN;
377
 
378
        // Set our timer to have us send a ping 1/sec
379
        zip->z_tma = CLOCKFREQ_HZ | TMR_INTERVAL;
380
 
381
        sys->io_enet.n_rxcmd = ENET_RXCLRERR|ENET_RXCLR;
382
 
383
        while(1) {
384
                unsigned        picv, bmsr;
385
 
386
                heartbeats++;
387
 
388
                // Wait while the link is being negotiated
389
                // --- Read the MDIO status register
390
                bmsr = sys->io_netmdio.e_v[MDIO_BMSR];
391
                if ((bmsr & 4)==0) {
392
                        // Link is down, do nothing this time through
393
                        sys->io_b.i_clrled[1] = LEDC_BRIGHTRED;
394
                        sys->io_b.i_clrled[2] = LEDC_BRIGHTRED;
395
                        sys->io_b.i_clrled[3] = LEDC_BRIGHTRED;
396
                } else {
397
                        sys->io_b.i_leds = 0x020;
398
                        sys->io_b.i_clrled[1] = LEDC_GREEN;
399
                        send_ping();
400
                        sys->io_b.i_clrled[2] = LEDC_BRIGHTRED; // Have we received a response?
401
                        sys->io_b.i_clrled[3] = LEDC_BRIGHTRED; // Was it our ping response?
402
                }
403
 
404
                // Clear any timer or PPS interrupts, disable all others
405
                zip->z_pic = DALLPIC;
406
                zip->z_pic = EINT(SYSINT_TMA|SYSINT_PPS|SYSINT_ENETRX);
407
                do {
408
                        if ((zip->z_pic & INTNOW)==0)
409
                                zip_rtu();
410
                        subbeats++;
411
 
412
                        picv = zip->z_pic;
413
                        gbl_picv = picv;
414
 
415
                        // Clear the ints we just saw.  Warning, though, we'll
416
                        // need to re-enable them later
417
                        zip->z_pic = (picv & 0x0ffff);
418
 
419
                        if (zip_ucc() & CC_FAULT) {
420
                                sys->io_b.i_leds = 0x0ff;
421
                                sys->io_b.i_clrled[0] = LEDC_BRIGHTRED;
422
                                sys->io_b.i_clrled[1] = LEDC_BRIGHTRED;
423
                                sys->io_b.i_clrled[2] = LEDC_BRIGHTRED;
424
                                sys->io_b.i_clrled[3] = LEDC_BRIGHTRED;
425
                                zip_halt();
426
                        } else if (zip_ucc() & CC_TRAP) {
427
                                save_context((int *)user_context);
428
                                // R0 = return address
429
                                // R1 = Trap ID, write or send
430
                                // R2 = FD, or socketFD
431
                                // R3 = buffer
432
                                // R4 = count
433
                                // R5 = (flags, i socket)
434
                                unsigned *sptr = (void *)user_context[3], ln;
435
                                ln = user_context[4];
436
                                while(sys->io_enet.n_txcmd & ENET_TXBUSY)
437
                                        ;
438
                                if (ln < 1400) {
439
                                        for(int i=0; i<(ln+3)>>2; i++)
440
                                                sys->io_enet_tx[i] = *sptr++;
441
                                        sys->io_enet.n_txcmd = ENET_TXCMD(ln);
442
 
443
                                        user_tx_packets++;
444
                                        // (Re-)Enable the transmit complete
445
                                        // interrupt
446
                                        //
447
                                        // At this point, we are also ready to
448
                                        // receive another packet
449
                                        zip->z_pic = EINT(SYSINT_ENETTX|SYSINT_ENETRX);
450
                                }
451
 
452
                                user_context[14] &= ~CC_TRAP;
453
                                restore_context(user_context);
454
                        } else if ((picv & INTNOW)==0) {
455
                                sys->io_b.i_leds = 0x0ff;
456
                                sys->io_b.i_clrled[0] = LEDC_BRIGHTRED;
457
                                sys->io_b.i_clrled[1] = LEDC_WHITE;
458
                                sys->io_b.i_clrled[2] = LEDC_BRIGHTRED;
459
                                sys->io_b.i_clrled[3] = LEDC_BRIGHTRED;
460
                                zip_halt();
461
                        } else if ((picv & DINT(SYSINT_TMA))==0) {
462
                                sys->io_b.i_leds = 0x0ff;
463
                                sys->io_b.i_clrled[0] = LEDC_BRIGHTRED;
464
                                sys->io_b.i_clrled[1] = LEDC_BRIGHTRED;
465
                                sys->io_b.i_clrled[2] = LEDC_WHITE;
466
                                sys->io_b.i_clrled[3] = LEDC_BRIGHTRED;
467
                                zip_halt();
468
                        } else if ((picv & DINT(SYSINT_PPS))==0) {
469
                                sys->io_b.i_leds = 0x0ff;
470
                                sys->io_b.i_clrled[0] = LEDC_BRIGHTRED;
471
                                sys->io_b.i_clrled[1] = LEDC_BRIGHTRED;
472
                                sys->io_b.i_clrled[2] = LEDC_BRIGHTRED;
473
                                sys->io_b.i_clrled[3] = LEDC_WHITE;
474
                                zip_halt();
475
                        } if (picv & SYSINT_ENETRX) {
476
                                // This will not clear until the packet has
477
                                // been removed and the interface reset.  For
478
                                // now, just double check that the interrupt
479
                                // has been disabled.
480
                                if (picv&(DINT(SYSINT_ENETRX))) {
481
                                        zip->z_pic = DINT(SYSINT_ENETRX);
482
                                        sys->io_b.i_leds = 0x040;
483
                                        sys->io_b.i_clrled[2] = LEDC_GREEN;
484
                                }
485
                        } else
486
                                zip->z_pic = EINT(SYSINT_ENETRX);
487
                        if (picv & SYSINT_ENETTX) {
488
                                // This will also likewise not clear until a 
489
                                // packet has been queued up for transmission.
490
                                // Hence, let's just disable the interrupt.
491
                                if (picv&(DINT(SYSINT_ENETTX)))
492
                                        zip->z_pic = DINT(SYSINT_ENETTX);
493
                        } else
494
                                zip->z_pic = EINT(SYSINT_ENETTX);
495
                        // Make certain interrupts remain enabled
496
                        zip->z_pic = EINT(SYSINT_TMA|SYSINT_PPS);
497
 
498
                        if (picv & SYSINT_TMA) {
499
                                if (lastpps==1)
500
                                        lastpps = 2;
501
                                else {
502
                                        picv &= ~SYSINT_TMA;
503
                                        lastpps = 0;
504
                                }
505
                        }
506
                } while((picv & (SYSINT_TMA|SYSINT_PPS))==0);
507
                if (picv & SYSINT_PPS) {
508
                        lastpps = 1;
509
                        zip->z_tma = CLOCKFREQ_HZ | TMR_INTERVAL;
510
                }
511
        }
512
}
513
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.