#ifndef _funits_fs
#define _funits_fs
/******************************************************************************
** FILE: funits.fs
** Manage function units in the OOO processor model.
*/

#include "param.h"
#include "instq.fs"
#include "funits.h"
#include "cache.fs"

///////////////////////////////////////////////////////////////////////////////
// Limit the number of each type of functional units.
// fu_alloc() -- try to allocate one of the functional units
// fu_clear() -- free all allocated units
//

const val fu_pool = array(FU_END1) {
    0,		// FU_NONE
    2,		// FU_ALU: 2 dyintegers ALUs
    2,		// FU_FPU: 2 floating-point units
    1		// FU_ADDR: 1 address adder
};

val fu_num_allocated = array(FU_END1) { 0 };

fun fu_alloc(ftype)
{
    assert(ftype?cvt(ulong) < FU_END);
    if(ftype?cvt(ulong) >= FU_END1) return true;
    if(fu_num_allocated[ftype] >= fu_pool[ftype])
	return false;
    fu_num_allocated[ftype] = fu_num_allocated[ftype] + 1;
    return true;
}

fun fu_clear()
{
    fu_num_allocated = array(FU_END1) { 0 };
}

///////////////////////////////////////////////////////////////////////////////
// fu_finish() -- called when the timer runs out on an instruction that has
// been assigned a function unit. This means that the instruction is finished
// with the function unit and is ready to move on.
// Returns true if the instruction is ready to execute (in the execute()
// function). Otherwise returns false.
//

fun fu_finish(ftype, var inst : InstOp, inum)
{
    switch(ftype?cvt(ulong)) {

     case FU_ALU: return true;
     case FU_FPU: return true;

     case FU_ADDR:
	val op = inst.op?cvt(ulong);
	val va = (rmap_read(inst.srcq[+0],inum) +
		  rmap_read(inst.srcq[+1],inum))?cvt(ulong);
	val delay : short;

	assert(inst.align <= 3?cvt(uchar));
	if((va & ((1<<inst.align?cvt(ulong))-1)) != 0) {
	    if(inum > 0) {
		inst.ftime = 1?cvt(uchar);
		return false;
	    }
	}

	// Unknown store address => no more loads or stores
	if(unk_store_addr) { inst.ftime = 1?cvt(uchar); return false; }

	if(OP_LOAD_BEGIN <= op && op <= OP_LOAD_END) {
	    if(cache_load_start(inum,va,no_loads,delay))
		return true;				// HIT
	    delay?static;
	    if(delay >= 0?cvt(short)) {
		inst.ftype = FU_LOAD?cvt(uchar);	// MISS
		inst.ftime = delay?cvt(uchar);
	    } else {
		// cache access aborted
		inst.ftime = (-delay)?cvt(uchar);
		unk_load_addr = true;
	    }

	} else if(OP_STORE_BEGIN <= op && op <= OP_STORE_END) {
	    // Unknown load address => store must issue speculatively
	    if(cache_store_start(inum,va,is_spec||unk_load_addr,delay)) {
		// Successful hand-off to cache
		delay?static;
		assert(delay >= 0?cvt(short));
		if(!is_spec && !unk_load_addr && delay == 0?cvt(short)) {
		    // This store committed
		    inst.ftype = FU_NONE?cvt(uchar);
		    return true;
		} else {
		    // This store still needs to be committed
		    inst.ftype = FU_STORE?cvt(uchar);
		    if(delay == 0?cvt(short))
			inst.ftime = 1?cvt(uchar);
		    else {
			assert(delay > 0?cvt(short));
			inst.ftime = delay?cvt(uchar);
		    }
		}
	    } else {
		// cache access aborted
		delay?static;
		assert(delay < 0?cvt(short));
		inst.ftime = (-delay)?cvt(uchar);
		unk_store_addr = true;
	    }

	} else {
	    assert(OP_SWAP_BEGIN <= op && op <= OP_SWAP_END);
	    // Unknown load address => store must issue speculatively
	    if(cache_store_start(inum,va,is_spec||unk_load_addr,delay)) {
		// Successful hand-off to cache
		delay?static;
		assert(delay >= 0?cvt(short));
		if(!is_spec && !unk_load_addr && delay == 0?cvt(short)) {
		    // This store committed
		    inst.ftype = FU_NONE?cvt(uchar);
		    return true;
		} else {
		    // This store still needs to be committed
		    inst.ftype = FU_STORE?cvt(uchar);
		    if(delay == 0?cvt(short))
			inst.ftime = 1?cvt(uchar);
		    else {
			assert(delay > 0?cvt(short));
			inst.ftime = delay?cvt(uchar);
		    }
		}
	    } else {
		// Cache access aborted
		delay?static;
		assert(delay < 0?cvt(short));
		inst.ftime = (-delay)?cvt(uchar);
		unk_store_addr = true;
	    }
	}

     case FU_LOAD:
	val delay : short;
	if(cache_load_continue(inum, delay)) return true;
	inst.ftime = delay?static?cvt(uchar);

     case FU_STORE:
	if(!is_spec && !unk_load_addr) {
	    val delay : short;
	    if(cache_store_commit(inum,delay)) {
		// This store committed
		inst.ftype = FU_NONE?cvt(uchar);
		return true;
	    } else {
		// Conflicts still exist => try again later
		delay?static;
		assert(delay > 0?cvt(short));
		inst.ftime = delay?cvt(uchar);
	    }

	} else inst.ftime = 1?cvt(uchar);

     default:
	// Unexpected ftype
	assert(false);
    }

    return false;
}

#endif