Introduction

Homework 4 is an introduction to design using the Nios II processor in Quartus. This homework makes you familiar with Platform Designer. Make sure that you understand all the steps in the homework.

Platform Designer is an extensive product. The Intel Quartus Prime User Guide is the standard reference on the tool.

There is a gentle introduction to Platform Designer available from Intel’s University Program. While the homework does not follow every step of the introductory guide, you still will find it useful to read it before starting the homework.

The first part of the homework is a step-by-step instruction guide that configures and builds a Nios II system on your FPGA board. Following that, you have to answer several questions on this system, and finally, you have to modify the design to add a new IP module to the Platform Designer system.

Getting Started

Start by accepting the homework 4 assignment from Github Classroom. Then, clone the resulting repository to your computer.

git clone https://github.com/vt-ece4530-f19/homework-4-USERNAME

This will clone the design that we discussed in class (example-nios-timer). The first step of the design is the implement the design for your DE1-SoC kit. Along the way, the homework will make several observations, which you have to use later on to answer questions. Don’t skip these observations! They are indicated as follows.

OBSERVE: Make a note of the memory map associated with the Nios II master interface

Synthesize the Hardware

Run the Quartus GUI, and open the exampleniostimer design into Quartus. Open the exampleniostimer module in the editor. The design toplevel instantiates a single module: the platform that we will create in platform designer.

module exampleniostimer(
  input                   CLOCK2_50,
  input                   CLOCK3_50,
  input                   CLOCK4_50,
  input                   CLOCK_50,
  input          [3:0]    KEY
);

  wire clk;
  wire rst;

  platformniostimer u0 ( .clk_clk(clk),
                          .reset_reset_n(rst)
                          );

  assign clk = CLOCK_50;
  assign rst = KEY[0];
                   
endmodule

Run the platform designer using Tools..Platform Designer. In platform designer, open platformniostimer.qsys. A window with a Nios II system opens. It includes the following components: a Nios II, a 32KByte on-chip memory, a JTAG UART, and a timer.

Figure: Nios II System nios2system

We can double click a component in the interconnect to go to the documentation page of that component. Try that for the Timer module. Find and open the User Guide of the Timer.

OBSERVE: Make a note of the register map of the 32-bit timer.

In the platform designer, open the Address Map tab and study the address map of the system.

OBSERVE: Make a note of the address ranges assigned to the on-chip memory and the timer.

To convert this system to synthesizable Verilog, click on ‘Generate HDL’ (followed by ‘Generate’). When the Verilog generation is complete, exit the Platform Designer environment.

We will run the following commands on the command line, so you can also exit the Quartus GUI.

Synthesize the bitstream of the design. In a Nios-II command shell, go to the directory where your program is installed and run quartus synthesis.

quartus_sh --flow compile exampleniostimer

The Quartus flow generates several report files which have an .rpt extension. There are five such report files.

File Purpose
exampleniostimer.flow.rpt Output of overall flow
exampleniostimer.map.rpt Output of Mapper (verilog synthesis)
exampleniostimer.fit.rpt Output of Fitter (netlist to FPGA fabric)
exampleniostimer.asm.rpt Output of Assembler (bitstream generator)
exampleniostimer.sta.rpt Output of Static Timing Analysis


OBSERVE: Find the number of RAM modules used, the number of ‘block memory bits’ used, the number of registers used, and the number of ALM’s used.

After you have obtained the bitstream, connect your board, check that it is properly connected, and download the bitstream. You can do that in a Nios II command shell, as well.

# Use this command to check the board is connected
jtagconfig

# Use this command to download the bitstream
nios2-configure-sof -d 2 exampleniostimer.sof

Compile the Software

To compile software for a Nios II system, we have to generate a board support package and compile that into a board support package library. Next, we have to write a C application and compile and link it with the board support package library into an executable. Finally, we have to download and run the executable on the Nios II configured in the FPGAS.

Open the BSP editor, a graphical environment to configure board support packages. In a Nios II command shell, run the following.

nios2-bsp-editor

In the BSP editor, create a new BSP by opening platformniostimer.sopcinfo. The BSP editor will be populated with default entries and looks as follows.

Figure: Board Support Package Editor bspeditor

Change the default options as follows.

  • In the main tab, change the sys_clk_timer pulldown to ‘none’
  • In the main tab, change the timestamp_timer pulldown to ‘timer_0’
  • In the main tab, enable the ‘enable_small_c_library’ option
  • In the drivers tab, enable to ‘enable_small_driver’ option

In the linker tab, you can find two elements that will sound familiar to the linker description file we studied for the MSP-430. The linker tab lists (a) the linker sections that are understood by the linker and (b) the memory regions that are available to the linker.

OBSERVE: Make a list of linker section names, and make a note of the memory address range occupied by the onchip_memory2_0.

Generate the board support package configuration by clicking the ‘Generate’ button. Next, exit the BSP editor.

Next, you have to generate the actual source code of the BSP. This is done with the following commands.

nios2-bsp-generate-files --settings=hal_bsp/settings.bsp \
                         --bsp-dir=hal_bsp
cd hal_bsp
make
cd ..

The BSP is created in software/hal_bsp. The most important file in the hal_bsp is system.h, a file that has system settings such as what peripherals are available, what their capabilities are, what their base addresses are, and so on.

OBSERVE: Find the base addresses of main components in the system. The base addresses are listed in the system.h file as macros with the extension _BASE.

Next, after the BSP configuration is created, we can generate actual BSP C code. In a Nios II command shell, navigate to the software subdirectory and run the following command.

nios2-app-generate-makefile     \
             --bsp-dir=hal_bsp  \
             --src-files=main.c \
             --elf-name=main.elf

After that, you can compile the software with a simple make. Note that the make command will also build the BSP library, in case it was not compiled yet.

make

The software application, in this case, is a simple module computation application. Notice that we used a function provided in the BSP library by including sys/alt_timestamp.h at the top of the file. This gives access to alt_timestamp_start(), alt_timestamp_freq() and alt_timestamp().

OBSERVE: The programming functions for the timer are in a directory called hal_bsp/drivers/inc.

#include <system.h>
#include <stdio.h>
#include "sys/alt_timestamp.h"

unsigned modk(unsigned x, unsigned k) {
  return (x & ((1 << k) - 1));
}

unsigned divk(unsigned x, unsigned k) {
  return (x >> k);
}

unsigned modulo(unsigned x) {
  unsigned r, q, k, a, m, z;
  m = 0xB3; // 179
  k = 8;
  a = (1 << k) - m;
  r = modk(x, k);
  q = divk(x, k);
  do {
    do {
      r = r + modk(q * a, k);
      q = divk(q * a, k);
    } while (q != 0);
    q = divk(r, k);
    r = modk(r, k);
  } while (q != 0);
  if (r >= m)
    z = r - m;
  else
    z = r;
  return z;
}

int main() {
  volatile unsigned a;
  unsigned i;
  unsigned ticks[10];

  alt_timestamp_start();

  printf("Timer Frequency %u\n", (alt_u32) alt_timestamp_freq());

  printf("Empty call ticks: ");
  for (i=0; i<9; i++) {
    ticks[i] = alt_timestamp();
    ticks[i] = alt_timestamp() - ticks[i];
    printf("%5d ", ticks[i]);
  }
  printf("\n");

  printf("Modulo call ticks: ");
  for (i=0; i<9; i++) {
    ticks[i] = alt_timestamp();
    a = modulo(0x00c0ffee);
    ticks[i] = alt_timestamp() - ticks[i];
    printf("%5d ", ticks[i]);
  }
  printf("\n");

  return 0;
}

We can now download the software application to the board and run it as follows.

In a first Nios II command shell, run the following command.

nios2-terminal

In a second Nios II command shell, run the following command.

nios2-download --go main.elf

If all has been configured correctly, you will see the following output in the Nios II terminal.

Timer Frequency 50000000
Empty call ticks:   337   337   337   337   337   337   337   337   337
Modulo call ticks: 14350 14350 14350 14350 14350 14350 14350 14350 14350

You are now ready to answer the questions in the homework. The questions require some experimentation and analysis.

Question 1: Hardware Analysis (6 points)

In an PDF Homework_4_USERNAME.pdf, answer the following questions.

  1. Draw a memory map of the Nios II system that you have just implemented. Find and indicate the memory ranges for the following components: the JTAG UART, the Nios II Debug unit, the on-chip memory, and the timer. Provide the address range for each component.

  2. Draw a table with the memory-mapped registers available in the timer module. For each register, list the address occupied in the Nios II data master memory space, and the purpose of the register in supporting the timer functionality.

  3. Draw a table and fill out the following elements: The number of RAM modules used, the number of block memory bits used, the number of registers used, and the number of ALM’s used.

Question 2: Software Analysis (6 points)

Appending to Homework_4_USERNAME.pdf, answer the following questions.

  1. List the linker section names and their mapping into the on-chip memory.

  2. Write a small C program (or modify the C program you have), and find the answer to the following questions.

    a. What is the value of the stack pointer at the start of the main function?

    b. Does the stack grow upward or downward? Explain your answer by discussing the C program you used to determine the direction of the stack.

    c. At what address does the main function start?

    d. Where are the global variables stored? Give a range of addresses

  3. How many bytes does the program use for the text segment, for the data segment, for the bss segment? You can make use of the nios2-elf-size utility.

  4. Make a list of all BSP functions used by the software. You can make use of the nios2-elf-nm utility. List only functions that start with alt_ or with altera_avalon. If you’re in doubt, browse the BSP source code to verify if a given symbol listed by nios2-elf-nm is a function or not. For example, alt_find_dev is a function; it is defined in HAL/src/alt_find_dev.c.

Question 3: Tweaking the platform (8 points)

The last question is a little more open-ended and requires you to make a minor change to the hardware platform and the software application. The objective is to make a ‘running light’ using eight of the red LEDs on the DE1-SoC board and a PIO port driven by the Nios II. This involves the following changes.

  1. Add a PIO port to the platform you have been working on.

  2. Modify the top-level system interconnect such that the platform from Platform Designer connects to the FPGA pins that drive the red LEDs.

  3. Change the project system constraint file to enable the FPGA pins that drive ther LEDs.

  4. Recompile the hardware to produce a new bitstream and to reconfigure the FPGA.

  5. Rebuild the board support package to enable support for the PIO port.

  6. Change the software driver to implement the running light application.

We elaborate on each step a little further.

1. Adding a PIO port: Reopen the design in platform designer. In the IP catalog, look for ‘PIO (Parallel I/O) Intel FPGA IP’. Add an 8-bit PIO port, which by default, works as an output. Make the following system interconnections: connect the clock and reset to the system-wide clock and reset, and connect the slave input port of the PIO to the data_master output port of the NIOS. Then, add an output conduit to that the PIO can drive LED pins. Call the conduit ‘led’. If all is well, the result will look as follows.

Figure: Platform with PIO platformpio

  • Modify the system interconnect: The addition of a conduit creates a new port in the interconnect of the platform module. In the platform editor, select Show .. Instantiation Template. You should see an extra led port appear:
   platformniostimer u0 (
        .clk_clk       (<connected-to-clk_clk>),       //   clk.clk
        .reset_reset_n (<connected-to-reset_reset_n>), // reset.reset_n
        .led_export    (<connected-to-led_export>)     //   led.export
    );

Now, go back to Quartus and edit the exampleniostimer module with the new port (led_export). Connect the led_export port to a new top-level IO called LEDR. Connect the 8 least significant bits of LEDR to led_export.

module exampleniostimer(
  input                   CLOCK2_50,
  input                   CLOCK3_50,
  input                   CLOCK4_50,
  input                   CLOCK_50,
  input          [3:0]    KEY,
  output         [9:0]    LEDR
);
  • Change the system constraint file. Open exampleniostimer.qsf in an editor and add the connectivity information for the red leds (LEDR) by adding the following lines.
#============================================================
# LED
#============================================================
set_location_assignment PIN_V16 -to LEDR[0]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[0]
set_location_assignment PIN_W16 -to LEDR[1]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[1]
set_location_assignment PIN_V17 -to LEDR[2]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[2]
set_location_assignment PIN_V18 -to LEDR[3]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[3]
set_location_assignment PIN_W17 -to LEDR[4]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[4]
set_location_assignment PIN_W19 -to LEDR[5]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[5]
set_location_assignment PIN_Y19 -to LEDR[6]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[6]
set_location_assignment PIN_W20 -to LEDR[7]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[7]
set_location_assignment PIN_W21 -to LEDR[8]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[8]
set_location_assignment PIN_Y21 -to LEDR[9]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[9]
  • Recompile the hardware. This step is identical to what you did previously.

  • Rebuild the board support package. This step is identical to what you did previously. Afterward, verify that your PIO port is integrated into the system by checking the system.h file.

/*
 * pio_0 configuration
 *
 */

#define ALT_MODULE_CLASS_pio_0 altera_avalon_pio
#define PIO_0_BASE 0x0
#define PIO_0_BIT_CLEARING_EDGE_REGISTER 0
#define PIO_0_BIT_MODIFYING_OUTPUT_REGISTER 0
#define PIO_0_CAPTURE 0
#define PIO_0_DATA_WIDTH 8
#define PIO_0_DO_TEST_BENCH_WIRING 0
#define PIO_0_DRIVEN_SIM_VALUE 0
#define PIO_0_EDGE_TYPE "NONE"
#define PIO_0_FREQ 50000000
...
  • Change the software driver. The final step is the change the program, main.c, so that it generates a ‘running light’ sequence on the PIO port. You can consult the include file altera_avalon_pio_regs.h in the board support package for the registers available in the PIO, as well as for low-level access functions. You should be able to use an expression such as the following to turn on all LEDs; you have to include the altera_avalon_pio_regs.h file in main.c to get access to this macro. Finally, recompile the program and download it to the board. Verify that the running light spins at approximately once per second, so that each of the 8 LEDs turns on for about 125 ms.
    unsigned int data = 0xff;
    IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, data);

What to turn in

  • For question 1 and 2, create a PDF an add it to the root of your github repository as homework-4-USERNAME.pdf.

  • For question 3, clean your project using the following commands, and push the result back onto github as follows.

# clean your implementation

cd software/hal_bsp
make clean
cd ..
make clean
cd ..
quartus_sh --clean exampleniostimer

# push the result on github

git add *
git commit -m 'my homework 4 solution'
git push
  • Verify that your PDF is included in the repository as well.

Good Luck!