Start date: March 8
End date: March 15
Lab: 3
Status Complete

Lab 3 - Software control of a datapath

Lab Overview

In this lab, we will integrate the video display controller developed in Lab 2 with the MicroBlaze processor built using the fabric of the Artix-7 FPGA. In the preceding lectures, we learned about the Vivado and SDK tool chains, now it's time to put that knowledge to the test by building a software controlled datapath. Lab 2 revealed some shortcomings of our oscilloscope that this lab intends on correcting. Specifically, we will add: The following figure shows required functionality - your program should allow the user to change the position of the triggerVolt and triggerTime indicators with the result that the waveform should be drawn so that the periodic waveform is increasing through that voltage at that time.


In order to accomplish this, you will need to make some minor changes to the lab2 component, create a new piece of IP, and then program that IP using the MicroBlaze, as described in the block diagram below. We will walk through these steps below.



Note: Remember the Ready signal in the diagram above is coming from the Flag Register (FlagQ). It is assumed that you hooked the Ready signal coming from the Audio Codec into the "Set Flag" input to the Flag Register and the output of the Flag register FlagQ is the new "Ready" signal coming out of the lab2_dp, and eventually connecting toe Microblaze's interrupt pin. If you accidentally skip the Flag Register and hook the audio codec's Ready signal directly to the interrupt pin, you will find your system plots the sinusoid's incorrectly, plotting what looks to be a very high frequency sinusoid instead of the correct frequency.

Note: If your Flag Register is 8-bits wide, you will need to extract the single bit of Q (the std_logic_vector output from the flag register) as the interrupt signal (the one set by the Ready signal). This may require you to extract the one bit Q as a separate signal to connect to the MicroBlaze in your block design.

Hardware

New for 2021: Quick instructions for creating your Microblaze project can be found in Lab3_Install_short_version.pdf which is a combination of “MicroBlaze_Install_Short_Version” and “Lec19_Install_Short_Version”, tailored for lab3

For the most part, your hardware you developed in lab2 will be unchanged. For controlling your TriggerVolt and TriggerTime, you can either
(1) Retain your Lab2 buttons to control Trigger Volt and Trigger Time, but you must input these signals into Microblaze, and be able to display the values on the UART monitor
or
(2) Control TriggerVolt and TriggerTime from your Microblaze UART keyboard interface (via teraterm)
  • Inside the lab2_dp component, remove the logic driving the triggerVolt and triggerTime signals into the video component.
  • Remove the buttons signal from the lab2 and lab2_dp entities.
  • Remove the buttons signal from the ucf file.
  • Add the triggerVolt and triggerTime signals to the lab2 and lab2_dp entity descriptions.
  • Drive the triggerTime and triggerVolt inputs on the video component with the corresponding signals on the lab2_dp entity.
  • Setup MicroBlaze slv_regs for microblaze to be able to write to TrigVolt and TrigTime into lab2 datapath ports

    Hardware Setup

    Your first step will be to create a component for your lab2 component in your Vivado repository. This will require you to think about what signals are routed to the MicroBlaze and what signals are going outside the Artix 7 chip. The following table should help.
    Signals To/From MicroBlaze Signals Going Outside Artix 7
    exWrAddr clk
    exWen reset
    exSel ac_mclk
    L_bus_out, R_bus_out ac_adc_sdata
    exLbus, exRbus ac_dac_sdata
    flagQ ac_bclk
    flagClear ac_lrclk
    triggerTime sda
    triggerVolt scl
    ready tmds
    ch1_enb, ch2_enb? tmdsb


    For the mapping of the MicroBlaze slv_reg to/from lab2 ports, you need to specify not only which 32-bit slv_reg for each signal, but also which bits are used, and whether it is read/write (in or out to microblaze). Hint: use this spreadsheet: lab3_signal_mapping.xlsx

    Implementation and Testing



    Lessons from previous years: Build your C-code incrementally, with baby-step tests such as these [and add each of these tests as one of your C-code menu options]:

    More Lessons learned the hard way in previous years:

    Note: Depending on your design, you will want to control some signals (like TrigVolt, TrigTime, Ch1_enb, Ch2_enb) from the UART keyboard Terminal while your program is running in continuous mode. (You will not want to halt the program in order to change these settings, but rather change them "live"). Therefore, in your C code "while loop", you may want to check if the user has hit the key on the keyboard without having to actually read the key. For these cases, the command XUartLite_IsReceiveEmpty() will prove useful. Note that "uartRegAddr" is a constant, the address of the uart.
    ...
    while(1) {   // run forever
    	if (!XUartLite_IsReceiveEmpty(uartRegAddr)) {
        	c=XUartLite_RecvByte(uartRegAddr);
    		switch(c) {  
    		  ...   // handle case for key press
    		}  // end switch
    	} // end if
    	If (live_mode == 1) {
    		Do live mode...  
    			grab samples from audio codec and put in Array;
    			find trigger location in Array;
    			Write 620 samples from Array to proper location in BRAM;
    	} // end if
    } // end while
    


    Note: Do NOT have polling FlagQ running at the same time as the ISR, as both would be attempting to clear the Flag, and you may find your system is missing samples, making the wave form plot appear to be a higher frequency than it actually is. One way to protect against this is to have all your polling cases disable interrupts with microblaze_disable_interrupts(), then microblaze_enable_interrupts() when you are done
    pseudo code:
            case 'm':     // some of these will be XIL commands
    		    microblaze_disable_interrupts();
                for (i=0;i<1024;i++) {
                  while(flagQ==0){}; //wait on ready
                  array_L[i] = LbusReg; // read audio values
                  array_R[i] = RbusReg;
                  ClearFlag = 1;   //clear the flag
                  ClearFlag = 0;   //release the clear, so can be set again
                }
    			microblaze_enable_interrupts();
    			break;
    


    Note: When you are finding your trigger point in lab3, just like in lab2, you need to compare apples to apples. If your number in your C-array is 16-bits, it will be a large number in decimal like 26572, and this large 16-bit value you will want to write to your BRAM (as your VHDL code will later pull out the upper 10 or 9 bits) Meanwhile your triggerVolt is a small 10-bit number, like decimal 220. So to compare apples to apples in your C-code, (just for the purpose of finding the trigger) you might what to change the value in your C-array to the scale of the trigger volt by grabbing only its upper 10-bits (or in math, shift right 6 times, or divide by 2^6). Remember, in lab2 you probably also added (or subtracted?) an offset like the number like 34 (for the DC offset), so you would also need to add or subtract this from either trigvolt or the scale array number when you are doing the search for the trigger value.

    Also, when doing your triggering math, make sure all your variables are declared as unsigned (like u16), not signed (like int), as the values are all unsigned and the comparisons will not work properly if declared as signed.

    Note: In the past, some students in their datapath hooked up L_Bus_out/R_Bus_Out directly to the 18-bit "signed" value coming out of the audio codec, not the 18-bit value converted to "unsigned". If you do this, your sine wave will look strange (or maybe off the screen)... this can also impact your triggering. At some point, you need to convert from signed to unsigned. If you didn't do this in your VHDL datapath, you could always do this in your C-code. If you are doing this conversion in C, remember you cannot just cast a variable from signed to unsigned. casting to unsigned in C would not do what you want (as we explained back in lab2). For example, if you have a signed value X = -7, and you cast X to an unsigned variable, what would -7 become? Unsigned values must be Zero or greater. -7 is undefined. If you go back to slide 11 in the lab2.pdf, you can see the pattern of this conversion with the 5 example numbers. It looks like all the lower bits stay the same, and only the MSB changes... it flips its value. I know two simple ways to flip the MSB (there are some other ways also), either (1) inverting the bit with a bitwise operator like XOR, or (2) adding a special number that keeps all the lower bits the same but only changes the MSB For method (2), adding the special number, look at the slide 11, you should be able to figure it out. For method (1), using XOR, ask yourself (a) what happens to a bit if I XOR it with ZERO?, and (b) what happens to a bit if I XOR it with ONE?

    Note: The XIL in and out functions are either 8-bit, 16-bit, or 32-bit [like Xil_Out16(exWraddr, i)], while you have some lab2 values that are different sizes (like trigvolt is 10-bits), so you may need to append bits in your VHDL code to make them match

    Note: Do not doing anything "slow" inside your ISR or while you are polling the samples into the array. Printf() is very slow. Also, writing your array_L values to the BRAM is very slow. So write your values from the Array_L to the BRAM in a different "for loop" than the route that grabs your samples from the audio codec into the Array_L.

    Gate Check 1

    You need to decide if TrigVolt, TrigTime, Ch1_enb, and Ch2_enb will be controlled via buttons on the FGPA board or using the UART keyboard (teraterm) controlling them through Microblaze. For this Gate Check you will turn in a revised Lab2_datapath block diagram, correctly showing all these signals (and their directions), and adding or removing parts (like button debouncing). Also for this Gate Check, you will turn in a mapping of 32 AXI registers (slv_reg) to lab2 signals. or the mapping of the MicroBlaze slv_reg to/from lab2 ports, you need to specify not only which 32-bit slv_reg for each signal, but also which bits are used, and whether it is read/write (in or out to microblaze). Hint: use this spreadsheet: lab3_signal_mapping.xlsx

    Gate Check 2

    You need to have all of your Lab 2 functionality implemented with the Microblaze. That is, you need to be able to set ExSel to '0' from your microblaze C program and be able to achieve the same functionality as you did in Lab 2.

    Gate Check 3

    For the first task for GC3, you need to be able to send UART commands using Tera Term (or another terminal emulator) to your FPGA to adjust the trigger on the screen. The trigger on the screen should properly react to moving the trigger either up or down. Or, if you choose to not to control the trigger from the terminal and kept the lab 2 Trigger Volt and Time buttons, then show on your UART display that your C program can read the Trigger Volt and Trigger Time from the hardware buttons.

    For the second task for GC3, you need to implement at least one of the baby-step tests mentioned above in the "Implementation and Testing" section.

    Required Functionality

    In order to achieve required functionality, you will need to properly trigger the oscilloscope on channel 1 using a positive edge trigger. Control of this process is to be performed using the MicroBlaze. The main tasks of the MicroBlaze will include:

    B-level Functionality

    A-level Functionality

    A-level Functionality

    README

    Milestone Date/Time What was achieved
    Gate Check 1
    Gate Check 2
    Gate Check 3
    Required Functionality
    A Functionality
  • Grading

    Item Points
    Gate Check 1 10
    Gate Check 2 10
    Gate Check 3 10
    Required Functionality 40
    A Functionality 10
    Use of Git / Bitbucket 5
    Code Style 5
    README 10
    Total 100