Sunday, 24 August 2014

Teensy 3.1 ISP - program an ATtiny85 with your Teensy!

A couple of weeks ago I had this brilliant idea of building an intervalometer for my canon 450D. As I already had my Teensy 3.1 at that stage, it was an obvious choice for me. I implemented a *very* simple Arduino code, uploaded it to my Teensy, hooked up some wires together and had a blast shooting some time lapse videos (see here for example).

However, I quickly realised that I want my intervalometer to be smaller, more portable and battery powered (no USB cable required). I started googling and discovered that people use their Arduinos as In-System Programmers (ISP, see here) to program smaller AVR chips like ATtiny85. What a brilliant idea! I quickly ordered some ATtinys and started building. Here are my results - who knows, maybe somebody will find them useful?

  1. First of all, you'll need to have the Teensy 3.1 configured and working with the Arduino IDE. To make sure everything is working as it should, please follow PJRC's tutorial. If you have already set up your Teensy 3.1, you can obviously skip this step.
  2. Open the Arduino IDE (I'm using version 1.0.5-r2). You should already see the ArduinoISP sketch in the Examples section (File -> Examples -> ArduinoISP).
    Select it and take a quick look at the source code. You should see a section where RESET, LED_HB, LED_ERR, etc. macros are defined. Since RESET's original value is SS, let's change it to 10. Additionally, make sure that the function start_pmode() calls spi_init() before anything else. Save the sketch and upload it to your Teensy 3.1.
  3. Now, let's wire the programmer's LEDs up. They will give you an indication of what your ISP is currently doing. And if it's not doing anything, you can always rely on the heartbeat LED. There are 3 LEDs I'm using:
    • Heartbeat (YELLOW, LED_HB, pin 9)
    • Error (RED, LED_ERR, pin 8)
    • Write (GREEN, LED_PMODE, pin 7)
  4. Now, the next step is to install the ATtiny85 definition in your Arduino IDE. David A. Mellis was kind enough to prepare ATtiny support libraries for the Arduino IDE and share it on GitHub (you can get the zip directly here).

    Once you download the zip, extract it and you should see a folder called attiny inside the package (attiny-master -> attiny). Copy the attiny folder to your Arduino's sketchbook folder's hardware subfolder (it should go like this: Arduino -> hardware -> attiny). Start or restart your Arduino IDE. You should see a list of newly added ATtiny definitions in the Board menu (Tools -> Board -> ATtiny...)

  5. Make sure your Teensy acts as a programmer from now on by choosing appropriate Programmer option (Tools -> Programmer -> Arduino as ISP):

    Select the basic Blink example and choose your board from the new list. I'm working with ATtiny85, so my choice is ATtiny85 (internal 1MHz clock) (I don't want it to consume too much energy, as it's going to be battery powered).

    The last thing before connecting the ATtiny is to make sure we are using correct pin number to drive the LED. ATtiny doesn't have pin 13 (otherwise it wouldn't be so tiny!), so let's change the value of the led variable to 0 (which, funnily enough, is ATtiny's pin number 5).

  6. Now, let's connect the ATtiny. Disconnect the Teensy and stick the ATtiny on a breadboard. Look closely at the top surface of the chip, you should see a small circular shape indicating where the RESET pin is. It will look like this:

    Connect the pins as follows:

    ATtiny85 Teensy 3.1
    RESET (1) 10
    VCC (8) 3.3V
    GND (4) GND
    7 13
    6 12
    5 11

    using the ATtiny's pinout sheet as a reference:

    Your connections should look like this:

  7. Now you are ready to program your ATtiny85. Connect your Teensy 3.1 again and, having the Blink example in front of you, simply upload it. If you connected everything correctly, avrdude should upload the Blink code with no problems reporting something like this:

    ...
    ...
    ...
    avrdude: verifying ...
    avrdude: 836 bytes of flash verified
    avrdude: Send: Q [51] [20]
    avrdude: Recv: . [14]
    avrdude: Recv: . [10]

    avrdude done. Thank you.

  8. Now your ATtiny85 is programmed and you can test it:
I had a couple of problems in the process, but it was mostly due to wrong connections. If avrdude complains about invalid ATtiny's signature (whether it's 0x000000 or 0xFFFFFF), double check the wires and try to connect everything again. I hope this is useful and I encourage you to let me know if it worked in for you.

Thanks and happy hacking!

Monday, 14 April 2014

Raspberry Pi - micro OS template

Just a very quick post to share my micro OS template for Raspberry Pi. I've been playing with this little device for the last two months and it's been a lot of fun for me. My main area of interest is the ARM assembly language which I can learn on a real device right now.

The template is written to serve as a starting point for those who want direct framebuffer access and nothing else (small GPU projects, simple debugger etc.). To use it, simply build it using YAGARTO (as soon as you install it, just run the 'make' command) and stick it on a SD card replacing an existing linux kernel (I'm using Arch Linux for Raspberry Pi).

As I'm not a professional ARM developer, I'm sure this code can be greatly improved, so your feedback is welcome!

Please find the project here: https://bitbucket.org/piotrjustyna/0xfe

Sunday, 12 January 2014

Multivariate polynomial representation library for .NET

Please feel free to take a look at my multivariate polynomial representation library for .NET:

https://bitbucket.org/piotrjustyna/netpolynomial

I couldn't find any .NET libraries supporting representation of (single or multivariate) polynomials, so I wrote my own. It can help in machine learning-related tasks, such as polynomial regression, or just in any class of tasks where changing polynomial's coefficients and indeterminates is required.

Your feedback is welcome!

Thursday, 6 June 2013

CUDA 5.0 grid size problem

Interesting thing. I was convinced that the maximum grid size of my machine (GeForce GTX 660M) can be [65535, 65535, 65535]. I checked the device parameters using the cudaGetDeviceProperties function and I couldn't believe the results. My GPU's limitation is actually _way_ higher than I expected and according to cudaGetDeviceProperties it's: [2147483647, 65535, 65535]. I looked my card up in the CUDA wiki and it turned out, that my device qery was not lying.

However, I had a serious problem testing the huge grid size I had unexpectedly discovered. My vector addition compiled successfully but returned errors and could not be profiled when the vector size was greater than 65535 (blocks) * 1024 (threads). I started two threads on NVIDIA dev forum and on stackoverflow and finally, this brilliant guy helped me.

What was the problem then? I didn't change the default Visual Studio setting which defines the arch and code parameters of nvcc. To change it to proper values for your card, please take a look at the CUDA wiki to locate your GPU and modify the following setting:

Now I can go beyond that old limit of 65535 and continue my research.

***I have updated my CUDA Hello World tutorial so you can update that setting as you create the project.***

Tuesday, 4 June 2013

CUDA 5.0 - optimising vector addition

This time I plan to concentrate just on optimising my vector addition a little bit. In my previous example I had a statically defined number of blocks and threads used by my kernel 'AddVectorsKernel'. This time I'd like to make it a bit more adjustable. I also got a slightly better syntax highlighter because the old one was pretty much unreadable.

To begin with, this is my vanilla code without the CPU vector addition, just plain CUDA. I will be introducing and explaining some changes to it as I go along.

#include <cuda.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <stdio.h>
#include <time.h>

#pragma comment(lib, "cudart") 

typedef struct 
{
    float *content;
    const unsigned int size;
} pjVector_t;

__global__ void AddVectorsKernel(float *firstVector, float *secondVector, float *resultVector)
{
    unsigned int index = threadIdx.x + blockIdx.x * blockDim.x;
    resultVector[index] = firstVector[index] + secondVector[index];
}

int main(void)
{
    const unsigned int vectorLength = 1000000;
    const unsigned int blocks = 1000;
    const unsigned int threads = 1000;
    const unsigned int vectorSize = sizeof(float) * vectorLength;

    pjVector_t firstVector = { (float *)calloc(vectorLength, sizeof(float)), vectorLength };
    pjVector_t secondVector = { (float *)calloc(vectorLength, sizeof(float)), vectorLength };
    pjVector_t resultVector = { (float *)calloc(vectorLength, sizeof(float)), vectorLength };

    float *d_firstVector;
    float *d_secondVector;
    float *d_resultVector;

    cudaMalloc((void **)&d_firstVector, vectorSize);
    cudaMalloc((void **)&d_secondVector, vectorSize);
    cudaMalloc((void **)&d_resultVector, vectorSize);

    for (unsigned int i = 0; i < vectorLength; i++)
    {
        firstVector.content[i] = 1.0f;
        secondVector.content[i] = 2.0f;
    }

    cudaMemcpy(d_firstVector, firstVector.content, vectorSize, cudaMemcpyHostToDevice);
    cudaMemcpy(d_secondVector, secondVector.content, vectorSize, cudaMemcpyHostToDevice);
    AddVectorsKernel<<<blocks, threads>>>(d_firstVector, d_secondVector, d_resultVector);
    cudaMemcpy(resultVector.content, d_resultVector, vectorSize, cudaMemcpyDeviceToHost);

    free(firstVector.content);
    free(secondVector.content);
    free(resultVector.content);

    cudaFree(d_firstVector);
    cudaFree(d_secondVector);
    cudaFree(d_resultVector);
    cudaDeviceReset();

    return 0;
}

First of all, I'm going to modify the number of threads per block to be as high as possible on my machine to decrease the number of used blocks. To do this, I'm calling cudaGetDeviceProperties function and reading the maxThreadsPerBlock field from the populated structure cudaDeviceProp. I also got rid of const keyword in for blocks and threads since they are populated dynamically.

#include <cuda.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <stdio.h>
#include <math.h>

#define VECTOR_LENGTH 1000000

#pragma comment(lib, "cudart") 

typedef struct 
{
    float *content;
    const unsigned int size;
} pjVector_t;

__global__ void AddVectorsKernel(float *firstVector, float *secondVector, float *resultVector)
{
    unsigned int index = threadIdx.x + blockIdx.x * blockDim.x;

    if(index < VECTOR_LENGTH)
    {
        resultVector[index] = firstVector[index] + secondVector[index];
    }
}

int main(void)
{
    const unsigned int vectorSize = sizeof(float) * VECTOR_LENGTH;
    int threads = 0;
    unsigned int blocks = 0;
    cudaDeviceProp deviceProperties;

    cudaGetDeviceProperties(&deviceProperties, 0);

    threads = deviceProperties.maxThreadsPerBlock;
    blocks = (unsigned int)ceil(VECTOR_LENGTH / (double)threads);

    pjVector_t firstVector = { (float *)calloc(VECTOR_LENGTH, sizeof(float)), VECTOR_LENGTH };
    pjVector_t secondVector = { (float *)calloc(VECTOR_LENGTH, sizeof(float)), VECTOR_LENGTH };
    pjVector_t resultVector = { (float *)calloc(VECTOR_LENGTH, sizeof(float)), VECTOR_LENGTH };

    float *d_firstVector;
    float *d_secondVector;
    float *d_resultVector;

    cudaMalloc((void **)&d_firstVector, vectorSize);
    cudaMalloc((void **)&d_secondVector, vectorSize);
    cudaMalloc((void **)&d_resultVector, vectorSize);

    for (unsigned int i = 0; i < VECTOR_LENGTH; i++)
    {
        firstVector.content[i] = 1.0f;
        secondVector.content[i] = 2.0f;
    }

    cudaMemcpy(d_firstVector, firstVector.content, vectorSize, cudaMemcpyHostToDevice);
    cudaMemcpy(d_secondVector, secondVector.content, vectorSize, cudaMemcpyHostToDevice);
    AddVectorsKernel<<<blocks, threads>>>(d_firstVector, d_secondVector, d_resultVector);
    cudaMemcpy(resultVector.content, d_resultVector, vectorSize, cudaMemcpyDeviceToHost);

    free(firstVector.content);
    free(secondVector.content);
    free(resultVector.content);

    cudaFree(d_firstVector);
    cudaFree(d_secondVector);
    cudaFree(d_resultVector);
    cudaDeviceReset();

    return 0;
}

After running this code, performance dropped to 833[µs] (~5%). 'Did I do something wrong?', I was asking myself. And actually I did - that 'if' statement in the kernel code was that 'something wrong'. Or maybe not entirely wrong, but not very well placed. I thought initially: how come now, since I'm utilising all possible threads in every block (well, maybe except the last one - it will have 448 unused threads, 977 * 1024 - 1000000 = 448), my kernel uses more time to do its job than previously, where the number of unused threads was a lot (~53 times!) higher? The answer was that 'if' - I quickly realised that every thread in every block had to evaluate it 1 million (+448) times. Why do that since I can precisely adjust the length of my vector (+ that 448 elements) so that 'if' is not needed anymore and I will never go outside my vector in the kernel?

I'm obviously just playing with the toolkit right now so it's not a problem to add something here and tweak something there to achieve my goal, but even in the real life scenario (which I highly doubt this code will ever be used for!) you could do the same thing - add some extra elements to you vector and fill them with zeros not to check if you're within your vector in the kernel code.

Let's do some (integer!) math:

  • Unused threads in my previous approach: (1024 - 1000) * 1000 = 24000 (!)
    Redundant threads: 0 (all working threads are important)
    index checked: 0 times
  • Unused threads in my current approach: ((1000000 / 1024) * 1024) - 1000000 + 1024 = -576 + 1024 = 448 (they are doing nothing)
    Redundant threads: 0 (all working threads are important)
    index checked: 1000448 times (!)
  • Unused threads in my new approach: 0 (all are used)
    Redundant threads: 1000448 - 1000000 = 448 (they are just adding zeros)
    index checked: 0 times
Now let's see some code (I'm not filling these last 448 vector items with zeros, but you get the idea):
#include <cuda.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <stdio.h>
#include <math.h>

#define VECTOR_LENGTH 1000448

#pragma comment(lib, "cudart") 

typedef struct 
{
    float *content;
    const unsigned int size;
} pjVector_t;

__global__ void AddVectorsKernel(float *firstVector, float *secondVector, float *resultVector)
{
    unsigned int index = threadIdx.x + blockIdx.x * blockDim.x;
    resultVector[index] = firstVector[index] + secondVector[index];
}

int main(void)
{
    const unsigned int vectorSize = sizeof(float) * VECTOR_LENGTH;
    int threads = 0;
    unsigned int blocks = 0;
    cudaDeviceProp deviceProperties;

    cudaGetDeviceProperties(&deviceProperties, 0);

    threads = deviceProperties.maxThreadsPerBlock;
    blocks = (unsigned int)ceil(VECTOR_LENGTH / (double)threads);

    pjVector_t firstVector = { (float *)calloc(VECTOR_LENGTH, sizeof(float)), VECTOR_LENGTH };
    pjVector_t secondVector = { (float *)calloc(VECTOR_LENGTH, sizeof(float)), VECTOR_LENGTH };
    pjVector_t resultVector = { (float *)calloc(VECTOR_LENGTH, sizeof(float)), VECTOR_LENGTH };

    float *d_firstVector;
    float *d_secondVector;
    float *d_resultVector;

    cudaMalloc((void **)&d_firstVector, vectorSize);
    cudaMalloc((void **)&d_secondVector, vectorSize);
    cudaMalloc((void **)&d_resultVector, vectorSize);

    for (unsigned int i = 0; i < VECTOR_LENGTH; i++)
    {
        firstVector.content[i] = 1.0f;
        secondVector.content[i] = 2.0f;
    }

    cudaMemcpy(d_firstVector, firstVector.content, vectorSize, cudaMemcpyHostToDevice);
    cudaMemcpy(d_secondVector, secondVector.content, vectorSize, cudaMemcpyHostToDevice);
    AddVectorsKernel<<<blocks, threads>>>(d_firstVector, d_secondVector, d_resultVector);
    cudaMemcpy(resultVector.content, d_resultVector, vectorSize, cudaMemcpyDeviceToHost);

    free(firstVector.content);
    free(secondVector.content);
    free(resultVector.content);

    cudaFree(d_firstVector);
    cudaFree(d_secondVector);
    cudaFree(d_resultVector);
    cudaDeviceReset();

    return 0;
}
And there it is: 756.479[µs] - time needed to process more data was actually ~5% shorter!



That's it for now, thanks for reading, I hope it was useful or at least interesting.

Monday, 3 June 2013

CUDA 5.0 first calculations

OK - now, since I have my 'Hello World' program done, I think it would make sense to write something that actually uses CUDA processing power. Inspired by the CUDA introductory video, I want to write a simple vector addition kernel and run a quick test on how it compares to the CPU processing. I haven't mentioned it earlier, but I'm writing and running this code on my GeForce GTX 660M laptop GPU. Let's get started!

My first piece of code runs on the CPU and its only purpose is to add two vectors (of size one million, 1,000,000) together.

#include <cuda.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <stdio.h>
#include <time.h>

#pragma comment(lib, "cudart") 

typedef struct 
{
 float * const content;
 const unsigned int size;
} pjVector_t;

void AddVectors(const pjVector_t * const firstVector, const pjVector_t * const secondVector, pjVector_t * const resultVector);

int main(void)
{
 unsigned int vectorSize = 1000000;
 double cpuTime;
 clock_t begin, end;
 pjVector_t firstVector = { (float *)calloc(vectorSize, sizeof(float)), vectorSize };
 pjVector_t secondVector = { (float *)calloc(vectorSize, sizeof(float)), vectorSize };
 pjVector_t resultVector = { (float *)calloc(vectorSize, sizeof(float)), vectorSize };

 for (unsigned int i = 0; i < vectorSize; i++)
 {
  firstVector.content[i] = 1.0f;
  secondVector.content[i] = 2.0f;
 }

 begin = clock();

 AddVectors(&firstVector, &secondVector, &resultVector);

 end = clock();
 cpuTime = (double)(end - begin) / CLOCKS_PER_SEC;

 printf("Result vector calculated in: %f[sec]\n", cpuTime);
 getchar();


 free(firstVector.content);
 free(secondVector.content);
 free(resultVector.content);
 
 return 0;
}

void AddVectors(const pjVector_t * const firstVector, const pjVector_t * const secondVector, pjVector_t * const resultVector)
{
 for (unsigned int i = 0; i < firstVector -> size; i++)
 {
  resultVector -> content[i] = firstVector -> content[i] + secondVector -> content[i];
 }
}


Even though it's single-threaded it's pretty fast and takes from 5 to 6 milliseconds to calculate the result vector.

My second piece of code combines GPU and CPU code to compare the performance. A couple of things before I show the code though:

  • To simplify passing parameters to my kernel, I ditched the structures and chose plain float arrays.
  • Although I'm sure it can be done a lot more efficient, I'm running my CUDA code using 1000 blocks and 1000 threads each (1,000 * 1,000 = 1,000,000) just to show how the problem is divided into sections.
  • Performance is measured by an external tool, NVIDIA Visual Profiler which comes with the CUDA Toolkit.
#include <cuda.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <stdio.h>
#include <time.h>

#pragma comment(lib, "cudart") 

typedef struct 
{
 float *content;
 const unsigned int size;
} pjVector_t;

__global__ void AddVectorsKernel(float *firstVector, float *secondVector, float *resultVector)
{
 unsigned int index = threadIdx.x + blockIdx.x * blockDim.x;
 resultVector[index] = firstVector[index] + secondVector[index];
}

void AddVectors(const pjVector_t * const firstVector, const pjVector_t * const secondVector, pjVector_t * const resultVector);

int main(void)
{
 const unsigned int vectorLength = 1000000;
 const unsigned int blocks = 1000;
 const unsigned int threads = 1000;
 const unsigned int vectorSize = sizeof(float) * vectorLength;
 double cpuTime;
 clock_t begin, end;

 pjVector_t firstVector = { (float *)calloc(vectorLength, sizeof(float)), vectorLength };
 pjVector_t secondVector = { (float *)calloc(vectorLength, sizeof(float)), vectorLength };
 pjVector_t resultVector = { (float *)calloc(vectorLength, sizeof(float)), vectorLength };

 float *d_firstVector;
 float *d_secondVector;
 float *d_resultVector;

 cudaMalloc((void **)&d_firstVector, vectorSize);
 cudaMalloc((void **)&d_secondVector, vectorSize);
 cudaMalloc((void **)&d_resultVector, vectorSize);

 for (unsigned int i = 0; i < vectorLength; i++)
 {
  firstVector.content[i] = 1.0f;
  secondVector.content[i] = 2.0f;
 }

 // CPU calculatons
 begin = clock();

 AddVectors(&firstVector, &secondVector, &resultVector);

 end = clock();
 cpuTime = (double)(end - begin) / CLOCKS_PER_SEC;
 // - CPU calculatons

 // GPU calculatons
 cudaMemcpy(d_firstVector, firstVector.content, vectorSize, cudaMemcpyHostToDevice);
 cudaMemcpy(d_secondVector, secondVector.content, vectorSize, cudaMemcpyHostToDevice);

 AddVectorsKernel<<<blocks, threads>>>(d_firstVector, d_secondVector, d_resultVector);

 cudaMemcpy(resultVector.content, d_resultVector, vectorSize, cudaMemcpyDeviceToHost);
 // - GPU calculatons

 free(firstVector.content);
 free(secondVector.content);
 free(resultVector.content);

 cudaFree(d_firstVector);
 cudaFree(d_secondVector);
 cudaFree(d_resultVector);
 cudaDeviceReset();

 printf("CPU result vector calculated in: %f[ms]\n", cpuTime * 1000.0);

 getchar();
 
 return 0;
}

void AddVectors(const pjVector_t * const firstVector, const pjVector_t * const secondVector, pjVector_t * const resultVector)
{
 for (unsigned int i = 0; i < firstVector -> size; i++)
 {
  resultVector -> content[i] = firstVector -> content[i] + secondVector -> content[i];
 }
}



This time the magic really happened: vector addition takes only 792.166[µs], which is roughly 15% of the previous value. That's simply amazing for the first try. Let me show some screenshots from the profiler:



But what really made me smile was the warning messages from the profiler:



It actually complains about the time needed to copy the data being longer than the time of the calculations! And see how much time the cudaMalloc needed? As it turns out, adding two vectors of size 1,000,000 is the smallest problem here! I am really pleased with my first real CUDA test and it certainly inspires me even more.

CUDA 5.0 Hello World

Time for my first CUDA 'Hello World' program. Well, there won't be any GPU processing in it, but at least I'll prove that my environment is fully operational. Let's get to work!

I'll start by creating new Win32 Console Application (selecting 'Empty Project' option). Although it is a Visual C++ project, I will try to stick to C as I simply feel more confident using this language and don't want the OOP getting in my way.

First of all, since I use 64-bit windows, I changed my program to target the x64 platform.

Next, I selected CUDA 5.0 targets in my project's build customization options.

And I modify my project's Include and Library directories adding $(CUDA_INC_PATH) and $(CUDA_LIB_PATH) respectively (these settings can be located in project's properties menu -> Configuration Properties -> VC++ Directories).

Next thing is to change the nvcc parameters (by default they are set to compute_10,sm_10 which you probably don't want). To utilise full potential of your GPU, please adjust these parameters according to CUDA wiki. In my case it is compute_30,sm_30.

Last thing to do before I started writing code was to change CUDA/C++ target machine platform, which was still 32-bit.

And finally, my project nicely compiles (Intellisense complains about the angle brackets though, but I have yet to find an answer for this one).

#include <cuda.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <stdio.h>

#pragma comment(lib, "cudart") 

__global__ void mykernel(void)
{
}

int main(void)
{
 mykernel<<<1,1>>>();
 printf("Hello World!\n");
 getchar();
 
 return 0;
}