/* Copyright (C) 2008 - Ruben Niederhagen
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you will find it online at
 * http://www.gnu.org/licenses/gpl.html
 */

#include <stdio.h>
#include <spu_mfcio.h>
#include <stdint.h>
#include <malloc.h>

#include "include/spu/tools.h"
#include "src/common/common.h"
#include "src/spu/types.h"

#include "include/spu/tools.h"

volatile spu_data_t spu_data __attribute__((aligned (16)));

static volatile uint32_t barriers[16];

void barrier(unsigned int tag_id)
{
	int j = 0;

	for (j = 0; j < spu_data.global_parameters.spe_threads; j++)
		if (j != spu_data.my_id)
			mfc_put(&barriers[spu_data.my_id], 
								spu_data.ls[j].ull + (unsigned int)(&barriers[spu_data.my_id]), 
								4, tag_id, 0, 0);

	mfc_write_tag_mask(1<<tag_id);
	mfc_read_tag_status_all();

	for (j = 0; j < spu_data.global_parameters.spe_threads; j++)
		if (j != spu_data.my_id)
			while (barriers[j] < barriers[spu_data.my_id]);	

	barriers[spu_data.my_id]++;
}

void init_spe(addr64_t argp, addr64_t envp, void* globals, uint16_t globals_size, 
													 void *locals, uint16_t locals_size)
{
	int i;
	
	mfc_get(&spu_data.global_parameters, envp.ull, sizeof(global_parameters_t), 30, 0, 0);
	mfc_get(&spu_data.local_parameters, argp.ull, sizeof(local_parameters_t), 30, 0, 0);
   mfc_write_tag_mask(1<<30);
   mfc_read_tag_status_all();


	spu_data.my_id = spu_data.local_parameters.id;


	barriers[spu_data.my_id] = 1;	

#ifdef DEBUG
	if (locals_size != spu_data.local_parameters.local_data_size)
	{
		printf("-%i- Size-mismatch between local data size on SPU and PPU!\n", spu_data.my_id);
		printf("-%i- Trying to go on...\n", spu_data.my_id);
	}
#endif

	mfc_get(locals, spu_data.local_parameters.local_data.ull, locals_size, 30, 0, 0);


#ifdef DEBUG
   if (globals_size != spu_data.global_parameters.global_data_size)
   {
      printf("-%i- Size-mismatch between global data size on SPU and PPU!\n", spu_data.my_id);
      printf("-%i- Trying to go on...\n", spu_data.my_id);
   }
#endif

   mfc_get(globals, spu_data.global_parameters.global_data.ull, globals_size, 30, 0, 0);


   mfc_write_tag_mask(1<<30);
   mfc_read_tag_status_all();

	for (i = 0; i < 16; i++)
	{
		spu_data.phys_id[i] = -1;
		spu_data.log_id[i] = -1;
	}

	
	for (i = 0; i < spu_data.global_parameters.spe_threads; i++)
	{
		spu_data.phys_id[i] = spu_read_in_mbox();
		spu_data.log_id[spu_data.phys_id[i]] = i;
	}

	
	for (i = 0; i < spu_data.global_parameters.spe_threads; i++)
		mfc_get(&spu_data.ls[i], spu_data.global_parameters.local_stores.ull + i*sizeof(addr64_t), 
							sizeof(addr64_t), 30, 0, 0);

	mfc_write_tag_mask(1<<30);
	mfc_read_tag_status_all();
}

int get_my_id()
{
    return spu_data.my_id;
}

int get_phys_id(int log_id)
{
	return spu_data.phys_id[log_id];
}

int get_log_id(int phys_id)
{
   return spu_data.log_id[phys_id];
}
