Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

fpvm: new scheduler from Werner (NEEDS REPLACEMENT OF alloca())

  • Loading branch information...
commit 0c4c26d100804c8a6e7e625cecc6e5805ed8c67b 1 parent 7414dca
@sbourdeauducq sbourdeauducq authored
View
11 software/bios/Makefile
@@ -44,7 +44,7 @@ boot.o: ../../software/include/net/microudp.h
boot.o: ../../software/include/net/tftp.h ../../software/include/hal/vga.h
boot.o: ../../software/include/hal/usb.h ../../software/include/hw/hpdmc.h
boot.o: ../../software/include/hw/common.h ../../software/include/hw/flash.h
-boot.o: boot.h
+boot.o: unlzma.h boot.h
isr.o: ../../software/include/hw/interrupts.h
isr.o: ../../software/include/base/irq.h ../../software/include/base/uart.h
isr.o: ../../software/include/hal/usb.h ../../software/include/hal/tmu.h
@@ -62,11 +62,12 @@ main.o: ../../software/include/base/version.h
main.o: ../../software/include/net/mdio.h ../../software/include/hw/fmlbrg.h
main.o: ../../software/include/hw/sysctl.h ../../software/include/hw/common.h
main.o: ../../software/include/hw/gpio.h ../../software/include/hw/flash.h
-main.o: ../../software/include/hal/vga.h ../../software/include/hal/tmu.h
-main.o: ../../software/include/hw/tmu.h ../../software/include/hal/brd.h
-main.o: ../../software/include/hal/usb.h ../../software/include/hal/ukb.h
-main.o: boot.h splash.h
+main.o: ../../software/include/hw/minimac.h ../../software/include/hal/vga.h
+main.o: ../../software/include/hal/tmu.h ../../software/include/hw/tmu.h
+main.o: ../../software/include/hal/brd.h ../../software/include/hal/usb.h
+main.o: ../../software/include/hal/ukb.h boot.h splash.h
splash.o: ../../software/include/base/stdio.h
splash.o: ../../software/include/base/stdlib.h
splash.o: ../../software/include/hal/vga.h ../../software/include/hw/flash.h
splash.o: splash.h
+unlzma.o: unlzma.h
View
3  software/demo/Makefile
@@ -62,7 +62,8 @@ eval.o: ../../software/include/base/stdlib.h
eval.o: ../../software/include/base/string.h ../../software/include/hw/pfpu.h
eval.o: ../../software/include/hw/common.h ../../software/include/hw/tmu.h
eval.o: ../../software/include/hal/pfpu.h ../../software/include/fpvm/fpvm.h
-eval.o: ../../software/include/fpvm/is.h ../../software/include/fpvm/gfpus.h
+eval.o: ../../software/include/fpvm/is.h
+eval.o: ../../software/include/fpvm/schedulers.h
eval.o: ../../software/include/fpvm/pfpu.h eval.h renderer.h
font.o: color.h font.h
isr.o: ../../software/include/hw/interrupts.h
View
8 software/demo/eval.c
@@ -1,6 +1,6 @@
/*
* Milkymist SoC (Software)
- * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
*
* 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
@@ -22,7 +22,7 @@
#include <hal/pfpu.h>
#include <fpvm/fpvm.h>
-#include <fpvm/gfpus.h>
+#include <fpvm/schedulers.h>
#include <fpvm/pfpu.h>
#include "eval.h"
@@ -281,7 +281,7 @@ static int finalize_pfv()
static int schedule_pfv()
{
- perframe_prog_length = gfpus_schedule(&pfv_fragment, (unsigned int *)perframe_prog, (unsigned int *)perframe_regs);
+ perframe_prog_length = fpvm_default_schedule(&pfv_fragment, (unsigned int *)perframe_prog, (unsigned int *)perframe_regs);
eval_reinit_all_pfv();
if(perframe_prog_length < 0) {
printf("EVL: per-frame VLIW scheduling failed\n");
@@ -527,7 +527,7 @@ static int finalize_pvv()
static int schedule_pvv()
{
- pervertex_prog_length = gfpus_schedule(&pvv_fragment, (unsigned int *)pervertex_prog, (unsigned int *)pervertex_regs);
+ pervertex_prog_length = fpvm_default_schedule(&pvv_fragment, (unsigned int *)pervertex_prog, (unsigned int *)pervertex_regs);
if(pervertex_prog_length < 0) {
printf("EVL: per-vertex VLIW scheduling failed\n");
return 0;
View
28 software/include/fpvm/gfpus.h → software/include/fpvm/schedulers.h
@@ -1,6 +1,6 @@
/*
* Milkymist SoC (Software)
- * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
*
* 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
@@ -15,6 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
+#ifndef __FPVM_SCHEDULERS_H
+#define __FPVM_SCHEDULERS_H
+
+#include <fpvm/fpvm.h>
+
+/*
+ * code must be able to hold PFPU_PROGSIZE 32-bit instructions.
+ * registers must be able to hold PFPU_REG_COUNT 32-bit values.
+ */
+
/*
* Greedy Floating Point Unit Scheduler
* This program takes FPVM code and performs greedy VLIW scheduling
@@ -23,17 +34,14 @@
* program and takes the first schedulable instruction, without trying to
* optimize the order of instructions.
*/
-
-#ifndef __FPVM_GFPUS_H
-#define __FPVM_GFPUS_H
-
-#include <fpvm/fpvm.h>
+int gfpus_schedule(struct fpvm_fragment *fragment, unsigned int *code, unsigned int *registers);
/*
- * code must be able to hold PFPU_PROGSIZE 32-bit instructions.
- * registers must be able to hold PFPU_REG_COUNT 32-bit values.
+ * Lean New / Lamely Named Floating Point Unit Scheduler
+ * A smarter, faster, optimizing scheduler by Werner Almesberger.
*/
+int lnfpus_schedule(struct fpvm_fragment *fragment, unsigned int *code, unsigned int *registers);
-int gfpus_schedule(struct fpvm_fragment *fragment, unsigned int *code, unsigned int *registers);
+#define fpvm_default_schedule lnfpus_schedule
-#endif /* __FPVM_GFPUS_H */
+#endif /* __FPVM_SCHEDULERS_H */
View
14 software/libfpvm/Makefile
@@ -1,7 +1,7 @@
MMDIR=../..
include $(MMDIR)/software/include.mak
-OBJECTS=fpvm.o parser_helper.o scanner.o parser.o gfpus.o pfpu.o
+OBJECTS=fpvm.o parser_helper.o scanner.o parser.o gfpus.o lnfpus.o pfpu.o
all: libfpvm.a
@@ -39,8 +39,16 @@ gfpus.o: ../../software/include/base/stdio.h
gfpus.o: ../../software/include/base/stdlib.h
gfpus.o: ../../software/include/fpvm/is.h ../../software/include/fpvm/fpvm.h
gfpus.o: ../../software/include/fpvm/pfpu.h
-gfpus.o: ../../software/include/fpvm/gfpus.h ../../software/include/hw/pfpu.h
-gfpus.o: ../../software/include/hw/common.h
+gfpus.o: ../../software/include/fpvm/schedulers.h
+gfpus.o: ../../software/include/hw/pfpu.h ../../software/include/hw/common.h
+lnfpus.o: ../../software/include/base/stdlib.h
+lnfpus.o: ../../software/include/base/stdio.h
+lnfpus.o: ../../software/include/base/string.h
+lnfpus.o: ../../software/include/base/assert.h
+lnfpus.o: ../../software/include/fpvm/is.h ../../software/include/fpvm/fpvm.h
+lnfpus.o: ../../software/include/fpvm/pfpu.h
+lnfpus.o: ../../software/include/fpvm/schedulers.h
+lnfpus.o: ../../software/include/hw/pfpu.h ../../software/include/hw/common.h
parser.o: ../../software/include/base/stdio.h
parser.o: ../../software/include/base/stdlib.h
parser.o: ../../software/include/base/assert.h
View
4 software/libfpvm/gfpus.c
@@ -1,6 +1,6 @@
/*
* Milkymist SoC (Software)
- * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
*
* 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
@@ -20,7 +20,7 @@
#include <fpvm/is.h>
#include <fpvm/fpvm.h>
#include <fpvm/pfpu.h>
-#include <fpvm/gfpus.h>
+#include <fpvm/schedulers.h>
#include <hw/pfpu.h>
View
666 software/libfpvm/lnfpus.c
@@ -0,0 +1,666 @@
+/*
+ * lnfpus.c - O(n) ... O(n^2) scheduler
+ *
+ * Copyright (C) 2011 Werner Almesberger
+ *
+ * Based on gfpus.c
+ * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
+ *
+ * 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, version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <fpvm/is.h>
+#include <fpvm/fpvm.h>
+#include <fpvm/pfpu.h>
+#include <fpvm/schedulers.h>
+
+#include <hw/pfpu.h>
+
+
+//#define REG_STATS
+#define LCPF /* longest critical path first */
+
+//#define DEBUG
+#ifdef DEBUG
+#define Dprintf printf
+#else
+#define Dprintf(...)
+#endif
+
+
+#define MAX_LATENCY 8 /* maximum latency; okay to make this bigger */
+
+#define CODE(n) (((pfpu_instruction *) (code+(n)))->i)
+
+
+struct list {
+ struct list *next, *prev;
+};
+
+
+struct insn {
+ struct list more; /* more insns on same schedule */
+ struct fpvm_instruction *vm_insn;
+ struct data_ref {
+ struct list more; /* more refs sharing the data */
+ struct insn *insn; /* insn this is part of */
+ struct insn *dep; /* insn we depend on */
+ } opa, opb, dest, cond;
+ int arity;
+ int latency;
+ int rmw; /* non-zero if instruction is read-modify-write */
+ int unresolved; /* number of data refs we need before we can sched */
+ int earliest; /* earliest cycle dependencies seen so far are met */
+ struct list dependants; /* list of dependencies (constant) */
+ int num_dependants; /* number of dependencies */
+ struct insn *next_setter; /* next setter of the same register */
+#ifdef LCPF
+ int distance; /* minimum cycles on this path until the end */
+#endif
+};
+
+
+struct vm_reg {
+ struct insn *setter; /* instruction setting it; NULL if none */
+ struct insn *first_setter; /* first setter */
+ int pfpu_reg; /* underlying PFPU register */
+ int refs; /* usage count */
+};
+
+
+struct pfpu_reg {
+ struct list more; /* list of unallocated PFPU registers */
+ int vm_reg; /* corresponding FPVM register if allocated */
+ int used; /* used somewhere in the program */
+};
+
+
+static struct sched_ctx {
+ struct fpvm_fragment *frag;
+ struct insn insns[FPVM_MAXCODELEN];
+ struct vm_reg *regs; /* dynamically allocated */
+ struct pfpu_reg pfpu_regs[PFPU_REG_COUNT];
+ struct list unallocated; /* unallocated registers */
+ struct list unscheduled; /* unscheduled insns */
+ struct list waiting; /* insns waiting to be scheduled */
+ struct list ready[PFPU_PROGSIZE]; /* insns ready at nth cycle */
+ int cycle; /* the current cycle */
+#ifdef REG_STATS
+ int max_regs, curr_regs; /* allocation statistics */
+#endif
+} *sc;
+
+
+/* ----- Register initialization ------------------------------------------- */
+
+
+/*
+ * Straight from gfpus.c, only with some whitespace changes.
+ */
+
+static void get_registers(struct fpvm_fragment *fragment,
+ unsigned int *registers)
+{
+ int i;
+ union {
+ float f;
+ unsigned int n;
+ } fconv;
+
+ for(i = 0; i < fragment->nbindings; i++)
+ if(fragment->bindings[i].isvar)
+ registers[i] = 0;
+ else {
+ fconv.f = fragment->bindings[i].b.c;
+ registers[i] = fconv.n;
+ }
+ for(; i < PFPU_REG_COUNT; i++)
+ registers[i] = 0;
+}
+
+
+/* ----- Doubly-linked list ------------------------------------------------ */
+
+
+/*
+ * Use the naming conventions of include/linux/list.h
+ */
+
+
+#ifdef DEBUG
+
+static void list_poison(struct list *list)
+{
+ list->next = list->prev = NULL;
+}
+
+#else /* DEBUG */
+
+#define list_poison(list)
+
+#endif /* !DEBUG */
+
+
+static void list_init(struct list *list)
+{
+ list->next = list->prev = list;
+}
+
+
+static void list_del(struct list *item)
+{
+ assert(item->next != item);
+ item->prev->next = item->next;
+ item->next->prev = item->prev;
+ list_poison(item);
+}
+
+
+static void *list_pop(struct list *list)
+{
+ struct list *first;
+
+ first = list->next;
+ if(first == list)
+ return NULL;
+ list_del(first);
+ return first;
+}
+
+
+static void list_add_tail(struct list *list, struct list *item)
+{
+ item->next = list;
+ item->prev = list->prev;
+ list->prev->next = item;
+ list->prev = item;
+}
+
+
+static void list_add(struct list *list, struct list *item)
+{
+ item->next = list->next;
+ item->prev = list;
+ list->next->prev = item;
+ list->next = item;
+}
+
+
+static void list_concat(struct list *a, struct list *b)
+{
+ if(b->next != b) {
+ a->prev->next = b->next;
+ b->next->prev = a->prev;
+ b->prev->next = a;
+ a->prev = b->prev;
+ }
+ list_poison(b);
+}
+
+
+/*
+ * Do not delete elements from the list while traversing it with foreach !
+ */
+
+#define foreach(var, head) \
+ for(var = (void *) ((head))->next; \
+ (var) != (void *) (head); \
+ var = (void *) ((struct list *) (var))->next)
+
+
+/* ----- Register management ----------------------------------------------- */
+
+
+static int vm_reg2idx(int reg)
+{
+ return reg >= 0 ? reg : sc->frag->nbindings-reg;
+}
+
+
+static int alloc_reg(struct insn *setter)
+{
+ struct pfpu_reg *reg;
+ int vm_reg, pfpu_reg, vm_idx;
+
+ vm_reg = setter->vm_insn->dest;
+ if(vm_reg >= 0) {
+ pfpu_reg = vm_reg;
+ sc->pfpu_regs[vm_reg].vm_reg = vm_reg; /* @@@ global init */
+ } else {
+ reg = list_pop(&sc->unallocated);
+ if(!reg)
+ return -1;
+
+ #ifdef REG_STATS
+ sc->curr_regs++;
+ if(sc->curr_regs > sc->max_regs)
+ sc->max_regs = sc->curr_regs;
+ #endif
+
+ reg->vm_reg = vm_reg;
+ pfpu_reg = reg-sc->pfpu_regs;
+ }
+
+ Dprintf(" alloc reg %d -> %d\n", vm_reg, pfpu_reg);
+
+ vm_idx = vm_reg2idx(vm_reg);
+ sc->regs[vm_idx].setter = setter;
+ sc->regs[vm_idx].pfpu_reg = pfpu_reg;
+ sc->regs[vm_idx].refs = setter->num_dependants+1;
+
+ return pfpu_reg;
+}
+
+
+static void put_reg(int vm_reg)
+{
+ int vm_idx;
+ struct vm_reg *reg;
+
+ if(vm_reg >= 0)
+ return;
+
+ vm_idx = vm_reg2idx(vm_reg);
+ reg = sc->regs+vm_idx;
+
+ assert(reg->refs);
+ if(--reg->refs)
+ return;
+
+ Dprintf(" free reg %d\n", reg->pfpu_reg);
+
+#ifdef REG_STATS
+ assert(sc->curr_regs);
+ sc->curr_regs--;
+#endif
+
+ /*
+ * Prepend so that register numbers stay small and bugs reveal
+ * themselves more rapidly.
+ */
+ list_add(&sc->unallocated, &sc->pfpu_regs[reg->pfpu_reg].more);
+
+ /* clear it for style only */
+ reg->setter = NULL;
+ reg->pfpu_reg = 0;
+}
+
+
+static int lookup_pfpu_reg(int vm_reg)
+{
+ return vm_reg >= 0 ? vm_reg : sc->regs[vm_reg2idx(vm_reg)].pfpu_reg;
+}
+
+
+static void mark(int vm_reg)
+{
+ if(vm_reg > 0)
+ sc->pfpu_regs[vm_reg].used = 1;
+}
+
+
+static int init_registers(struct fpvm_fragment *frag,
+ unsigned int *registers)
+{
+ int i;
+
+ sc->regs =
+ calloc(frag->nbindings-frag->next_sur, sizeof(struct vm_reg));
+ if(!sc->regs)
+ return -1;
+
+ get_registers(frag, registers);
+
+ for(i = 0; i != frag->ninstructions; i++) {
+ mark(frag->code[i].opa);
+ mark(frag->code[i].opb);
+ mark(frag->code[i].dest);
+ }
+
+ list_init(&sc->unallocated);
+ for(i = PFPU_SPREG_COUNT; i != PFPU_REG_COUNT; i++)
+ if(!sc->pfpu_regs[i].used)
+ list_add_tail(&sc->unallocated, &sc->pfpu_regs[i].more);
+
+ return 0;
+}
+
+
+/* ----- Instruction scheduler --------------------------------------------- */
+
+
+static struct vm_reg *add_data_ref(struct insn *insn, struct data_ref *ref,
+ int reg_num)
+{
+ struct vm_reg *reg;
+
+ reg = sc->regs+vm_reg2idx(reg_num);
+ ref->insn = insn;
+ ref->dep = reg->setter;
+ if(insn->vm_insn->dest == reg_num)
+ insn->rmw = 1;
+ if(!ref->dep)
+ reg->refs++;
+ else {
+ list_add_tail(&ref->dep->dependants, &ref->more);
+ ref->dep->num_dependants++;
+ insn->unresolved++;
+
+ Dprintf("insn %lu: reg %d setter %lu unresolved %d\n",
+ insn-sc->insns, reg_num, reg->setter-sc->insns,
+ insn->unresolved);
+ }
+ return reg;
+}
+
+
+static void init_scheduler(struct fpvm_fragment *frag)
+{
+ int i;
+ struct insn *insn;
+ struct vm_reg *reg;
+ struct data_ref *ref;
+
+ list_init(&sc->unscheduled);
+ list_init(&sc->waiting);
+ for(i = 0; i != PFPU_PROGSIZE; i++)
+ list_init(sc->ready+i);
+
+ for(i = 0; i != frag->ninstructions; i++) {
+ insn = sc->insns+i;
+ insn->vm_insn = frag->code+i;
+ insn->arity = fpvm_get_arity(frag->code[i].opcode);
+ insn->latency = pfpu_get_latency(frag->code[i].opcode);
+ list_init(&insn->dependants);
+ switch (insn->arity) {
+ case 3:
+ add_data_ref(insn, &insn->cond, FPVM_REG_IFB);
+ /* fall through */
+ case 2:
+ add_data_ref(insn, &insn->opb, frag->code[i].opb);
+ /* fall through */
+ case 1:
+ add_data_ref(insn, &insn->opa, frag->code[i].opa);
+ /* fall through */
+ case 0:
+ reg = sc->regs+vm_reg2idx(frag->code[i].dest);
+ if(reg->setter) {
+ reg->setter->next_setter = insn;
+ foreach(ref, &reg->setter->dependants)
+ if(ref->insn != insn)
+ insn->unresolved++;
+ if(!insn->rmw)
+ insn->unresolved++;
+ } else {
+ if(!insn->rmw)
+ insn->unresolved += reg->refs;
+ reg->first_setter = insn;
+ }
+ reg->setter = insn;
+ break;
+ default:
+ abort();
+ }
+ if(insn->unresolved)
+ list_add_tail(&sc->unscheduled, &insn->more);
+ else
+ list_add_tail(&sc->ready[0], &insn->more);
+ }
+
+#ifdef LCPF
+ struct data_ref *dep;
+
+ for(i = frag->ninstructions-1; i >= 0; i--) {
+ insn = sc->insns+i;
+#if 0
+ /*
+ * Theoretically, we should consider the distance through
+ * write-write dependencies too. In practice, this would
+ * mainly matter if we had operations whose result is ignored.
+ * This is a degenerate case that's probably not worth
+ * spending much effort on.
+ */
+ if(insn->next_setter) {
+ insn->distance =
+ insn->next_setter->distance-insn->distance+1;
+ if(insn->distance < 1)
+ insn->distance = 1;
+ }
+#endif
+ foreach(dep, &insn->dependants)
+ if(dep->insn->distance > insn->distance)
+ insn->distance = dep->insn->distance;
+ /*
+ * While it would be more correct to add one for the cycle
+ * following the write cycle, this also has the effect of
+ * producing slighly worse results on the example set of
+ * patches. Let's thus keep this "bug" for now.
+ */
+// insn->distance += insn->latency+1;
+ insn->distance += insn->latency;
+ }
+#endif
+}
+
+
+static void unblock(struct insn *insn)
+{
+ int slot;
+
+ assert(insn->unresolved);
+ if(--insn->unresolved)
+ return;
+ Dprintf(" unblocked %lu -> %u\n", insn-sc->insns, insn->earliest);
+ list_del(&insn->more);
+ slot = insn->earliest;
+ if(slot <= sc->cycle)
+ slot = sc->cycle+1;
+ list_add_tail(sc->ready+slot, &insn->more);
+}
+
+
+static void put_reg_by_ref(struct data_ref *ref, int vm_reg)
+{
+ struct insn *setter = ref->dep;
+ struct vm_reg *reg;
+
+ if(setter) {
+ put_reg(setter->vm_insn->dest);
+ if(setter->next_setter && setter->next_setter != ref->insn)
+ unblock(setter->next_setter);
+ } else {
+ reg = sc->regs+vm_reg2idx(vm_reg);
+ if(reg->first_setter && !reg->first_setter->rmw)
+ unblock(reg->first_setter);
+ }
+}
+
+
+static void unblock_after(struct insn *insn, int cycle)
+{
+ if(insn->earliest <= cycle)
+ insn->earliest = cycle+1;
+ unblock(insn);
+}
+
+
+static int issue(struct insn *insn, unsigned *code)
+{
+ struct data_ref *ref;
+ int end, reg;
+
+ end = sc->cycle+insn->latency;
+
+ Dprintf("cycle %d: insn %lu L %d (A %d B %d)\n", sc->cycle,
+ insn-sc->insns, insn->latency, insn->vm_insn->opa,
+ insn->vm_insn->opb);
+
+ switch (insn->arity) {
+ case 3:
+ put_reg_by_ref(&insn->cond, FPVM_REG_IFB);
+ /* fall through */
+ case 2:
+ CODE(sc->cycle).opb = lookup_pfpu_reg(insn->vm_insn->opb);
+ put_reg_by_ref(&insn->opb, insn->vm_insn->opb);
+ /* fall through */
+ case 1:
+ CODE(sc->cycle).opa = lookup_pfpu_reg(insn->vm_insn->opa);
+ put_reg_by_ref(&insn->opa, insn->vm_insn->opa);
+ break;
+ case 0:
+ break;
+ default:
+ abort();
+ }
+
+ reg = alloc_reg(insn);
+ if(reg < 0)
+ return -1;
+ CODE(end).dest = reg;
+ CODE(sc->cycle).opcode = fpvm_to_pfpu(insn->vm_insn->opcode);
+
+ foreach(ref, &insn->dependants)
+ unblock_after(ref->insn, end);
+ if(insn->next_setter && !insn->next_setter->rmw)
+ unblock_after(insn->next_setter,
+ end-insn->next_setter->latency);
+
+ return 0;
+}
+
+
+#ifdef DEBUG
+static int count(const struct list *list)
+{
+ int n = 0;
+ const struct list *p;
+
+ for(p = list->next; p != list; p = p->next)
+ n++;
+ return n;
+}
+#endif
+
+
+static int schedule(unsigned int *code)
+{
+ int remaining;
+ int i, last, end;
+ struct insn *insn;
+ struct insn *best;
+
+ remaining = sc->frag->ninstructions;
+ for(i = 0; remaining; i++) {
+ if(i == PFPU_PROGSIZE)
+ return -1;
+
+ sc->cycle = i;
+ Dprintf("@%d --- remaining %d, waiting %d + ready %d\n",
+ i, remaining, count(&sc->waiting), count(&sc->ready[i]));
+
+ list_concat(&sc->waiting, sc->ready+i);
+ best = NULL;
+ foreach(insn, &sc->waiting) {
+ end = i+insn->latency;
+ if(end >= PFPU_PROGSIZE)
+ return -1;
+ if(!CODE(end).dest) {
+#ifdef LCPF
+ if(!best || best->distance < insn->distance)
+ best = insn;
+#else
+ best = insn;
+ break;
+#endif
+ }
+ }
+ if(best) {
+ if(issue(best, code) < 0)
+ return -1;
+ list_del(&best->more);
+ remaining--;
+ }
+ if(CODE(i).dest)
+ put_reg(sc->pfpu_regs[CODE(i).dest].vm_reg);
+ }
+
+ /*
+ * Add NOPs to cover unfinished instructions.
+ */
+ last = i;
+ end = i+MAX_LATENCY;
+ if(end > PFPU_PROGSIZE)
+ end = PFPU_PROGSIZE;
+ while(i != end) {
+ if(CODE(i).dest)
+ last = i+1;
+ i++;
+ }
+ return last;
+}
+
+
+static int init_scheduler_context(struct fpvm_fragment *frag,
+ unsigned int *reg)
+{
+ sc = calloc(1, sizeof(*sc));
+ if(!sc)
+ return -1;
+
+ sc->frag = frag;
+
+ if(init_registers(frag, reg) < 0) {
+ free(sc);
+ return -1;
+ }
+
+ init_scheduler(frag);
+ return 0;
+}
+
+
+int lnfpus_schedule(struct fpvm_fragment *frag, unsigned int *code,
+ unsigned int *reg)
+{
+ pfpu_instruction vecout;
+ int res;
+
+ if(init_scheduler_context(frag, reg) < 0)
+ return -1;
+ memset(code, 0, PFPU_PROGSIZE*sizeof(*code));
+ res = schedule(code);
+
+#ifdef REG_STATS
+ printf("regs: %d/%d\n", sc->curr_regs, sc->max_regs);
+#endif
+
+ free(sc->regs);
+ free(sc);
+ if(res < 0)
+ return res;
+ if(frag->vector_mode)
+ return res;
+ if(res == PFPU_PROGSIZE)
+ return -1;
+
+ vecout.w = 0;
+ vecout.i.opcode = FPVM_OPCODE_VECTOUT;
+ code[res] = vecout.w;
+
+ return res+1;
+}
View
2  software/libfpvm/subdir.mak
@@ -1,4 +1,4 @@
-OBJECTS=fpvm.o parser_helper.o scanner.o parser.o gfpus.o pfpu.o
+OBJECTS=fpvm.o parser_helper.o scanner.o parser.o gfpus.o lnfpus.o pfpu.o
all: libfpvm.a
View
10 software/libfpvm/x86-linux/test.c
@@ -1,6 +1,6 @@
/*
* Milkymist SoC (Software)
- * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
*
* 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
@@ -18,7 +18,7 @@
#include <stdio.h>
#include <fpvm/fpvm.h>
-#include <fpvm/gfpus.h>
+#include <fpvm/schedulers.h>
#include <fpvm/pfpu.h>
#include <hw/pfpu.h>
@@ -40,7 +40,7 @@ int main(int argc, char *argv[])
fpvm_finalize(&frag);
fpvm_dump(&frag);
printf("== PFPU:\n");
- len = gfpus_schedule(&frag, code, registers);
+ len = fpvm_default_schedule(&frag, code, registers);
if(len > 0)
pfpu_dump(code, len);
@@ -60,7 +60,7 @@ int main(int argc, char *argv[])
fpvm_finalize(&frag);
fpvm_dump(&frag);
printf("== PFPU:\n");
- len = gfpus_schedule(&frag, code, registers);
+ len = fpvm_default_schedule(&frag, code, registers);
if(len > 0)
pfpu_dump(code, len);
@@ -73,7 +73,7 @@ int main(int argc, char *argv[])
fpvm_finalize(&frag);
fpvm_dump(&frag);
printf("== PFPU:\n");
- len = gfpus_schedule(&frag, code, registers);
+ len = fpvm_default_schedule(&frag, code, registers);
if(len > 0)
pfpu_dump(code, len);
Please sign in to comment.
Something went wrong with that request. Please try again.