shithub: riscv

Download patch

ref: 561346d07f176356e9841891e5213672c0a33878
parent: 7cf8369411a3b9348f862bccaa4147b9aaf38e67
author: aiju <devnull@localhost>
date: Sat Jan 11 22:40:42 EST 2020

cyclone v kernel: fpga support, fix CONFADDR

--- a/sys/src/9/cycv/cycv
+++ b/sys/src/9/cycv/cycv
@@ -2,7 +2,7 @@
 	root
 	cons
 	swap
-#	arch
+	arch
 	uart
 	mnt
 	srv
@@ -24,6 +24,7 @@
 	segment
 	
 link
+	dma
 	ethercycv
 	ethermedium
 	loopbackmedium
--- a/sys/src/9/cycv/dat.h
+++ b/sys/src/9/cycv/dat.h
@@ -13,6 +13,7 @@
 typedef struct PMMU	PMMU;
 typedef struct Ureg	Ureg;
 typedef struct ISAConf	ISAConf;
+typedef struct DMAC	DMAC;
 typedef uvlong		Tval;
 
 #pragma incomplete Ureg
@@ -202,3 +203,7 @@
 #define mpcore ((ulong*)MPCORE_BASE)
 #define resetmgr ((ulong*)RESETMGR_BASE)
 #define sysmgr ((ulong*)SYSMGR_BASE)
+
+/*dmacopy*/
+#define SRC_INC (1<<0)
+#define DST_INC (1<<14)
--- a/sys/src/9/cycv/devarch.c
+++ b/sys/src/9/cycv/devarch.c
@@ -7,11 +7,44 @@
 #include "ureg.h"
 #include "../port/error.h"
 
+#define fpga ((ulong*) FPGAMGR_BASE)
+
+enum { Timeout = 3000 };
+
 enum {
+	FPGASTAT,
+	FPGACTRL,
+	FPGAINTEN = 0x830/4,
+	FPGAINTTYPE = 0x838/4,
+	FPGAINTPOL = 0x83C/4,
+	FPGAINTSTATUS = 0x840/4,
+	FPGAEOI = 0x84C/4,
+	FPGAPINS = 0x850/4,
+};
+
+enum {
+	/*FPGACTRL*/
+	HPSCONFIG = 1<<0,
+	NCONFIGPULL = 1<<2,
+	AXICFGEN = 1<<8,
+	/*FPGAPINS*/
+	NSTATUS = 1<<0,
+	CONF_DONE = 1<<1,
+	INIT_DONE = 1<<2,
+	CRC_ERROR = 1<<3,
+	CVP_CONF_DONE = 1<<4,
+	PR_READY = 1<<5,
+	PR_ERROR = 1<<6,
+	PR_DONE = 1<<7,
+	NCONFIG_PIN = 1<<8,
+	NSTATUS_PIN = 1<<9,
+	CONF_DONE_PIN = 1<<10,
+	FPGA_POWER_ON = 1<<11
+};
+
+enum {
 	Qdir = 0,
-	Qtemp,
-	Qpl,
-	Qfbctl,
+	Qfpga,
 	Qbase,
 
 	Qmax = 16,
@@ -19,288 +52,149 @@
 
 static Dirtab archdir[Qmax] = {
 	".",		{ Qdir, 0, QTDIR },	0,	0555,
-	"temp",		{ Qtemp, 0},		0,	0440,
-	"pl",		{ Qpl, 0 }, 		0,	0660,
-	"fbctl",	{ Qfbctl, 0 }, 		0,	0660,
+	"fpga",		{ Qfpga, 0 }, 		0,	0660,
 };
 static int narchdir = Qbase;
 
-static int temp = -128;
-static ulong *devc;
-static int dmadone;
-enum { PLBUFSIZ = 8192 };
-static uchar *plbuf;
-static Rendez plinitr, pldoner, pldmar;
-static QLock plrlock, plwlock;
-static Ref plwopen;
-static Physseg *axi;
+static Ref fpgawopen;
+enum { FPGABUFSIZ = 65536 };
+static uchar *fpgabuf;
+static int fpgabufp;
+static int fpgaok;
 
-enum {
-	DEVCTRL = 0,
-	DEVISTS = 0xc/4,
-	DEVMASK,
-	DEVSTS,
-	DMASRC = 0x18/4,
-	DMADST,
-	DMASRCL,
-	DMADSTL,
-	XADCCFG = 0x100/4,
-	XADCSTS,
-	XADCMASK,
-	XADCMSTS,
-	XADCCMD,
-	XADCREAD,
-	XADCMCTL,
-	
-	FPGA0_CLK_CTRL = 0x170/4,
-};
-
-enum {
-	PROG = 1<<30,
-	DONE = 1<<2,
-	INITPE = 1<<1,
-	INIT = 1<<4,
-	DMADONE = 1<<13,
-};
-
-static void
-scram(void)
+static Rendez fpgarend;
+static u32int fpgawaitset, fpgawaitclr;
+static int
+donewaiting(void *)
 {
-	splhi();
-	slcr[0x100/4] |= 1<<4;
-	slcr[0x104/4] |= 1<<4;
-	slcr[0x108/4] |= 1<<4;
-	slcr[DEVCTRL] &= ~PROG;
-	slcr[0x244/4] = 1<<4|1<<5;
-}
-
-static void
-xadcirq(Ureg *, void *)
-{
-	int v;
-	static int al, notfirst;
+	u32int s;
 	
-	while((devc[XADCMSTS] & 1<<8) == 0){
-		v = ((u16int)devc[XADCREAD]) >> 4;
-		if(v == 0){
-			if(notfirst)
-				print("temperature sensor reads 0, shouldn't happen\n");
-			break;
-		}
-		notfirst = 1;
-		temp = v * 5040 / 4096 - 2732;
-		if(temp >= 800){
-			if(al == 0)
-				print("temperature exceeds 80 deg C\n");
-			al = 1;
-		}
-		if(temp <= 750)
-			al = 0;
-		if(temp >= 900){
-			print("chip temperature exceeds 90 deg C, shutting down");
-			scram();
-		}
-	}
-	devc[XADCSTS] = -1;
+	s = fpga[FPGAPINS];
+	return (s & fpgawaitset | ~s & fpgawaitclr) != 0;
+	
 }
-
 static void
-xadctimer(void)
+fpgairq(Ureg *, void *)
 {
-	devc[XADCCMD] = 1<<26 | 0<<16;
+	fpga[FPGAEOI] = -1;
+	if(donewaiting(nil))
+		wakeup(&fpgarend);
 }
-
-static void
-xadcinit(void)
-{
-	int i;
-	int x;
-
-	devc = vmap(DEVC_BASE, 0x11C);
-	devc[XADCMCTL] |= 1<<4;
-	devc[XADCMCTL] &= ~(1<<4);
-	devc[XADCCMD] = 0x08030000;
-	for(i = 0; i < 15; i++)
-		devc[XADCCMD] = 0;
-	while((devc[XADCMSTS] & 1<<10) == 0)
-		;
-	while((devc[XADCMSTS] & 1<<8) == 0){
-		x = devc[XADCREAD];
-		USED(x);
-	}
-	devc[XADCCFG] = 0x80001114;
-	devc[XADCMASK] = ~(1<<8);
-	devc[XADCSTS] = -1;
-	intrenable(XADCIRQ, xadcirq, nil, LEVEL, "xadc");
-	addclock0link(xadctimer, XADCINTERVAL);
-}
-
 static int
-isplinit(void *)
+fpgawait(u32int set, u32int clr, int timeout)
 {
-	return devc[DEVSTS] & INIT;
-}
+	int s;
 
-static int
-ispldone(void *)
-{
-	return devc[DEVISTS] & DONE;
+	fpgawaitset = set;
+	fpgawaitclr = clr;
+	if(donewaiting(nil)) return 0;
+	s = spllo();
+	fpga[FPGAINTEN] = 0;
+	fpga[FPGAEOI] = -1;
+	fpga[FPGAINTPOL] = set;
+	fpga[FPGAINTEN] = set | clr;
+	tsleep(&fpgarend, donewaiting, nil, timeout);
+	fpga[FPGAINTEN] = 0;
+	fpga[FPGAEOI] = -1;
+	splx(s);
+	return donewaiting(nil) ? 0 : -1;
 }
 
-static int
-isdmadone(void *)
-{
-	return dmadone;
-}
-
 static void
-plirq(Ureg *, void *)
+fpgaconf(void)
 {
-	ulong fl;
+	int msel;
+	enum { PORFAST = 1, AES = 2, AESMAYBE = 4, COMP = 8, FPP32 = 16 };
+	static uchar mseltab[16][3] = {
+		[0] {0, 1, PORFAST},
+		[4] {0, 1, 0},
+		[1] {0, 2, PORFAST|AES},
+		[5] {0, 2, AES},
+		[2] {0, 3, COMP|AESMAYBE|PORFAST},
+		[6] {0, 3, COMP|AESMAYBE},
+		[8] {1, 1, PORFAST|FPP32},
+		[12] {1, 1, FPP32},
+		[9] {1, 2, AES|FPP32|PORFAST},
+		[13] {1, 2, AES|FPP32},
+		[10] {1, 4, COMP|AESMAYBE|PORFAST|FPP32},
+		[14] {1, 4, COMP|AESMAYBE|FPP32}
+	};
 	
-	fl = devc[DEVISTS];
-	if((fl & INITPE) != 0)
-		wakeup(&plinitr);
-	if((fl & DONE) != 0){
-		slcr[0x900/4] = 0xf;
-		slcr[0x240/4] = 0;
-		devc[DEVMASK] |= DONE;
-		axi->attr &= ~SG_FAULT;
-		wakeup(&pldoner);
+	if((fpga[FPGAPINS] & FPGA_POWER_ON) == 0)
+		error("FPGA powered off");
+	msel = fpga[FPGASTAT] >> 3 & 0x1f;
+	if(msel >= 16 || mseltab[msel][1] == 0){
+		print("MSEL set to invalid setting %#.2x\n", msel);
+		error("MSEL set to invalid setting");
 	}
-	if((fl & DMADONE) != 0){
-		dmadone++;
-		wakeup(&pldmar);
-	}
-	devc[DEVISTS] = fl;
+	fpga[FPGACTRL] = fpga[FPGACTRL] & ~0x3ff
+		| mseltab[msel][0] << 9 /* cfgwdth */
+		| mseltab[msel][1]-1 << 6 /* cdratio */
+		| NCONFIGPULL | HPSCONFIG;
+	if(fpgawait(0, NSTATUS, Timeout) < 0 || fpgawait(0, CONF_DONE, Timeout) < 0)
+		error("FPGA won't enter reset phase");
+	fpga[FPGACTRL] &= ~NCONFIGPULL;
+	if(fpgawait(NSTATUS, 0, Timeout) < 0)
+		error("FPGA won't enter configuration phase");
+	fpga[FPGACTRL] |= AXICFGEN;
 }
 
 static void
-plinit(void)
+fpgawrite(uchar *a, int n)
 {
-	Physseg seg;
+	int m;
 
-	memset(&seg, 0, sizeof seg);
-	seg.attr = SG_PHYSICAL | SG_DEVICE | SG_NOEXEC | SG_FAULT;
-	seg.name = "axi";
-	seg.pa = 0x40000000;
-	seg.size = 0x8000000;
-	axi = addphysseg(&seg);
-
-	devc[DEVCTRL] &= ~(PROG|1<<25);
-	devc[DEVCTRL] |= 3<<26|PROG;
-	devc[DEVISTS] = -1;
-	devc[DEVMASK] = ~(DONE|INITPE|DMADONE);
-	intrenable(DEVCIRQ, plirq, nil, LEVEL, "pl");
-	
-	slcr[FPGA0_CLK_CTRL] = 1<<20 | 10<<8;
-}
-
-static void
-plconf(void)
-{
-	axi->attr |= SG_FAULT;
-	procflushpseg(axi);
-	flushmmu();
-
-	slcr[0x240/4] = 0xf;
-	slcr[0x900/4] = 0xa;
-	devc[DEVISTS] = DONE|INITPE|DMADONE;
-	devc[DEVCTRL] |= PROG;
-	devc[DEVCTRL] &= ~PROG;
-	devc[DEVMASK] &= ~DONE;
-	devc[DEVCTRL] |= PROG;
-
-	while(waserror())
-		;
-	sleep(&plinitr, isplinit, nil);
-	poperror();
-}
-
-static long
-plwrite(uintptr pa, long n)
-{
-	dmadone = 0;
-	coherence();
-	devc[DMASRC] = pa;
-	devc[DMADST] = -1;
-	devc[DMASRCL] = n>>2;
-	devc[DMADSTL] = 0;
-
-	while(waserror())
-		;
-	sleep(&pldmar, isdmadone, nil);
-	poperror();
-
-	return n;
-}
-
-static long
-plcopy(uchar *d, long n)
-{
-	long ret;
-	ulong nn;
-	uintptr pa;
-	
-	if((n & 3) != 0 || n <= 0)
-		error(Eshort);
-
-	eqlock(&plwlock);
 	if(waserror()){
-		qunlock(&plwlock);
+		fpgaok = 0;
 		nexterror();
 	}
-
-	ret = n;
-	pa = PADDR(plbuf);
-	while(n > 0){
-		if(n > PLBUFSIZ)
-			nn = PLBUFSIZ;
-		else
-			nn = n;
-		memmove(plbuf, d, nn);
-		cleandse(plbuf, plbuf + nn);
-		clean2pa(pa, pa + nn);
-		n -= plwrite(pa, nn);
+	if((fpga[FPGAPINS] & NSTATUS) == 0)
+		error("FPGA reports configuration error");
+	fpgaok = 1;
+	while(fpgabufp + n >= FPGABUFSIZ){
+		m = FPGABUFSIZ - fpgabufp;
+		memmove(&fpgabuf[fpgabufp], a, m);
+		cleandse(fpgabuf, fpgabuf + FPGABUFSIZ);
+		dmacopy((void *) FPGAMGRDATA, fpgabuf, FPGABUFSIZ, SRC_INC);
+		a += m;
+		n -= m;
+		fpgabufp = 0;
 	}
-
-	qunlock(&plwlock);
+	memmove(&fpgabuf[fpgabufp], a, n);
+	fpgabufp += n;
 	poperror();
-
-	return ret;
 }
 
-void
-archinit(void)
+static void
+fpgafinish(void)
 {
-	slcr[2] = 0xDF0D;
-	xadcinit();
-	plinit();
+	if(!fpgaok) return;
+	while((fpgabufp & 3) != 0)
+		fpgabuf[fpgabufp++] = 0;
+	cleandse(fpgabuf, fpgabuf + fpgabufp);
+	dmacopy((void *) FPGAMGRDATA, fpgabuf, fpgabufp, SRC_INC);
+	fpga[FPGACTRL] &= ~AXICFGEN;
+	if(fpgawait(CONF_DONE, NSTATUS, Timeout) < 0){
+		print("FPGA stuck in configuration phase -- truncated file?\n");
+		return;
+	}
+	if((fpga[FPGAPINS] & NSTATUS) == 0){
+		print("FPGA reports configuration error\n");
+		return;
+	}
+	if(fpgawait(INIT_DONE, 0, Timeout) < 0){
+		print("FPGA stuck in initialization phase\n");
+		return;
+	}
+	fpga[FPGACTRL] &= ~HPSCONFIG;
 }
 
 static long
-archread(Chan *c, void *a, long n, vlong offset)
+archread(Chan *c, void *a, long n, vlong)
 {
-	char buf[64];
-
 	switch((ulong)c->qid.path){
 	case Qdir:
 		return devdirread(c, a, n, archdir, narchdir, devgen);
-	case Qtemp:
-		snprint(buf, sizeof(buf), "%d.%d\n", temp/10, temp%10);
-		return readstr(offset, a, n, buf);
-	case Qpl:
-		eqlock(&plrlock);
-		if(waserror()){
-			qunlock(&plrlock);
-			nexterror();
-		}
-		sleep(&pldoner, ispldone, nil);
-		qunlock(&plrlock);
-		poperror();
-		return 0;
-	case Qfbctl:
-		return fbctlread(c, a, n, offset);
 	default:
 		error(Egreg);
 		return -1;
@@ -308,13 +202,12 @@
 }
 
 static long
-archwrite(Chan *c, void *a, long n, vlong offset)
+archwrite(Chan *c, void *a, long n, vlong)
 {
 	switch((ulong)c->qid.path){
-	case Qpl:
-		return plcopy(a, n);
-	case Qfbctl:
-		return fbctlwrite(c, a, n, offset);
+	case Qfpga:
+		fpgawrite(a, n);
+		return n;
 	default:
 		error(Egreg);
 		return -1;
@@ -337,14 +230,16 @@
 archopen(Chan* c, int omode)
 {
 	devopen(c, omode, archdir, narchdir, devgen);
-	if((ulong)c->qid.path == Qpl && (c->mode == OWRITE || c->mode == ORDWR)){
-		if(incref(&plwopen) != 1){
+	if((ulong)c->qid.path == Qfpga && (c->mode == OWRITE || c->mode == ORDWR)){
+		if(incref(&fpgawopen) != 1){
 			c->flag &= ~COPEN;
-			decref(&plwopen);
+			decref(&fpgawopen);
 			error(Einuse);
 		}
-		plbuf = smalloc(PLBUFSIZ);
-		plconf();
+		fpgaok = 0;
+		fpgaconf();
+		fpgabuf = xspanalloc(FPGABUFSIZ, 64, 0);
+		fpgabufp = 0;
 	}
 	return c;
 }
@@ -353,13 +248,23 @@
 archclose(Chan* c)
 {
 	if((c->flag & COPEN) != 0)
-	if((ulong)c->qid.path == Qpl && (c->mode == OWRITE || c->mode == ORDWR)){
-		free(plbuf);
-		plbuf = nil;
-		decref(&plwopen);
+	if((ulong)c->qid.path == Qfpga && (c->mode == OWRITE || c->mode == ORDWR)){
+		fpgafinish();
+		//xfree(fpgabuf);
+		fpgabuf = nil;
+		decref(&fpgawopen);
 	}
 }
 
+static void
+archreset(void)
+{
+	fpga[FPGAINTEN] = 0;
+	fpga[FPGAEOI] = -1;
+	fpga[FPGAINTTYPE] = -1;
+	intrenable(FPGAMGRIRQ, fpgairq, nil, LEVEL, "fpgamgr");
+}
+
 static Chan*
 archattach(char* spec)
 {
@@ -370,7 +275,7 @@
 	'P',
 	"arch",
 	
-	devreset,
+	archreset,
 	devinit,
 	devshutdown,
 	archattach,
--- /dev/null
+++ b/sys/src/9/cycv/dma.c
@@ -1,0 +1,265 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#define dmar ((ulong*)DMAS_BASE)
+
+enum {
+	DSR = 0x000,
+	DPC = 0x004/4,
+	INTEN = 0x20 / 4,
+	INT_EVENT_RIS = 0x024 / 4,
+	INTMIS = 0x028 / 4,
+	INTCLR = 0x02C / 4,
+	FSRD = 0x030 / 4,
+	FSRC = 0x034 / 4,
+	FTRD = 0x038 / 4,
+	DBGSTATUS = 0xD00 / 4,
+	DBGCMD = 0xD04 / 4,
+	DBGINST0 = 0xD08 / 4,
+	DBGINST1 = 0xD0C / 4,
+	CR0 = 0xE00 / 4,
+	CR1, CR2, CR3, CR4, CRD,
+	WD = 0xE80 / 4,
+};
+enum {
+	DMAStopped,
+	DMAExecuting,
+	DMACacheMiss,
+	DMAUpdatingPC,
+	DMAWaitingForEvent,
+	DMAAtBarrier,
+	DMAWaitingForPeripheral = 7,
+	DMAKilling,
+	DMACompleting,
+	DMAFaultingCompleting=14,
+	DMAFaulting,
+};
+#define FTR(n) dmar[(0x40/4 + (n))]
+#define CSR(n) dmar[(0x100/4 + (n)*2)]
+#define CPC(n) dmar[(0x104/4 + (n)*2)]
+#define SAR(n) dmar[(0x400/4 + (n)*8)]
+#define DAR(n) dmar[(0x404/4 + (n)*8)]
+#define CCR(n) dmar[(0x408/4 + (n)*8)]
+#define LC0(n) dmar[(0x40C/4 + (n)*8)]
+#define LC1(n) dmar[(0x410/4 + (n)*8)]
+
+#define DST_BURST(n) (((n)-1&0xf)<<18)
+#define DST_BEAT_1 (0)
+#define DST_BEAT_2 (1<<15)
+#define DST_BEAT_4 (2<<15)
+#define DST_BEAT_8 (3<<15)
+#define DST_BEAT_16 (4<<15)
+#define SRC_BURST(n) (((n)-1&0xf)<<4)
+#define SRC_BEAT_1 (0)
+#define SRC_BEAT_2 (1<<1)
+#define SRC_BEAT_4 (2<<1)
+#define SRC_BEAT_8 (3<<1)
+#define SRC_BEAT_16 (4<<1)
+
+#define dmaMOV_SARn 0x00bc
+#define dmaMOV_CCRn 0x01bc
+#define dmaMOV_DARn 0x02bc
+#define dmaLP0(n) (((n)-1&0xff)<<8|0x20)
+#define dmaLP1(n) (((n)-1&0xff)<<8|0x22)
+#define dmaLPEND0(n) (((n)&0xff)<<8|0x38)
+#define dmaLPEND1(n) (((n)&0xff)<<8|0x3c)
+#define dmaLD 0x04
+#define dmaST 0x08
+#define dmaSEV(n) (((n)&31)<<11|0x34)
+#define dmaEND 0x00
+#define dmaWMB 0x13
+
+static QLock dmalock;
+static Rendez dmarend;
+static int finished;
+static ulong code[64];
+
+static int
+isfinished(void *)
+{
+	return finished;
+}
+static void
+dmairq(Ureg *, void *)
+{
+	dmar[INTCLR] = -1;
+	finished = 1;
+	wakeup(&dmarend);
+}
+
+static void
+compactify(ulong *lp)
+{
+	uchar *p, *q;
+	
+	q = p = (uchar *) lp;
+	for(;;){
+		switch(*p){
+		case 0xbc:
+			q[0] = p[0];
+			q[1] = p[1];
+			q[2] = p[4];
+			q[3] = p[5];
+			q[4] = p[6];
+			q[5] = p[7];
+			q += 6; p += 8;
+			break;
+		case 0x20: case 0x22: case 0x38: case 0x3c: case 0x34:
+			q[0] = p[0];
+			q[1] = p[1];
+			q += 2; p += 4;
+			break;
+		case 0x04: case 0x08: case 0x13:
+			q[0] = p[0];
+			q++; p += 4;
+			break;
+		case 0x00:
+			q[0] = 0;
+			return;
+		default:
+			panic("DMA: unknown opcode %.2x", *p);
+		}
+	}
+}
+
+#define BURST(n) *p++ = dmaMOV_CCRn, *p++ = DST_BEAT_4 | SRC_BEAT_4 | SRC_BURST(n) | DST_BURST(n) | attr
+
+void
+dmacopy(void *dst, void *src, ulong n, int attr)
+{
+	ulong *p;
+	
+	assert((n & 3) == 0 && ((uintptr)src & 3) == 0 && ((uintptr)dst & 3) == 0);
+	while(n > (1<<22)){
+		dmacopy(dst, src, 1<<22, attr);
+		if((attr & SRC_INC) != 0) src = (uchar*)src + (1<<22);
+		if((attr & DST_INC) != 0) dst = (uchar*)dst + (1<<22);
+	}
+	if(n == 0) return;
+	qlock(&dmalock);
+	p = code;
+	*p++ = dmaMOV_SARn; *p++ = PADDR(src);
+	*p++ = dmaMOV_DARn; *p++ = PADDR(dst);
+	if((n >> 6) >= 1){
+		BURST(16);
+		if((n>>14) >= 1){
+			if((n>>14) > 1) *p++ = dmaLP0(n >> 14);
+			*p++ = dmaLP1(256);
+			*p++ = dmaLD;
+			*p++ = dmaST;
+			*p++ = dmaLPEND1(2);
+			if((n>>14) > 1) *p++ = dmaLPEND0(6);
+			n &= (1<<14)-1;
+		}
+		if((n >> 6) >= 1){
+			if((n>>6) > 1) *p++ = dmaLP0(n >> 6);
+			*p++ = dmaLD;
+			*p++ = dmaST;
+			if((n>>6) > 1) *p++ = dmaLPEND0(2);
+			n &= 63;
+		}
+	}
+	if(n >= 4){
+		BURST(n>>2);
+		*p++ = dmaLD;
+		*p++ = dmaST;
+	}
+	*p++ = dmaWMB;
+	*p++ = dmaSEV(0);
+	*p = dmaEND;
+	compactify(code);
+	if((CSR(0) & 0xf) != DMAStopped){
+		while((dmar[DBGSTATUS] & 1) != 0)
+			tsleep(&up->sleep, return0, nil, 1);
+		dmar[DBGINST0] = 0x1 << 16 | 1;
+		dmar[DBGCMD] = 0;
+		while((dmar[DBGSTATUS] & 1) != 0)
+			tsleep(&up->sleep, return0, nil, 1);
+		while((CSR(0) & 0xf) != DMAStopped)
+			tsleep(&up->sleep, return0, nil, 1);
+	}
+	cleandse(code, code + nelem(code));
+	while((dmar[DBGSTATUS] & 1) != 0)
+		tsleep(&up->sleep, return0, nil, 1);
+	dmar[DBGINST0] = 0xa0 << 16;
+	dmar[DBGINST1] = PADDR(code);
+	finished = 0;
+	dmar[DBGCMD] = 0;
+	while(!finished)
+		sleep(&dmarend, isfinished, nil);
+	qunlock(&dmalock);
+}
+
+
+static void
+dmaabort(Ureg *, void *)
+{
+	int i;
+
+	if((dmar[FSRD] & 1) != 0){
+		iprint("dma: manager fault: ");
+		if((dmar[FTRD] & 1<<30) != 0)
+			iprint("debug instruction, ");
+		if((dmar[FTRD] & 1<<16) != 0)
+			iprint("instruction fetch error, ");
+		if((dmar[FTRD] & 1<<5) != 0)
+			iprint("event security violation, ");
+		if((dmar[FTRD] & 1<<4) != 0)
+			iprint("DMAGO security violation, ");
+		if((dmar[FTRD] & 1<<1) != 0)
+			iprint("operand invalid, ");
+		if((dmar[FTRD] & 1<<0) != 0)
+			iprint("undefined instruction, ");
+		iprint("\n");
+	}
+	for(i = 0; i < 8; i++){
+		if((dmar[FSRC] & 1<<i) == 0)
+			continue;
+		iprint("dma: channel %d fault\n", i);
+		iprint("code = %.8p,   PC   = %.8ulx\n", code, CPC(i));
+		iprint("CCRn = %.8ulx, CSRn = %.8ulx\n", CCR(i), CSR(i));
+		iprint("LC0  = %.2ulx, LC1  = %.2ulx\n", LC0(i), LC1(i));
+		if((FTR(i) & 1<<31) != 0)
+			iprint("insufficient resources, ");
+		if((FTR(i) & 1<<30) != 0)
+			iprint("debug instruction, ");
+		if((FTR(i) & 1<<18) != 0)
+			iprint("read error, ");
+		if((FTR(i) & 1<<17) != 0)
+			iprint("write error, ");
+		if((FTR(i) & 1<<16) != 0)
+			iprint("instruction fetch error, ");
+		if((FTR(i) & 1<<13) != 0)
+			iprint("FIFO underflow, ");
+		if((FTR(i) & 1<<12) != 0)
+			iprint("FIFO error, ");
+		if((FTR(i) & 1<<7) != 0)
+			iprint("CCRn security violation, ");
+		if((FTR(i) & 1<<6) != 0)
+			iprint("peripheral security violation, ");
+		if((FTR(i) & 1<<5) != 0)
+			iprint("event security violation, ");
+		if((FTR(i) & 1<<4) != 0)
+			iprint("DMAGO security violation, ");
+		if((FTR(i) & 1<<1) != 0)
+			iprint("operand invalid, ");
+		if((FTR(i) & 1<<0) != 0)
+			iprint("undefined instruction, ");
+		iprint("\n");
+	}
+	panic("DMA fault");
+}
+
+void
+dmalink(void)
+{
+	dmar[INTEN] = 1;
+	intrenable(DMAIRQ0, dmairq, nil, LEVEL, "dma");
+	intrenable(DMAABORTIRQ, dmaabort, nil, LEVEL, "dma_abort");
+}
--- a/sys/src/9/cycv/ethercycv.c
+++ b/sys/src/9/cycv/ethercycv.c
@@ -199,8 +199,6 @@
 		r = &c->rxr[4 * c->rxconsi];
 		if((r[0] >> 31) != 0)
 			break;
-		if((r[0] & 1<<15) != 0)
-			iprint("eth: error frame\n");
 		if((r[0] & (3<<8)) != (3<<8))
 			iprint("eth: lilu dallas multidescriptor\n");
 		bp = c->rxs[c->rxconsi];
@@ -404,6 +402,38 @@
 	}
 }
 
+static long
+ethifstat(Ether *edev, void *a, long n, ulong offset)
+{
+	static char *names[] = {
+		"txoctetcount_gb", "txframecount_gb", "txbroadcastframes_g", "txmulticastframes_g",
+		"tx64octets_gb", "tx65to127octets_gb", "tx128to255octets_gb", "tx256to511octets_gb",
+		"tx512to1023octets_gb", "tx1024tomaxoctets_gb", "txunicastframes_gb", "txmulticastframes_gb",
+		"txbroadcastframes_gb", "txunderflowerror", "txsinglecol_g", "txmulticol_g",
+		"txdeferred", "txlatecol", "txexesscol", "txcarriererr",
+		"txoctetcnt", "txframecount_g", "txexcessdef", "txpauseframes",
+		"txvlanframes_g", "txoversize_g", "rxframecount_gb", "rxoctetcount_gb",
+		"rxoctetcount_g", "rxbroadcastframes_g", "rxmulticastframes_g", "rxcrcerror",
+		"rxalignmenterror", "rxrunterror", "rxjabbererror", "rxundersize_g",
+		"rxoversize_g", "rx64octets_gb", "rx65to127octets_gb", "rx128to255octets_gb",
+		"rx256to511octets_gb", "rx512to1023octets_gb", "rx1024tomaxoctets_gb", "rxunicastframes_g",
+		"rxlengtherror", "rxoutofrangetype", "rxpauseframes", "rxfifooverflow",
+		"rxvlanframes_gb", "rxwatchdogerror", "rxrcverror", "rxctrlframes_g",
+	};
+	int i;
+	char *buf, *p, *e;
+	Ctlr *c;
+	
+	p = buf = smalloc(READSTR);
+	e = p + READSTR;
+	c = edev->ctlr;
+	for(i = 0; i < nelem(names); i++)
+		p = seprint(p, e, "%s: %lud\n", names[i], c->r[0x114/4 + i]);
+	n = readstr(offset, a, n, buf);
+	free(buf);
+	return n;
+}
+
 static int
 etherpnp(Ether *edev)
 {
@@ -425,6 +455,7 @@
 	edev->mbps = 1000;
 	edev->promiscuous = ethprom;
 	edev->multicast = ethmcast;
+	edev->ifstat = ethifstat;
 
 	if(ethinit(edev) < 0){
 		edev->ctlr = nil;
--- a/sys/src/9/cycv/fns.h
+++ b/sys/src/9/cycv/fns.h
@@ -76,3 +76,4 @@
 void screeninit(void);
 int isaconfig(char*, int, ISAConf*);
 void cputhex(u32int);
+void dmacopy(void *, void *, ulong, int);
--- a/sys/src/9/cycv/io.h
+++ b/sys/src/9/cycv/io.h
@@ -5,7 +5,10 @@
 #define EMAC1_BASE 0xFF702000
 #define RESETMGR_BASE 0xFFD05000
 #define SYSMGR_BASE 0xFFD08000
+#define FPGAMGR_BASE 0xFF706000
+#define FPGAMGRDATA 0xFFB90000
 #define OCRAM 0xFFFF0000
+#define DMAS_BASE 0xFFE01000
 
 /*RESETMGR*/
 #define PERMODRST (0x14/4)
@@ -17,6 +20,9 @@
 #define TIMERIRQ 29
 #define UART0IRQ 194
 #define EMAC1IRQ 152
+#define FPGAMGRIRQ 207
+#define DMAIRQ0 136
+#define DMAABORTIRQ 144
 
 #define LEVEL 0
 #define EDGE 1
--- a/sys/src/9/cycv/l.s
+++ b/sys/src/9/cycv/l.s
@@ -37,7 +37,7 @@
 _clrstart:
 	MOVW.P R0, 4(R1)
 	CMP.S R1, R2
-	BGE _clrstart
+	BGT _clrstart
 
 	/* clean BSS */
 	MOVW $edata-KZERO(SB), R1
@@ -45,7 +45,7 @@
 _clrbss:
 	MOVW.P R0, 4(R1)
 	CMP.S R1, R2
-	BGE _clrbss
+	BGT _clrbss
 	
 	PUTC('a')