#ifndef __pipeline_fs
#define __pipeline_fs
/******************************************************************************
** FILE: pipeline.fs
** In-order pipeline simulation using reservation tables.
*/

extern cache_load(ulong) : ulong;
extern cache_store(ulong) : ulong;

extern instructions : ullong;
val instructions = 0?cvt(ullong);

extern cycles : ullong;
val cycles = 0?cvt(ullong);

#define NONE		0x00
#define ALU1		0x04
#define ALU2		0x08
#define FPU		0x10
#define LOAD		0x20
#define STORE		0x40

type Pipe = struct {
    resources : uchar queue,
    instructions : struct {
	    ready : uchar,
	    dest : ushort
	} queue
};

val pipe : Pipe = struct { resources = queue{}, instructions = queue{} };

type InstInfo1 = struct { reserve : uchar array[2], ready : uchar };
val _alu1_1 = struct { reserve = array { ALU1, NONE },  ready = 1?cvt(uchar) };
val _alu1_2 = struct { reserve = array { NONE, ALU1 },  ready = 2?cvt(uchar) };
val _alu2_1 = struct { reserve = array { ALU2, NONE },  ready = 1?cvt(uchar) };
val _alu2_2 = struct { reserve = array { NONE, ALU2 },  ready = 2?cvt(uchar) };
val _fpu    = struct { reserve = array { FPU,  FPU },   ready = 2?cvt(uchar) };
val _load1  = struct { reserve = array { ALU1, LOAD },  ready = 2?cvt(uchar) };
val _load2  = struct { reserve = array { ALU2, LOAD },  ready = 2?cvt(uchar) };
val _store1 = struct { reserve = array { ALU1, STORE }, ready = 0?cvt(uchar) };
val _store2 = struct { reserve = array { ALU2, STORE }, ready = 0?cvt(uchar) };

type InstInfo = InstInfo1 queue;
const val alu : InstInfo = queue { _alu1_1, _alu1_2, _alu2_1, _alu2_2 };
const val fpu : InstInfo = queue { _fpu };
const val load : InstInfo = queue { _load1, _load2 };
const val store :InstInfo = queue { _store1, _store2 };

val srcQ : ushort queue = queue {};
val storeQ : ushort queue = queue {};
val destQ : ushort queue = queue {};
val address = 0;

fun ready(var reserve : uchar array[2])
{
    val ii = 0;
    while(ii < 2) {
	val funit = reserve[ii];

	if(ii < pipe.resources?length)
	    if((funit & pipe.resources[+ii]) != 0?cvt(uchar))
		return false;

	if(funit & (ALU1|ALU2|FPU) != 0?cvt(uchar)) {
	    val jj = 0;
	    while(jj < pipe.instructions?length) {
		var old = pipe.instructions[+jj];

		val kk = 0;
		while(kk < srcQ?length) {
		    if(srcQ[+kk] == old.dest)
			if(old.ready > 0?cvt(uchar)) return false;
		    kk = kk + 1;
		}

		jj = jj + 1;
	    }

	} else if(funit == STORE) {
	    val jj = 0;
	    while(jj < pipe.instructions?length) {
		var old = pipe.instructions[+jj];

		val kk = 0;
		while(kk < storeQ?length) {
		    if(storeQ[+kk] == old.dest)
			if(old.ready > 0?cvt(uchar)) return false;
		    kk = kk + 1;
		}

		jj = jj + 1;
	    }
	}

	ii = ii + 1;
    }

    return true;
}

fun update_pipe(var inst1 : InstInfo1)
{
    val ready = inst1.ready?cvt(ulong);

    val ii = 0;
    while(ii < 2) {
	val funit = inst1.reserve[ii];

	if(ii < pipe.resources?length) {
	    var funits = pipe.resources[+ii];
	    funits = funits | funit;
	} else
	    pipe.resources?push_back(funit);

	val delay = 0;
	if(funit == LOAD)
	    delay = cache_load(address)?static();
	else if(funit == STORE)
	    delay = cache_store(address)?static();

	val jj = 0;
	while(jj < delay) {
	    ii = ii + 1;
	    if(ready > 0) ready = ready + 1;
	    if(ii < pipe.resources?length) {
		var funits = pipe.resources[+ii];
		funits = funits | funit;
	    } else
		pipe.resources?push_back(funit);
	    jj = jj + 1;
	}

	ii = ii + 1;
    }

    if(ready > 0) {
	ii = 0;
	while(ii < destQ?length) {
	    pipe.instructions?push_back(struct { ready = ready?cvt(uchar),
						 dest = destQ[+ii] });
	    ii = ii + 1;
	}
    }
}

fun delay_pipe()
{
    pipe.resources?pop_front();

    val ii = 0;
    val new_first = 0;
    while(ii < pipe.instructions?length) {
	var inst = pipe.instructions[+ii];
	if(inst.ready > 0?cvt(uchar)) inst.ready = inst.ready - 1?cvt(uchar);
	if(inst.ready > 0?cvt(uchar) && new_first <= 0) new_first = ii;
	ii = ii + 1;
    }

    pipe.instructions?pop_front(new_first?cvt(uchar));

    cycles = cycles + 1?cvt(ullong);
}

fun schedule(var inst : InstInfo)
{
    while(true) {
	val ii = 0;
	while(ii < inst?length) {

	    if(ready(inst[+ii].reserve)) {
		update_pipe(inst[+ii]);
		return;
	    }

	    ii = ii + 1;
	}

	delay_pipe();
    }
}

#endif