Skip to content

Instantly share code, notes, and snippets.

@neworderofjamie
Created March 7, 2022 15:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save neworderofjamie/fefd1ba5d035456518e50726c4db013a to your computer and use it in GitHub Desktop.
Save neworderofjamie/fefd1ba5d035456518e50726c4db013a to your computer and use it in GitHub Desktop.
Sample GeNN-like CUDA code for simulating Izhikevich neurons
// C++ includes
#include <algorithm>
#include <fstream>
// ------------------------------------------------------------------------
// Helper macro for error-checking CUDA calls
#define CHECK_CUDA_ERRORS(call) {\
cudaError_t error = call;\
if (error != cudaSuccess) {\
throw std::runtime_error(__FILE__": " + std::to_string(__LINE__) + ": cuda error " + std::to_string(error) + ": " + cudaGetErrorString(error));\
}\
}
namespace
{
unsigned int* glbSpkCntNeurons;
unsigned int* d_glbSpkCntNeurons;
unsigned int* glbSpkNeurons;
unsigned int* d_glbSpkNeurons;
float* VNeurons;
float* d_VNeurons;
float* UNeurons;
float* d_UNeurons;
float* aNeurons;
float* d_aNeurons;
float* bNeurons;
float* d_bNeurons;
float* cNeurons;
float* d_cNeurons;
float* dNeurons;
float* d_dNeurons;
__global__ void preNeuronResetKernel(unsigned int* d_glbSpkCntNeurons) {
unsigned int id = 32 * blockIdx.x + threadIdx.x;
if(id == 0) {
d_glbSpkCntNeurons[0] = 0;
}
}
__global__ void updateNeuronsKernel(unsigned int *d_glbSpkCntNeurons, unsigned int *d_glbSpkNeurons, float *d_VNeurons, float *d_UNeurons,
const float *d_aNeurons, const float *d_bNeurons, const float *d_cNeurons, const float *d_dNeurons)
{
const unsigned int id = 32 * blockIdx.x + threadIdx.x;
__shared__ unsigned int shSpk[32];
__shared__ unsigned int shPosSpk;
__shared__ unsigned int shSpkCount;
if (threadIdx.x == 0); {
shSpkCount = 0;
}
__syncthreads();
// Neurons
if(id < 32) {
if(id < 4) {
float lV = d_VNeurons[id];
float lU = d_UNeurons[id];
const float la = d_aNeurons[id];
const float lb = d_bNeurons[id];
const float lc = d_cNeurons[id];
const float ld = d_dNeurons[id];
float Isyn = 0;
// calculate membrane potential
if (lV >= 30.0f){
lV=lc;
lU+=ld;
}
lV+=0.5f*(0.04f*lV*lV+5.0f*lV+140.0f-lU+Isyn+(1.00000000000000000e+01f))*1.0f; //at two times for numerical stability
lV+=0.5f*(0.04f*lV*lV+5.0f*lV+140.0f-lU+Isyn+(1.00000000000000000e+01f))*1.0f;
lU+=la*(lb*lV-lU)*1.0f;
if(lV > 30.0){ //keep this to not confuse users with unrealistiv voltage values
lV=30.0;
}
// test for and register a true spike
if (lV >= 29.99f) {
const unsigned int spkIdx = atomicAdd((unsigned int *) &shSpkCount, 1);
shSpk[spkIdx] = id;
}
d_VNeurons[id] = lV;
d_UNeurons[id] = lU;
}
__syncthreads();
if (threadIdx.x == 0) {
if (shSpkCount > 0) {
shPosSpk = atomicAdd((unsigned int*)&d_glbSpkCntNeurons[0], shSpkCount);
}
}
__syncthreads();
if (threadIdx.x < shSpkCount) {
const unsigned int n = shSpk[threadIdx.x];
d_glbSpkNeurons[shPosSpk + threadIdx.x] = n;
}
}
}
}
int main()
{
CHECK_CUDA_ERRORS(cudaSetDevice(0));
// Allocate memory
CHECK_CUDA_ERRORS(cudaHostAlloc(&glbSpkCntNeurons, 1 * sizeof(unsigned int), cudaHostAllocPortable));
CHECK_CUDA_ERRORS(cudaMalloc(&d_glbSpkCntNeurons, 1 * sizeof(unsigned int)));
CHECK_CUDA_ERRORS(cudaHostAlloc(&glbSpkNeurons, 4 * sizeof(unsigned int), cudaHostAllocPortable));
CHECK_CUDA_ERRORS(cudaMalloc(&d_glbSpkNeurons, 4 * sizeof(unsigned int)));
CHECK_CUDA_ERRORS(cudaHostAlloc(&VNeurons, 4 * sizeof(float), cudaHostAllocPortable));
CHECK_CUDA_ERRORS(cudaMalloc(&d_VNeurons, 4 * sizeof(float)));
CHECK_CUDA_ERRORS(cudaHostAlloc(&UNeurons, 4 * sizeof(float), cudaHostAllocPortable));
CHECK_CUDA_ERRORS(cudaMalloc(&d_UNeurons, 4 * sizeof(float)));
CHECK_CUDA_ERRORS(cudaHostAlloc(&aNeurons, 4 * sizeof(float), cudaHostAllocPortable));
CHECK_CUDA_ERRORS(cudaMalloc(&d_aNeurons, 4 * sizeof(float)));
CHECK_CUDA_ERRORS(cudaHostAlloc(&bNeurons, 4 * sizeof(float), cudaHostAllocPortable));
CHECK_CUDA_ERRORS(cudaMalloc(&d_bNeurons, 4 * sizeof(float)));
CHECK_CUDA_ERRORS(cudaHostAlloc(&cNeurons, 4 * sizeof(float), cudaHostAllocPortable));
CHECK_CUDA_ERRORS(cudaMalloc(&d_cNeurons, 4 * sizeof(float)));
CHECK_CUDA_ERRORS(cudaHostAlloc(&dNeurons, 4 * sizeof(float), cudaHostAllocPortable));
CHECK_CUDA_ERRORS(cudaMalloc(&d_dNeurons, 4 * sizeof(float)));
// Initialise
glbSpkCntNeurons[0] = 0;
std::fill_n(VNeurons, 4, -6.50000000000000000e+01f);
std::fill_n(UNeurons, 4, -2.00000000000000000e+01f);
// Regular
aNeurons[0] = 0.02; bNeurons[0] = 0.2; cNeurons[0] = -65.0; dNeurons[0] = 8.0;
// Fast
aNeurons[1] = 0.1; bNeurons[1] = 0.2; cNeurons[1] = -65.0; dNeurons[1] = 2.0;
// Chattering
aNeurons[2] = 0.02; bNeurons[2] = 0.2; cNeurons[2] = -50.0; dNeurons[2] = 2.0;
// Bursting
aNeurons[3] = 0.02; bNeurons[3] = 0.2; cNeurons[3] = -55.0; dNeurons[3] = 4.0;
// Copy to device
CHECK_CUDA_ERRORS(cudaMemcpy(d_glbSpkCntNeurons, glbSpkCntNeurons, 1 * sizeof(unsigned int), cudaMemcpyHostToDevice));
CHECK_CUDA_ERRORS(cudaMemcpy(d_VNeurons, VNeurons, 4 * sizeof(float), cudaMemcpyHostToDevice));
CHECK_CUDA_ERRORS(cudaMemcpy(d_UNeurons, UNeurons, 4 * sizeof(float), cudaMemcpyHostToDevice));
CHECK_CUDA_ERRORS(cudaMemcpy(d_aNeurons, aNeurons, 4 * sizeof(float), cudaMemcpyHostToDevice));
CHECK_CUDA_ERRORS(cudaMemcpy(d_bNeurons, bNeurons, 4 * sizeof(float), cudaMemcpyHostToDevice));
CHECK_CUDA_ERRORS(cudaMemcpy(d_cNeurons, cNeurons, 4 * sizeof(float), cudaMemcpyHostToDevice));
CHECK_CUDA_ERRORS(cudaMemcpy(d_dNeurons, dNeurons, 4 * sizeof(float), cudaMemcpyHostToDevice));
std::ofstream spikes("spikes.csv");
std::ofstream voltages("voltages.csv");
for(unsigned int i = 0; i < 200; i++) {
// Launch kernels
const dim3 threads(32, 1);
const dim3 grid(1, 1);
preNeuronResetKernel<<<grid, threads>>>(d_glbSpkCntNeurons);
updateNeuronsKernel<<<grid, threads>>>(d_glbSpkCntNeurons, d_glbSpkNeurons, d_VNeurons, d_UNeurons,
d_aNeurons, d_bNeurons, d_cNeurons, d_dNeurons);
// Copy voltages back from device and write to file
CHECK_CUDA_ERRORS(cudaMemcpy(VNeurons, d_VNeurons, 4 * sizeof(float), cudaMemcpyDeviceToHost));
voltages << i << "," << VNeurons[0] << "," << VNeurons[1] << "," << VNeurons[2] << "," << VNeurons[3] << std::endl;
// Copy spikes back from device
CHECK_CUDA_ERRORS(cudaMemcpy(glbSpkCntNeurons, d_glbSpkCntNeurons, 1 * sizeof(unsigned int), cudaMemcpyDeviceToHost));
CHECK_CUDA_ERRORS(cudaMemcpy(glbSpkNeurons, d_glbSpkNeurons, 4 * sizeof(unsigned int), cudaMemcpyDeviceToHost));
for(unsigned int s = 0; s < glbSpkCntNeurons[0]; s++) {
spikes << i << "," << glbSpkNeurons[s] << std::endl;
}
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment