Custom Instruction On Fpga For Viterbi Algorithm

  • Uploaded by: Project Symphony Collection
  • 0
  • 0
  • April 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Custom Instruction On Fpga For Viterbi Algorithm as PDF for free.

More details

  • Words: 2,479
  • Pages: 6
Embedded Systems

Lab Experience Guide Speeding up the execution of the Viterbi decoding algorithm by adding custom instructions to a Nios­based system

Matteo Bosio 148451 Patrick Chiapello 152123

Note: the experience you're about to start was prepared for a 3­hour lab; the durations you find   at the beginning of each paragraph are the reasonable time that every step should take to finish the   experience in three hours. However, if you're able to quickly complete the first fully­guided paragraphs,   you will have more   time for the part in which you will actually have to implement  your  own  work  (paragraphs from Profiling the code on). Introduction (5 mins) The aim of this experience is to gain familiarity with the possibility of adding extra instructions to an  FPGA­based microprocessor, in order to increase the performance of the CPU on a specific application  (in this case the Viterbi decoding algorithm). The Viterbi decoding algorithm is a quite complex procedure, which may be hard to understand for  people that is not involved in signal processing; however it is NOT needed to understand in depth the  steps of the algorithm! What you just have to do, is to identify the most time consuming part of the  procedure, and then create some instructions that will speed up its execution. If you're interested in the details of the Viterbi algorithm, you can refer to the web page: http://home.netcom.com/%7Echip.f/viterbi/tutorial.html The code used as a basis for the experience is the one you can find in the above link, however it's been  necessary to modify it, since it was too memory­consuming to fit in our system. We had to neglect the convolutional encoding part and the Gaussian white noise addition (Nios  base­processor doesn't have a floating point unit!), and so we will just feed the decoding function by  means of a sequence of ­1 and +1. The code you will work on is: files\viterbi.c

Setting up the system (40 mins) In   order   to   execute  the  code,   we  need  to  create  a   system  on  the   FPGA   board  with  the  following  components:    

Nios CPU: is the cpu that actually executes your program On­chip memory: the memory on which our program and our variables are stored JTAG Uart: allows you to communicate with the PC (vital for debugging!) Interval timer: useful for code profiling.

In order to begin the design we need to open the development environment Quartus II. From the file  menu select   ‘‘New Project Wizard’’ and click Next. From Explorer, create a working directory (for  example c:\viterbi ). Pay attention: do not use datapaths that contain blank spaces! From Quartus Project Wizard select the created directory as working directory, chose “nios_system” as  project name and “nios_system” as top level entity name. Then press next and skip the second tab  pressing next again. 

As FPGA chose the Cyclone II EP2C35F672C6 and than press finish button. Now that you have set up  a   project   we   have   to   add   the   Nios   sub­system.   Click   on   Tools   →   SOPC   Builder   and   choose   “nios_system” as System Name. Be careful to choose VHDL as implementation language. On the left  side of the tool you have all the components you can add to the system. The first operation is adding a  Nios II processor. Look for the component in the left­side list and add it to the design by double­ clicking it. In the following wizard select NIOS II/e (RISC, 32­bit) then press finish.   Following the same procedure, add to your system the “jtag uart” component (“Communication”menu),  45kB of on­chip memory (“Memory” menu) and two Interval timers (“Other” menu). Now your system  is almost ready. In the system menu press “auto­assign­base­addresses” and you are ready to generate  your code. Press Generate to create your Nios II system. Before implementing the design we need to  assign pin location at list for two important signals: clock and reset. To do so, we first need the system  to   complete   Synthesis   and   Implementation   process,   so   press   the   Start   Compilation   Button   in   the  Quartus environment, but you can stop the process as soon as the Synthesis and Implementation phase  is completed.  Now, from Quartus open Assignments → Pin Planner and select the location PIN N2 for  the clock and reserve it as a tri­state input. The reset signal is the Key[0] push button in location PIN  G26 and it is an input as well. We are ready for the implementation. Close the Pin Planner and press on  the Start Compilation Button. If everything is alright you should obtain a successful compilation and  your design is ready to be tested. But our system is composed of a processor, so to test it we need to  instruct the processor to do something.  In the next section how to complete this task will be explained. 

Running the software (20 mins) Now that we have a complete (and hopefully functional) hardware, we are interested in writing some  software to use it. From the Sopc­builder interface it is possible to execute the Software IDE (menu  tools). From Explorer you created a new folder (for example c : /viterbi/ ), now choose it when the tool  prompt asks you for the project folder location. Now from menu file you chose new­project and select  Altera   Nios   II   C/C++   application,   click   Next.   Give   a   name   to   your   software   and   select  Hello_world_small in Select Project Template, then click Finish. Now select all the lines in the .C file  and replace them with the code you have in the file: files\viterbi.c. From the menu Project select Build  all to create the executable for your processor.  Up to now we have built a system from scratch and compiled some software for it. Now, our task is to  find out whether what we have done is working or not. The first step is connecting the DE2 board  (Blaster port) to the usb of your lab PC. Then you have to connect the power supplier and press the red  button on the board. If you see a lot of led flashing and the message Welcome to the Altera DE2 board  on the display, you are ready to test your design. Next step is programming the FPGA with your design.  From  Quartus open Tools   →  Programmer, then press  Hardware setup and look for the usb­blaster   interface. Then you mark the Program/configure flag and press start. On success a window labeled  OpenCore Plus Status will appear telling you your system is running. Do not close the window or your  system will stop working. From the Nios IDE now you can try to execute your software. Open the run  menu, select run as and select Nios Hardware. The program calls 1000 times the function sdvd() and  each time the function is called, it prints an increasing number (from 0 to 999). In the end you'll see a  sequence of zeros and ones, which are the decoded sequence. You should compare it with  the bit  sequence contained in the files\bit_sequence.txt and check that they are identical. Note: if you press the Key[0] button, your program will restart from the beginning, since you assigned   the reset to the pin associated to this button. 

Profiling the code (15 mins) In this phase it's time to use the timers we have instantiated in the creation of our system.  Right click on your project folder in the left­side panel, then click System Libraries Properties. Now  select “Link with Profiling Libraries”,  “Reduced device drivers”, set timer_0 as System clock timer  and timer_1 as Timestamp timer. Now build all and run the program. The output of the profiling process is stored into the file:  <project_name>\Debug\gmon.out; to read it,  double click on the file.

Adding the custom instructions (1 h 10 mins) As you have performed the code profiling, you should be able to identify the most time­consuming part  of the code and it's now time to speed it up. To do so, we need to add some custom instructions to the Nios processor. NiosII embedded processor provides the possibility of extending its instruction set, mapping custom  instructions on user designed hardware. Then, these instructions can be directly used in C language,  through proper wrappers provided by the development tools. Custom hardware instructions are usually  connected to the ALU unit as shown in the figure below:    

You have the possibility to add both combinatorial and sequential instructions (of course these ones  need more input ports, such as clk, reset...): our suggestion is to use combinatorial ones, since they are  the easiest ones to implement. The custom logics that you add in parallel to the CPU in order to execute your instructions, have to be  described   in   .vhd   files,   which   must   have   well­defined   ports.   You   can   find   a   trivial   example   in  files\adder.vhd  where a simple adder is implemented, however your instruction structure must be the  same. Once you have written the .vhd blocks that implement your instructions, you have to add them to the 

system. To do so, you have two choices: you can either modify the system you have already built, or you can  build a new system from scratch (which might be better). Anyway, whatever choice you take, you must  open the SOPC Builder, either in the old or in the new project. The components you need to instantiate  are the same of the previous project, but this time, when we pick the Nios processor, we need to use the  Custom Instruction tab.  Note: the procedure described below has to be done for each .vhd file you have written.  1. Click on the Custom Instructions tab.  2. Click Import... The Interface to User Logic wizard appears. The Interface to User Logic     wizard is used to import Nios II custom instruction logic. To import custom instruction logic  into the system, you must:          • Add HDL source files to the list.          • Specify the top level module          • Read in the port list.         3.   Click Add. A Windows Explorer dialog box appears. Click on the src              folder.        4.   Choose your .vhd file        5.   Click Open to select the .vhd file and return back to the Interface to User Logic wizard.        6.   Click the Read port­list from files button. This will read the port information from your files.        7.   Click Add to System to complete the custom instruction importing process.        8.   Click Finish to add the custom instruction to the system and return to              the SOPC Builder window.  Note:  whenever you add, in C, an integer to a variable defined as int*, the compiler automatically  performs a times 4 multiplication on the integer to add. This is because an integer is defined on 32 bits,   that is 4 bytes . Watch out when you implement this kind of instructions in vhdl! When you're done, you have to re­generate the system and to follow once again the steps of the  Setting up the system paragraph.

Modifying the software (20 mins) Now that the new system has been compiled, it's necessary to modify the software, in order to make it  exploit our new set of instructions. Open the IDE tool and, using the same code used above, clean the project: Project menu → Clean. Now   build   it   again   (Project   menu   →   Build   all).   Now   expand   the   folder   on   the   left­side   panel   named   <project_name>_syslib, expand the folder Release, then the folder system_description and double click  on system.h to open it. Scroll down the file until you reach the “ custom instruction macros section”.  Here   you   can   find   the   macros   associated   to   the   custom   instructions   you   have   just   added   to   the  processor. To use them in the C code, just call them and pass the right arguments. You can now rebuild your code and try to run it.

Profiling the code on the customized system (10 mins) To profile the code on your new system, you have to follow the same procedure used for profiling the  first program. Now compare the results of the two profilings: have you been able to gain performance? If the answer is  yes, you have successfully accomplished this experience. Note: to give an idea of the result you can get, look at the following table. It is shown an extract of the   profiling, with the most time­consuming functions. By adding five custom instructions, we managed to   accelerate the execution of  the kernel_sdvd function of about 18%. Execution with no custom instructions

Execution with  custom instructions

Flat profile: 

Flat profile: 

Each sample counts as 0.001 seconds.    %   cumulative   self              self     total             time   seconds   seconds    calls  ms/call  ms/call  name      58.05     25.67    25.67      500    51.34    53.82  sdvd   20.68     34.81     9.14   795000     0.01     0.01  kernel_sdvd    6.11     37.52     2.70                             udivmodsi4    3.23     38.94     1.43                             __muldi3    2.73     40.15     1.21                             __mulsi3    1.50     40.81     0.66   202000     0.00     0.00  soft_quant    1.10     41.30     0.49                             sbrk    1.04     41.76     0.46                             __divsi3    0.93     42.17     0.41                             __floatsidf    0.70     42.48     0.31      500     0.62     0.62  init_adaptive_quant    0.48     42.69     0.21                             __modsi3    0.40     42.87     0.18                             __divdf3    0.38     43.04     0.17     2320     0.07     0.07  alt_avalon_jtag_uart_write    0.27     43.16     0.12                             _start    0.21     43.25     0.09                             __unpack_d    0.20     43.34     0.09                             __pack_d    0.19     43.42     0.08        1    82.68   129.51  alt_sim_halt    0.18     43.50     0.08                             alt_dev_null_write    0.17     43.57     0.07     7000     0.01     0.01  deci2bin    0.15     43.64     0.07     6000     0.01     0.01  soft_metric    0.15     43.71     0.07     8000     0.01     0.01  bin2deci    0.13     43.76     0.06     4000     0.01     0.03  nxt_stat    0.11     43.81     0.05                             __fixdfsi    0.11     43.86     0.05        1    46.83    46.83  alt_busy_sleep    0.10     43.90     0.04        1    42.59   172.10  exit    0.09     43.94     0.04                             __muldf3    0.08     43.98     0.04                             pow    0.05     44.00     0.02                             __ieee754_pow    0.05     44.02     0.02                             isnan    0.05     44.05     0.02                             __eqdf2    0.05     44.07     0.02     2320     0.01     0.08  write    0.05     44.09     0.02                             __fpcmp_parts_d    0.04     44.11     0.02                             __sfvwrite_small_dev    0.03     44.12     0.02                             ___vfprintf_internal_r    0.03     44.14     0.01                             __lshrdi3    0.02     44.15     0.01                             __udivsi3    0.02     44.15     0.01                             _write_r    0.01     44.16     0.01        1     5.37     5.37  main    0.01     44.16     0.01                             __nedf2    0.01     44.17     0.00                             _malloc_r

Each sample counts as 0.001 seconds.    %   cumulative   self              self     total             time   seconds   seconds    calls  ms/call  ms/call  name      62.21     25.46    25.46      500    50.93    53.59  sdvd   18.32     32.96     7.50   795000     0.01     0.01  kernel_sdvd    6.75     35.73     2.76                             udivmodsi4    2.28     36.66     0.93                             __mulsi3    1.62     37.32     0.66   202000     0.00     0.00  soft_quant    1.20     37.81     0.49                             __divsi3    1.11     38.27     0.45                             __floatsidf    1.07     38.71     0.44                             sbrk    0.79     39.03     0.33      500     0.65     0.65  init_adaptive_quant    0.55     39.25     0.22                             __muldi3    0.47     39.45     0.19                             __divdf3    0.38     39.60     0.15                             __modsi3    0.31     39.73     0.12        1   124.97   124.97  alt_busy_sleep    0.30     39.85     0.12        1   123.97   248.94  alt_sim_halt    0.27     39.96     0.11     1000     0.11     0.11  __malloc_unlock    0.27     40.07     0.11                             __unpack_d    0.27     40.18     0.11                             _start    0.23     40.28     0.09     6000     0.02     0.02  soft_metric    0.22     40.36     0.09     2320     0.04     0.04  alt_avalon_jtag_uart_write    0.19     40.44     0.08                             __pack_d    0.18     40.52     0.08     7000     0.01     0.01  deci2bin    0.15     40.58     0.06     8000     0.01     0.01  bin2deci    0.13     40.63     0.05     4000     0.01     0.03  nxt_stat    0.10     40.67     0.04                             __muldf3    0.07     40.70     0.03                             __ieee754_pow    0.06     40.73     0.02                             __fixdfsi    0.05     40.75     0.02                             __umodsi3    0.05     40.77     0.02                             __eqdf2    0.05     40.79     0.02                             __sfvwrite_small_dev    0.04     40.80     0.02                             pow    0.04     40.82     0.02                             __fpcmp_parts_d    0.04     40.84     0.01                             ___vfprintf_internal_r    0.03     40.85     0.01                             __udivsi3    0.03     40.86     0.01                             __lshrdi3    0.03     40.87     0.01                             isnan    0.02     40.88     0.01     2320     0.00     0.04  write    0.01     40.89     0.00                             rint

Related Documents


More Documents from ""