Avaris on Ultrascale+ using USB3

Hello,
First of all, thank you for building this project.
I’m currently studying the feasibility of using Aravis on a mixed environment with a proprietary camera connected to a Xilinx Ultrascale+ (with ARM APU and FPGA), whose processed video stream would be transferred through an USB 3.0 connection to a Linux PC running as host. The main idea would be to use the Aravis software on both Ultrascale ARM baremetal (no OS) environment (as a device) and on a PC Linux environment (as a host) for collecting the video stream.

It is possible to port the required Avaris software to an ARM environment to create an USB device and “simulate” a camera? Or this software is only prepared to be used as an USB host running Linux? I see many Linux dependencies such as glib, gio, etc. are used, so this port would require some “tweaks” which could make it very difficult.
Any help and ideas would be much appreciated.
Thank you.

Hi João,

Welcome here !

The USB code in aravis only implements the controller and stream receiving part of the USB protocol. It should be relatively easy to write the device USB code though, not very different than the GigEVision one I wrote some years ago for a work project running bare metal on a luminary microcontroller. Its is just a matter of receiving read/write command packets, and send back the request informations or an acknowledge.

Here is the code:

  • cocagne.c:
#include <inc/hw_ints.h>
#include <inc/hw_memmap.h>
#include <inc/hw_nvic.h>
#include <inc/hw_types.h>
#include <inc/hw_epi.h>
#include <inc/hw_flash.h>
#include <inc/lm3s9b92.h>
#include <driverlib/ethernet.h>
#include <driverlib/flash.h>
#include <driverlib/gpio.h>
#include <driverlib/interrupt.h>
#include <driverlib/rom.h>
#include <driverlib/rom_map.h>
#include <driverlib/sysctl.h>
#include <driverlib/systick.h>
#include <utils/uartstdio.h>
#include <utils/ustdlib.h>
#include <utils/lwiplib.h>
#include <ccggvcp.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>

#include <cocagne.zip.c>

#define CCG_FPGA_BOARD_ID_OFFSET		0
#define CCG_FPGA_BOARD_ID_MASK			0x7fff
#define CCG_FPGA_FIRMWARE_VERSION_OFFSET	2
#define CCG_FPGA_FIRMWARE_VERSION_MASK		0x00ff

/* System clock setting */

#define SYSTICKHZ               100
#define SYSTICKMS               (1000 / SYSTICKHZ)

#ifdef DEBUG
void
__error__(char *pcFilename, unsigned long ulLine)
{
}
#endif

static int firmware_update_request = 0;

/* Callback called periodically in the lwIP context. */

void
lwIPHostTimerHandler(void)
{
	static char g_pcTwirl[4] = { '\\', '|', '/', '-' };
	static unsigned long g_ulTwirlPos = 0;
	static unsigned long g_ulLastIPAddr = 0;
	unsigned long ulIPAddress;

	ulIPAddress = lwIPLocalIPAddrGet();

	if(ulIPAddress == 0) {
		UARTprintf("\b%c", g_pcTwirl[g_ulTwirlPos]);
		g_ulTwirlPos = (g_ulTwirlPos + 1) & 3;
	} else if (ulIPAddress != g_ulLastIPAddr) {
		UARTprintf("\rIP: %d.%d.%d.%d       \n", ulIPAddress & 0xff,
			   (ulIPAddress >> 8) & 0xff, (ulIPAddress >> 16) & 0xff,
			   (ulIPAddress >> 24) & 0xff);

		g_ulLastIPAddr = ulIPAddress;

		ulIPAddress = lwIPLocalNetMaskGet();
		UARTprintf("Netmask: %d.%d.%d.%d\n", ulIPAddress & 0xff,
			   (ulIPAddress >> 8) & 0xff, (ulIPAddress >> 16) & 0xff,
			   (ulIPAddress >> 24) & 0xff);

		ulIPAddress = lwIPLocalGWAddrGet();
		UARTprintf("Gateway: %d.%d.%d.%d\n\n", ulIPAddress & 0xff,
			   (ulIPAddress >> 8) & 0xff, (ulIPAddress >> 16) & 0xff,
			   (ulIPAddress >> 24) & 0xff);
	}
}

void
SysTickIntHandler(void)
{
    lwIPTimer (SYSTICKMS);
}

#define CCG_MEMORY_SIZE 		0x400
#define CCG_FPGA_REGISTERS_OFFSET	0x10000
#define CCG_FPGA_REGISTERS_SIZE		0x2000

typedef struct {
	void *pcb;
	volatile uint16_t *epi_memory;
	uint32_t control_channel_privilege;
	uint32_t heartbeat_value;
	char memory[CCG_MEMORY_SIZE];
} Cocagne;

static void
ccg_receive_gvcp_packet (void *arg, struct udp_pcb *pcb, struct pbuf *p,
			 struct ip_addr *addr, uint16_t port)
{
	Cocagne *cocagne = arg;
	CcgGvcpPacket *packet;
	struct pbuf *ack = NULL;
	uint32_t packet_count;
	uint32_t address;
	uint32_t value;
	uint32_t size;
	uint32_t IP_address;
	int dump_memory = 0;

	if (arg == NULL) {
		pbuf_free (p);
		return;
	}

	IP_address = lwIPLocalIPAddrGet();
	memcpy (cocagne->memory + CCG_GVBS_CURRENT_IP_ADDRESS_OFFSET, &IP_address, sizeof (IP_address));

	packet = p->payload;
	packet_count = ccg_gvcp_packet_get_packet_count (packet);

	/* A firmware update request is triggered by a write at the
	 * address CCG_FPGA_REGISTERS_OFFSET + CCG_FPGA_REGISTERS_SIZE */

	switch (ntohs (packet->header.command)) {
		case CCG_GVCP_COMMAND_DISCOVERY_CMD:
			UARTprintf ("Discovery\n");
			ack = ccg_gvcp_packet_new_discovery_ack ();
			if (ack != NULL)
				packet = ack->payload;
				memcpy (&packet->data, cocagne->memory, CCG_GVBS_DISCOVERY_DATA_SIZE);
			break;

		case CCG_GVCP_COMMAND_READ_MEMORY_CMD:
			ccg_gvcp_packet_get_read_memory_cmd_infos (packet, &address, &size);
			UARTprintf ("Read %d bytes at 0x%08x\n", size, address);
			ack = ccg_gvcp_packet_new_read_memory_ack (address, size, packet_count);
			if (ack == NULL)
				break;

			packet = ack->payload;
			if (size + address <= CCG_MEMORY_SIZE) {
				memcpy (ccg_gvcp_packet_get_read_memory_ack_data (packet),
					cocagne->memory + address, size);
				dump_memory = 1;
			} else if (address == CCG_FPGA_REGISTERS_OFFSET + CCG_FPGA_REGISTERS_SIZE &&
				   size == sizeof (uint32_t)) {
				value = 0;

				memcpy (ccg_gvcp_packet_get_read_memory_ack_data (packet),
					&value,
					sizeof (uint32_t));
			} else if (address >= CCG_FPGA_REGISTERS_OFFSET &&
				   address + size <= CCG_FPGA_REGISTERS_OFFSET + CCG_FPGA_REGISTERS_SIZE &&
				   size == sizeof (uint32_t)) {
				uint16_t low, high;
				unsigned epi_address;

				epi_address = (address - CCG_FPGA_REGISTERS_OFFSET) / 2;

				low = cocagne->epi_memory[epi_address];
				high =  cocagne->epi_memory[epi_address + 1];

				value = low + (high << 16);

				memcpy (ccg_gvcp_packet_get_read_memory_ack_data (packet),
					&value,
					sizeof (uint32_t));

			} else if (address >= CCG_GENICAM_DATA_OFFSET &&
				   address + size <= CCG_GENICAM_DATA_OFFSET + CCG_GENICAM_DATA_SIZE + 4 ) {
				memcpy (ccg_gvcp_packet_get_read_memory_ack_data (packet),
					ccg_genicam_xml + address - CCG_GENICAM_DATA_OFFSET,
					size);
			} else {
				memset (ccg_gvcp_packet_get_read_memory_ack_data (packet), 0, size);
				UARTprintf ("Invalid read area\n");
			}
			break;

		case CCG_GVCP_COMMAND_WRITE_MEMORY_CMD:
			ccg_gvcp_packet_get_write_memory_cmd_infos (packet, &address, &size);
			UARTprintf ("Write %d bytes at 0x%08x\n", size, address);

			if (address == CCG_FPGA_REGISTERS_OFFSET + CCG_FPGA_REGISTERS_SIZE &&
			    size == sizeof (uint32_t)) {
				UARTprintf ("Update request\n");
				firmware_update_request = 1;
			} else if (address >= CCG_FPGA_REGISTERS_OFFSET &&
			    address + size <= CCG_FPGA_REGISTERS_OFFSET + CCG_FPGA_REGISTERS_SIZE &&
			    size == sizeof (uint32_t)) {
				uint16_t low, high;
				unsigned epi_address;

				epi_address = (address - CCG_FPGA_REGISTERS_OFFSET) / 2;

				memcpy (&value,
					ccg_gvcp_packet_get_write_memory_cmd_data (packet),
					sizeof (uint32_t));

				low = value & 0xffff;
				high = (value >> 16) & 0xffff;

				cocagne->epi_memory[epi_address] = low;
				cocagne->epi_memory[epi_address + 1] = high;
			} else {
				UARTprintf ("Invalid write area\n");
			}
			ack = ccg_gvcp_packet_new_write_memory_ack (address, packet_count);
			break;

		case CCG_GVCP_COMMAND_READ_REGISTER_CMD:
			ccg_gvcp_packet_get_read_register_cmd_infos (packet, &address);
			UARTprintf ("Read register at 0x%08x\n", address);

			if (address + sizeof (uint32_t) <= CCG_MEMORY_SIZE) {
				value = *((uint32_t *) (char *) (cocagne->memory + address));
			} else if (address == CCG_FPGA_REGISTERS_OFFSET + CCG_FPGA_REGISTERS_SIZE) {
				value = 0;
			} else if (address >= CCG_FPGA_REGISTERS_OFFSET &&
				   address + sizeof (uint32_t) <= CCG_FPGA_REGISTERS_OFFSET + CCG_FPGA_REGISTERS_SIZE) {
				uint16_t low, high;
				unsigned epi_address;

				epi_address = (address - CCG_FPGA_REGISTERS_OFFSET) / 2;

				low = cocagne->epi_memory[epi_address];
				high =  cocagne->epi_memory[epi_address + 1];

				value = low + (high << 16);
			} else
				switch (address) {
					case CCG_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET:
						value = cocagne->control_channel_privilege;
						break;
					case CCG_GVBS_GVCP_CAPABILITY_OFFSET:
						value = 0;
						break;
					case CCG_GVBS_HEARTBEAT_TIMEOUT_OFFSET:
						value = cocagne->heartbeat_value;
						break;
					default:
						UARTprintf ("Invalid read address\n");
						value = 0;
				}
			ack = ccg_gvcp_packet_new_read_register_ack (value, packet_count);
			break;

		case CCG_GVCP_COMMAND_WRITE_REGISTER_CMD:
			ccg_gvcp_packet_get_write_register_cmd_infos (packet, &address, &value);
			UARTprintf ("Write 0x%08x at register 0x%08x\n", value, address);

			if (address + sizeof (u32_t) <= CCG_MEMORY_SIZE) {
				*((u32_t *) (char *) (cocagne->memory + address)) = value;
			} else if (address == CCG_FPGA_REGISTERS_OFFSET + CCG_FPGA_REGISTERS_SIZE) {
				UARTprintf ("Update request\n");
				firmware_update_request = 1;
			} else if (address >= CCG_FPGA_REGISTERS_OFFSET &&
				   address + sizeof (uint32_t) - CCG_FPGA_REGISTERS_OFFSET <= CCG_FPGA_REGISTERS_SIZE) {
				uint16_t low, high;
				unsigned epi_address;

				epi_address = (address - CCG_FPGA_REGISTERS_OFFSET) / 2;

				low = value & 0xffff;
				high = (value >> 16) & 0xffff;

				cocagne->epi_memory[epi_address] = low;
				cocagne->epi_memory[epi_address + 1] = high;
			} else {
				switch (address) {
					case CCG_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET:
						cocagne->control_channel_privilege = value;
						break;
					case CCG_GVBS_HEARTBEAT_TIMEOUT_OFFSET:
						cocagne->heartbeat_value = value;
						break;
					default:
						UARTprintf ("Invalid write address\n");
				}
			}
			ack = ccg_gvcp_packet_new_write_register_ack (address, packet_count);
			break;

		default:
			UARTprintf ("Unknown command\n");
	}

	pbuf_free(p);

	if (ack != NULL) {
		udp_sendto (cocagne->pcb, ack, addr, port);
		pbuf_free (ack);
	}

#if 0
	if (dump_memory) {
		int i;
		for (i = 0; i < block_size; i++) {
			UARTprintf ("0x%02x ", *(cocagne->memory + block_address + i));
			if ((i % 8) == 7)
				UARTprintf ("\n");
			else
				UARTprintf (" ");
		}
		if (i != 0)
			UARTprintf ("\n");
	}
#endif
}

static int
ccg_get_board_id (Cocagne *cocagne)
{
	if (cocagne == NULL)
		return 0;

	return cocagne->epi_memory[CCG_FPGA_BOARD_ID_OFFSET] & CCG_FPGA_BOARD_ID_MASK;
}

static int
ccg_get_firmware_version (Cocagne *cocagne)
{
	if (cocagne == NULL)
		return 0;

	return cocagne->epi_memory[CCG_FPGA_FIRMWARE_VERSION_OFFSET] & CCG_FPGA_FIRMWARE_VERSION_MASK;
}

static Cocagne *
ccg_new (uint16_t *epi_memory)
{
	Cocagne *cocagne;
	unsigned char pucMACArray[8];
	char *memory;
	int result;
	uint32_t value;

	cocagne = mem_malloc (sizeof (Cocagne));
	if (cocagne == NULL)
		return NULL;

	memset (cocagne, 0, sizeof (Cocagne));
	cocagne->epi_memory = epi_memory;
	memory = cocagne->memory;

	strcpy (memory + CCG_GVBS_MANUFACTURER_NAME_OFFSET, "LAPP");
	strcpy (memory + CCG_GVBS_MODEL_NAME_OFFSET, "CameraBox");
	strcpy (memory + CCG_GVBS_DEVICE_VERSION_OFFSET, "1.0");
	strcpy (memory + CCG_GVBS_MANUFACTURER_INFORMATIONS_OFFSET, "Annecy-le-Vieux - France");

	usnprintf (memory + CCG_GVBS_SERIAL_NUMBER_OFFSET, CCG_GVBS_SERIAL_NUMBER_SIZE,
		   "CCG%04x", ccg_get_board_id (cocagne));

	strcpy (memory + CCG_GVBS_XML_URL_0_OFFSET, ccg_genicam_filename);

	lwIPLocalMACGet (pucMACArray);

	memcpy (memory + CCG_GVBS_DEVICE_MAC_ADDRESS_HIGH_OFFSET + 2, pucMACArray, sizeof (pucMACArray));

	value = 0;
	memcpy (memory + CCG_GVBS_DEVICE_MODE_OFFSET, &value, sizeof (value));

	cocagne->pcb = udp_new ();
	udp_recv (cocagne->pcb, ccg_receive_gvcp_packet, cocagne);
	result = udp_bind (cocagne->pcb, IP_ADDR_ANY, CCG_GVCP_PORT);
	if (result != ERR_OK)
		UARTprintf("Bind failed\n");

	return cocagne;
}

static uint16_t *
epi_init_gpio_16_12 (uint32_t map_address_code, unsigned int clock_divider)
{
	uint32_t map_address;
	uint16_t *p_uint16;

	/* Configuration of EPI in GPIO mode, 16 data bits, 12 address bits. */

	/* 1. Enable the EPI module using the RCGC1 register. */

	ROM_SysCtlPeripheralEnable (SYSCTL_PERIPH_EPI0);

	/* 2. Enable the clock to the appropriate GPIO module via the RCGC2 
	 * register. See page 286. To find out which GPIO port to enable, refer
	 * to Table 9-1 on page 455 or Table 9-2 on page 456. */

	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);

	/* 3. Set the GPIO AFSEL bits for the appropriate pins. See page 421.
	 * To determine which GPIOs to configure, see Table 24-4 on page 1238. */

	GPIOPinConfigure(GPIO_PH3_EPI0S0);
	GPIOPinConfigure(GPIO_PH2_EPI0S1);
	GPIOPinConfigure(GPIO_PC4_EPI0S2);
	GPIOPinConfigure(GPIO_PC5_EPI0S3);
	GPIOPinConfigure(GPIO_PC6_EPI0S4);
	GPIOPinConfigure(GPIO_PC7_EPI0S5);
	GPIOPinConfigure(GPIO_PH0_EPI0S6);
	GPIOPinConfigure(GPIO_PH1_EPI0S7);
	GPIOPinConfigure(GPIO_PE0_EPI0S8);
	GPIOPinConfigure(GPIO_PE1_EPI0S9);
	GPIOPinConfigure(GPIO_PH4_EPI0S10);
	GPIOPinConfigure(GPIO_PH5_EPI0S11);
	GPIOPinConfigure(GPIO_PF4_EPI0S12);
	GPIOPinConfigure(GPIO_PG0_EPI0S13);
	GPIOPinConfigure(GPIO_PG1_EPI0S14);
	GPIOPinConfigure(GPIO_PF5_EPI0S15);
	GPIOPinConfigure(GPIO_PJ0_EPI0S16);
	GPIOPinConfigure(GPIO_PJ1_EPI0S17);
	GPIOPinConfigure(GPIO_PJ2_EPI0S18);
	GPIOPinConfigure(GPIO_PJ3_EPI0S19);
	GPIOPinConfigure(GPIO_PD2_EPI0S20);
	GPIOPinConfigure(GPIO_PD3_EPI0S21);
	GPIOPinConfigure(GPIO_PB5_EPI0S22);
	GPIOPinConfigure(GPIO_PB4_EPI0S23);
	GPIOPinConfigure(GPIO_PE2_EPI0S24);
	GPIOPinConfigure(GPIO_PE3_EPI0S25);
	GPIOPinConfigure(GPIO_PH6_EPI0S26);
	GPIOPinConfigure(GPIO_PH7_EPI0S27);
	GPIOPinConfigure(GPIO_PJ4_EPI0S28);
	GPIOPinConfigure(GPIO_PJ5_EPI0S29);
	GPIOPinConfigure(GPIO_PJ6_EPI0S30);
	GPIOPinConfigure(GPIO_PG7_EPI0S31);

	/* 4. Configure the GPIO current level and/or slew rate as specified for
	 *  the mode selected. See page 423 and page 431. */

	GPIOPinTypeEPI(GPIO_PORTB_BASE, GPIO_PIN_5 | GPIO_PIN_4);
	GPIOPinTypeEPI(GPIO_PORTC_BASE, GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4);
	GPIOPinTypeEPI(GPIO_PORTD_BASE, GPIO_PIN_3 | GPIO_PIN_2);
	GPIOPinTypeEPI(GPIO_PORTE_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1 | GPIO_PIN_0);
	GPIOPinTypeEPI(GPIO_PORTF_BASE, GPIO_PIN_5 | GPIO_PIN_4);
	GPIOPinTypeEPI(GPIO_PORTG_BASE, GPIO_PIN_7 | GPIO_PIN_1 | GPIO_PIN_0);
	GPIOPinTypeEPI(GPIO_PORTH_BASE, GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4 |
		       GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1 | GPIO_PIN_0);
	GPIOPinTypeEPI(GPIO_PORTJ_BASE, GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 |
		       GPIO_PIN_2 | GPIO_PIN_1 | GPIO_PIN_0);

	/* 6.1. select the mode (GPIO) using the MODE field in EPICFG register - see page 368.
	 * EPI_CFG_BLKEN is mandatory for correct further register programming
	 * EPI_CFG_MODE_NONE sets "General Purpose" mode. */

	EPI0_CFG_R = EPI_CFG_BLKEN | EPI_CFG_MODE_NONE;

	 /* 6.2. set mode-specific details in registers EPIxxxCFG and EPIxxxCFG2
	  * -> GPIO mode - see pages 379, 388
	  * D16/A12, 1-cycle writes, 2-cycle reads, free running clock. */

	EPI0_GPCFG_R = EPI_GPCFG_CLKPIN |
		EPI_GPCFG_DSIZE_16BIT | EPI_GPCFG_ASIZE_12BIT |
		EPI_GPCFG_RD2CYC | EPI_GPCFG_RW;

	EPI0_BAUD_R = (EPI0_BAUD_R & ~EPI_BAUD_COUNT_M) | ((clock_divider - 1) & 0xffff);

	switch (map_address_code) {
		case EPI_ADDRMAP_EPADR_A000:
			map_address = 0xA0000000;
			EPI0_ADDRMAP_R = EPI_ADDRMAP_EPSZ_64KB | map_address_code;
			break;
		case EPI_ADDRMAP_EPADR_C000:
			map_address = 0xC0000000;
			EPI0_ADDRMAP_R = EPI_ADDRMAP_EPSZ_64KB | map_address_code;
			break;
		case EPI_ADDRMAP_ERADR_6000:
			map_address = 0x60000000;
			EPI0_ADDRMAP_R = EPI_ADDRMAP_ERSZ_64KB | map_address_code;
			break;
		case EPI_ADDRMAP_ERADR_8000:
			map_address = 0x80000000;
			EPI0_ADDRMAP_R = EPI_ADDRMAP_ERSZ_64KB | map_address_code;
			break;
		default:
			return NULL;
	}


	/* Set default addresses and increments - see pages 391 - 392. */

	EPI0_RADDR0_R = 0;
	EPI0_RSIZE0_R = EPI_RSIZE0_SIZE_16BIT;
	EPI0_RADDR1_R = 0;
	EPI0_RSIZE1_R = EPI_RSIZE1_SIZE_16BIT;

	p_uint16 = (void *) map_address;

	return (void *) map_address;
}

int
main(void)
{
	Cocagne *cocagne;
	unsigned long ulUser0, ulUser1;
	unsigned char pucMACArray[8];
	uint16_t *epi_memory;

	/* Set the clocking to run directly from the crystal */
	ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
			   SYSCTL_XTAL_16MHZ);

	/* Initialize the UART */
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
	GPIOPinConfigure(GPIO_PA0_U0RX);
	GPIOPinConfigure(GPIO_PA1_U0TX);
	ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	UARTStdioInit(0);
	UARTprintf("\033[2JCOCAGNE - COntrole de CAmeras Gigabit etherNEt\n\n");
	UARTprintf("Laboratoire d'Annecy-le-Vieux de physique des particules - 2010-2013\n\n");

	/* Enable and Reset the Ethernet Controller */
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ETH);
	ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_ETH);

	/* Enable Port F for Ethernet LEDs */
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
	GPIOPinConfigure(GPIO_PF2_LED1);
	GPIOPinConfigure(GPIO_PF3_LED0);
	GPIOPinTypeEthernetLED(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3);

	/* Configure SysTick for a periodic interrupt */
	ROM_SysTickPeriodSet(ROM_SysCtlClockGet() / SYSTICKHZ);
	ROM_SysTickEnable();
	ROM_SysTickIntEnable();

	epi_memory = epi_init_gpio_16_12 (EPI_ADDRMAP_EPADR_C000, 4);
	if (epi_memory == NULL) {
		UARTprintf("EPI initialization failed!\n");
		while(1) { }
	}

	/* Enable processor interrupts */
	ROM_IntMasterEnable();

	/* Read the MAC address from the user registers */
	ROM_FlashUserGet(&ulUser0, &ulUser1);
	if((ulUser0 == 0xffffffff) || (ulUser1 == 0xffffffff))
	{
		uint32_t board_id = epi_memory[CCG_FPGA_BOARD_ID_OFFSET] & CCG_FPGA_BOARD_ID_MASK;

		pucMACArray[0] = 0x02;
		pucMACArray[1] = 0x63;
		pucMACArray[2] = 0x63;
		pucMACArray[3] = 0x67;
		pucMACArray[4] = (board_id & 0xff00) >> 8;
		pucMACArray[5] = board_id & 0xff;

		ulUser0 = pucMACArray[0] | (pucMACArray[1] << 8) | (pucMACArray[2] << 16);
		ulUser1 = pucMACArray[3] | (pucMACArray[4] << 8) | (pucMACArray[5] << 16);;

		ROM_FlashUserSet(ulUser0, ulUser1);

		UARTprintf("MAC address initialized!\n\n");
	} else {
		/* Convert the 24/24 split MAC address from NV ram into a 32/16 split MAC
		   address needed to program the hardware registers, then program the MAC
		   address into the Ethernet Controller registers. */

		pucMACArray[0] = ((ulUser0 >>  0) & 0xff);
		pucMACArray[1] = ((ulUser0 >>  8) & 0xff);
		pucMACArray[2] = ((ulUser0 >> 16) & 0xff);
		pucMACArray[3] = ((ulUser1 >>  0) & 0xff);
		pucMACArray[4] = ((ulUser1 >>  8) & 0xff);
		pucMACArray[5] = ((ulUser1 >> 16) & 0xff);
	}

	/* Initialze the lwIP library, using DHCP. */
	lwIPInit(pucMACArray, 0, 0, 0, IPADDR_USE_DHCP);

	cocagne = ccg_new (epi_memory);

	UARTprintf ("Board ID             : 0x%04x\n", ccg_get_board_id (cocagne));
	UARTprintf ("FPGA Firmware version: 0x%02x\n", ccg_get_firmware_version (cocagne));
	UARTprintf ("MAC address          : %02x:%02x:%02x:%02x:%02x:%02x\n\n",
		    pucMACArray[0], pucMACArray[1], pucMACArray[2],
		    pucMACArray[3], pucMACArray[4], pucMACArray[5]);

	if (HWREG (FLASH_BOOTCFG) & FLASH_BOOTCFG_NW) {
		unsigned long boot_cfg;

		UARTprintf ("Commit boot loader configuration\n");

		/* Boot loader enabled with Port E / pin 4 - lsm3s9b92.pdf - page 328 */
		boot_cfg = 0x00000102;
		HWREG (FLASH_FMD) = boot_cfg;

		/* Commit */
		HWREG(FLASH_FMA) = 0x75100000;
		HWREG(FLASH_FMC) = FLASH_FMC_WRKEY | FLASH_FMC_COMT;

		/* Wait for completion */
		while(HWREG(FLASH_FMC) & FLASH_FMC_COMT);

		UARTprintf ("Boot loader configuration commited\n");
	} else {
		unsigned long boot_cfg;

		boot_cfg = HWREG (FLASH_BOOTCFG);
		UARTprintf ("Boot loader configuration: %08x\n\n", boot_cfg);
	}

	/* Indicate that DHCP has started */
	UARTprintf("Waiting for IP... ");

	/* Loop until a firmware update request.  All the work is done in interrupt handlers. */
	while(!firmware_update_request) {
	}

	/* Wait for 3 seconds. (SysCtlDelay takes 3 cycles) */
	MAP_SysCtlDelay(ROM_SysCtlClockGet());

	UARTprintf("Load firmware...");

	/* Disable all processor interrupts.  Instead of disabling them
	   one at a time (and possibly missing an interrupt if new sources
	   are added), a direct write to NVIC is done to disable all
	   peripheral interrupts. */
	HWREG(NVIC_DIS0) = 0xffffffff;
	HWREG(NVIC_DIS1) = 0xffffffff;

	/* Also disable the SysTick interrupt. */
	SysTickIntDisable();

	/* Return control to the boot loader.  This is a call to the SVC
	   handler in the boot loader, or to the ROM if available. */
#ifdef ROM_UpdateEthernet
	ROM_UpdateEthernet(ROM_SysCtlClockGet());
#else
	(*((void (*)(void))(*(unsigned long *)0x2c)))();
#endif
  • ccggvcp.h:
#ifndef CCG_GVCP_H
#define CCG_GVCP_H

#include <stdlib.h>
#include <arch/cc.h>
#include <lwip/inet.h>

#define CCG_GVCP_PORT	3956

#define CCG_GVBS_VERSION_OFFSET				0x00000000
#define CCG_GVBS_VERSION_MINOR_MASK			0x0000ffff
#define CCG_GVBS_VERSION_MINOR_POS			0
#define CCG_GVBS_VERSION_MAJOR_MASK			0xffff0000
#define CCG_GVBS_VERSION_MAJOR_POS			16

#define CCG_GVBS_DEVICE_MODE_OFFSET			0x00000004
#define CCG_GVBS_DEVICE_MODE_BIG_ENDIAN			1 << 31
#define CCG_GVBS_DEVICE_MODE_CHARACTER_SET_MASK		0x0000ffff
#define CCG_GVBS_DEVICE_MODE_CHARACTER_SET_POS		0

#define CCG_GVBS_DEVICE_MAC_ADDRESS_HIGH_OFFSET		0x00000008
#define CCG_GVBS_DEVICE_MAC_ADDRESS_LOW_OFFSET		0x0000000c

#define CCG_GVBS_SUPPORTED_IP_CONFIGURATION_OFFSET	0x00000010
#define CCG_GVBS_CURRENT_IP_CONFIGURATION_OFFSET	0x00000014
#define CCG_GVBS_IP_CONFIGURATION_PERSISTENT		1 << 0
#define CCG_GVBS_IP_CONFIGURATION_DHCP			1 << 1
#define CCG_GVBS_IP_CONFIGURATION_LLA			1 << 2

#define CCG_GVBS_CURRENT_IP_ADDRESS_OFFSET		0x00000024
#define CCG_GVBS_CURRENT_SUBNET_MASK_OFFSET		0x00000034
#define CCG_GVBS_CURRENT_GATEWAY_OFFSET			0x00000044

#define CCG_GVBS_MANUFACTURER_NAME_OFFSET		0x00000048
#define CCG_GVBS_MANUFACTURER_NAME_SIZE			32

#define CCG_GVBS_MODEL_NAME_OFFSET			0x00000068
#define CCG_GVBS_MODEL_NAME_SIZE			32

#define CCG_GVBS_DEVICE_VERSION_OFFSET			0x00000088
#define CCG_GVBS_DEVICE_VERSION_SIZE			32

#define CCG_GVBS_MANUFACTURER_INFORMATIONS_OFFSET	0x000000a8
#define CCG_GVBS_MANUFACTURER_INFORMATIONS_SIZE		48

#define CCG_GVBS_SERIAL_NUMBER_OFFSET			0x000000d8
#define CCG_GVBS_SERIAL_NUMBER_SIZE			16

#define CCG_GVBS_USER_DEFINED_NAME_OFFSET		0x000000e8
#define CCG_GVBS_USER_DEFINED_NAME_SIZE			16

#define CCG_GVBS_DISCOVERY_DATA_SIZE			0xf8

#define CCG_GVBS_XML_URL_0_OFFSET			0x00000200
#define CCG_GVBS_XML_URL_1_OFFSET			0x00000400
#define CCG_GVBS_XML_URL_SIZE				512

#define CCG_GVBS_N_NETWORK_INTERFACES_OFFSET		0x00000600

#define	CCG_GVBS_PERSISTENT_IP_ADDRESS_0_OFFSET		0x0000064c
#define	CCG_GVBS_PERSISTENT_SUBNET_MASK_0_OFFSET	0x0000065c
#define	CCG_GVBS_PERSISTENT_GATEWAY_0_OFFSET		0x0000066c

#define CCG_GVBS_N_MESSAGE_CHANNELS_OFFSET		0x00000900
#define CCG_GVBS_N_STREAM_CHANNELS_OFFSET		0x00000904

#define CCG_GVBS_GVCP_CAPABILITY_OFFSET			0x00000934
#define CCG_GVBS_GVCP_CAPABILITY_CONCATENATION		1 << 0
#define CCG_GVBS_GVCP_CAPABILITY_WRITE_MEMORY		1 << 1
#define CCG_GVBS_GVCP_CAPABILITY_PACKET_RESEND		1 << 2
#define CCG_GVBS_GVCP_CAPABILITY_EVENT			1 << 3
#define CCG_GVBS_GVCP_CAPABILITY_EVENT_DATA		1 << 4
#define CCG_GVBS_GVCP_CAPABILITY_SERIAL_NUMBER		1 << 30
#define CCG_GVBS_GVCP_CAPABILITY_NAME_REGISTER		1 << 31

#define CCG_GVBS_HEARTBEAT_TIMEOUT_OFFSET		0x00000938
#define CCG_GVBS_TIMESTAMP_TICK_FREQUENCY_HIGH_OFFSET	0x0000093c
#define CCG_GVBS_TIMESTAMP_TICK_FREQUENCY_LOW_OFFSET	0x00000940
#define CCG_GVBS_TIMESTAMP_CONTROL_OFFSET		0x00000944
#define CCG_GVBS_TIMESTAMP_LATCHED_VALUE_HIGH_OFFSET	0x00000948
#define CCG_GVBS_TIMESTAMP_LATCHED_VALUE_LOW_OFFSET	0x0000094c

#define CCG_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET	0x00000a00
#define CCG_GVBS_CONTROL_CHANNEL_PRIVILEGE_CONTROL	1 << 1
#define CCG_GVBS_CONTROL_CHANNEL_PRIVILEGE_EXCLUSIVE	1 << 0

#define CCG_GVBS_STREAM_CHANNEL_0_PORT_OFFSET		0x00000d00

#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_OFFSET		0x00000d04
#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_MASK		0x0000ffff
#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_POS		0
#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_BIG_ENDIAN		1 << 29
#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_DO_NOT_FRAGMENT	1 << 30
#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_FIRE_TEST		1 << 31

#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_DELAY_OFFSET		0x00000d08

#define CCG_GVBS_STREAM_CHANNEL_0_IP_ADDRESS_OFFSET		0x00000d18

#define CCG_GVBS_DEVICE_LINK_SPEED_0_OFFSET			0x0000b000

#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_MIN_OFFSET		0x0000c000
#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_MAX_OFFSET		0x0000c004
#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_DELAY_MIN_OFFSET		0x0000c008
#define CCG_GVBS_STREAM_CHANNEL_0_PACKET_DELAY_MAX_OFFSET		0x0000c00c
#define CCG_GVBS_STREAM_CHANNEL_0_FRAME_TRANSMISSION_DELAY_OFFSET	0x0000c010
#define CCG_GVBS_STREAM_CHANNEL_0_FRAME_TRANSMISSION_DELAY_MIN_OFFSET	0x0000c014
#define CCG_GVBS_STREAM_CHANNEL_0_FRAME_TRANSMISSION_DELAY_MAX_OFFSET	0x0000c018
#define CCG_GVBS_STREAM_CHANNEL_0_BANDWITDH_RESERVE_OFFSET		0x0000c01c
#define CCG_GVBS_STREAM_CHANNEL_0_BANDWITDH_RESERVE_MIN_OFFSET		0x0000c020
#define CCG_GVBS_STREAM_CHANNEL_0_BANDWITDH_RESERVE_MAX_OFFSET		0x0000c024
#define CCG_GVBS_STREAM_CHANNEL_0_BANDWITDH_RESERVE_ACCUMULATION_OFFSET	0x0000c028
#define CCG_GVBS_STREAM_CHANNEL_0_BANDWITDH_RESERVE_ACCUMULATION_MIN_OFFSET	0x0000c02c
#define CCG_GVBS_STREAM_CHANNEL_0_BANDWITDH_RESERVE_ACCUMULATION_MAX_OFFSET	0x0000c030
#define CCG_GVBS_STREAM_CHANNEL_0_THROUGHPUT_MAX_OFFSET			0x0000c100
#define CCG_GVBS_STREAM_CHANNEL_0_CURRENT_THOURGHPUT_OFFSET		0x0000c104
#define CCG_GVBS_STREAM_CHANNEL_0_ASSIGNED_BANDWIDTH_OFFSET		0x0000c108
#define CCG_GVBS_STREAM_CHANNEL_0_FRAME_JITTER_MAX_OFFSET		0x0000c10c

#define CCG_GVCP_DATA_SIZE_MAX				512

typedef enum {
	CCG_GVCP_PACKET_TYPE_ACK =		0x0000,
	CCG_GVCP_PACKET_TYPE_RESEND =		0x4200,
	CCG_GVCP_PACKET_TYPE_CMD = 		0x4201,
	CCG_GVCP_PACKET_TYPE_ERROR =		0x8006
} CcgGvcpPacketType;

typedef enum {
	CCG_GVCP_COMMAND_DISCOVERY_CMD =	0x0002,
	CCG_GVCP_COMMAND_DISCOVERY_ACK =	0x0003,
	CCG_GVCP_COMMAND_BYE_CMD = 		0x0004,
	CCG_GVCP_COMMAND_BYE_ACK = 		0x0005,
	CCG_GVCP_COMMAND_PACKET_RESEND_CMD =	0x0040,
	CCG_GVCP_COMMAND_PACKET_RESEND_ACK =	0x0041,
	CCG_GVCP_COMMAND_READ_REGISTER_CMD =	0x0080,
	CCG_GVCP_COMMAND_READ_REGISTER_ACK =	0x0081,
	CCG_GVCP_COMMAND_WRITE_REGISTER_CMD =	0x0082,
	CCG_GVCP_COMMAND_WRITE_REGISTER_ACK =	0x0083,
	CCG_GVCP_COMMAND_READ_MEMORY_CMD =	0x0084,
	CCG_GVCP_COMMAND_READ_MEMORY_ACK =	0x0085,
	CCG_GVCP_COMMAND_WRITE_MEMORY_CMD =	0x0086,
	CCG_GVCP_COMMAND_WRITE_MEMORY_ACK =	0x0087
} CcgGvcpCommand;

typedef struct {
	u16_t packet_type;
	u16_t command;
	u16_t size;
	u16_t count;
}  __attribute__((__packed__)) CcgGvcpHeader;

typedef struct {
	CcgGvcpHeader header;
	unsigned char data[];
} CcgGvcpPacket;

struct pbuf * 		ccg_gvcp_packet_new_read_memory_ack 	(u32_t address, u32_t size, u32_t packet_count);
struct pbuf * 		ccg_gvcp_packet_new_write_memory_ack	(u32_t address, u32_t packet_count);
struct pbuf * 		ccg_gvcp_packet_new_read_register_ack 	(u32_t value, u32_t packet_count);
struct pbuf * 		ccg_gvcp_packet_new_write_register_ack 	(u32_t address, u32_t packet_count);
struct pbuf * 		ccg_gvcp_packet_new_discovery_cmd 	(void);
struct pbuf * 		ccg_gvcp_packet_new_discovery_ack 	(void);

static inline void
ccg_gvcp_packet_set_packet_count (CcgGvcpPacket *packet, u16_t count)
{
	if (packet != NULL)
		packet->header.count = htons (count);
}

static inline u16_t
ccg_gvcp_packet_get_packet_count (CcgGvcpPacket *packet)
{
	if (packet == NULL)
		return 0;

	return ntohs (packet->header.count);
}

static inline void
ccg_gvcp_packet_get_read_memory_cmd_infos (const CcgGvcpPacket *packet, u32_t *address, u32_t *size)
{
	if (packet == NULL)
		return;
	if (address != NULL)
		*address = ntohl (*((u32_t *) ((char *) packet + sizeof (CcgGvcpPacket))));
	if (size != NULL)
		*size = (ntohl (*((u32_t *) ((char *) packet + sizeof (CcgGvcpPacket) + sizeof (u32_t))))) &
			0xffff;
}

static inline size_t
ccg_gvcp_packet_get_read_memory_ack_size (u32_t data_size)
{
	return sizeof (CcgGvcpHeader) + sizeof (u32_t) + data_size;
}

static inline void *
ccg_gvcp_packet_get_read_memory_ack_data (const CcgGvcpPacket *packet)
{
	return (char *) packet + sizeof (CcgGvcpHeader) + sizeof (u32_t);
}

static inline void
ccg_gvcp_packet_get_write_memory_cmd_infos (const CcgGvcpPacket *packet, u32_t *address, u32_t *size)
{
	if (packet == NULL)
		return;

	if (address != NULL)
		*address = ntohl (*((u32_t *) ((char *) packet + sizeof (CcgGvcpPacket))));
	if (size != NULL)
		*size = ntohs (packet->header.size) - sizeof (u32_t);
}

static inline void *
ccg_gvcp_packet_get_write_memory_cmd_data (const CcgGvcpPacket *packet)
{
	return (char *) packet + sizeof (CcgGvcpPacket) + sizeof (u32_t);
}

static inline size_t
ccg_gvcp_packet_get_write_memory_ack_size (void)
{
	return sizeof (CcgGvcpPacket) + sizeof (u32_t);
}

static inline void
ccg_gvcp_packet_get_read_register_cmd_infos (const CcgGvcpPacket *packet, u32_t *address)
{
	if (packet == NULL)
		return;
	if (address != NULL)
		*address = ntohl (*((u32_t *) ((char *) packet + sizeof (CcgGvcpPacket))));
}

static inline u32_t
ccg_gvcp_packet_get_read_register_ack_value (const CcgGvcpPacket *packet)
{
	if (packet == NULL)
		return 0;
	return ntohl (*((u32_t *) ((char *) packet + sizeof (CcgGvcpPacket))));
}

static inline void
ccg_gvcp_packet_get_write_register_cmd_infos (const CcgGvcpPacket *packet, u32_t *address, u32_t *value)
{
	if (packet == NULL)
		return;
	if (address != NULL)
		*address = ntohl (*((u32_t *) ((char *) packet + sizeof (CcgGvcpPacket))));
	if (value != NULL)
		*value = ntohl (*((u32_t *) ((char *) packet + sizeof (CcgGvcpPacket) + sizeof (u32_t))));
}

#endif
  • ccgvcp.c:
#include <ccggvcp.h>
#include <string.h>
#include <utils/lwiplib.h>

struct pbuf *
ccg_gvcp_packet_new_read_memory_ack (u32_t address, u32_t size, u32_t packet_count)
{
	CcgGvcpPacket *packet;
	struct pbuf *buffer;
	u32_t n_address = htonl (address);

	buffer = pbuf_alloc(PBUF_TRANSPORT, sizeof (CcgGvcpHeader) + sizeof (u32_t) + size, PBUF_RAM);
	packet = buffer->payload;

	packet->header.packet_type = htons (CCG_GVCP_PACKET_TYPE_ACK);
	packet->header.command = htons (CCG_GVCP_COMMAND_READ_MEMORY_ACK);
	packet->header.size = htons (sizeof (u32_t) + size);
	packet->header.count = htons (packet_count);

	memcpy (&packet->data, &n_address, sizeof (u32_t));

	return buffer;
}

struct pbuf *
ccg_gvcp_packet_new_write_memory_ack (u32_t address, u32_t packet_count)
{
	CcgGvcpPacket *packet;
	struct pbuf *buffer;
	u32_t n_address = htonl (address);

	buffer = pbuf_alloc(PBUF_TRANSPORT, sizeof (CcgGvcpHeader) + sizeof (u32_t), PBUF_RAM);
	packet = buffer->payload;

	packet->header.packet_type = htons (CCG_GVCP_PACKET_TYPE_ACK);
	packet->header.command = htons (CCG_GVCP_COMMAND_WRITE_MEMORY_ACK);
	packet->header.size = htons (sizeof (u32_t));
	packet->header.count = htons (packet_count);

	memcpy (&packet->data, &n_address, sizeof (u32_t));

	return buffer;
}

struct pbuf *
ccg_gvcp_packet_new_read_register_ack (u32_t value, u32_t packet_count)
{
	CcgGvcpPacket *packet;
	struct pbuf *buffer;
	u32_t n_value = htonl (value);

	buffer = pbuf_alloc(PBUF_TRANSPORT, sizeof (CcgGvcpHeader) + sizeof (u32_t), PBUF_RAM);
	packet = buffer->payload;

	packet->header.packet_type = htons (CCG_GVCP_PACKET_TYPE_ACK);
	packet->header.command = htons (CCG_GVCP_COMMAND_READ_REGISTER_ACK);
	packet->header.size = htons (sizeof (u32_t));
	packet->header.count = htons (packet_count);

	memcpy (&packet->data, &n_value, sizeof (u32_t));

	return buffer;
}

struct pbuf *
ccg_gvcp_packet_new_write_register_ack 	(u32_t address, u32_t packet_count)
{
	CcgGvcpPacket *packet;
	u32_t n_address = htonl (address);
	struct pbuf *buffer;

	buffer = pbuf_alloc(PBUF_TRANSPORT, sizeof (CcgGvcpHeader) + sizeof (u32_t), PBUF_RAM);
	packet = buffer->payload;

	packet->header.packet_type = htons (CCG_GVCP_PACKET_TYPE_ACK);
	packet->header.command = htons (CCG_GVCP_COMMAND_WRITE_REGISTER_ACK);
	packet->header.size = htons (sizeof (u32_t));
	packet->header.count = htons (packet_count);

	memcpy (&packet->data, &n_address, sizeof (u32_t));

	return buffer;
}

struct pbuf *
ccg_gvcp_packet_new_discovery_cmd (void)
{
	CcgGvcpPacket *packet;
	struct pbuf *buffer;

	buffer = pbuf_alloc(PBUF_TRANSPORT, sizeof (CcgGvcpHeader), PBUF_RAM);
	packet = buffer->payload;

	packet->header.packet_type = htons (CCG_GVCP_PACKET_TYPE_CMD);
	packet->header.command = htons (CCG_GVCP_COMMAND_DISCOVERY_CMD);
	packet->header.size = htons (0x0000);
	packet->header.count = htons (0xffff);

	return buffer;
}

struct pbuf *
ccg_gvcp_packet_new_discovery_ack (void)
{
	CcgGvcpPacket *packet;
	struct pbuf *buffer;

	buffer = pbuf_alloc(PBUF_TRANSPORT, sizeof (CcgGvcpHeader) + CCG_GVBS_DISCOVERY_DATA_SIZE, PBUF_RAM);
	packet = buffer->payload;

	packet->header.packet_type = htons (CCG_GVCP_PACKET_TYPE_ACK);
	packet->header.command = htons (CCG_GVCP_COMMAND_DISCOVERY_ACK);
	packet->header.size = htons (CCG_GVBS_DISCOVERY_DATA_SIZE);
	packet->header.count = htons (0xffff);

	return buffer;
}

The USB packets have a different format than for GigEVision. In aravis, you will find the necessary informations in arvuvcp.c, arvuvcpprvate.h, arvuvsp.c and arvuvspprivate.h.

Cheers,

Emmanuel.

1 Like