CMPE691 : Reconfigurable System Design

Discussion 8

Dr Ryan Robucci and Saad Rahman

October 27,2020

Part I Setup and Hello World

Tools

Create helloworld.c

Native vs Cross Compilation

We will use cross-compilation to compile the ARM code from our PC.

Create a file on the host computer:
helloworld.c

 #include <stdio.h>
 int main(void){
 printf("Hello World!\n");
 return 0;
 }

Compile C code for HPS

Start Embedded Design Suite Command Shell and Customize environment (my_env.sh):

Windows

Run Embedded_Command_Shell.bat. The default location of Embedded_Command_Shell.bat is C:\intelFPGA_pro\18.1\embedded if you’re using version 18.1 in Windows

Your environment including the following tools:

To customize the environment for your installation, create my_intel_fpga_env.sh on Windows Cygwin (note the use of c: since the arm compiler doesn’t seem to like /cygdrive/c):

export LM_LICENSE_FILE=1701@eclipse.umbc.edu
export QUARTUS_ROOTDIR_OVERRIDE=/cygdrive/c/intelFPGA_lite/18.1/quartus
export SOCEDS_DEST_ROOT=c:/intelFPGA_pro/18.1/embedded
source $SOCEDS_DEST_ROOT/env.sh
> source my_intel_fpga_env.sh 

Linux

my_intel_fpga_env.sh on Linux:

export LM_LICENSE_FILE=1701@eclipse.umbc.edu
export QUARTUS_ROOTDIR=/opt/intelFPGA_lite/18.1/quartus
export SOCEDS_DEST_ROOT=/opt/intelFPGA_pro/18.1/embedded
source $SOCEDS_DEST_ROOT/env.sh
> source my_intel_fpga_env.sh 

Your environment including the following tools:

Compilation

  1. Navigate to the folder in which your code is present (cd).
  2. Compile the helloworld program using the following command: arm-linux-gnueabihf-gcc helloworld.c -o helloworld.

This creates the output file helloworld, which is the helloworld ARM binary executable that we can copy to our Linux microSD card and execute on the DE1-SoC.

Copying the Executable to the SD Card

Create Root Password

This step may be optional. Depending on the boot image you used, it may already have a default root password terasic.

  1. To send files in the Linux OS running in HPS through a remote terminal, we need to set a password for the root user on the embedded OS. In the embedded OS terminal (e.g. through Putty/minicom) type passwd and set a desired password.

Network Connection

Use Method 1 or Method 2

Method 1: Network Option Peer-to-peer Network (Windows)

  1. Connect and ethernet cable direction from the DE1-SOC board to the host PC

  2. Obtain the IP address of the host pc: Open cmd in your host PC. Run ipconfig (ifconfig in linux) and find your 169.254… address lets call it 169.254.ABC.XYZ, you’ll need ABC and XYZ. (This is the IP address of the host)

  3. Open Putty and log in the Linux OS of the HPS. Then type vi /etc/network/interfaces.

    Find auto eth0 in that file and add/change the following lines to match the following listing. Here we randomly picked 169.254.167.163 to be the embedded system IP address (should not be the same as the host computer) ABC.XYZ are from earlier(IP of host PC)

    # Wired or wireless interfaces
    auto eth0
    #iface eth0 inet dhcp
    iface eth0 inet static
        address 169.254.167.163
        netmask 255.255.0.0
        gateway 169.254.ABC.XYZ
    iface eth1 inet dhcp
    
    

    How to edit using vi? https://www.guru99.com/the-vi-editor.html
    type i to enter insert mode to edit
    when finished editing hit esc-key to return to command mode then type :wq to write and quit

  4. . The setting in this file is persistent and will take effect on next boot, but to load the setting immediately you can restart network services in the embedded OS, e.g.

    /etc/init.d/networking restart
    

    or

    ifdown eth0
    ifup eth0
    ip a show eth0
    

Method 2: Network Option Router

  1. Connect and ethernet cable direction from the DE1-SOC board to the host router
  2. Run micro dhcp client: udhcpc
    root@socfpga:~# udhcpc
    udhcpc (v1.20.2) started
    Sending discover...
    Sending select for 192.168.1.7...
    Lease of 192.168.1.7 obtained, lease time 86400
    /etc/udhcpc.d/50default: Adding DNS 192.168.1.1
    
  3. The example showed obtaining an address of 192.168.1.7

Debugging notes

A quick method to set a temporary IP address is sudo ifconfig eth0 xxx.xxx.xxx.xxx

Test Connection

From the host command prompt ping the IP address and verify the socfpga is reachable

$ping 169.254.167.163

Network File Transfer

Method 1: SCP

SCP should be installed in your windows unix-like enviroment already

  1. Open Embedded_Command_Shell.bat.
  2. Navigate to the folder in which your code is present.
  3. type in this command. scp <filename> root@<IP address of HPS>:/home/root/. When prompted provide the password for root user (Default password is terasic, but we set it to something else earlier). scp will securely and directly copy the file in the /home/root directory of the HPS.

Method 2: FileZilla

Open FileZilla. Type the IP address you set in Host , Username should be root type in the password you set and Port should be 22. Then hit Quickconnect

  1. Then transfer the code and object file into any directory in the Linux OS of HPS.

Executing the code

  1. Add execute permissions: in the socfpga OS terminal, navigate (cd) to the directory in which the code and object file are located. Type chmod 777 <filename> to change the execution (x) permissions. ls -l <filename> will show that execute permissions have been added

  2. Execute: type ./<filename> to execute the command

  3. For more details, download Altera tutorial from here.

Part II

Objective

Tools :
By this point you should already have all the required tools installed

  1. Quartus Prime.
  2. Platform designer.
  3. Intel SoC FPGA Embedded Development Suite.

Notes :

Create a Makefile:

Makefile (!!!REPLACE <TAB> with actual tabs!!!):
Makefiles require actual Tab characters 0x08 which are difficult to embedded. I placed <TAB> where you should use the TAB key

Makefile: (no extension)

#filename: Makefile
#Ryan Robucci
#Edit the first two lines as needed
#assumes source is main.c
TARGET = custom_component_test
IP = 169.254.167.163
#
ALT_DEVICE_FAMILY ?= soc_cv_av
CROSS_COMPILE = arm-linux-gnueabihf-
#CFLAGS = -std=gnu99 -static -g -pg -Wall  -I${SOCEDS_DEST_ROOT}/ip/altera/hps/altera_hps/hwlib/include -I${SOCEDS_DEST_ROOT}/ip/altera/hps/altera_hps/hwlib/include/$(ALT_DEVICE_FAMILY) -D$(ALT_DEVICE_FAMILY)
CFLAGS = -std=gnu99 -static -g -pg -Wall  -I${SOCEDS_DEST_ROOT}/ip/altera/hps/altera_hps/hwlib/include -Iip/altera/hps/altera_hps/hwlib/include/$(ALT_DEVICE_FAMILY) -D$(ALT_DEVICE_FAMILY)
LDFLAGS =  -g -pg -Wall
CC = $(CROSS_COMPILE)gcc
ARCH= arm
DEPS = *.h


build: $(TARGET)
$(TARGET): main.o
<TAB><TAB>$(CC) $(LDFLAGS)   $^ -o $@  
%.o : %.c $(DEPS) 
<TAB><TAB>$(CC) $(CFLAGS) -c $< -o $@

.PHONY: clean
clean:
<TAB><TAB>rm -f $(TARGET) *.a *.o *~ 

scp: build
<TAB><TAB>scp $(TARGET) root@$(IP):

Create a C code file main.c:

This writes 0 to 9 in the custom component’s base address using a for loop.
main.c:

//filename: main.c
//Ryan Robucci
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "hwlib.h"
#include "socal/socal.h"
#include "socal/hps.h"
#include "socal/alt_gpio.h"
#include "hps_0.h"
//socks
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>

#include <time.h>

#define HW_REGS_BASE ( ALT_STM_OFST )
#define HW_REGS_SPAN ( 0x04000000 )
#define HW_REGS_MASK ( HW_REGS_SPAN - 1 )

// convert 8-digit hex string to uint32_t
uint32_t h2i(char c) {
uint32_t i = 0;
switch(c) {
case '0': i=0; break;
case '1': i=1; break;
case '2': i=2; break;
case '3': i=3; break;
case '4': i=4; break;
case '5': i=5; break;
case '6': i=6; break;
case '7': i=7; break;
case '8': i=8; break;
case '9': i=9; break;
case 'a':
case 'A': i=10; break;
case 'b':
case 'B': i=11; break;
case 'c':
case 'C': i=12; break;
case 'd':
case 'D': i=13; break;
case 'e':
case 'E': i=14; break;
case 'f':
case 'F': i=15; break;
}
return i;
}

//convert hex assii string to integer
uint32_t sh2i(char *s) {
    //printf("Calling sh2i\n");
uint32_t integer = 0;
uint32_t index=0;
while (s[index] != '\0') {
  integer = (integer<<4) + h2i(s[index]);
index++;
//printf(":%d:", index);
}
    //printf("\nExiting sh2i\n");
return integer;
}
  
//convert decimal ascii string to integer
uint32_t sd2i(char *s) {
    //printf("Calling sh2i\n");
uint32_t integer = 0;
uint32_t index=0;
while (s[index] != '\0') {
  integer = (integer*10) + s[index]-'0';
index++;
//printf(":%d:", index);
}
    //printf("\nExiting sh2i\n");
return integer;
}
  
//convert binary ascii string to integer
uint32_t sb2i(char *s) {
    //printf("Calling sh2i\n");
uint32_t integer = 0;
uint32_t index=0;
while (s[index] != '\0') {
  integer = (integer*2) + s[index]-'0';
index++;
//printf(":%d:", index);
}
    //printf("\nExiting sh2i\n");
return integer;
}

//convert ascii string to integer
//if string has prefix 0x or 0b then the rest of the string is assumed to be hex or binary respectively
//otherwise, a decimal string is assumed
// i = s2i("0xhf")
// i = s2i("0b010110")
// i = s2i("0b921")
uint32_t s2i(char *s) {
uint32_t integer;
if (s[0]=='0' && s[1] == 'x'){
integer = sh2i(s);
} else if (s[0]=='0' && s[1] == 'b'){
integer = sb2i(s);
}else{
integer = sd2i(s);
}
return integer;
}

#define IO_PTR(_BASE) (virtual_base+((unsigned long)(ALT_LWFPGASLVS_OFST+(_BASE))&(unsigned long)(HW_REGS_MASK)))
#define GET_BIT(x,b) (((x)>>(b))&1)

int main(int argc, char *argv[])
{

  void *virtual_base;
  int fd;
   
  ///////////////////////////////////
  // map the address space for the LED registers into user space so we can interact   with them.
  // we'll actually map in the entire CSR span of the HPS since we want to access   various registers within that span
 

  if( ( fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) {
    printf( "ERROR: could not open \"/dev/mem\"...\n" );
    return( 1 );
  }
 
  virtual_base = mmap( NULL, HW_REGS_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED,   fd, HW_REGS_BASE );
 
  if( virtual_base == MAP_FAILED ) {
    printf( "ERROR: mmap() failed...\n" );
    close( fd );
    return( 1 );
  }

  //void * ptr_dac_db_14b  =  virtual_base + ( ( unsigned long  )(   ALT_LWFPGASLVS_OFST + PIO_DAC_DB_14B_BASE ) & ( unsigned long)( HW_REGS_MASK ) );
  // Writting 0-9 in the custom component's base address
  for(int i = 0; i<10;i++){
  *(uint32_t *)IO_PTR(REG32_AVALON_INTERFACE_0_BASE) = i;
  printf("i:%8d, CUSTOM: 0x%8x PIO_FEEDBACK: 0x%8x\n", i,
     *(uint32_t *)IO_PTR(REG32_AVALON_INTERFACE_0_BASE),
     *(uint32_t *)IO_PTR(PIO_0_BASE));
  }    
   
  /* End Main Loop */  
 
  // clean up our memory mapping and exit
  if( munmap( virtual_base, HW_REGS_SPAN ) != 0 ) {
    printf( "ERROR: munmap() failed...\n" );
    close( fd );
    return( 1 );
  }  
  close( fd );  
  return 0;
}
  1. (Windows Only) Extract the altera_ip.tar.gz file from the downloaded folder of step 2, and copy theip folder over in this directory C:\intelFPGA_pro\18.1\embedded . This is the default location for Intel SoC FPGA Embedded Development Suite and it may change according to the version you installed.

  2. Then go to the directory of the downloaded folder in step 2 and copy the *.sopcinfo (Default location : Your Quartus project directory) file from the steps in Demo_7.pdf. We will need the *.sopcinfo file to generate header for our C code.

  3. Start Embedded Command Shell

  1. Generate Custom Header

    • UMBC Linux: /umbc/software/intelFPGA/20.1/embedded/embedded_command_shell.sh sopc-create-header-files "../FPGA/soc_system.sopcinfo" --single ../FPGA/C/hps_0.h --module hps_0

      • it is suggested to make an executable script called generate_hps_qsys_header.sh based on the previous command:
      #!/bin/sh
      /umbc/software/intelFPGA/20.1/embedded/embedded_command_shell.sh sopc-create-header-files "../FPGA/soc_system.sopcinfo" --single ../FPGA/C/hps_0.h --module hps_0
      
    • If you face errors while running “generate_hps_qsys_header.sh”, be sure to use the updated one from ‘/umbc/software/scripts’ and add ‘hwlib.h’, ‘socal/socal.h’ ,‘hps_0.h’ files in the working directory from ‘altera_ip’ directory corresponding to

      #include "hwlib.h"
      #include "socal/socal.h"
      #include "socal/hps.h"
      

    The files themselves are also provided here:
    /umbc/software/intelFPGA/20.1/embedded/ip/altera/hps/altera_hps/hwlib/include/hwlib.h
    /umbc/software/intelFPGA/20.1/embedded/ip/altera/hps/altera_hps/hwlib/include/soc_cv_av/socal/socal.h
    /umbc/software/intelFPGA/20.1/embedded/ip/altera/hps/altera_hps/hwlib/include/soc_cv_av/socal/hps.h

    • Windows: Type in chmod 777 generate_hps_qsys_header.sh to change the the permissions and then run ./generate_hps_qsys_header.sh to execute the script. It will create a header file named hps_0.h

Figure 1
  1. Create Makefile
    Makefile (check that appropriate lines are indented using TAB)
#File:Makefile
#Ryan Robucci
#Edit the first two lines as needed
#assumes source is main.c
TARGET = custom_component_test
IP = 130.85.93.99
#
ALT_DEVICE_FAMILY ?= soc_cv_av
CROSS_COMPILE = arm-linux-gnueabihf-
CFLAGS = -std=gnu99 -static -g -pg -Wall  -I${SOCEDS_DEST_ROOT}/ip/altera/hps/altera_hps/hwlib/include -I${SOCEDS_DEST_ROOT}/ip/altera/hps/altera_hps/hwlib/include/$(ALT_DEVICE_FAMILY) -D$(ALT_DEVICE_FAMILY)
LDFLAGS =  -g -pg -Wall
CC = $(CROSS_COMPILE)gcc
ARCH= arm
DEPS = *.h


build: $(TARGET)
$(TARGET): main.o
	$(CC) $(LDFLAGS)   $^ -o $@  
%.o : %.c $(DEPS) 
	$(CC) $(CFLAGS) -c $< -o $@

.PHONY: clean
clean:
	rm -f $(TARGET) *.a *.o *~ 

scp: build
	scp $(TARGET) root@$(IP):
  1. Then, run make to compile the code (main.c). It will create an ARM executable named custom_component_test.

Figure 2

Use custom component via HPS

  1. Transfer the executable in the HPS SD Card directory using any of the method described previously
  2. Connect with HPS using Putty or MobaXterm. Then change the permission typing chmod 777 custom_component_test and type ./custom_component_test to execute the program. It will generate an output like in the follow figure. You’ll see that the custom component and PIO is sending back the data provided from HPS code (0 to 9) incrementing once (1 to a).
    (Sorry this picture is a little old, your output will be a little different)
Figure 3

References

  1. Descriptions of mmap command.https://en.wikipedia.org/wiki/Mmap
  2. Descriptions of U-Boot command. http://www.denx.de/wiki/publish/DULG/to-delete/UBootCmdGroupMemory.html
  3. Discussions on multi-port/multi-register slaves. https://www.altera.com/support/support-resources/design-examples/intellectual-property/embedded/nios-ii/exm-avalon-memory-slave.html