In previous post, I went through example how to get raw IP packets instead of going to link/phy layer. So, in this post, I thought what if i have IP stack in verilog and i want to get actual IP packets from the interface instead of creating packets myself? why not?! It’s slow sunday after all.

So, the idea is using VPI to get the packets and pass it back to verilog. Simple, Right?

Setup Link to heading

we need to create the tunnel network interface first using the following command


sudo ip tuntap add mode tun dev tun0
sudo ip addr add 10.0.3.0/24 dev tun0 
sudo ip link set dev tun0 up

and in the same shell, we can start the ping to send something on the tunnel

ping 10.0.3.50

Verilog side Link to heading

On verilog side, it’s pretty simple, just 2-d memory to hold bytes sent to tunnel interface.

module top();

reg [7:0] mem [1023:0];

reg [31:0] i;

initial begin
$read_tun(mem);
for (i=0; i< 100;i++)  begin
   $display("mem[%0d]=%0x", i,  mem[i]);
end
end
endmodule

C side Link to heading

on C side, it’s more complicated as i have to register the vpi task first in the bootstrap. Then when called i open up the tap device and read the buffer and use vpi_put_value to set it to buffer on verilog side.

#include "vpi_user.h"

#include <fcntl.h>              /* O_RDWR */
#include <string.h>             /* memset(), memcpy() */
#include <stdio.h>              /* perror(), printf(), fprintf() */
#include <stdlib.h>             /* exit(), malloc(), free() */
#include <sys/ioctl.h>          /* ioctl() */
#include <unistd.h>             /* read(), close() */

/* includes for struct ifreq, etc */
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_tun.h>

int tun_open(char *devname)
{
    struct ifreq ifr;
    int fd, err;

    if ((fd = open("/dev/net/tun", O_RDWR)) == -1) {
        perror("open /dev/net/tun");
        exit(1);
    }
    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TUN;
    strncpy(ifr.ifr_name, devname, IFNAMSIZ);   // devname = "tun0" or "tun1", etc

    /* ioctl will use ifr.if_name as the name of TUN
     * interface to open: "tun0", etc. */
    if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) == -1) {
        perror("ioctl TUNSETIFF");
        close(fd);
        exit(1);
    }

    /* After the ioctl call the fd is "connected" to tun device specified
     * by devname ("tun0", "tun1", etc)*/

    return fd;
}

int Compiletf(char *user_data)
{
    return 0;
}

int Sizetf(char *user_data)
{
    return (32);
}



int Calltf(char *user_data)
{
    int fd, nbytes;
    char buf[1600];

    vpiHandle systfref, args_iter, argh;

    vpiHandle mem_handle, elem;
    s_vpi_value *value;
    int i;

    systfref = vpi_handle(vpiSysTfCall, NULL);
    args_iter = vpi_iterate(vpiArgument, systfref);

    mem_handle = vpi_scan(args_iter);

    printf("%x\n", mem_handle);

    vpi_printf("Hello World From VPI bootstrap\n");
    fd = tun_open("tun0");      /* devname = ifr.if_name = "tun0" */
    printf("Device tun0 opened\n");
    while (1) {
        nbytes = read(fd, buf, sizeof(buf));
        printf("Read %d bytes from tun0\n", nbytes);
        for (i = 0; i < nbytes; i++) {
            elem = vpi_handle_by_index(mem_handle, i);

            value = (s_vpi_value *) malloc(sizeof(s_vpi_value));
            value->format = vpiIntVal;
            value->value.integer = buf[i];
            vpi_put_value(elem, value, NULL, vpiForceFlag);
            free(value);
            printf("value[%d]=%x\n", i, buf[i]);
        }


        break;
    }
}

void vpi_entrypoint()
{
    s_vpi_systf_data tf_data;
    s_cb_data cb_data_s;
    s_vpi_time time_s;

    tf_data.type = vpiSysTask;
    tf_data.sysfunctype = vpiSysTask;
    tf_data.tfname = "$read_tun";
    tf_data.calltf = Calltf;
    tf_data.compiletf = Compiletf;
    tf_data.sizetf = Sizetf;
    vpi_register_systf(&tf_data);

}

void (*vlog_startup_routines[])() = {
    vpi_entrypoint,
    0
};