1
0
mirror of synced 2026-01-19 01:07:31 +00:00

Merge branch 'main' of github.com:rdolbeau/SBusFPGA into main

This commit is contained in:
Romain Dolbeau 2021-10-09 08:27:34 +02:00
commit 0b3cd1bfb5
34 changed files with 13220 additions and 173 deletions

View File

@ -0,0 +1,4 @@
sbusfpga256|SBusFPGA with 256 MiB SDRAM: \
:ns#2:nt#4:nc#65536:se#512: \
:oa#0:pa#524288:ta=4.2BSD: \
:oc#0:pc#524288:

View File

@ -0,0 +1,13 @@
#!/bin/sh
#
# $NetBSD$
#
# PROVIDE: SBUSFPGA_SDRAM
if test -b /dev/sbusfpga_sdram0; then
for DEVICE in /dev/sbusfpga_sdram[0-9]; do
test -b ${DEVICE} && disklabel -w ${DEVICE} sbusfpga256 || return
test -b ${DEVICE}a && newfs ${DEVICE}a || return
done
fi

View File

@ -0,0 +1,197 @@
/* $NetBSD$ */
/*
* Copyright (c) 1998, 2021 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Lennart Augustsson (lennart@augustsson.net) at
* Carlstedt Research & Technology.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/bus.h>
#include <dev/sbus/sbusvar.h>
#include <machine/autoconf.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_mem.h>
#include <dev/usb/ohcireg.h>
#include <dev/usb/ohcivar.h>
struct ohci_sbus_softc {
ohci_softc_t sc;
void *sc_ih;
int sc_node;
int sc_burst;
};
static int
ohci_sbus_match(device_t parent, cfdata_t match, void *aux)
{
struct sbus_attach_args *sa = (struct sbus_attach_args *)aux;
/* generic-ohci is the default name, from device-tree */
if (strcmp("generic-ohci", sa->sa_name) == 0)
return 1;
/* usb is the OFW name, qualified by device-type */
const char* type = prom_getpropstring(sa->sa_node, "device-type");
if (type != NULL && (strcmp("ohci", type) == 0))
return 1;
return 0;
}
static void
ohci_sbus_attach(device_t parent, device_t self, void *aux)
{
struct ohci_sbus_softc *sc = device_private(self);
struct sbus_attach_args *sa = (struct sbus_attach_args *)aux;
struct sbus_softc *sbsc = device_private(parent);
int sbusburst;
sc->sc.sc_dev = self;
sc->sc.sc_bus.ub_hcpriv = sc;
sc->sc.iot = sa->sa_bustag;
sc->sc.sc_size = sa->sa_size;
/* **** SBus specific */
sc->sc_node = sa->sa_node;
/*
* Get transfer burst size from PROM
*/
sbusburst = sbsc->sc_burst;
if (sbusburst == 0)
sbusburst = SBUS_BURST_32 - 1; /* 1->16 */
sc->sc_burst = prom_getpropint(sc->sc_node, "burst-sizes", -1);
if (sc->sc_burst == -1)
/* take SBus burst sizes */
sc->sc_burst = sbusburst;
/* Clamp at parent's burst sizes */
sc->sc_burst &= sbusburst;
if (0) { /* in PCI there's a test for some specific controller */
sc->sc.sc_flags = OHCIF_SUPERIO;
}
/* check if memory space access is enabled */
/* CHECKME: not needed ? */
/* Map I/O registers */
if (sbus_bus_map(sc->sc.iot, sa->sa_slot, sa->sa_offset, sc->sc.sc_size,
BUS_SPACE_MAP_LINEAR, &sc->sc.ioh) != 0) {
aprint_error_dev(self, ": cannot map registers\n");
return;
}
aprint_normal_dev(self, "nid 0x%x, bustag %p (0x%zx @ 0x%08lx), burst 0x%x (parent 0x%0x)\n",
sc->sc_node,
sc->sc.iot,
(size_t)sc->sc.sc_size,
sc->sc.ioh,
sc->sc_burst,
sbsc->sc_burst);
/* we're SPECIAL!!! */
/* sc->sc.sc_endian = OHCI_BIG_ENDIAN; */
/* Disable interrupts, so we don't get any spurious ones. */
bus_space_write_4(sc->sc.iot, sc->sc.ioh, OHCI_INTERRUPT_DISABLE,
OHCI_ALL_INTRS);
sc->sc.sc_bus.ub_dmatag = sa->sa_dmatag;
/* sc->sc.sc_bus.ub_dmatag = (void*)((char*)sc->sc.ioh + 0x10000); */
/* Enable the device. */
/* CHECKME: not needed ? */
/* Map and establish the interrupt. */
if (sa->sa_nintr != 0) {
sc->sc_ih = bus_intr_establish(sc->sc.iot, sa->sa_pri,
IPL_NET, ohci_intr, sc); // checkme: interrupt priority
if (sc->sc_ih == NULL) {
aprint_error_dev(self, "couldn't establish interrupt (%d)\n", sa->sa_nintr);
} else
aprint_normal_dev(self, "interrupting at %d / %d / %d\n", sa->sa_nintr, sa->sa_pri, IPL_NET);
} else {
aprint_error_dev(self, "no interrupt defined in PROM\n");
goto fail;
}
int err = ohci_init(&sc->sc);
if (err) {
aprint_error_dev(self, "init failed, error=%d\n", err);
goto fail;
}
if (!pmf_device_register1(self, ohci_suspend, ohci_resume,
ohci_shutdown))
aprint_error_dev(self, "couldn't establish power handler\n");
/* Attach usb device. */
sc->sc.sc_child = config_found(self, &sc->sc.sc_bus, usbctlprint);
return;
fail:
/* should we unmap ? */
return;
}
static int
ohci_sbus_detach(device_t self, int flags)
{
struct ohci_sbus_softc *sc = device_private(self);
int rv;
rv = ohci_detach(&sc->sc, flags);
if (rv)
return rv;
pmf_device_deregister(self);
ohci_shutdown(self, flags);
/* Disable interrupts, so we don't get any spurious ones. */
bus_space_write_4(sc->sc.iot, sc->sc.ioh,
OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
/* can we disestablish the interrupt ? */
/* can we unmap the registers ? */
return 0;
}
CFATTACH_DECL3_NEW(ohci_sbus, sizeof(struct ohci_sbus_softc),
ohci_sbus_match, ohci_sbus_attach, ohci_sbus_detach, ohci_activate, NULL,
ohci_childdet, DVF_DETACH_SHUTDOWN);

View File

@ -112,7 +112,7 @@ extern struct cfdriver rdfpga_sdcard_cd;
static int rdfpga_sdcard_wait_dma_ready(struct rdfpga_sdcard_softc *sc, const int count);
static int rdfpga_sdcard_wait_device_ready(struct rdfpga_sdcard_softc *sc, const int count);
static int rdfpga_sdcard_read_block(struct rdfpga_sdcard_softc *sc, const u_int32_t block, void *data);
static int rdfpga_sdcard_read_block(struct rdfpga_sdcard_softc *sc, const u_int32_t block, const u_int32_t blkcnt, void *data);
static int rdfpga_sdcard_write_block(struct rdfpga_sdcard_softc *sc, const u_int32_t block, void *data);
struct rdfpga_sdcard_rb_32to512 {
@ -179,7 +179,7 @@ rdfpga_sdcard_ioctl (dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
case RDFPGA_SDCARD_RB:
{
struct rdfpga_sdcard_rb_32to512* u = data;
err = rdfpga_sdcard_read_block(sc, u->block, u->data);
err = rdfpga_sdcard_read_block(sc, u->block, 1, u->data);
break;
}
case RDFPGA_SDCARD_WB:
@ -188,49 +188,6 @@ rdfpga_sdcard_ioctl (dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
err = rdfpga_sdcard_write_block(sc, u->block, u->data);
break;
}
#if 0
case DIOCGDINFO:
*(struct disklabel *)data = *(sc->dk.sc_dkdev.dk_label);
break;
case DIOCGDEFLABEL:
{
struct disklabel *lp = sc->dk.sc_dkdev.dk_label;
struct cpu_disklabel *clp = sc->dk.sc_dkdev.dk_cpulabel;
memset(lp, 0, sizeof(struct disklabel));
memset(clp, 0, sizeof(struct cpu_disklabel));
if (readdisklabel(dev, rdfpga_sdcard_strategy, lp, clp) != NULL) {
int i;
aprint_normal_dev(sc->dk.sc_dev, "read disk label OK\n");
strncpy(lp->d_packname, "default label", sizeof(lp->d_packname));
/*
* Reset the partition info; it might have gotten
* trashed in readdisklabel().
*
* XXX Why do we have to do this? readdisklabel()
* should be safe...
*/
for (i = 0; i < MAXPARTITIONS; ++i) {
lp->d_partitions[i].p_offset = 0;
if (i == RAW_PART) {
lp->d_partitions[i].p_size =
lp->d_secpercyl * lp->d_ncylinders;
lp->d_partitions[i].p_fstype = FS_BSDFFS;
} else {
lp->d_partitions[i].p_size = 0;
lp->d_partitions[i].p_fstype = FS_UNUSED;
}
}
lp->d_npartitions = RAW_PART + 1;
memcpy(data, lp, sizeof(struct disklabel));
} else {
aprint_normal_dev(sc->dk.sc_dev, "read disk label FAILED\n");
}
}
break;
#endif
/* case VNDIOCCLR: */
/* case VNDIOCCLR50: */
@ -505,9 +462,10 @@ static int rdfpga_sdcard_wait_device_ready(struct rdfpga_sdcard_softc *sc, const
return rdfpga_sdcard_wait_dma_ready(sc, count);
}
static int rdfpga_sdcard_read_block(struct rdfpga_sdcard_softc *sc, const u_int32_t block, void *data) {
static int rdfpga_sdcard_read_block(struct rdfpga_sdcard_softc *sc, const u_int32_t block, const u_int32_t blkcnt, void *data) {
int res = 0;
u_int32_t ctrl;
u_int32_t ctrl = 0;
u_int32_t idx = 0;
/* aprint_normal_dev(sc->dk.sc_dev, "Reading block %u from sdcard\n", block); */
if ((res = rdfpga_sdcard_wait_device_ready(sc, 50000)) != 0)
@ -524,9 +482,6 @@ static int rdfpga_sdcard_read_block(struct rdfpga_sdcard_softc *sc, const u_int3
bus_dmamem_free(sc->sc_dmatag, &sc->sc_segs, 1);
return ENXIO;
}
/* for testing only, remove */
//memcpy(kvap, data, 512);
if (bus_dmamap_load(sc->sc_dmatag, sc->sc_dmamap, kvap, RDFPGA_SDCARD_VAL_DMA_MAX_SZ, /* kernel space */ NULL,
BUS_DMA_NOWAIT | BUS_DMA_STREAMING | BUS_DMA_WRITE)) {
@ -536,24 +491,28 @@ static int rdfpga_sdcard_read_block(struct rdfpga_sdcard_softc *sc, const u_int3
return ENXIO;
}
bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, 0, 512, BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, 0, blkcnt * 512, BUS_DMASYNC_PREWRITE);
/* set DMA address */
bus_space_write_4(sc->sc_bustag, sc->sc_bhregs, RDFPGA_SDCARD_REG_DMAW_ADDR, (uint32_t)(sc->sc_dmamap->dm_segs[0].ds_addr));
/* set block to read */
bus_space_write_4(sc->sc_bustag, sc->sc_bhregs, RDFPGA_SDCARD_REG_ADDR, block);
ctrl = RDFPGA_SDCARD_CTRL_START | RDFPGA_SDCARD_CTRL_READ;
/* initiate reading block from SDcard; once the read request is acknowledged, the HW will start the DMA engine */
bus_space_write_4(sc->sc_bustag, sc->sc_bhregs, RDFPGA_SDCARD_REG_CTRL, ctrl);
for (idx = 0 ; idx < blkcnt && !res; idx++) {
bus_addr_t addr = sc->sc_dmamap->dm_segs[0].ds_addr + 512 * idx;
/* set DMA address */
bus_space_write_4(sc->sc_bustag, sc->sc_bhregs, RDFPGA_SDCARD_REG_DMAW_ADDR, (uint32_t)(addr));
/* set block to read */
bus_space_write_4(sc->sc_bustag, sc->sc_bhregs, RDFPGA_SDCARD_REG_ADDR, (block + idx));
ctrl = RDFPGA_SDCARD_CTRL_START | RDFPGA_SDCARD_CTRL_READ;
/* initiate reading block from SDcard; once the read request is acknowledged, the HW will start the DMA engine */
bus_space_write_4(sc->sc_bustag, sc->sc_bhregs, RDFPGA_SDCARD_REG_CTRL, ctrl);
res = rdfpga_sdcard_wait_device_ready(sc, 100000);
}
res = rdfpga_sdcard_wait_device_ready(sc, 100000);
bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, 0, 512, BUS_DMASYNC_POSTWRITE);
bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, 0, blkcnt * 512, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap);
/* aprint_normal_dev(sc->dk.sc_dev, "dma: unloaded\n"); */
memcpy(data, kvap, 512);
memcpy(data, kvap, blkcnt * 512);
bus_dmamem_unmap(sc->sc_dmatag, kvap, RDFPGA_SDCARD_VAL_DMA_MAX_SZ);
/* aprint_normal_dev(sc->dk.sc_dev, "dma: unmapped\n"); */
@ -625,100 +584,9 @@ static int rdfpga_sdcard_write_block(struct rdfpga_sdcard_softc *sc, const u_int
void
rdfpga_sdcard_strategy(struct buf *bp)
{
#if 0
struct rdfpga_sdcard_softc *sc = device_lookup_private(&rdfpga_sdcard_cd, DISKUNIT(bp->b_dev));
int err = 0;
if (sc == NULL) {
aprint_error("%s:%d: sc == NULL! giving up\n", __PRETTY_FUNCTION__, __LINE__);
bp->b_resid = bp->b_bcount;
bp->b_error = EINVAL;
goto done;
}
/* aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_bflags = 0x%08x\n", __PRETTY_FUNCTION__, __LINE__, bp->b_flags); */
/* aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_bufsize = %d\n", __PRETTY_FUNCTION__, __LINE__, bp->b_bufsize); */
/* aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_blkno = %lld\n", __PRETTY_FUNCTION__, __LINE__, bp->b_blkno); */
/* aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_rawblkno = %lld\n", __PRETTY_FUNCTION__, __LINE__, bp->b_rawblkno); */
/* aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_bcount = %d\n", __PRETTY_FUNCTION__, __LINE__, bp->b_bcount); */
bp->b_resid = bp->b_bcount;
if (bp->b_bcount == 0) {
goto done;
}
if (bp->b_flags & B_READ) {
unsigned char* data = bp->b_data;
daddr_t blk = bp->b_blkno;
struct partition *p = NULL;
if (DISKPART(bp->b_dev) != RAW_PART) {
if ((err = bounds_check_with_label(&sc->dk.sc_dkdev, bp, 0)) <= 0) {
aprint_error("%s:%d: bounds_check_with_label -> %d\n", __PRETTY_FUNCTION__, __LINE__, err);
bp->b_resid = bp->b_bcount;
goto done;
}
p = &sc->dk.sc_dkdev.dk_label->d_partitions[DISKPART(bp->b_dev)];
blk = bp->b_blkno + p->p_offset;
}
while (bp->b_resid >= 512 && !bp->b_error) {
if (blk < 62521344) {
aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_blkno = %lld, computed %lld (part %d)\n", __PRETTY_FUNCTION__, __LINE__, bp->b_blkno, blk, DISKPART(bp->b_dev));
aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_rawblkno = %lld\n", __PRETTY_FUNCTION__, __LINE__, bp->b_rawblkno);
bp->b_error = rdfpga_sdcard_read_block(sc, blk, data);
} else {
aprint_error("%s:%d: blk = %lld read out of range! giving up\n", __PRETTY_FUNCTION__, __LINE__, blk);
bp->b_error = EINVAL;
}
blk ++;
data += 512;
bp->b_resid -= 512;
}
} else {
#if 1
bp->b_error = EINVAL;
aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_bflags = 0x%08x\n", __PRETTY_FUNCTION__, __LINE__, bp->b_flags);
aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_bufsize = %d\n", __PRETTY_FUNCTION__, __LINE__, bp->b_bufsize);
aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_blkno = %lld\n", __PRETTY_FUNCTION__, __LINE__, bp->b_blkno);
aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_rawblkno = %lld\n", __PRETTY_FUNCTION__, __LINE__, bp->b_rawblkno);
aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_bcount = %d\n", __PRETTY_FUNCTION__, __LINE__, bp->b_bcount);
#else
unsigned char* data = bp->b_data;
daddr_t blk = bp->b_blkno;
if (DISKPART(bp->b_dev) != RAW_PART) {
if (bounds_check_with_label(&sc->dk.sc_dkdev, bp, 0) <= 0) {
bp->b_resid = bp->b_bcount;
goto done;
}
p = &sc->dk.sc_dkdev.dk_label->d_partitions[DISKPART(bp->b_dev)];
blk = bp->b_blkno + p->p_offset;
}
while (bp->b_resid >= 512 && !bp->b_error) {
if (blk < 62521344) {
bp->b_error = rdfpga_sdcard_write_block(sc, blk, data);
} else {
aprint_error("%s:%d: blk = %lld write out of range! giving up\n", __PRETTY_FUNCTION__, __LINE__, blk);
bp->b_error = EINVAL;
}
blk ++;
data += 512;
bp->b_resid -= 512;
}
#endif
}
/* aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_resid = %d\n", __PRETTY_FUNCTION__, __LINE__, bp->b_resid); */
/* aprint_normal_dev(sc->dk.sc_dev, "%s:%d: bp->b_error = %d\n", __PRETTY_FUNCTION__, __LINE__, bp->b_error); */
done:
biodone(bp);
#else
struct rdfpga_sdcard_softc *sc = device_lookup_private(&rdfpga_sdcard_cd, DISKUNIT(bp->b_dev));
dk_strategy(&sc->dk, bp);
#endif
}
static void rdfpga_sdcard_set_geometry(struct rdfpga_sdcard_softc *sc) {
@ -749,8 +617,8 @@ rdfpga_sdcard_size(dev_t dev) {
static void
rdfpga_sdcard_minphys(struct buf *bp)
{
if (bp->b_bcount > 16)
bp->b_bcount = 16;
if (bp->b_bcount > RDFPGA_SDCARD_VAL_DMA_MAX_SZ)
bp->b_bcount = RDFPGA_SDCARD_VAL_DMA_MAX_SZ;
}
static int
@ -792,15 +660,20 @@ rdfpga_sdcard_diskstart(device_t self, struct buf *bp)
/* } */
while (bp->b_resid >= 512 && !err) {
if (blk < 62521344) {
err = rdfpga_sdcard_read_block(sc, blk, data);
u_int32_t blkcnt = bp->b_resid / 512;
if (blkcnt > (RDFPGA_SDCARD_VAL_DMA_MAX_SZ/512))
blkcnt = (RDFPGA_SDCARD_VAL_DMA_MAX_SZ/512);
if (blk+blkcnt <= 62521344) {
err = rdfpga_sdcard_read_block(sc, blk, blkcnt, data);
} else {
aprint_error("%s:%d: blk = %lld read out of range! giving up\n", __PRETTY_FUNCTION__, __LINE__, blk);
err = EINVAL;
}
blk ++;
data += 512;
bp->b_resid -= 512;
blk += blkcnt;
data += 512 * blkcnt;
bp->b_resid -= 512 * blkcnt;
}
} else {
#if 1

View File

@ -66,7 +66,7 @@ struct rdfpga_sdcard_softc {
#define RDFPGA_SDCARD_CTRL_START 0x80000000
#define RDFPGA_SDCARD_CTRL_READ 0x40000000
/* one page, though we're likely to only use 512 bytes (one block) ATM */
#define RDFPGA_SDCARD_VAL_DMA_MAX_SZ (4096)
/* 16 pages, though we're likely to only use 512 bytes (one block) ATM */
#define RDFPGA_SDCARD_VAL_DMA_MAX_SZ (65536)
#endif /* _RDFPGA_SDCARD_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
/* $NetBSD$ */
/*-
* Copyright (c) 2020 Romain Dolbeau <romain@dolbeau.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SBUSFPGA_CURVE25519ENGINE_H_
#define _SBUSFPGA_CURVE25519ENGINE_H_
#define MAX_SESSION 32 // HW limit
#define MAX_ACTIVE_SESSION 8 // SW-imposed limit
// Single 4KiB pages per session
#define SBUSFPGA_CURVE25519ENGINE_VAL_DMA_MAX_SZ (MAX_ACTIVE_SESSION*4*1024)
struct sbusfpga_curve25519engine_softc {
device_t sc_dev; /* us as a device */
u_int sc_rev; /* revision */
int sc_node; /* PROM node ID */
int sc_burst; /* DVMA burst size in effect */
bus_space_tag_t sc_bustag; /* bus tag */
bus_space_handle_t sc_bhregs_curve25519engine; /* bus handle */
bus_space_handle_t sc_bhregs_microcode; /* bus handle */
bus_space_handle_t sc_bhregs_regfile; /* bus handle */
//void * sc_buffer; /* VA of the registers */
int sc_bufsiz_curve25519engine; /* Size of buffer */
int sc_bufsiz_microcode; /* Size of buffer */
int sc_bufsiz_regfile; /* Size of buffer */
int initialized;
uint32_t active_sessions;
uint32_t mapped_sessions;
uint32_t sessions_cookies[MAX_ACTIVE_SESSION];
/* DMA kernel structures */
bus_dma_tag_t sc_dmatag;
bus_dmamap_t sc_dmamap;
bus_dma_segment_t sc_segs;
int sc_rsegs;
void * sc_dma_kva;
};
#endif /* _SBUSFPGA_CURVE25519ENGINE_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
/* $NetBSD$ */
/*-
* Copyright (c) 2020 Romain Dolbeau <romain@dolbeau.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SBUSFPGA_SDRAM_H_
#define _SBUSFPGA_SDRAM_H_
struct sbusfpga_sdram_softc {
struct dk_softc dk;
/* device_t sc_dev; */ /* us as a device */ /* in dk */
u_int sc_rev; /* revision */
int sc_node; /* PROM node ID */
int sc_burst; /* DVMA burst size in effect */
bus_space_tag_t sc_bustag; /* bus tag */
bus_space_handle_t sc_bhregs_ddrphy; /* bus handle */
bus_space_handle_t sc_bhregs_sdram; /* bus handle */
bus_space_handle_t sc_bhregs_exchange_with_mem; /* bus handle */
bus_space_handle_t sc_bhregs_mmap; /* bus handle */
int sc_bufsiz_ddrphy; /* Size of buffer */
int sc_bufsiz_sdram; /* Size of buffer */
int sc_bufsiz_exchange_with_mem; /* bus handle */
int sc_bufsiz_mmap; /* bus handle */
/* specific of the DMA engine */
u_int dma_blk_size;
u_int dma_blk_base;
u_int dma_mem_size; /* in blk_size */
u_int dma_real_mem_size; /* precomputed in bytes */
/* DMA kernel structures */
bus_dma_tag_t sc_dmatag;
bus_dmamap_t sc_dmamap;
bus_dma_segment_t sc_segs;
int sc_rsegs;
void * sc_dma_kva;
};
#define SBUSFPGA_SDRAM_VAL_DMA_MAX_SZ (64*1024)
#endif /* _SBUSFPGA_SDRAM_H_ */

View File

@ -0,0 +1,257 @@
/* $NetBSD$ */
/*-
* Copyright (c) 2020 Romain Dolbeau <romain@dolbeau.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/bus.h>
#include <machine/autoconf.h>
#include <sys/cpu.h>
#include <sys/conf.h>
#include <sys/rndsource.h>
#include <dev/sbus/sbusvar.h>
#include <dev/sbus/sbusfpga_stat.h>
#include <machine/param.h>
int sbusfpga_stat_print(void *, const char *);
int sbusfpga_stat_match(device_t, cfdata_t, void *);
void sbusfpga_stat_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(sbusfpga_stat, sizeof(struct sbusfpga_sbus_bus_stat_softc),
sbusfpga_stat_match, sbusfpga_stat_attach, NULL, NULL);
dev_type_open(sbusfpga_stat_open);
dev_type_close(sbusfpga_stat_close);
dev_type_ioctl(sbusfpga_stat_ioctl);
const struct cdevsw sbusfpga_stat_cdevsw = {
.d_open = sbusfpga_stat_open,
.d_close = sbusfpga_stat_close,
.d_read = noread,
.d_write = nowrite,
.d_ioctl = sbusfpga_stat_ioctl,
.d_stop = nostop,
.d_tty = notty,
.d_poll = nopoll,
.d_mmap = nommap,
.d_kqfilter = nokqfilter,
.d_discard = nodiscard,
.d_flag = 0
};
extern struct cfdriver sbusfpga_stat_cd;
int
sbusfpga_stat_open(dev_t dev, int flags, int mode, struct lwp *l)
{
return (0);
}
int
sbusfpga_stat_close(dev_t dev, int flags, int mode, struct lwp *l)
{
return (0);
}
int
sbusfpga_stat_print(void *aux, const char *busname)
{
sbus_print(aux, busname);
return (UNCONF);
}
int
sbusfpga_stat_match(device_t parent, cfdata_t cf, void *aux)
{
struct sbus_attach_args *sa = (struct sbus_attach_args *)aux;
return (strcmp("RDOL,sbusstat", sa->sa_name) == 0);
}
#define CONFIG_CSR_DATA_WIDTH 32
// define CSR_LEDS_BASE & others to avoid defining the CSRs of HW we don't handle
#define CSR_LEDS_BASE
#define CSR_CURVE25519ENGINE_BASE
#define CSR_DDRPHY_BASE
#define CSR_EXCHANGE_WITH_MEM_BASE
// #define CSR_SBUS_BUS_STAT_BASE
#define CSR_SDRAM_BASE
#define CSR_SDBLOCK2MEM_BASE
#define CSR_SDCORE_BASE
#define CSR_SDIRQ_BASE
#define CSR_SDMEM2BLOCK_BASE
#define CSR_SDPHY_BASE
#define CSR_TRNG_BASE
#include "dev/sbus/litex_csr.h"
#undef CSR_LEDS_BASE
#undef CSR_CURVE25519ENGINE_BASE
#undef CSR_DDRPHY_BASE
#undef CSR_EXCHANGE_WITH_MEM_BASE
// #undef CSR_SBUS_BUS_STAT_BASE
#undef CSR_SDRAM_BASE
#undef CSR_SDBLOCK2MEM_BASE
#undef CSR_SDCORE_BASE
#undef CSR_SDIRQ_BASE
#undef CSR_SDMEM2BLOCK_BASE
#undef CSR_SDPHY_BASE
//#undef CSR_TRNG_BASE
static void sbusfpga_stat_display(void *);
/*
* Attach all the sub-devices we can find
*/
void
sbusfpga_stat_attach(device_t parent, device_t self, void *aux)
{
struct sbus_attach_args *sa = aux;
struct sbusfpga_sbus_bus_stat_softc *sc = device_private(self);
struct sbus_softc *sbsc = device_private(parent);
int node;
int sbusburst;
sc->sc_bustag = sa->sa_bustag;
sc->sc_dev = self;
if (sbus_bus_map(sc->sc_bustag, sa->sa_slot, sa->sa_offset, sa->sa_size,
BUS_SPACE_MAP_LINEAR, &sc->sc_bhregs_sbus_bus_stat) != 0) {
aprint_error(": cannot map registers\n");
return;
}
sc->sc_bufsiz = sa->sa_size;
node = sc->sc_node = sa->sa_node;
/*
* Get transfer burst size from PROM
*/
sbusburst = sbsc->sc_burst;
if (sbusburst == 0)
sbusburst = SBUS_BURST_32 - 1; /* 1->16 */
sc->sc_burst = prom_getpropint(node, "burst-sizes", -1);
if (sc->sc_burst == -1)
/* take SBus burst sizes */
sc->sc_burst = sbusburst;
/* Clamp at parent's burst sizes */
sc->sc_burst &= sbusburst;
aprint_normal("\n");
aprint_normal_dev(self, "nid 0x%x, bustag %p, burst 0x%x (parent 0x%0x)\n",
sc->sc_node,
sc->sc_bustag,
sc->sc_burst,
sbsc->sc_burst);
sc->sc_delay = 5 * hz; // five seconds
callout_init(&sc->sc_display, CALLOUT_MPSAFE);
callout_setfunc(&sc->sc_display, sbusfpga_stat_display, sc);
/* disable by default */
sc->sc_enable = 0;
/* do it once during boot*/
callout_schedule(&sc->sc_display, sc->sc_delay);
}
#define SBUSFPGA_STAT_ON _IO(0, 1)
#define SBUSFPGA_STAT_OFF _IO(0, 0)
int
sbusfpga_stat_ioctl (dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
{
struct sbusfpga_sbus_bus_stat_softc *sc = device_lookup_private(&sbusfpga_stat_cd, minor(dev));
int err = 0;
switch (cmd) {
case SBUSFPGA_STAT_ON:
if (!sc->sc_enable) {
sc->sc_enable = 1;
callout_schedule(&sc->sc_display, sc->sc_delay);
}
break;
case SBUSFPGA_STAT_OFF:
if (sc->sc_enable) {
callout_stop(&sc->sc_display);
sc->sc_enable = 0;
}
break;
default:
err = ENOTTY;
break;
}
return err;
}
static void sbusfpga_stat_display(void *args) {
struct sbusfpga_sbus_bus_stat_softc *sc = args;
unsigned int c = sbus_bus_stat_stat_cycle_counter_read(sc), c2;
int count;
sbus_bus_stat_stat_ctrl_write(sc, 1);
delay(1);
count = 0;
while (count < 10 && ((c2 = sbus_bus_stat_stat_cycle_counter_read(sc)) == c)) {
count ++;
delay(1);
}
if ((c2 == c) || (c2 == 0)){
device_printf(sc->sc_dev, "Statistics didn't update\n");
} else {
device_printf(sc->sc_dev, "%u: slave %u %u %u %u\n",
c2,
sbus_bus_stat_stat_slave_start_counter_read(sc),
sbus_bus_stat_stat_slave_done_counter_read(sc),
sbus_bus_stat_stat_slave_rerun_counter_read(sc),
sbus_bus_stat_stat_slave_early_error_counter_read(sc));
device_printf(sc->sc_dev, "%u: master %u %u %u %u (0x%08x)\n",
c2,
sbus_bus_stat_stat_master_start_counter_read(sc),
sbus_bus_stat_stat_master_done_counter_read(sc),
sbus_bus_stat_stat_master_error_counter_read(sc),
sbus_bus_stat_stat_master_rerun_counter_read(sc),
sbus_bus_stat_sbus_master_error_virtual_read(sc));
}
sbus_bus_stat_stat_ctrl_write(sc, 0);
if (sc->sc_enable)
callout_schedule(&sc->sc_display, sc->sc_delay);
}

View File

@ -0,0 +1,45 @@
/* $NetBSD$ */
/*-
* Copyright (c) 2020 Romain Dolbeau <romain@dolbeau.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SBUSFPGA_STAT_H_
#define _SBUSFPGA_STAT_H_
struct sbusfpga_sbus_bus_stat_softc {
device_t sc_dev; /* us as a device */
u_int sc_rev; /* revision */
int sc_node; /* PROM node ID */
int sc_burst; /* DVMA burst size in effect */
bus_space_tag_t sc_bustag; /* bus tag */
bus_space_handle_t sc_bhregs_sbus_bus_stat; /* bus handle */
int sc_bufsiz; /* Size of buffer */
callout_t sc_display;
int sc_delay;
int sc_enable;
};
#endif /* _SBUSFPGA_STAT_H_ */

View File

@ -0,0 +1,213 @@
/* $NetBSD$ */
/*-
* Copyright (c) 2020 Romain Dolbeau <romain@dolbeau.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/bus.h>
#include <machine/autoconf.h>
#include <sys/cpu.h>
#include <sys/conf.h>
#include <sys/rndsource.h>
#include <dev/sbus/sbusvar.h>
#include <dev/sbus/sbusfpga_trng.h>
#include <machine/param.h>
int sbusfpga_trng_print(void *, const char *);
int sbusfpga_trng_match(device_t, cfdata_t, void *);
void sbusfpga_trng_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(sbusfpga_trng, sizeof(struct sbusfpga_trng_softc),
sbusfpga_trng_match, sbusfpga_trng_attach, NULL, NULL);
dev_type_open(sbusfpga_trng_open);
dev_type_close(sbusfpga_trng_close);
dev_type_ioctl(sbusfpga_trng_ioctl);
const struct cdevsw sbusfpga_trng_cdevsw = {
.d_open = sbusfpga_trng_open,
.d_close = sbusfpga_trng_close,
.d_read = noread,
.d_write = nowrite,
.d_ioctl = noioctl,
.d_stop = nostop,
.d_tty = notty,
.d_poll = nopoll,
.d_mmap = nommap,
.d_kqfilter = nokqfilter,
.d_discard = nodiscard,
.d_flag = 0
};
extern struct cfdriver sbusfpga_trng_cd;
int
sbusfpga_trng_open(dev_t dev, int flags, int mode, struct lwp *l)
{
return (0);
}
int
sbusfpga_trng_close(dev_t dev, int flags, int mode, struct lwp *l)
{
return (0);
}
int
sbusfpga_trng_print(void *aux, const char *busname)
{
sbus_print(aux, busname);
return (UNCONF);
}
int
sbusfpga_trng_match(device_t parent, cfdata_t cf, void *aux)
{
struct sbus_attach_args *sa = (struct sbus_attach_args *)aux;
return (strcmp("RDOL,neorv32trng", sa->sa_name) == 0);
}
#define CONFIG_CSR_DATA_WIDTH 32
// define CSR_LEDS_BASE & others to avoid defining the CSRs of HW we don't handle
#define CSR_LEDS_BASE
#define CSR_CURVE25519ENGINE_BASE
#define CSR_DDRPHY_BASE
#define CSR_EXCHANGE_WITH_MEM_BASE
#define CSR_SBUS_BUS_STAT_BASE
#define CSR_SDRAM_BASE
#define CSR_SDBLOCK2MEM_BASE
#define CSR_SDCORE_BASE
#define CSR_SDIRQ_BASE
#define CSR_SDMEM2BLOCK_BASE
#define CSR_SDPHY_BASE
//#define CSR_TRNG_BASE
#include "dev/sbus/litex_csr.h"
#undef CSR_LEDS_BASE
#undef CSR_CURVE25519ENGINE_BASE
#undef CSR_DDRPHY_BASE
#undef CSR_EXCHANGE_WITH_MEM_BASE
#undef CSR_SBUS_BUS_STAT_BASE
#undef CSR_SDRAM_BASE
#undef CSR_SDBLOCK2MEM_BASE
#undef CSR_SDCORE_BASE
#undef CSR_SDIRQ_BASE
#undef CSR_SDMEM2BLOCK_BASE
#undef CSR_SDPHY_BASE
//#undef CSR_TRNG_BASE
static void
sbusfpga_trng_getentropy(size_t nbytes, void *cookie) {
struct sbusfpga_trng_softc *sc = cookie;
size_t dbytes = 0;
int failure = 0;
while (nbytes > dbytes) {
u_int32_t data = trng_data_read(sc);
if (data) {
rnd_add_data_sync(&sc->sc_rndsource, &data, 4, 32); // 32 is perhaps optimistic
dbytes += 4;
} else {
failure ++;
if (failure > (1+(dbytes/4))) { // something going on
device_printf(sc->sc_dev, "out of entropy after %zd / %zd bytes\n", dbytes, nbytes);
return;
}
delay(1);
}
if (((dbytes%32)==0) && (nbytes > dbytes))
delay(1); // let the hardware breathes if the OS needs a lof of bytes
}
device_printf(sc->sc_dev, "gathered %zd bytes [%d]\n", dbytes, failure);
}
/*
* Attach all the sub-devices we can find
*/
void
sbusfpga_trng_attach(device_t parent, device_t self, void *aux)
{
struct sbus_attach_args *sa = aux;
struct sbusfpga_trng_softc *sc = device_private(self);
struct sbus_softc *sbsc = device_private(parent);
int node;
int sbusburst;
sc->sc_bustag = sa->sa_bustag;
sc->sc_dev = self;
if (sbus_bus_map(sc->sc_bustag, sa->sa_slot, sa->sa_offset, sa->sa_size,
BUS_SPACE_MAP_LINEAR, &sc->sc_bhregs_trng) != 0) {
aprint_error(": cannot map registers\n");
return;
}
//sc->sc_buffer = bus_space_vaddr(sc->sc_bustag, sc->sc_bhregs_trng);
sc->sc_bufsiz = sa->sa_size;
node = sc->sc_node = sa->sa_node;
/*
* Get transfer burst size from PROM
*/
sbusburst = sbsc->sc_burst;
if (sbusburst == 0)
sbusburst = SBUS_BURST_32 - 1; /* 1->16 */
sc->sc_burst = prom_getpropint(node, "burst-sizes", -1);
if (sc->sc_burst == -1)
/* take SBus burst sizes */
sc->sc_burst = sbusburst;
/* Clamp at parent's burst sizes */
sc->sc_burst &= sbusburst;
aprint_normal("\n");
aprint_normal_dev(self, "nid 0x%x, bustag %p, burst 0x%x (parent 0x%0x)\n",
sc->sc_node,
sc->sc_bustag,
sc->sc_burst,
sbsc->sc_burst);
trng_ctrl_write(sc, 0x02); // start the TRNG
rndsource_setcb(&sc->sc_rndsource, sbusfpga_trng_getentropy, sc);
rnd_attach_source(&sc->sc_rndsource, device_xname(self), RND_TYPE_RNG, RND_FLAG_HASCB | RND_FLAG_COLLECT_VALUE);
}

View File

@ -0,0 +1,43 @@
/* $NetBSD$ */
/*-
* Copyright (c) 2020 Romain Dolbeau <romain@dolbeau.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SBUSFPGA_TRNG_H_
#define _SBUSFPGA_TRNG_H_
struct sbusfpga_trng_softc {
device_t sc_dev; /* us as a device */
u_int sc_rev; /* revision */
int sc_node; /* PROM node ID */
int sc_burst; /* DVMA burst size in effect */
bus_space_tag_t sc_bustag; /* bus tag */
bus_space_handle_t sc_bhregs_trng; /* bus handle */
int sc_bufsiz; /* Size of buffer */
struct krndsource sc_rndsource;
};
#endif /* _SBUSFPGA_TRNG_H_ */

View File

@ -2,7 +2,7 @@
## Goal
The goal of this repository is to be able to interface a modern (2020 era) [FPGA](https://en.wikipedia.org/wiki/Field-programmable_gate_array) with a [SBus](https://en.wikipedia.org/wiki/SBus) host. SBus was widely used in SPARCstation and compatibles system in the first halt of the 90s. It was progressively displaced by PCI from the mid-90s onward, and is thoroughly obsolete.
The goal of this repository is to be able to interface a modern (2020 era) [FPGA](https://en.wikipedia.org/wiki/Field-programmable_gate_array) with a [SBus](https://en.wikipedia.org/wiki/SBus) host. SBus was widely used in SPARCstation and compatibles system in the first half of the 90s. It was progressively displaced by PCI from the mid-90s onward, and is thoroughly obsolete.
So unless you're a retrocomputing enthusiast with such a machine, this is useless. To be honest, even if you are such an enthusiast, it's probably not that useful...
@ -12,25 +12,50 @@ To save on PCB cost, the board is smaller than a 'true' SBus board; the hardware
## Current status
2021-03-21: The adapter board seems to work fine in two different SS20. Currently the embedded PROM code exposes three devices in the FPGA:
2021-07-18: The old VHDL gateware has been replaced by a new Migen-based gateware, see below for details.
* "RDOL,cryptoengine": exposes a (way too large) polynomial multiplier to implement GCM mode and a AES block. Currently used to implement DMA-based acceleration of AES-256-CBC through /dev/crypto. Unfortunately OpenSSL doesn't support AES-256-GCM in the cryptodev engine, and disagree with NetBSD's /dev/crypto on how to implement AES-256-CTR. And the default SSH cannot use cryptodev, it closes all file descriptors after cryptodev has opened /dev/crypto... still WiP.
* "RDOL,trng": exposes a 5 MHz counter (didn't realize the SS20 already had a good counter) and a so-far-not-true TRNG (implemented by a PRNG). The 'true' random generators I've found make Vivado screams very loudly when synthesizing... anyway both works fine in NetBSD 9.0 as a timecounter and an entropy source (which a PRNG really isn't, I know). still WiP.
* "RDOL,sdcard": trying to expose the micro-sd card slot as a storage device, at first using SPI mode. So far reading seems to work, and NetBSD can see a Sun disklabel on the micro-sd card if it has been partitioned that way. Mounting a FAT filesystem read-only now works (with very little testing as of yet). Writing not working yet. Very much WiP.
2021-08-22: Short version: the board enables a 256 MiB SDRAM disk (for fast swapping), a TRNG, a USB OHCI host controller (for USB peripherals) and a Curve25519 accelerator.
## The hardware
Directory 'sbus-to-ztex'
The custom board is a SBus-compliant (I hope...) board, designed to receive a [ZTex USB-FPGA Module 2.13](https://www.ztex.de/usb-fpga-2/usb-fpga-2.13.e.html) as a daughterboard. The ZTex module contains the actual FPGA (Artix-7), some RAM, programming hardware, etc. The SBus board contains level-shifters ICs to interface between the SBus signals and the FPGA, a serial header, some Leds, a JTAG header, and a micro-sd card slot.
The custom board is a SBus-compliant (I hope...) board, designed to receive a [ZTex USB-FPGA Module 2.13](https://www.ztex.de/usb-fpga-2/usb-fpga-2.13.e.html) as a daughterboard. The ZTex module contains the actual FPGA (Artix-7), some RAM, programming hardware, etc. The SBus board contains level-shifters ICs to interface between the SBus signals and the FPGA, a serial header, some Leds, a JTAG header, and a micro-sd card slot. It only connects interrupt line 7 (highest priority) and 1 (lowest priority), which was a mistake (more interrupts are needed and 7 is too high-priority to use at this stage, so just the level 1 is usable), but otherwise supports every SBus feature except the optional parity (i.e. it can do both slave and master modes).
The PCB was designed with Kicad 5.0
## The gateware
## The gateware (Migen)
Directory 'sbus-to-ztex-gateware'
### Intro
The gateware was rewritten from scratch in the Migen language, choosen because that's what [Litex](https://github.com/enjoy-digital/litex/) uses.
It implements a simple CPU-less Litex SoC built around a Wishbone bus, with a bridge between the SBus and the Wishbone.
A ROM, a SDRAM controller ([litedram](https://github.com/enjoy-digital/litedram) to the on-board DDR3), a TRNG (using the [NeoRV32](https://github.com/stnolting/neorv32) TRNG), an USB OHCI (host controller, using the Litex wrapper around the [SpinalHDL](https://github.com/SpinalHDL/SpinalHDL) implementation) and a Curve25519 Crypto Engine (taken from the [Betrusted.IO](https://betrusted.io/) project) are connected to that bus.
### Details
Master access to the SBus by the host are routed to the Wishbone to access the various CSRs / control registers of the devices.
The ROM doesn't do much beyond exposing the devices' existence and specifications to the host.
The SDRAM has its own custom DMA controller, using native Litedram DMA to the memory, and some FIFO to/from the SBus. A custom NetBSD driver exposes it as a drive on which you can swap. It's also usable as a 'fast', volatile disk (for e.g. /tmp or similar temporary filesystem). It could use a interrupt line, but the only usable one in the current HW design is in use by the USB.
The TRNG has a NetBSD driver to add entropy to the entropy pool.
The USB OHCI DMA is bridged from the Wishbone to the SBus by having the physical addresses of the Wishbone (that match the virtual addresses from NetBSD DVMA allocations) to the bridge. Reads are buffered by block of 16 bytes; currently writes are unbuffered (and somewhat slow, as they need a full SBus master cycle for every transaction of 32 bits or less). The standard NetBSD OHCI driver is used, with just a small custom SBus-OHCI driver mirroring the PCI-OHCI one. It uses the interrupt level 1 available on the board. As the board has no USB connectors, the D+ and D- lines are routed to the Serial header pins, those (and GND) are connected to a pair of pins of [Dolu1990's USB PMod](https://github.com/Dolu1990/pmod_usb_host_x4), and the associated USB port is connected to an external self-powered USB hub (which is the one supplying the VBus). It's quite ugly but it works (of course I should redesign the PCB with a proper USB connector and a VBus).
The Curve25519 Engine currently exposes an IOCTL to do the computation, which has yet to be integrated usefully in e.g. OpenSSL. It could use a interrupt line, but the only usable one in the current HW design is in use by the USB.
### Special Notes
Currently the design uses a Wishbone Crossbar Interconnect from Litex instead of a Shared Interconnect, as for some reason using a Shared Interconnect causes issues between devices (disabling the USB OHCI seem also to solve the issue, it generates a lot of cycles on the buses). I might be misusing Wishbone. With the Crossbar, all devices are usable simultaneously.
As not everything lives in the same clock domain, the design also use a Wishbone CDC, a wrapper around the one from [Verilog Wishbone Components](https://github.com/alexforencich/verilog-wishbone).
## The gateware (VHDL, obsolete)
Directory 'sbus-to-ztex-gateware', this is obsolete and replaced by the Migen gateware above.
The function embedded in the FPGA currently includes the PROM, lighting Led to display a 32-bits value, and a GHASH MAC (128 polynomial accumulator, used for the AES-GCM encryption scheme). The device is a fairly basic scale, but should be able to read from the PROM and read/write from the GCM space with any kind of SBus burst (1, 2, 4, 8 or 16 words).
@ -40,5 +65,5 @@ The gateware is currently synthesized with Vivado 2020.1
Directory 'NetBSD'
Some basic drivers for NetBSD 9.0/sparc to enable the deviced as described above.
Some basic drivers for NetBSD 9.0/sparc to enable the devices as described above.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,23 @@
[package]
name = "engine_code"
version = "0.1.0"
authors = ["Romain Dolbeau <romain@dolbeau.org>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[dependencies.engine25519-as]
#git="https://github.com/betrusted-io/engine25519-as.git"
#rev="6681e73c1fdc4a460b5ef9f9c7c91aef546d00f3"
path = "/home/dolbeau/engine25519-as"
[dev-dependencies.engine25519-as]
#git="https://github.com/betrusted-io/engine25519-as.git"
#rev="6681e73c1fdc4a460b5ef9f9c7c91aef546d00f3"
path = "/home/dolbeau/engine25519-as"
[[bin]]
name = "engine_code"
path = "engine_code.rs"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
#!/bin/bash
PFX=prom_migen
rm -f ${PFX}.fc
# (export BP=~/SPARC/SBusFPGA/sbus-to-ztex/openfirmware ; toke ${PFX}.forth )
( export BP=`pwd`/openfirmware ; openfirmware/cpu/x86/Linux/forth openfirmware/cpu/x86/build/builder.dic prom_migen.bth ) 2>&1 | tee forth.log
rm -f /tmp/${PFX}.hexa
od --endian=big -w4 -x ${PFX}.fc | awk '{ print $2,$3"," }' >| /tmp/${PFX}.hexa
rm -f /tmp/${PFX}.txt_hexa
cat /tmp/${PFX}.hexa | sed -e 's/^\([a-f0-9][a-f0-9][a-f0-9][a-f0-9]\) \([a-f0-9][a-f0-9][a-f0-9][a-f0-9]\),/0x\1\2,/g' -e 's/^\([a-f0-9][a-f0-9]*\) ,/0x\10000,/' -e 's/^ ,/0x00000000,/' -e 's/\(0x[0-9a-fA-F]*\),/if (idx == 0):\n\treturn \1;/' > /tmp/${PFX}.txt_hexa
#echo "rom = ["
#cat /tmp/${PFX}.txt_hexa
#echo "]"

View File

@ -0,0 +1,382 @@
-- # THIS IS NOT THE ORIGINAL FILE
-- # THIS WAS MODIFIED TO EXPOSE THE TRNG IN LITEX
-- # See the link in the copyright header to find the original file
--
--
-- #################################################################################################
-- # << NEORV32 - True Random Number Generator (TRNG) >> #
-- # ********************************************************************************************* #
-- # This unit implements a *true* random number generator which uses several ring oscillators as #
-- # entropy source. The outputs of all chains are XORed and de-biased using a John von Neumann #
-- # randomness extractor. The de-biased signal is further processed by a simple LFSR for improved #
-- # whitening. #
-- # ********************************************************************************************* #
-- # BSD 3-Clause License #
-- # #
-- # Copyright (c) 2021, Stephan Nolting. All rights reserved. #
-- # #
-- # Redistribution and use in source and binary forms, with or without modification, are #
-- # permitted provided that the following conditions are met: #
-- # #
-- # 1. Redistributions of source code must retain the above copyright notice, this list of #
-- # conditions and the following disclaimer. #
-- # #
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of #
-- # conditions and the following disclaimer in the documentation and/or other materials #
-- # provided with the distribution. #
-- # #
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to #
-- # endorse or promote products derived from this software without specific prior written #
-- # permission. #
-- # #
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF #
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED #
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
-- # OF THE POSSIBILITY OF SUCH DAMAGE. #
-- # ********************************************************************************************* #
-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
-- #################################################################################################
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library neorv32;
-- use neorv32.neorv32_package.all;
entity neorv32_trng is
port (
-- host access --
clk_i : in std_ulogic; -- global clock line
-- addr_i : in std_ulogic_vector(31 downto 0); -- address
rden_i : in std_ulogic; -- read enable
wren_i : in std_ulogic; -- write enable
data_i : in std_ulogic_vector(31 downto 0); -- data in
data_o : out std_ulogic_vector(31 downto 0)--; -- data out
-- ack_o : out std_ulogic -- transfer acknowledge
);
end neorv32_trng;
architecture neorv32_trng_rtl of neorv32_trng is
-- Advanced Configuration --------------------------------------------------------------------------------
constant num_roscs_c : natural := 4; -- total number of ring oscillators
constant num_inv_start_c : natural := 5; -- number of inverters in FIRST ring oscillator (has to be odd)
constant num_inv_inc_c : natural := 2; -- number of inverters increment for each next ring oscillator (has to be even)
constant lfsr_en_c : boolean := true; -- use LFSR-based post-processing
constant lfsr_taps_c : std_ulogic_vector(7 downto 0) := "10111000"; -- Fibonacci post-processing LFSR feedback taps
-- -------------------------------------------------------------------------------------------------------
-- control register bits --
constant ctrl_data_lsb_c : natural := 0; -- r/-: Random data byte LSB
constant ctrl_data_msb_c : natural := 7; -- r/-: Random data byte MSB
--
constant ctrl_en_c : natural := 30; -- r/w: TRNG enable
constant ctrl_valid_c : natural := 31; -- r/-: Output data valid
-- IO space: module base address --
-- constant hi_abb_c : natural := index_size_f(io_size_c)-1; -- high address boundary bit
-- constant lo_abb_c : natural := index_size_f(trng_size_c); -- low address boundary bit
-- copy/pasted from the rtl/core/neorv32_package.vhd file
function xor_reduce_f(a : std_ulogic_vector) return std_ulogic is
variable tmp_v : std_ulogic;
begin
tmp_v := '0';
if (a'low < a'high) then -- not null range?
for i in a'low to a'high loop
tmp_v := tmp_v xor a(i);
end loop; -- i
end if;
return tmp_v;
end function xor_reduce_f;
-- Component: Ring-Oscillator --
component neorv32_trng_ring_osc
generic (
NUM_INV : natural := 16 -- number of inverters in chain
);
port (
clk_i : in std_ulogic;
enable_i : in std_ulogic; -- enable chain input
enable_o : out std_ulogic; -- enable chain output
data_o : out std_ulogic -- sync random bit
);
end component;
-- access control --
signal acc_en : std_ulogic; -- module access enable
-- signal wren : std_ulogic; -- full word write enable
-- signal rden : std_ulogic; -- read enable
-- ring-oscillator array --
signal osc_array_en_in : std_ulogic_vector(num_roscs_c-1 downto 0);
signal osc_array_en_out : std_ulogic_vector(num_roscs_c-1 downto 0);
signal osc_array_data : std_ulogic_vector(num_roscs_c-1 downto 0);
-- von-Neumann de-biasing --
type debiasing_t is record
sreg : std_ulogic_vector(1 downto 0);
state : std_ulogic; -- process de-biasing every second cycle
valid : std_ulogic; -- de-biased data
data : std_ulogic; -- de-biased data valid
end record;
signal debiasing : debiasing_t;
-- (post-)processing core --
type processing_t is record
enable : std_ulogic; -- TRNG enable flag
cnt : std_ulogic_vector(3 downto 0); -- bit counter
sreg : std_ulogic_vector(7 downto 0); -- data shift register
output : std_ulogic_vector(7 downto 0); -- output register
valid : std_ulogic; -- data output valid flag
end record;
signal processing : processing_t;
begin
-- Sanity Checks --------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
assert not (num_roscs_c = 0) report "NEORV32 PROCESSOR CONFIG ERROR: TRNG - Total number of ring-oscillators has to be >0." severity error;
assert not ((num_inv_start_c mod 2) = 0) report "NEORV32 PROCESSOR CONFIG ERROR: TRNG - Number of inverters in fisrt ring has to be odd." severity error;
assert not ((num_inv_inc_c mod 2) /= 0) report "NEORV32 PROCESSOR CONFIG ERROR: TRNG - Number of inverters increment for each next ring has to be even." severity error;
-- Access Control -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
-- acc_en <= '1' when (addr_i(hi_abb_c downto lo_abb_c) = trng_base_c(hi_abb_c downto lo_abb_c)) else '0';
-- wren <= acc_en and wren_i;
-- rden <= acc_en and rden_i;
-- Read/Write Access ----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
rw_access: process(clk_i)
begin
if rising_edge(clk_i) then
-- ack_o <= wren_i or rden_i;
-- write access --
if (wren_i = '1') then
processing.enable <= data_i(ctrl_en_c);
end if;
-- read access --
-- data_o <= (others => '0');
if (rden_i = '1') then
data_o(ctrl_data_msb_c downto ctrl_data_lsb_c) <= processing.output;
data_o(ctrl_en_c) <= processing.enable;
data_o(ctrl_valid_c) <= processing.valid;
end if;
end if;
end process rw_access;
-- Entropy Source -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_trng_ring_osc_inst:
for i in 0 to num_roscs_c-1 generate
neorv32_trng_ring_osc_inst_i: neorv32_trng_ring_osc
generic map (
NUM_INV => num_inv_start_c + (i*num_inv_inc_c) -- number of inverters in chain
)
port map (
clk_i => clk_i,
enable_i => osc_array_en_in(i),
enable_o => osc_array_en_out(i),
data_o => osc_array_data(i)
);
end generate;
-- RO enable chain --
array_intercon: process(processing.enable, osc_array_en_out)
begin
for i in 0 to num_roscs_c-1 loop
if (i = 0) then -- start of enable chain
osc_array_en_in(i) <= processing.enable;
else
osc_array_en_in(i) <= osc_array_en_out(i-1);
end if;
end loop; -- i
end process array_intercon;
-- John von Neumann De-Biasing ------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neumann_debiasing_sync: process(clk_i)
begin
if rising_edge(clk_i) then
debiasing.sreg <= debiasing.sreg(debiasing.sreg'left-1 downto 0) & xor_reduce_f(osc_array_data);
debiasing.state <= (not debiasing.state) and osc_array_en_out(num_roscs_c-1); -- start toggling when last RO is enabled -> process in every second cycle
end if;
end process neumann_debiasing_sync;
-- Edge detector --
neumann_debiasing_comb: process(debiasing)
variable tmp_v : std_ulogic_vector(2 downto 0);
begin
-- check groups of two non-overlapping bits from the input stream
tmp_v := debiasing.state & debiasing.sreg;
case tmp_v is
when "101" => debiasing.valid <= '1'; debiasing.data <= '1'; -- rising edge -> '1'
when "110" => debiasing.valid <= '1'; debiasing.data <= '0'; -- falling edge -> '0'
when others => debiasing.valid <= '0'; debiasing.data <= '0'; -- no valid data
end case;
end process neumann_debiasing_comb;
-- Processing Core ------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
processing_core: process(clk_i)
begin
if rising_edge(clk_i) then
-- sample random data bit and apply post-processing --
if (processing.enable = '0') then
processing.cnt <= (others => '0');
processing.sreg <= (others => '0');
elsif (debiasing.valid = '1') then -- valid random sample?
if (processing.cnt = "1000") then
processing.cnt <= (others => '0');
else
processing.cnt <= std_ulogic_vector(unsigned(processing.cnt) + 1);
end if;
if (lfsr_en_c = true) then -- LFSR post-processing
processing.sreg <= processing.sreg(processing.sreg'left-1 downto 0) & ((not xor_reduce_f(processing.sreg and lfsr_taps_c)) xnor debiasing.data);
else -- NO post-processing
processing.sreg <= processing.sreg(processing.sreg'left-1 downto 0) & debiasing.data;
end if;
end if;
-- data output register --
if (processing.cnt = "1000") then
processing.output <= processing.sreg;
end if;
-- data ready/valid flag --
if (processing.cnt = "1000") then -- new sample ready?
processing.valid <= '1';
elsif (processing.enable = '0') or (rden_i = '1') then -- clear when deactivated or on data read
processing.valid <= '0';
end if;
end if;
end process processing_core;
end neorv32_trng_rtl;
-- ############################################################################################################################
-- ############################################################################################################################
-- #################################################################################################
-- # << NEORV32 - True Random Number Generator (TRNG) - Ring-Oscillator-Based Entropy Source >> #
-- # ********************************************************************************************* #
-- # An inverter chain (ring oscillator) is used as entropy source. #
-- # The inverter chain is constructed as an "asynchronous" LFSR. The single inverters are #
-- # connected via latches that are used to enable/disable the TRNG. Also, these latches are used #
-- # as additional delay element. By using unique enable signals for each latch, the synthesis #
-- # tool cannot "optimize" (=remove) any of the inverters out of the design. Furthermore, the #
-- # latches prevent the synthesis tool from detecting combinatorial loops. #
-- # ********************************************************************************************* #
-- # BSD 3-Clause License #
-- # #
-- # Copyright (c) 2021, Stephan Nolting. All rights reserved. #
-- # #
-- # Redistribution and use in source and binary forms, with or without modification, are #
-- # permitted provided that the following conditions are met: #
-- # #
-- # 1. Redistributions of source code must retain the above copyright notice, this list of #
-- # conditions and the following disclaimer. #
-- # #
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of #
-- # conditions and the following disclaimer in the documentation and/or other materials #
-- # provided with the distribution. #
-- # #
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to #
-- # endorse or promote products derived from this software without specific prior written #
-- # permission. #
-- # #
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF #
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED #
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
-- # OF THE POSSIBILITY OF SUCH DAMAGE. #
-- # ********************************************************************************************* #
-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
-- #################################################################################################
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library neorv32;
-- use neorv32.neorv32_package.all;
entity neorv32_trng_ring_osc is
generic (
NUM_INV : natural := 15 -- number of inverters in chain
);
port (
clk_i : in std_ulogic;
enable_i : in std_ulogic; -- enable chain input
enable_o : out std_ulogic; -- enable chain output
data_o : out std_ulogic -- sync random bit
);
end neorv32_trng_ring_osc;
architecture neorv32_trng_ring_osc_rtl of neorv32_trng_ring_osc is
signal inv_chain : std_ulogic_vector(NUM_INV-1 downto 0); -- oscillator chain
signal enable_sreg : std_ulogic_vector(NUM_INV-1 downto 0); -- enable shift register
signal sync_ff : std_ulogic_vector(1 downto 0); -- output signal synchronizer
begin
-- Ring Oscillator ------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
ring_osc: process(enable_i, enable_sreg, inv_chain)
begin
-- Using individual enable signals for each inverter - derived from a shift register - to prevent the synthesis tool
-- from removing all but one inverter (since they implement "logical identical functions").
-- This also allows to make the TRNG platform independent.
for i in 0 to NUM_INV-1 loop -- inverters in chain
if (enable_i = '0') then -- start with a defined state (latch reset)
inv_chain(i) <= '0';
elsif (enable_sreg(i) = '1') then
-- here we have the inverter chain --
if (i = NUM_INV-1) then -- left-most inverter?
inv_chain(i) <= not inv_chain(0);
else
inv_chain(i) <= not inv_chain(i+1);
end if;
end if;
end loop; -- i
end process ring_osc;
-- Control --------------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
ctrl_unit: process(clk_i)
begin
if rising_edge(clk_i) then
enable_sreg <= enable_sreg(enable_sreg'left-1 downto 0) & enable_i; -- activate right-most inverter first
sync_ff <= sync_ff(0) & inv_chain(0); -- synchronize to prevent metastability
end if;
end process ctrl_unit;
-- output for "enable chain" --
enable_o <= enable_sreg(enable_sreg'left);
-- rnd output --
data_o <= sync_ff(1);
end neorv32_trng_ring_osc_rtl;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
\ auto-generated base regions for CSRs in the PROM
h# 40000 constant sbusfpga_csraddr_leds
h# 41000 constant sbusfpga_csraddr_curve25519engine
h# 42000 constant sbusfpga_csraddr_ddrphy
h# 43000 constant sbusfpga_csraddr_exchange_with_mem
h# 44000 constant sbusfpga_csraddr_sbus_bus_stat
h# 45000 constant sbusfpga_csraddr_sdram
h# 46000 constant sbusfpga_csraddr_trng
h# 80000 constant sbusfpga_regionaddr_usb_host_ctrl
h# 0 constant sbusfpga_regionaddr_prom
h# 80000000 constant sbusfpga_regionaddr_main_ram
h# fc000000 constant sbusfpga_regionaddr_usb_fake_dma
h# a0000 constant sbusfpga_regionaddr_curve25519engine
h# 40000 constant sbusfpga_regionaddr_csr

View File

@ -0,0 +1,31 @@
purpose: Load file for SBusFPGA
command: &builder &this
\ in: ${BP}/dev/usb2/device/hub/build/hub.fc
\ in: ${BP}/dev/usb2/device/generic/build/generic.fc
\ in: ${BP}/dev/usb2/device/net/build/usbnet.fc
\ in: ${BP}/dev/usb2/device/serial/build/usbserial.fc
\ in: ${BP}/dev/usb2/device/storage/build/usbstorage.fc
\ in: ${BP}/dev/usb2/device/keyboard/build/usbkbd.fc
\ in: ${BP}/dev/usb2/device/mouse/build/usbmouse.fc
build-now
\ silent on
begin-tokenizing prom_migen.fc
fload prom_migen.fth
end-tokenizing
\ h# 8000 to reserved-start
\ h# f000 to reserved-end
\ " ${BP}/dev/usb2/device/hub/build/hub.fc" " usb,class9" $add-dropin
\ " ${BP}/dev/usb2/device/generic/build/generic.fc" " usbdevice" $add-deflated-dropin
\ " ${BP}/dev/usb2/device/net/build/usbnet.fc" " usbnet" $add-deflated-dropin
\ " ${BP}/dev/usb2/device/keyboard/build/usbkbd.fc" " usb,class3,1,1" $add-deflated-dropin
\ " ${BP}/dev/usb2/device/mouse/build/usbmouse.fc" " usb,class3,1,2" $add-deflated-dropin
\ " ${BP}/dev/usb2/device/serial/build/usbserial.fc" " usbserial" $add-deflated-dropin
\ " ${BP}/dev/usb2/device/storage/build/usbstorage.fc" " usbstorage" $add-deflated-dropin

View File

@ -0,0 +1,247 @@
fcode-version2
\ loads constants
fload prom_csr.fth
\ fload v2compat.fth
\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ LEDs
\ Absolute minimal stuff; name & registers def.
" RDOL,led" device-name
my-address sbusfpga_csraddr_leds + my-space h# 4 reg
\ we don't support ET or HWORD
h# 7d xdrint " slave-burst-sizes" attribute
h# 7d xdrint " burst-sizes" attribute
headers
-1 instance value led-virt
my-address constant my-sbus-address
my-space constant my-sbus-space
: map-in ( adr space size -- virt ) " map-in" $call-parent ;
: map-out ( virt size -- ) " map-out" $call-parent ;
: map-in-led ( -- ) my-sbus-address sbusfpga_csraddr_leds + my-sbus-space h# 4 map-in is led-virt ;
: map-out-led ( -- ) led-virt h# 4 map-out ;
: setled! ( pattern -- )
map-in-led
led-virt l! ( pattern virt -- )
map-out-led
;
\ h# a5 setled!
\ OpenBIOS tokenizer won't accept finish-device without new-device
\ Cheat by using the tokenizer so we can do OpenBoot 2.x siblings
\ tokenizer[ 01 emit-byte h# 27 emit-byte h# 01 emit-byte h# 1f emit-byte ]tokenizer
\ The OpenFirmware tokenizer does accept the 'clean' syntax
finish-device
\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ USB OHCI
new-device
\ Absolute minimal stuff; name & registers def.
" generic-ohci" device-name
\ USB registers are in the device space, not the CSR space
my-address sbusfpga_regionaddr_usb_host_ctrl + my-space h# 1000 reg
\ we don't support ET or anything non-32bits
h# 7c xdrint " slave-burst-sizes" attribute
h# 7c xdrint " burst-sizes" attribute
1 xdrint " interrupts" attribute
headers
-1 instance value regs-virt
my-address constant my-sbus-address
my-space constant my-sbus-space
: map-in ( adr space size -- virt ) " map-in" $call-parent ;
: map-out ( virt size -- ) " map-out" $call-parent ;
: map-in-regs ( -- ) my-sbus-address sbusfpga_regionaddr_usb_host_ctrl + my-sbus-space h# 1000 map-in is regs-virt ;
: map-out-regs ( -- ) regs-virt h# 1000 map-out ;
: my-reset! ( -- )
map-in-regs
00000001 regs-virt h# 4 + l! ( -- ) ( reset the HC )
00000000 regs-virt h# 18 + l! ( -- ) ( reset HCCA & friends )
00000000 regs-virt h# 1c + l! ( -- )
00000000 regs-virt h# 20 + l! ( -- )
00000000 regs-virt h# 24 + l! ( -- )
00000000 regs-virt h# 28 + l! ( -- )
00000000 regs-virt h# 2c + l! ( -- )
00000000 regs-virt h# 30 + l! ( -- )
map-out-regs
;
my-reset!
\ " ohci" encode-string " device_type" property
\ fload openfirmware/dev/usb2/hcd/ohci/loadpkg-sbus.fth
\ open
\ OpenBIOS tokenizer won't accept finish-device without new-device
\ Cheat by using the tokenizer so we can do OpenBoot 2.x siblings
\ tokenizer[ 01 emit-byte h# 27 emit-byte h# 01 emit-byte h# 1f emit-byte ]tokenizer
\ The OpenFirmware tokenizer does accept the 'clean' syntax
finish-device
\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ SDRAM
new-device
\ Absolute minimal stuff; name & registers def.
" RDOL,sdram" device-name
\ three pages of registers:
my-address sbusfpga_csraddr_ddrphy + my-space xdrphys \ Offset#1
h# 1000 xdrint xdr+ \ Merge size#1
my-address sbusfpga_csraddr_sdram + my-space xdrphys xdr+ \ Merge offset#2
h# 1000 xdrint xdr+ \ Merge size#2
my-address sbusfpga_csraddr_exchange_with_mem + my-space xdrphys xdr+ \ Merge offset#3
h# 1000 xdrint xdr+ \ Merge size#3
\ my-address sbusfpga_regionaddr_main_ram + my-space xdrphys xdr+ \ Merge offset#4
\ h# 10000 xdrint xdr+ \ Merge size#4
" reg" attribute
\ we don't support ET or anything non-32bits
h# 7c xdrint " slave-burst-sizes" attribute
h# 7c xdrint " burst-sizes" attribute
headers
-1 instance value mregs-ddrphy-virt
-1 instance value mregs-sdram-virt
-1 instance value mregs-exchange_with_mem-virt
my-address constant my-sbus-address
my-space constant my-sbus-space
: map-in ( adr space size -- virt ) " map-in" $call-parent ;
: map-out ( virt size -- ) " map-out" $call-parent ;
: map-in-mregs ( -- )
my-sbus-address sbusfpga_csraddr_ddrphy + my-sbus-space h# 1000 map-in is mregs-ddrphy-virt
my-sbus-address sbusfpga_csraddr_sdram + my-sbus-space h# 1000 map-in is mregs-sdram-virt
my-sbus-address sbusfpga_csraddr_exchange_with_mem + my-sbus-space h# 1000 map-in is mregs-exchange_with_mem-virt
;
: map-out-mregs ( -- )
mregs-ddrphy-virt h# 1000 map-out
mregs-sdram-virt h# 1000 map-out
mregs-exchange_with_mem-virt h# 1000 map-out
;
\ fload sdram_init.fth
\ init!
\ OpenBIOS tokenizer won't accept finish-device without new-device
\ Cheat by using the tokenizer so we can do OpenBoot 2.x siblings
\ tokenizer[ 01 emit-byte h# 27 emit-byte h# 01 emit-byte h# 1f emit-byte ]tokenizer
\ The OpenFirmware tokenizer does accept the 'clean' syntax
finish-device
\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ TRNG
new-device
\ Absolute minimal stuff; name & registers def.
" RDOL,neorv32trng" device-name
my-address sbusfpga_csraddr_trng + my-space h# 8 reg
\ we don't support ET or HWORD
h# 7d xdrint " slave-burst-sizes" attribute
h# 7d xdrint " burst-sizes" attribute
headers
-1 instance value trng-virt
my-address constant my-sbus-address
my-space constant my-sbus-space
: map-in ( adr space size -- virt ) " map-in" $call-parent ;
: map-out ( virt size -- ) " map-out" $call-parent ;
: map-in-trng ( -- ) my-sbus-address sbusfpga_csraddr_trng + my-sbus-space h# 8 map-in is trng-virt ;
: map-out-trng ( -- ) trng-virt h# 8 map-out ;
: disabletrng! ( -- )
map-in-trng
1 trng-virt l! ( pattern virt -- )
map-out-trng
;
disabletrng!
\ OpenBIOS tokenizer won't accept finish-device without new-device
\ Cheat by using the tokenizer so we can do OpenBoot 2.x siblings
\ tokenizer[ 01 emit-byte h# 27 emit-byte h# 01 emit-byte h# 1f emit-byte ]tokenizer
\ The OpenFirmware tokenizer does accept the 'clean' syntax
finish-device
\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ CURVE25519
new-device
\ Absolute minimal stuff; name & registers def.
" betrustedc25519e" device-name
\ one page of CSR registers, plus the memory
\ we might want to replace the slave memory access
\ by another instance of exchange_with_mem ?
\ we split the memory space in two
\ 0x1000 @ 0x0 for the microcode
\ 0x10000 @ 0x10000 for the register file
my-address sbusfpga_csraddr_curve25519engine + my-space xdrphys \ Offset#1
h# 1000 xdrint xdr+ \ Merge size#1
my-address sbusfpga_regionaddr_curve25519engine + my-space xdrphys xdr+ \ Merge offset#2
h# 1000 xdrint xdr+ \ Merge size#2
my-address sbusfpga_regionaddr_curve25519engine h# 10000 + + my-space xdrphys xdr+ \ Merge offset#3
h# 10000 xdrint xdr+ \ Merge size#3
" reg" attribute
\ we don't support ET or HWORD
h# 7d xdrint " slave-burst-sizes" attribute
h# 7d xdrint " burst-sizes" attribute
headers
-1 instance value curve25519engine-virt
-1 instance value curve25519engine-microcode-virt
-1 instance value curve25519engine-regfile-virt
my-address constant my-sbus-address
my-space constant my-sbus-space
: map-in ( adr space size -- virt ) " map-in" $call-parent ;
: map-out ( virt size -- ) " map-out" $call-parent ;
: map-in-curve25519engine ( -- )
my-sbus-address sbusfpga_csraddr_curve25519engine + my-sbus-space h# 1000 map-in is curve25519engine-virt
my-sbus-address sbusfpga_regionaddr_curve25519engine + my-sbus-space h# 1000 map-in is curve25519engine-microcode-virt
my-sbus-address sbusfpga_regionaddr_curve25519engine h# 10000 + + my-sbus-space h# 10000 map-in is curve25519engine-regfile-virt
;
: map-out-curve25519engine ( -- )
curve25519engine-virt h# 1000 map-out
curve25519engine-microcode-virt h# 1000 map-out
curve25519engine-regfile-virt h# 10000 map-out
;
\ OpenBIOS tokenizer won't accept finish-device without new-device
\ Cheat by using the tokenizer so we can do OpenBoot 2.x siblings
\ tokenizer[ 01 emit-byte h# 27 emit-byte h# 01 emit-byte h# 1f emit-byte ]tokenizer
\ The OpenFirmware tokenizer does accept the 'clean' syntax
finish-device
\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ STAT
new-device
\ Absolute minimal stuff; name & registers def.
" RDOL,sbusstat" device-name
my-address sbusfpga_csraddr_sbus_bus_stat + my-space h# 100 reg
\ we don't support ET or HWORD
h# 7d xdrint " slave-burst-sizes" attribute
h# 7d xdrint " burst-sizes" attribute
headers
-1 instance value sbus_bus_stat-virt
my-address constant my-sbus-address
my-space constant my-sbus-space
: map-in ( adr space size -- virt ) " map-in" $call-parent ;
: map-out ( virt size -- ) " map-out" $call-parent ;
: map-in-sbus_bus_stat ( -- ) my-sbus-address sbusfpga_csraddr_sbus_bus_stat + my-sbus-space h# 100 map-in is sbus_bus_stat-virt ;
: map-out-sbus_bus_stat ( -- ) sbus_bus_stat-virt h# 100 map-out ;
end0

View File

@ -0,0 +1,239 @@
from migen import *
from migen.genlib.fifo import *
from migen.genlib.cdc import BusSynchronizer
from litex.soc.interconnect.csr import *
from litex.soc.interconnect import wishbone
# width of towrite_fifo is '32'+'burst_size * 32' (vaddr + data)
# so the SBus DMA has all the needed info
# width of fromsbus_req_fifo is 'blk_addr_width' + 'vaddr' (blk_addr + vaddr)
# width of fromsbus_fifo is 'blk_addr_width' + 'burst_size * 32' (blk_addr + data)
# the blk_addr does the round-trip to accompany the data
class ExchangeWithMem(Module, AutoCSR):
def __init__(self, soc, tosbus_fifo, fromsbus_fifo, fromsbus_req_fifo, dram_dma_writer, dram_dma_reader, burst_size = 8, do_checksum = False):
#self.wishbone_r_slave = wishbone.Interface(data_width=soc.bus.data_width)
#self.wishbone_w_slave = wishbone.Interface(data_width=soc.bus.data_width)
self.tosbus_fifo = tosbus_fifo
self.fromsbus_fifo = fromsbus_fifo
self.fromsbus_req_fifo = fromsbus_req_fifo
self.dram_dma_writer = dram_dma_writer
self.dram_dma_reader = dram_dma_reader
data_width = burst_size * 4
data_width_bits = burst_size * 32
blk_addr_width = 32 - log2_int(data_width) # 27 for burst_size == 8
assert(len(self.dram_dma_writer.sink.data) == data_width_bits)
assert(len(self.dram_dma_reader.source.data) == data_width_bits)
#self.wishbone_r_master = wishbone.Interface(data_width=data_width_bits)
#self.wishbone_w_master = wishbone.Interface(data_width=data_width_bits)
#self.submodules += wishbone.Converter(self.wishbone_r_master, self.wishbone_r_slave)
#self.submodules += wishbone.Converter(self.wishbone_w_master, self.wishbone_w_slave)
print("ExchangeWithMem: data_width = {}, data_width_bits = {}, blk_addr_width = {}\n".format(data_width, data_width_bits, blk_addr_width))
print("ExchangeWithMem: tosbus_fifo width = {}, fromsbus_fifo width = {}, fromsbus_req_fifo width = {}\n".format(len(tosbus_fifo.din), len(fromsbus_fifo.dout), len(fromsbus_req_fifo.din)))
local_r_addr = Signal(blk_addr_width)
dma_r_addr = Signal(32)
#local_r_widx = Signal(log2_int(burst_size)) # so width is 3 for burst_size == 8
#local_r_buffer = Signal(data_width_bits)
local_w_addr = Signal(blk_addr_width)
dma_w_addr = Signal(32)
#local_w_widx = Signal(log2_int(burst_size)) # so width is 3 for burst_size == 8
#local_w_buffer = Signal(data_width_bits)
max_block_bits=16
# CSRConstant do not seem to appear in the CSR Map, but they need to be accessible to the OS driver
#self.blk_size = CSRConstant(value=data_width) # report the block size to the SW layer
#self.blk_base = CSRConstant(value=soc.wb_mem_map["main_ram"] >> log2_int(data_width)) # report where the blk starts
self.blk_size = CSRStatus(32) # report the block size to the SW layer
self.blk_base = CSRStatus(32) # report where the blk starts
self.mem_size = CSRStatus(32) # report how much memory we have
self.comb += self.blk_size.status.eq(data_width)
self.comb += self.blk_base.status.eq(soc.wb_mem_map["main_ram"] >> log2_int(data_width))
self.comb += self.mem_size.status.eq((256 * 1024 * 1024) >> log2_int(data_width)) # is it already available from mem_regions ?
self.blk_addr = CSRStorage(32, description = "SDRAM Block address to read/write from Wishbone memory (block of size {})".format(data_width))
self.dma_addr = CSRStorage(32, description = "Host Base address where to write/read data (i.e. SPARC Virtual addr)")
#self.blk_cnt = CSRStorage(32, write_from_dev=True, description = "How many blk to read/write (max 2^{}-1); bit 31 is RD".format(max_block_bits), reset = 0)
self.blk_cnt = CSRStorage(write_from_dev=True, fields = [CSRField("blk_cnt", max_block_bits, description = "How many blk to read/write (max 2^{}-1)".format(max_block_bits)),
CSRField("rsvd", 32 - (max_block_bits + 1), description = "Reserved"),
CSRField("rd_wr", 1, description = "Read/Write selector"),
])
self.last_blk = CSRStatus(32, description = "Last Blk addr finished on WB side")
self.last_dma = CSRStatus(32, description = "Last DMA addr finished on WB side")
self.dma_wrdone = CSRStatus(32, description = "DMA Block written to SDRAM", reset = 0)
self.blk_rem = CSRStatus(32, description = "How many block remaining; bit 31 is RD", reset = 0)
self.dma_status = CSRStatus(fields = [CSRField("rd_fsm_busy", 1, description = "Read FSM is doing some work"),
CSRField("wr_fsm_busy", 1, description = "Write FSM is doing some work"),
CSRField("has_wr_data", 1, description = "Data available to write to SDRAM"),
CSRField("has_requests", 1, description = "There's outstanding requests to the SBus"),
CSRField("has_rd_data", 1, description = "Data available to write to SBus"),
])
self.wr_tosdram = CSRStatus(32, description = "Last address written to SDRAM")
if (do_checksum):
self.checksum = CSRStorage(data_width_bits, write_from_dev=True, description = "checksum (XOR)");
self.submodules.req_r_fsm = req_r_fsm = FSM(reset_state="Reset")
self.submodules.req_w_fsm = req_w_fsm = FSM(reset_state="Reset")
self.comb += self.dma_status.fields.rd_fsm_busy.eq(~req_r_fsm.ongoing("Idle")) # Read FSM Busy
self.comb += self.dma_status.fields.wr_fsm_busy.eq(~req_w_fsm.ongoing("Idle")) # Write FSM Busy
self.comb += self.dma_status.fields.has_wr_data.eq(self.fromsbus_fifo.readable) # Some data available to write to memory
# The next two status bits reflect stats in the SBus clock domain
self.submodules.fromsbus_req_fifo_readable_sync = BusSynchronizer(width = 1, idomain = "sbus", odomain = "sys")
fromsbus_req_fifo_readable_in_sys = Signal()
self.comb += self.fromsbus_req_fifo_readable_sync.i.eq(self.fromsbus_req_fifo.readable)
self.comb += fromsbus_req_fifo_readable_in_sys.eq(self.fromsbus_req_fifo_readable_sync.o)
# w/o this extra delay, the driver sees an outdated checksum for some reason...
# there's probably a more fundamental issue :-(
# note: replaced PulseSynchronizer with BusSynchronizer, should I retry w/o this ?
fromsbus_req_fifo_readable_in_sys_cnt = Signal(5)
self.sync += If(fromsbus_req_fifo_readable_in_sys,
fromsbus_req_fifo_readable_in_sys_cnt.eq(0x1F)
).Else(
If(fromsbus_req_fifo_readable_in_sys_cnt > 0,
fromsbus_req_fifo_readable_in_sys_cnt.eq(fromsbus_req_fifo_readable_in_sys_cnt - 1)
)
)
#self.comb += self.dma_status.fields.has_requests.eq(fromsbus_req_fifo_readable_in_sys) # we still have outstanding requests
self.comb += self.dma_status.fields.has_requests.eq(fromsbus_req_fifo_readable_in_sys | (fromsbus_req_fifo_readable_in_sys_cnt != 0)) # we still have outstanding requests, or had recently
self.submodules.tosbus_fifo_readable_sync = BusSynchronizer(width = 1, idomain = "sbus", odomain = "sys")
tosbus_fifo_readable_in_sys = Signal()
self.comb += self.tosbus_fifo_readable_sync.i.eq(self.tosbus_fifo.readable)
self.comb += tosbus_fifo_readable_in_sys.eq(self.tosbus_fifo_readable_sync.o)
self.comb += self.dma_status.fields.has_rd_data.eq(tosbus_fifo_readable_in_sys) # there's still data to be sent to memory; this will drop before the last SBus Master Cycle is finished, but then the SBus is busy so the host won't be able to read the status before the cycle is finished so we're good
#self.comb += self.dma_status.status[16:17].eq(self.wishbone_w_master.cyc) # show the WB iface status (W)
#self.comb += self.dma_status.status[17:18].eq(self.wishbone_w_master.stb)
#self.comb += self.dma_status.status[18:19].eq(self.wishbone_w_master.we)
#self.comb += self.dma_status.status[19:20].eq(self.wishbone_w_master.ack)
#self.comb += self.dma_status.status[20:21].eq(self.wishbone_w_master.err)
#self.comb += self.dma_status.status[24:25].eq(self.wishbone_r_master.cyc) # show the WB iface status (R)
#self.comb += self.dma_status.status[25:26].eq(self.wishbone_r_master.stb)
#self.comb += self.dma_status.status[26:27].eq(self.wishbone_r_master.we)
#self.comb += self.dma_status.status[27:28].eq(self.wishbone_r_master.ack)
#self.comb += self.dma_status.status[28:29].eq(self.wishbone_r_master.err)
req_r_fsm.act("Reset",
NextState("Idle")
)
req_r_fsm.act("Idle",
If(((self.blk_cnt.fields.blk_cnt != 0) & # checking self.blk_cnt.re might be too transient ? -> need to auto-reset
(~self.blk_cnt.fields.rd_wr)), # !read -> write
NextValue(local_r_addr, self.blk_addr.storage),
NextValue(dma_r_addr, self.dma_addr.storage),
NextValue(self.blk_rem.status, Cat(self.blk_cnt.fields.blk_cnt, Signal(32-max_block_bits, reset = 0))),
NextState("ReqFromMemory")
).Elif(((self.blk_cnt.fields.blk_cnt != 0) & # checking self.blk_cnt.re might be too transient ? -> need to auto-reset
(self.blk_cnt.fields.rd_wr)), # read
NextValue(local_r_addr, self.blk_addr.storage),
NextValue(dma_r_addr, self.dma_addr.storage),
NextValue(self.blk_rem.status, Cat(self.blk_cnt.fields.blk_cnt, Signal(32-max_block_bits, reset = 0))),
NextState("QueueReqToMemory")
)
)
req_r_fsm.act("ReqFromMemory",
self.dram_dma_reader.sink.address.eq(local_r_addr),
self.dram_dma_reader.sink.valid.eq(1),
If(self.dram_dma_reader.sink.ready,
NextState("WaitForData")
)
)
req_r_fsm.act("WaitForData",
If(self.dram_dma_reader.source.valid & self.tosbus_fifo.writable,
self.tosbus_fifo.we.eq(1),
self.tosbus_fifo.din.eq(Cat(dma_r_addr, self.dram_dma_reader.source.data)),
If(do_checksum,
self.checksum.we.eq(1),
self.checksum.dat_w.eq(self.checksum.storage ^ self.dram_dma_reader.source.data),
),
self.dram_dma_reader.source.ready.eq(1),
NextValue(self.last_blk.status, local_r_addr),
NextValue(self.last_dma.status, dma_r_addr),
NextValue(self.blk_rem.status, self.blk_rem.status - 1),
If(self.blk_rem.status[0:max_block_bits] <= 1,
self.blk_cnt.we.eq(1), ## auto-reset
self.blk_cnt.dat_w.eq(0),
NextState("Idle"),
).Else(
NextValue(local_r_addr, local_r_addr + 1),
NextValue(dma_r_addr, dma_r_addr + data_width),
NextState("ReqFromMemory"),
)
)
)
req_r_fsm.act("QueueReqToMemory",
If(self.fromsbus_req_fifo.writable,
self.fromsbus_req_fifo.we.eq(1),
self.fromsbus_req_fifo.din.eq(Cat(local_r_addr, dma_r_addr)),
NextValue(self.last_blk.status, local_r_addr),
NextValue(self.last_dma.status, dma_r_addr),
NextValue(self.blk_rem.status, self.blk_rem.status - 1),
If(self.blk_rem.status[0:max_block_bits] <= 1,
self.blk_cnt.we.eq(1), ## auto-reset
self.blk_cnt.dat_w.eq(0),
NextState("Idle"),
).Else(
NextValue(local_r_addr, local_r_addr + 1),
NextValue(dma_r_addr, dma_r_addr + data_width),
NextValue(self.blk_rem.status, self.blk_rem.status - 1),
NextState("QueueReqToMemory"), #redundant
)
)
)
# req_w_fsm.act("Reset",
# NextState("Idle")
# )
# req_w_fsm.act("Idle",
# If(self.fromsbus_fifo.readable &
# ~self.wishbone_w_master.ack,
# self.fromsbus_fifo.re.eq(1),
# NextValue(self.wishbone_w_master.cyc, 1),
# NextValue(self.wishbone_w_master.stb, 1),
# NextValue(self.wishbone_w_master.sel, 2**len(self.wishbone_w_master.sel)-1),
# NextValue(self.wishbone_w_master.we, 1),
# NextValue(self.wishbone_w_master.adr, self.fromsbus_fifo.dout[0:blk_addr_width]),
# NextValue(self.wishbone_w_master.dat_w, self.fromsbus_fifo.dout[blk_addr_width:(blk_addr_width + data_width_bits)]),
# NextValue(self.wr_tosdram.status, self.fromsbus_fifo.dout[0:blk_addr_width]),
# NextState("WaitForAck")
# )
# )
# req_w_fsm.act("WaitForAck",
# If(self.wishbone_w_master.ack,
# NextValue(self.wishbone_w_master.cyc, 0),
# NextValue(self.wishbone_w_master.stb, 0),
# NextState("Idle"),
# )
# )
req_w_fsm.act("Reset",
NextState("Idle")
)
req_w_fsm.act("Idle",
If(self.fromsbus_fifo.readable,
self.dram_dma_writer.sink.address.eq(self.fromsbus_fifo.dout[0:blk_addr_width]),
self.dram_dma_writer.sink.data.eq(self.fromsbus_fifo.dout[blk_addr_width:(blk_addr_width + data_width_bits)]),
self.dram_dma_writer.sink.valid.eq(1),
NextValue(self.wr_tosdram.status, self.fromsbus_fifo.dout[0:blk_addr_width]),
If(self.dram_dma_writer.sink.ready,
self.fromsbus_fifo.re.eq(1),
NextValue(self.dma_wrdone.status, self.dma_wrdone.status + 1),
If(do_checksum,
self.checksum.we.eq(1),
self.checksum.dat_w.eq(self.checksum.storage ^ self.fromsbus_fifo.dout[blk_addr_width:(blk_addr_width + data_width_bits)]),
)
)
)
)

View File

@ -0,0 +1,133 @@
import os
import json
import inspect
from shutil import which
from sysconfig import get_platform
from migen import *
from litex.soc.interconnect.csr import CSRStatus
from litex.build.tools import generated_banner
from litex.soc.doc.rst import reflow
from litex.soc.doc.module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts
from litex.soc.doc.csr import DocumentedCSRRegion
from litex.soc.interconnect.csr import _CompoundCSR
# for generating a timestamp in the description field, if none is otherwise given
import datetime
import time
def _get_rw_functions_c(name, csr_name, reg_base, area_base, nwords, busword, alignment, read_only, with_access_functions):
reg_name = name + "_" + csr_name
r = ""
addr_str = "CSR_{}_ADDR".format(reg_name.upper())
size_str = "CSR_{}_SIZE".format(reg_name.upper())
r += "#define {} (CSR_{}_BASE + {}L)\n".format(addr_str, name.upper(), hex(reg_base - area_base))
r += "#define {} {}\n".format(size_str, nwords)
size = nwords*busword//8
if size > 8:
# downstream should select appropriate `csr_[rd|wr]_buf_uintX()` pair!
return r
elif size > 4:
ctype = "uint64_t"
elif size > 2:
ctype = "uint32_t"
elif size > 1:
ctype = "uint16_t"
else:
ctype = "uint8_t"
stride = alignment//8;
if with_access_functions:
r += "static inline {} {}_read(struct sbusfpga_{}_softc *sc) {{\n".format(ctype, reg_name, name)
if nwords > 1:
r += "\t{} r = bus_space_read_4(sc->sc_bustag, sc->sc_bhregs_{}, {}L);\n".format(ctype, name, hex(reg_base - area_base))
for sub in range(1, nwords):
r += "\tr <<= {};\n".format(busword)
r += "\tr |= bus_space_read_4(sc->sc_bustag, sc->sc_bhregs_{}, {}L);\n".format(name, hex(reg_base - area_base + sub*stride))
r += "\treturn r;\n}\n"
else:
r += "\treturn bus_space_read_4(sc->sc_bustag, sc->sc_bhregs_{}, {}L);\n}}\n".format(name, hex(reg_base - area_base))
if not read_only:
r += "static inline void {}_write(struct sbusfpga_{}_softc *sc, {} v) {{\n".format(reg_name, name, ctype)
for sub in range(nwords):
shift = (nwords-sub-1)*busword
if shift:
v_shift = "v >> {}".format(shift)
else:
v_shift = "v"
r += "\tbus_space_write_4(sc->sc_bustag, sc->sc_bhregs_{}, {}L, {});\n".format(name, hex(reg_base - area_base + sub*stride), v_shift)
r += "}\n"
return r
def get_csr_header(regions, constants, csr_base=None, with_access_functions=True):
alignment = constants.get("CONFIG_CSR_ALIGNMENT", 32)
r = generated_banner("//")
#if with_access_functions: # FIXME
# r += "#include <generated/soc.h>\n"
r += "#ifndef __GENERATED_CSR_H\n#define __GENERATED_CSR_H\n"
#if with_access_functions:
# r += "#include <stdint.h>\n"
# r += "#include <system.h>\n"
# r += "#ifndef CSR_ACCESSORS_DEFINED\n"
# r += "#include <hw/common.h>\n"
# r += "#endif /* ! CSR_ACCESSORS_DEFINED */\n"
csr_base = csr_base if csr_base is not None else regions[next(iter(regions))].origin
r += "#ifndef CSR_BASE\n"
r += "#define CSR_BASE {}L\n".format(hex(csr_base))
r += "#endif\n"
for name, region in regions.items():
origin = region.origin - csr_base
r += "\n/* "+name+" */\n"
r += "#ifndef CSR_"+name.upper()+"_BASE\n"
r += "#define CSR_"+name.upper()+"_BASE (CSR_BASE + "+hex(origin)+"L)\n"
if not isinstance(region.obj, Memory):
for csr in region.obj:
nr = (csr.size + region.busword - 1)//region.busword
r += _get_rw_functions_c(name, csr.name, origin, region.origin - csr_base, nr, region.busword, alignment,
getattr(csr, "read_only", False), with_access_functions)
origin += alignment//8*nr
if hasattr(csr, "fields"):
for field in csr.fields.fields:
offset = str(field.offset)
size = str(field.size)
r += "#define CSR_"+name.upper()+"_"+csr.name.upper()+"_"+field.name.upper()+"_OFFSET "+offset+"\n"
r += "#define CSR_"+name.upper()+"_"+csr.name.upper()+"_"+field.name.upper()+"_SIZE "+size+"\n"
if with_access_functions and csr.size <= 32: # FIXME: Implement extract/read functions for csr.size > 32-bit.
reg_name = name + "_" + csr.name.lower()
field_name = reg_name + "_" + field.name.lower()
r += "static inline uint32_t " + field_name + "_extract(struct sbusfpga_" + name + "_softc *sc, uint32_t oldword) {\n"
r += "\tuint32_t mask = ((1 << " + size + ")-1);\n"
r += "\treturn ( (oldword >> " + offset + ") & mask );\n}\n"
r += "static inline uint32_t " + field_name + "_read(struct sbusfpga_" + name + "_softc *sc) {\n"
r += "\tuint32_t word = " + reg_name + "_read(sc);\n"
r += "\treturn " + field_name + "_extract(sc, word);\n"
r += "}\n"
if not getattr(csr, "read_only", False):
r += "static inline uint32_t " + field_name + "_replace(struct sbusfpga_" + name + "_softc *sc, uint32_t oldword, uint32_t plain_value) {\n"
r += "\tuint32_t mask = ((1 << " + size + ")-1);\n"
r += "\treturn (oldword & (~(mask << " + offset + "))) | (mask & plain_value)<< " + offset + " ;\n}\n"
r += "static inline void " + field_name + "_write(struct sbusfpga_" + name + "_softc *sc, uint32_t plain_value) {\n"
r += "\tuint32_t oldword = " + reg_name + "_read(sc);\n"
r += "\tuint32_t newword = " + field_name + "_replace(sc, oldword, plain_value);\n"
r += "\t" + reg_name + "_write(sc, newword);\n"
r += "}\n"
r += "#endif // CSR_"+name.upper()+"_BASE\n"
r += "\n#endif\n"
return r
def get_csr_forth_header(csr_regions, mem_regions, constants, csr_base=None):
r = "\\ auto-generated base regions for CSRs in the PROM\n"
for name, region in csr_regions.items():
r += "h# " + hex(region.origin).replace("0x", "") + " constant " + "sbusfpga_csraddr_{}".format(name) + "\n"
for name, region in mem_regions.items():
r += "h# " + hex(region.origin).replace("0x", "") + " constant " + "sbusfpga_regionaddr_{}".format(name) + "\n"
return r

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
from migen import *
from migen.genlib.cdc import BusSynchronizer
from litex.soc.interconnect.csr import *
from litex.soc.interconnect import wishbone
class SBusFPGABusStat(Module, AutoCSR):
def __init__(self, sbus_bus):
self.stat_ctrl = CSRStorage(fields = [CSRField("update", 1, description = "update")])
self.submodules.sync_update = BusSynchronizer(width = 1, idomain="sys", odomain="sbus")
self.comb += self.sync_update.i.eq(self.stat_ctrl.fields.update)
self.comb += sbus_bus.stat_update.eq(self.sync_update.o)
self.live_stat_cycle_counter = CSRStatus(32, description="live_stat_cycle_counter")
self.stat_cycle_counter = CSRStatus(32, description="stat_cycle_counter")
self.stat_slave_start_counter = CSRStatus(32, description="stat_slave_start_counter")
self.stat_slave_done_counter = CSRStatus(32, description="stat_slave_done_counter")
self.stat_slave_rerun_counter = CSRStatus(32, description="stat_slave_rerun_counter")
self.stat_slave_early_error_counter = CSRStatus(32, description="stat_slave_early_error_counter")
self.stat_master_start_counter = CSRStatus(32, description="stat_master_start_counter")
self.stat_master_done_counter = CSRStatus(32, description="stat_master_done_counter")
self.stat_master_error_counter = CSRStatus(32, description="stat_master_error_counter")
self.stat_master_rerun_counter = CSRStatus(32, description="stat_master_rerun_counter")
self.sbus_master_error_virtual = CSRStatus(32, description="sbus_master_error_virtual")
self.submodules.sync_live_stat_cycle_counter = BusSynchronizer(width = 32, idomain="sbus", odomain="sys")
self.comb += self.sync_live_stat_cycle_counter.i.eq(sbus_bus.stat_cycle_counter)
self.comb += self.live_stat_cycle_counter.status.eq(self.sync_live_stat_cycle_counter.o)
self.submodules.sync_stat_cycle_counter = BusSynchronizer(width = 32, idomain="sbus", odomain="sys")
self.comb += self.sync_stat_cycle_counter.i.eq(sbus_bus.buf_stat_cycle_counter)
self.comb += self.stat_cycle_counter.status.eq(self.sync_stat_cycle_counter.o)
self.submodules.sync_stat_slave_start_counter = BusSynchronizer(width = 32, idomain="sbus", odomain="sys");
self.comb += self.sync_stat_slave_start_counter.i.eq(sbus_bus.buf_stat_slave_start_counter)
self.comb += self.stat_slave_start_counter.status.eq(self.sync_stat_slave_start_counter.o)
self.submodules.sync_stat_slave_done_counter = BusSynchronizer(width = 32, idomain="sbus", odomain="sys");
self.comb += self.sync_stat_slave_done_counter.i.eq(sbus_bus.buf_stat_slave_done_counter)
self.comb += self.stat_slave_done_counter.status.eq(self.sync_stat_slave_done_counter.o)
self.submodules.sync_stat_slave_rerun_counter = BusSynchronizer(width = 32, idomain="sbus", odomain="sys");
self.comb += self.sync_stat_slave_rerun_counter.i.eq(sbus_bus.buf_stat_slave_rerun_counter)
self.comb += self.stat_slave_rerun_counter.status.eq(self.sync_stat_slave_rerun_counter.o)
self.submodules.sync_stat_slave_early_error_counter = BusSynchronizer(width = 32, idomain="sbus", odomain="sys");
self.comb += self.sync_stat_slave_early_error_counter.i.eq(sbus_bus.buf_stat_slave_early_error_counter)
self.comb += self.stat_slave_early_error_counter.status.eq(self.sync_stat_slave_early_error_counter.o)
self.submodules.sync_stat_master_start_counter = BusSynchronizer(width = 32, idomain="sbus", odomain="sys");
self.comb += self.sync_stat_master_start_counter.i.eq(sbus_bus.buf_stat_master_start_counter)
self.comb += self.stat_master_start_counter.status.eq(self.sync_stat_master_start_counter.o)
self.submodules.sync_stat_master_done_counter = BusSynchronizer(width = 32, idomain="sbus", odomain="sys");
self.comb += self.sync_stat_master_done_counter.i.eq(sbus_bus.buf_stat_master_done_counter)
self.comb += self.stat_master_done_counter.status.eq(self.sync_stat_master_done_counter.o)
self.submodules.sync_stat_master_error_counter = BusSynchronizer(width = 32, idomain="sbus", odomain="sys");
self.comb += self.sync_stat_master_error_counter.i.eq(sbus_bus.buf_stat_master_error_counter)
self.comb += self.stat_master_error_counter.status.eq(self.sync_stat_master_error_counter.o)
self.submodules.sync_stat_master_rerun_counter = BusSynchronizer(width = 32, idomain="sbus", odomain="sys");
self.comb += self.sync_stat_master_rerun_counter.i.eq(sbus_bus.buf_stat_master_rerun_counter)
self.comb += self.stat_master_rerun_counter.status.eq(self.sync_stat_master_rerun_counter.o)
self.submodules.sync_sbus_master_error_virtual = BusSynchronizer(width = 32, idomain="sbus", odomain="sys");
self.comb += self.sync_sbus_master_error_virtual.i.eq(sbus_bus.buf_sbus_master_error_virtual)
self.comb += self.sbus_master_error_virtual.status.eq(self.sync_sbus_master_error_virtual.o)

View File

@ -0,0 +1,350 @@
import os
import argparse
from migen import *
import litex
from litex.build.generic_platform import *
from litex.build.xilinx.vivado import vivado_build_args, vivado_build_argdict
from litex.soc.integration.soc import *
from litex.soc.integration.soc_core import *
from litex.soc.integration.builder import *
from litex.soc.interconnect import wishbone
from litex.soc.cores.clock import *
from litex.soc.cores.led import LedChaser
import ztex213_sbus
from migen.genlib.fifo import *
from litedram.modules import MT41J128M16
from litedram.phy import s7ddrphy
from sbus_to_fpga_fsm import *
from sbus_to_fpga_fsmstat import *
from sbus_to_fpga_blk_dma import *
from sbus_to_fpga_trng import *
from litedram.frontend.dma import *
from engine import Engine;
from migen.genlib.cdc import BusSynchronizer
from migen.genlib.resetsync import AsyncResetSynchronizer;
import sbus_to_fpga_export;
# CRG ----------------------------------------------------------------------------------------------
class _CRG(Module):
def __init__(self, platform, sys_clk_freq, usb=True):
self.clock_domains.cd_sys = ClockDomain() # 100 MHz PLL, reset'ed by SBus (via pll), SoC/Wishbone main clock
self.clock_domains.cd_sys4x = ClockDomain(reset_less=True)
self.clock_domains.cd_sys4x_dqs = ClockDomain(reset_less=True)
self.clock_domains.cd_idelay = ClockDomain()
## self.clock_domains.cd_sys = ClockDomain() # 16.67-25 MHz SBus, reset'ed by SBus, native SBus & SYS clock domain
self.clock_domains.cd_native = ClockDomain(reset_less=True) # 48MHz native, non-reset'ed (for power-on long delay, never reset, we don't want the delay after a warm reset)
self.clock_domains.cd_sbus = ClockDomain() # 16.67-25 MHz SBus, reset'ed by SBus, native SBus clock domain
# self.clock_domains.cd_por = ClockDomain() # 48 MHz native, reset'ed by SBus, power-on-reset timer
if (usb):
self.clock_domains.cd_usb = ClockDomain() # 48 MHZ PLL, reset'ed by SBus (via pll), for USB controller
self.clock_domains.cd_clk50 = ClockDomain() # 50 MHz (gated) for curve25519engine -> eng_clk
#self.clock_domains.cd_clk100 = ClockDomain() # 100 MHz for curve25519engine -> sys_clk
self.clock_domains.cd_clk100_gated = ClockDomain() # 100 MHz (gated) for curve25519engine -> mul_clk
self.clock_domains.cd_clk200 = ClockDomain() # 200 MHz (gated) for curve25519engine -> rf_clk
# # #
clk48 = platform.request("clk48")
###### explanations from betrusted-io/betrusted-soc/betrusted_soc.py
# Note: below feature cannot be used because Litex appends this *after* platform commands! This causes the generated
# clock derived constraints immediately below to fail, because .xdc file is parsed in-order, and the main clock needs
# to be created before the derived clocks. Instead, we use the line afterwards.
platform.add_platform_command("create_clock -name clk48 -period 20.8333 [get_nets clk48]")
# The above constraint must strictly proceed the below create_generated_clock constraints in the .XDC file
# This allows PLLs/MMCMEs to be placed anywhere and reference the input clock
self.clk48_bufg = Signal()
self.specials += Instance("BUFG", i_I=clk48, o_O=self.clk48_bufg)
self.comb += self.cd_native.clk.eq(self.clk48_bufg)
#self.cd_native.clk = clk48
clk_sbus = platform.request("SBUS_3V3_CLK")
self.cd_sbus.clk = clk_sbus
rst_sbus = platform.request("SBUS_3V3_RSTs")
self.comb += self.cd_sbus.rst.eq(~rst_sbus)
##self.cd_sys.clk = clk_sbus
##self.comb += self.cd_sys.rst.eq(~rst_sbus)
self.curve25519_on = Signal()
self.submodules.pll = pll = S7MMCM(speedgrade=-1)
#pll.register_clkin(clk48, 48e6)
pll.register_clkin(self.clk48_bufg, 48e6)
pll.create_clkout(self.cd_sys, sys_clk_freq, gated_replicas={self.cd_clk100_gated : pll.locked & self.curve25519_on})
platform.add_platform_command("create_generated_clock -name sysclk [get_pins {{MMCME2_ADV/CLKOUT0}}]")
pll.create_clkout(self.cd_sys4x, 4*sys_clk_freq)
platform.add_platform_command("create_generated_clock -name sys4xclk [get_pins {{MMCME2_ADV/CLKOUT1}}]")
pll.create_clkout(self.cd_sys4x_dqs, 4*sys_clk_freq, phase=90)
platform.add_platform_command("create_generated_clock -name sys4x90clk [get_pins {{MMCME2_ADV/CLKOUT2}}]")
self.comb += pll.reset.eq(~rst_sbus) # | ~por_done
platform.add_false_path_constraints(self.cd_native.clk, self.cd_sbus.clk)
platform.add_false_path_constraints(self.cd_sbus.clk, self.cd_native.clk)
#platform.add_false_path_constraints(self.cd_sys.clk, self.cd_sbus.clk)
#platform.add_false_path_constraints(self.cd_sbus.clk, self.cd_sys.clk)
##platform.add_false_path_constraints(self.cd_native.clk, self.cd_sys.clk)
pll.create_clkout(self.cd_clk50, sys_clk_freq/2, ce=pll.locked & self.curve25519_on)
platform.add_platform_command("create_generated_clock -name clk50 [get_pins {{MMCME2_ADV/CLKOUT3}}]")
pll.create_clkout(self.cd_clk200, sys_clk_freq*2, ce=pll.locked & self.curve25519_on)
platform.add_platform_command("create_generated_clock -name clk200 [get_pins {{MMCME2_ADV/CLKOUT4}}]")
#self.submodules.curve25519_pll = curve25519_pll = S7MMCM(speedgrade=-1)
#curve25519_clk_freq = 90e6
##self.curve25519_on = Signal()
##curve25519_pll.register_clkin(clk48, 48e6)
#curve25519_pll.register_clkin(self.clk48_bufg, 48e6)
#curve25519_pll.create_clkout(self.cd_clk50, curve25519_clk_freq/2, margin=0, ce=curve25519_pll.locked & self.curve25519_on)
#platform.add_platform_command("create_generated_clock -name clk50 [get_pins {{MMCME2_ADV_1/CLKOUT0}}]")
#curve25519_pll.create_clkout(self.cd_clk100, curve25519_clk_freq, margin=0, ce=curve25519_pll.locked,
# gated_replicas={self.cd_clk100_gated : curve25519_pll.locked & self.curve25519_on})
#platform.add_platform_command("create_generated_clock -name clk100 [get_pins {{MMCME2_ADV_1/CLKOUT1}}]")
#curve25519_pll.create_clkout(self.cd_clk200, curve25519_clk_freq*2, margin=0, ce=curve25519_pll.locked & self.curve25519_on)
#platform.add_platform_command("create_generated_clock -name clk200 [get_pins {{MMCME2_ADV_1/CLKOUT2}}]")
##self.comb += curve25519_pll.reset.eq(~rst_sbus) # | ~por_done
#platform.add_false_path_constraints(self.cd_sys.clk, self.cd_clk50.clk)
#platform.add_false_path_constraints(self.cd_sys.clk, self.cd_clk100.clk)
#platform.add_false_path_constraints(self.cd_sys.clk, self.cd_clk200.clk)
#platform.add_false_path_constraints(self.cd_clk50.clk, self.cd_sys.clk)
#platform.add_false_path_constraints(self.cd_clk100.clk, self.cd_sys.clk)
#platform.add_false_path_constraints(self.cd_clk200.clk, self.cd_sys.clk)
# Power on reset, reset propagate from SBus to SYS
# por_count = Signal(16, reset=2**16-1)
# por_done = Signal()
# self.comb += self.cd_por.clk.eq(clk48)
# self.comb += por_done.eq(por_count == 0)
# self.sync.por += If(~por_done, por_count.eq(por_count - 1))
# self.comb += self.cd_por.rst.eq(~rst_sbus)
# self.comb += pll.reset.eq(~por_done | ~rst_sbus)
# USB
if (usb):
self.submodules.usb_pll = usb_pll = S7MMCM(speedgrade=-1)
#usb_pll.register_clkin(clk48, 48e6)
usb_pll.register_clkin(self.clk48_bufg, 48e6)
usb_pll.create_clkout(self.cd_usb, 48e6, margin = 0)
platform.add_platform_command("create_generated_clock -name usbclk [get_pins {{MMCME2_ADV_2/CLKOUT0}}]")
self.comb += usb_pll.reset.eq(~rst_sbus) # | ~por_done
platform.add_false_path_constraints(self.cd_sys.clk, self.cd_usb.clk)
self.submodules.pll_idelay = pll_idelay = S7MMCM(speedgrade=-1)
#pll_idelay.register_clkin(clk48, 48e6)
pll_idelay.register_clkin(self.clk48_bufg, 48e6)
pll_idelay.create_clkout(self.cd_idelay, 200e6, margin = 0)
platform.add_platform_command("create_generated_clock -name idelayclk [get_pins {{MMCME2_ADV_3/CLKOUT0}}]")
self.comb += pll_idelay.reset.eq(~rst_sbus) # | ~por_done
self.submodules.idelayctrl = S7IDELAYCTRL(self.cd_idelay)
class SBusFPGA(SoCCore):
def __init__(self, version, usb, **kwargs):
print(f"Building SBusFPGA for board version {version}")
kwargs["cpu_type"] = "None"
kwargs["integrated_sram_size"] = 0
kwargs["with_uart"] = False
kwargs["with_timer"] = False
self.sys_clk_freq = sys_clk_freq = 100e6 ## 25e6
self.platform = platform = ztex213_sbus.Platform(variant="ztex2.13a", version = version)
if (version == "V1.0"):
self.platform.add_extension(ztex213_sbus._usb_io_v1_0)
SoCCore.__init__(self,
platform=platform,
sys_clk_freq=sys_clk_freq,
clk_freq=sys_clk_freq,
csr_paging=0x1000, # default is 0x800
**kwargs)
# This mem-map is also exposed in the FSM (matched prefixes)
# and in the PROM (to tell NetBSD where everything is)
# Currently it is a straight mapping between the two:
# the physical address here are used as offset in the SBus
# reserved area of 256 MiB
# Anything at 0x10000000 is therefore unreachable directly
# The position of the 'usb_fake_dma' is so it overlaps
# the virtual address space used by NetBSD DMA allocators
# (themselves constrained by the SBus MMU capabilities)
self.wb_mem_map = wb_mem_map = {
"prom": 0x00000000,
"csr" : 0x00040000,
"usb_host": 0x00080000,
"usb_shared_mem": 0x00090000, # unused
"curve25519engine": 0x000a0000,
"main_ram": 0x80000000,
"usb_fake_dma": 0xfc000000,
}
self.mem_map.update(wb_mem_map)
self.submodules.crg = _CRG(platform=platform, sys_clk_freq=sys_clk_freq, usb=usb)
self.platform.add_period_constraint(self.platform.lookup_request("SBUS_3V3_CLK", loose=True), 1e9/25e6) # SBus max
if (version == "V1.0"):
self.submodules.leds = LedChaser(
pads = platform.request("SBUS_DATA_OE_LED_2"), #platform.request("user_led", 7),
sys_clk_freq = sys_clk_freq)
self.add_csr("leds")
if (usb):
self.add_usb_host(pads=platform.request("usb"), usb_clk_freq=48e6)
if (version == "V1.0"):
pad_usb_interrupt = platform.request("SBUS_3V3_INT1s") ## only one usable
elif (version == "V1.2"):
pad_usb_interrupt = platform.request("SBUS_3V3_INT3s") ## can be 1-6, beware others
sig_usb_interrupt = Signal(reset=1)
# the 74LVC2G07 takes care of the Z state: 1 -> Z on the bus, 0 -> 0 on the bus (asserted interrupt)
self.comb += pad_usb_interrupt.eq(sig_usb_interrupt)
self.comb += sig_usb_interrupt.eq(~self.usb_host.interrupt) ##
#pad_SBUS_DATA_OE_LED = platform.request("SBUS_DATA_OE_LED")
#SBUS_DATA_OE_LED_o = Signal()
#self.comb += pad_SBUS_DATA_OE_LED.eq(SBUS_DATA_OE_LED_o)
#pad_SBUS_DATA_OE_LED_2 = platform.request("SBUS_DATA_OE_LED_2")
#SBUS_DATA_OE_LED_2_o = Signal()
#self.comb += pad_SBUS_DATA_OE_LED_2.eq(SBUS_DATA_OE_LED_2_o)
#self.comb += SBUS_DATA_OE_LED_o.eq(~SBUS_3V3_INT1s_o)
prom_file = "prom_migen.fc"
prom_data = soc_core.get_mem_data(prom_file, "big")
# prom = Array(prom_data)
#print("\n****************************************\n")
#for i in range(len(prom)):
# print(hex(prom[i]))
#print("\n****************************************\n")
self.add_ram("prom", origin=self.mem_map["prom"], size=2**16, contents=prom_data, mode="r")
#getattr(self,"prom").mem.init = prom_data
#getattr(self,"prom").mem.depth = 2**14
self.submodules.ddrphy = s7ddrphy.A7DDRPHY(platform.request("ddram"),
memtype = "DDR3",
nphases = 4,
sys_clk_freq = sys_clk_freq)
self.add_sdram("sdram",
phy = self.ddrphy,
module = MT41J128M16(sys_clk_freq, "1:4"),
l2_cache_size = 0,
)
# don't enable anything on the SBus side for 20 seconds after power up
# this avoids FPGA initialization messing with the cold boot process
# requires us to reset the SPARCstation afterward so the FPGA board
# is properly identified
# This is in the 'native' ClockDomain that is never reset
hold_reset_ctr = Signal(30, reset=960000000)
self.sync.native += If(hold_reset_ctr>0, hold_reset_ctr.eq(hold_reset_ctr - 1))
hold_reset = Signal(reset=1)
self.comb += hold_reset.eq(~(hold_reset_ctr == 0))
# Interface SBus to wishbone
# we need to cross clock domains
wishbone_slave_sbus = wishbone.Interface(data_width=self.bus.data_width)
wishbone_master_sys = wishbone.Interface(data_width=self.bus.data_width)
self.submodules.wishbone_master_sbus = wishbone.WishboneDomainCrossingMaster(platform=self.platform, slave=wishbone_master_sys, cd_master="sbus", cd_slave="sys")
self.submodules.wishbone_slave_sys = wishbone.WishboneDomainCrossingMaster(platform=self.platform, slave=wishbone_slave_sbus, cd_master="sys", cd_slave="sbus")
# SPARCstation 20 slave interface to the main memory are limited to 32-bytes burst (32-bits wide, 8 word long)
# burst_size=16 should work on Ultra systems, but then they probably should go for 64-bits ET as well...
# Older systems are probably limited to burst_size=4, (it should always be available)
burst_size=8
self.submodules.tosbus_fifo = ClockDomainsRenamer({"read": "sbus", "write": "sys"})(AsyncFIFOBuffered(width=(32+burst_size*32), depth=burst_size))
self.submodules.fromsbus_fifo = ClockDomainsRenamer({"write": "sbus", "read": "sys"})(AsyncFIFOBuffered(width=((30-log2_int(burst_size))+burst_size*32), depth=burst_size))
self.submodules.fromsbus_req_fifo = ClockDomainsRenamer({"read": "sbus", "write": "sys"})(AsyncFIFOBuffered(width=((30-log2_int(burst_size))+32), depth=burst_size))
self.submodules.dram_dma_writer = LiteDRAMDMAWriter(port=self.sdram.crossbar.get_port(mode="write", data_width=burst_size*32),
fifo_depth=4,
fifo_buffered=True)
self.submodules.dram_dma_reader = LiteDRAMDMAReader(port=self.sdram.crossbar.get_port(mode="read", data_width=burst_size*32),
fifo_depth=4,
fifo_buffered=True)
self.submodules.exchange_with_mem = ExchangeWithMem(soc=self,
tosbus_fifo=self.tosbus_fifo,
fromsbus_fifo=self.fromsbus_fifo,
fromsbus_req_fifo=self.fromsbus_req_fifo,
dram_dma_writer=self.dram_dma_writer,
dram_dma_reader=self.dram_dma_reader,
burst_size=burst_size,
do_checksum = True)
_sbus_bus = SBusFPGABus(platform=self.platform,
hold_reset=hold_reset,
wishbone_slave=wishbone_slave_sbus,
wishbone_master=self.wishbone_master_sbus,
tosbus_fifo=self.tosbus_fifo,
fromsbus_fifo=self.fromsbus_fifo,
fromsbus_req_fifo=self.fromsbus_req_fifo,
burst_size=burst_size)
#self.submodules.sbus_bus = _sbus_bus
self.submodules.sbus_bus = ClockDomainsRenamer("sbus")(_sbus_bus)
self.submodules.sbus_bus_stat = SBusFPGABusStat(sbus_bus = self.sbus_bus)
self.bus.add_master(name="SBusBridgeToWishbone", master=wishbone_master_sys)
if (usb):
self.bus.add_slave(name="usb_fake_dma", slave=self.wishbone_slave_sys, region=SoCRegion(origin=self.mem_map.get("usb_fake_dma", None), size=0x03ffffff, cached=False))
#self.bus.add_master(name="mem_read_master", master=self.exchange_with_mem.wishbone_r_slave)
#self.bus.add_master(name="mem_write_master", master=self.exchange_with_mem.wishbone_w_slave)
#self.add_sdcard()
self.submodules.trng = NeoRV32TrngWrapper(platform=platform)
# beware the naming, as 'clk50' 'sysclk' 'clk200' are used in the original platform constraints
# the local engine.py was slightly modified to have configurable names, so we can have 'clk50', 'clk100', 'clk200'
# Beware that Engine implicitely runs in 'sys' by default, need to rename that one as well
# Actually renaming 'sys' doesn't work - unless we can CDC the CSRs as well
self.submodules.curve25519engine = ClockDomainsRenamer({"eng_clk":"clk50", "rf_clk":"clk200", "mul_clk":"clk100_gated"})(Engine(platform=platform,prefix=self.mem_map.get("curve25519engine", None))) # , "sys":"clk100"
#self.submodules.curve25519engine_wishbone_cdc = wishbone.WishboneDomainCrossingMaster(platform=self.platform, slave=self.curve25519engine.bus, cd_master="sys", cd_slave="clk100")
#self.bus.add_slave("curve25519engine", self.curve25519engine_wishbone_cdc, SoCRegion(origin=self.mem_map.get("curve25519engine", None), size=0x20000, cached=False))
self.bus.add_slave("curve25519engine", self.curve25519engine.bus, SoCRegion(origin=self.mem_map.get("curve25519engine", None), size=0x20000, cached=False))
self.bus.add_master(name="curve25519engineLS", master=self.curve25519engine.busls)
#self.submodules.curve25519_on_sync = BusSynchronizer(width = 1, idomain = "clk100", odomain = "sys")
#self.comb += self.curve25519_on_sync.i.eq(self.curve25519engine.power.fields.on)
#self.comb += self.crg.curve25519_on.eq(self.curve25519_on_sync.o)
self.comb += self.crg.curve25519_on.eq(self.curve25519engine.power.fields.on)
def main():
parser = argparse.ArgumentParser(description="SbusFPGA")
parser.add_argument("--build", action="store_true", help="Build bitstream")
parser.add_argument("--version", default="V1.0", help="SBusFPGA board version (default V1.0)")
parser.add_argument("--usb", action="store_true", help="add a USB OHCI controller")
builder_args(parser)
vivado_build_args(parser)
args = parser.parse_args()
soc = SBusFPGA(**soc_core_argdict(args),
version=args.version,
usb=args.usb)
#soc.add_uart(name="uart", baudrate=115200, fifo_depth=16)
builder = Builder(soc, **builder_argdict(args))
builder.build(**vivado_build_argdict(args), run=args.build)
# Generate modified CSR registers definitions/access functions to netbsd_csr.h.
# should be split per-device (and without base) to still work if we have identical devices in different configurations on multiple boards
csr_contents = sbus_to_fpga_export.get_csr_header(
regions = soc.csr_regions,
constants = soc.constants,
csr_base = soc.mem_regions['csr'].origin)
write_to_file(os.path.join("netbsd_csr.h"), csr_contents)
# tells the prom where to find what
# just one, as that is board-specific
# BEWARE! then need to run 'forth_to_migen_rom.sh' *and* regenerate the bitstream with the proper PROM built-in!
# (there's surely a better way...)
csr_forth_contents = sbus_to_fpga_export.get_csr_forth_header(
csr_regions = soc.csr_regions,
mem_regions = soc.mem_regions,
constants = soc.constants,
csr_base = soc.mem_regions['csr'].origin)
write_to_file(os.path.join("prom_csr.fth"), csr_forth_contents)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,94 @@
from migen import *
from migen.genlib.fifo import *
from litex.soc.interconnect.csr import *
class NeoRV32TrngWrapper(Module, AutoCSR):
def __init__(self, platform):
self.add_sources(platform)
rden_i = Signal()
wren_i = Signal()
data_i = Signal(32)
data_o = Signal(32)
self.ctrl = CSRStorage(32, description = "CTRL register; bit 0 : disable ; bit 1 : enable")
self.data = CSRStatus(32, description = "Rnd Data or 0")
self.submodules.ctrl_fsm = ctrl_fsm = FSM(reset_state = "Reset")
ctrl_fsm.act("Reset",
NextState("Idle")
)
ctrl_fsm.act("Idle",
If(self.ctrl.re, # someone has written control
If(self.ctrl.storage[0],
data_i.eq(0),
wren_i.eq(1),
).Elif(self.ctrl.storage[1],
data_i.eq(0xffffffff),
wren_i.eq(1),
)
),
If(self.data.we, # someone has read the data, reset so that the same value is never read twice
NextValue(self.data.status, 0),
)
)
# fill out an intermediate buffer, one byte every 11 cycles
# then copy the 4 bytes to data CST and do it all over again
buf = Array(Signal(8) for a in range(4))
idx = Signal(2)
cnt = Signal(4)
self.submodules.upd_fsm = upd_fsm = FSM(reset_state = "Reset")
upd_fsm.act("Reset",
NextValue(cnt, 11),
NextValue(idx, 0),
NextState("ByteWait")
)
upd_fsm.act("ByteWait",
If(cnt == 0,
rden_i.eq(1),
NextState("ByteWrite"),
).Else(
NextValue(cnt, cnt - 1)
)
)
upd_fsm.act("ByteWrite",
If (data_o[31] & data_o[30],
NextValue(buf[idx], data_o[0:8]),
NextValue(cnt, 11),
NextValue(idx, idx + 1),
If(idx == 3,
NextState("Copy"),
).Else(
NextState("ByteWait"),
)
).Else( # try again
NextValue(cnt, 11),
NextState("ByteWait"),
)
)
upd_fsm.act("Copy",
NextValue(self.data.status, Cat(buf[0], buf[1], buf[2], buf[3])),
NextValue(buf[0], 0),
NextValue(buf[1], 0),
NextValue(buf[2], 0),
NextValue(buf[3], 0),
NextState("ByteWait")
)
self.specials += Instance(self.get_netlist_name(),
i_clk_i = ClockSignal("sys"),
i_rden_i = rden_i,
i_wren_i = wren_i,
i_data_i = data_i,
o_data_o = data_o)
def get_netlist_name(self):
return "neorv32_trng"
def add_sources(self, platform):
platform.add_source("neorv32_trng_patched.vhd", "vhdl")

View File

@ -0,0 +1,55 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/ioccom.h>
#include <errno.h>
#define SBUSFPGA_STAT_ON _IO(0, 1)
#define SBUSFPGA_STAT_OFF _IO(0, 0)
int main(int argc, char **argv) {
const char const * device = "/dev/sbusfpga_stat0";
int devfd;
int onoff;
if (argc != 2) {
fprintf(stderr, "Usage: %s on|off\n", argv[0]);
return -1;
}
if (strncmp("on", argv[1], 2) == 0) {
onoff = 1;
} else if (strncmp("off", argv[1], 3) == 0) {
onoff = 0;
} else {
fprintf(stderr, "Usage: %s on|off\n", argv[0]);
return -1;
}
if ( (devfd = open(device, O_RDWR)) == -1) {
perror("can't open device file");
return -1;
}
switch (onoff) {
case 0:
if (ioctl(devfd, SBUSFPGA_STAT_OFF, NULL)) {
perror("Turning statistics off failed.");
close(devfd);
return -1;
}
break;
case 1:
if (ioctl(devfd, SBUSFPGA_STAT_ON, NULL)) {
perror("Turning statistics on failed.");
close(devfd);
return -1;
}
break;
}
return 0;
}

View File

@ -0,0 +1,228 @@
: dphy_rst_rd ( -- csr_value )
mregs-virt h# 1000 + l@
;
: dphy_half_sys8x_taps_rd ( -- csr_value )
mregs-virt h# 1004 + l@
;
: dphy_wlevel_en_rd ( -- csr_value )
mregs-virt h# 1008 + l@
;
: dphy_wlevel_strobe_rd ( -- csr_value )
mregs-virt h# 100c + l@
;
: dphy_dly_sel_rd ( -- csr_value )
mregs-virt h# 1010 + l@
;
: dphy_rdly_dq_rst_rd ( -- csr_value )
mregs-virt h# 1014 + l@
;
: dphy_rdly_dq_inc_rd ( -- csr_value )
mregs-virt h# 1018 + l@
;
: dphy_rdly_dq_bitslip_rst_rd ( -- csr_value )
mregs-virt h# 101c + l@
;
: dphy_rdly_dq_bitslip_rd ( -- csr_value )
mregs-virt h# 1020 + l@
;
: dphy_wdly_dq_bitslip_rst_rd ( -- csr_value )
mregs-virt h# 1024 + l@
;
: dphy_wdly_dq_bitslip_rd ( -- csr_value )
mregs-virt h# 1028 + l@
;
: dphy_rdphase_rd ( -- csr_value )
mregs-virt h# 102c + l@
;
: dphy_wrphase_rd ( -- csr_value )
mregs-virt h# 1030 + l@
;
: sdr_dfii_control_rd ( -- csr_value )
mregs-virt h# 2000 + l@
;
: sdr_dfii_pi0_command_rd ( -- csr_value )
mregs-virt h# 2004 + l@
;
: sdr_dfii_pi0_command_issue_rd ( -- csr_value )
mregs-virt h# 2008 + l@
;
: sdr_dfii_pi0_address_rd ( -- csr_value )
mregs-virt h# 200c + l@
;
: sdr_dfii_pi0_baddress_rd ( -- csr_value )
mregs-virt h# 2010 + l@
;
: sdr_dfii_pi0_wrdata_rd ( -- csr_value )
mregs-virt h# 2014 + l@
;
: sdr_dfii_pi0_rddata_rd ( -- csr_value )
mregs-virt h# 2018 + l@
;
: sdr_dfii_pi1_command_rd ( -- csr_value )
mregs-virt h# 201c + l@
;
: sdr_dfii_pi1_command_issue_rd ( -- csr_value )
mregs-virt h# 2020 + l@
;
: sdr_dfii_pi1_address_rd ( -- csr_value )
mregs-virt h# 2024 + l@
;
: sdr_dfii_pi1_baddress_rd ( -- csr_value )
mregs-virt h# 2028 + l@
;
: sdr_dfii_pi1_wrdata_rd ( -- csr_value )
mregs-virt h# 202c + l@
;
: sdr_dfii_pi1_rddata_rd ( -- csr_value )
mregs-virt h# 2030 + l@
;
: sdr_dfii_pi2_command_rd ( -- csr_value )
mregs-virt h# 2034 + l@
;
: sdr_dfii_pi2_command_issue_rd ( -- csr_value )
mregs-virt h# 2038 + l@
;
: sdr_dfii_pi2_address_rd ( -- csr_value )
mregs-virt h# 203c + l@
;
: sdr_dfii_pi2_baddress_rd ( -- csr_value )
mregs-virt h# 2040 + l@
;
: sdr_dfii_pi2_wrdata_rd ( -- csr_value )
mregs-virt h# 2044 + l@
;
: sdr_dfii_pi2_rddata_rd ( -- csr_value )
mregs-virt h# 2048 + l@
;
: sdr_dfii_pi3_command_rd ( -- csr_value )
mregs-virt h# 204c + l@
;
: sdr_dfii_pi3_command_issue_rd ( -- csr_value )
mregs-virt h# 2050 + l@
;
: sdr_dfii_pi3_address_rd ( -- csr_value )
mregs-virt h# 2054 + l@
;
: sdr_dfii_pi3_baddress_rd ( -- csr_value )
mregs-virt h# 2058 + l@
;
: sdr_dfii_pi3_wrdata_rd ( -- csr_value )
mregs-virt h# 205c + l@
;
: sdr_dfii_pi3_rddata_rd ( -- csr_value )
mregs-virt h# 2060 + l@
;
: dphy_rst_wr ( value -- )
mregs-virt h# 1000 + l!
;
: dphy_half_sys8x_taps_wr ( value -- )
mregs-virt h# 1004 + l!
;
: dphy_wlevel_en_wr ( value -- )
mregs-virt h# 1008 + l!
;
: dphy_wlevel_strobe_wr ( value -- )
mregs-virt h# 100c + l!
;
: dphy_dly_sel_wr ( value -- )
mregs-virt h# 1010 + l!
;
: dphy_rdly_dq_rst_wr ( value -- )
mregs-virt h# 1014 + l!
;
: dphy_rdly_dq_inc_wr ( value -- )
mregs-virt h# 1018 + l!
;
: dphy_rdly_dq_bitslip_rst_wr ( value -- )
mregs-virt h# 101c + l!
;
: dphy_rdly_dq_bitslip_wr ( value -- )
mregs-virt h# 1020 + l!
;
: dphy_wdly_dq_bitslip_rst_wr ( value -- )
mregs-virt h# 1024 + l!
;
: dphy_wdly_dq_bitslip_wr ( value -- )
mregs-virt h# 1028 + l!
;
: dphy_rdphase_wr ( value -- )
mregs-virt h# 102c + l!
;
: dphy_wrphase_wr ( value -- )
mregs-virt h# 1030 + l!
;
: sdr_dfii_control_wr ( value -- )
mregs-virt h# 2000 + l!
;
: sdr_dfii_pi0_command_wr ( value -- )
mregs-virt h# 2004 + l!
;
: sdr_dfii_pi0_command_issue_wr ( value -- )
mregs-virt h# 2008 + l!
;
: sdr_dfii_pi0_address_wr ( value -- )
mregs-virt h# 200c + l!
;
: sdr_dfii_pi0_baddress_wr ( value -- )
mregs-virt h# 2010 + l!
;
: sdr_dfii_pi0_wrdata_wr ( value -- )
mregs-virt h# 2014 + l!
;
: sdr_dfii_pi0_rddata_wr ( value -- )
mregs-virt h# 2018 + l!
;
: sdr_dfii_pi1_command_wr ( value -- )
mregs-virt h# 201c + l!
;
: sdr_dfii_pi1_command_issue_wr ( value -- )
mregs-virt h# 2020 + l!
;
: sdr_dfii_pi1_address_wr ( value -- )
mregs-virt h# 2024 + l!
;
: sdr_dfii_pi1_baddress_wr ( value -- )
mregs-virt h# 2028 + l!
;
: sdr_dfii_pi1_wrdata_wr ( value -- )
mregs-virt h# 202c + l!
;
: sdr_dfii_pi1_rddata_wr ( value -- )
mregs-virt h# 2030 + l!
;
: sdr_dfii_pi2_command_wr ( value -- )
mregs-virt h# 2034 + l!
;
: sdr_dfii_pi2_command_issue_wr ( value -- )
mregs-virt h# 2038 + l!
;
: sdr_dfii_pi2_address_wr ( value -- )
mregs-virt h# 203c + l!
;
: sdr_dfii_pi2_baddress_wr ( value -- )
mregs-virt h# 2040 + l!
;
: sdr_dfii_pi2_wrdata_wr ( value -- )
mregs-virt h# 2044 + l!
;
: sdr_dfii_pi2_rddata_wr ( value -- )
mregs-virt h# 2048 + l!
;
: sdr_dfii_pi3_command_wr ( value -- )
mregs-virt h# 204c + l!
;
: sdr_dfii_pi3_command_issue_wr ( value -- )
mregs-virt h# 2050 + l!
;
: sdr_dfii_pi3_address_wr ( value -- )
mregs-virt h# 2054 + l!
;
: sdr_dfii_pi3_baddress_wr ( value -- )
mregs-virt h# 2058 + l!
;
: sdr_dfii_pi3_wrdata_wr ( value -- )
mregs-virt h# 205c + l!
;
: sdr_dfii_pi3_rddata_wr ( value -- )
mregs-virt h# 2060 + l!
;

View File

@ -0,0 +1,533 @@
headers
fload sdram_csr.fth
external
: popcnt ( n -- u)
0 swap
BEGIN dup WHILE tuck 1 AND + swap 1 rshift REPEAT
DROP
;
: cdelay ( count -- )
\ Forth loop always have a least one iteration
dup 0<> if
0 do noop loop
else drop then
;
headers
: sdram_software_control_on ( -- )
sdr_dfii_control_rd
h# e <> if h# e sdr_dfii_control_wr then
;
: sdram_software_control_off ( -- )
sdr_dfii_control_rd
h# 1 <> if h# 1 sdr_dfii_control_wr then
;
: command_p0 ( cmd -- )
sdr_dfii_pi0_command_wr
1 sdr_dfii_pi0_command_issue_wr
;
: command_p1 ( cmd -- )
sdr_dfii_pi1_command_wr
1 sdr_dfii_pi1_command_issue_wr
;
: command_p2 ( cmd -- )
sdr_dfii_pi2_command_wr
1 sdr_dfii_pi2_command_issue_wr
;
: command_p3 ( cmd -- )
sdr_dfii_pi3_command_wr
1 sdr_dfii_pi3_command_issue_wr
;
: init_sequence ( -- )
.( init_sequence ) cr
h# 0 sdr_dfii_pi0_address_wr
h# 0 sdr_dfii_pi0_baddress_wr
h# c sdr_dfii_control_wr
50 ms
h# 0 sdr_dfii_pi0_address_wr
h# 0 sdr_dfii_pi0_baddress_wr
h# e sdr_dfii_control_wr
10 ms
h# 200 sdr_dfii_pi0_address_wr
h# 2 sdr_dfii_pi0_baddress_wr
h# f command_p0
h# 0 sdr_dfii_pi0_address_wr
h# 3 sdr_dfii_pi0_baddress_wr
h# f command_p0
h# 6 sdr_dfii_pi0_address_wr
h# 1 sdr_dfii_pi0_baddress_wr
h# f command_p0
h# 920 sdr_dfii_pi0_address_wr
h# 0 sdr_dfii_pi0_baddress_wr
h# f command_p0
200 cdelay
h# 400 sdr_dfii_pi0_address_wr
0 sdr_dfii_pi0_baddress_wr
h# 3 command_p0
200 cdelay
;
: sdram_read_leveling_rst_delay ( modulenum -- )
h# 1 swap << dphy_dly_sel_wr
h# 1 dphy_rdly_dq_rst_wr
h# 0 dphy_dly_sel_wr
;
: sdram_read_leveling_inc_delay ( modulenum -- )
h# 1 swap << dphy_dly_sel_wr
h# 1 dphy_rdly_dq_inc_wr
h# 0 dphy_dly_sel_wr
;
: sdram_read_leveling_rst_bitslip ( modulenum -- )
h# 1 swap << dphy_dly_sel_wr
h# 1 dphy_rdly_dq_bitslip_rst_wr
h# 0 dphy_dly_sel_wr
;
: sdram_read_leveling_inc_bitslip ( modulenum -- )
h# 1 swap << dphy_dly_sel_wr
h# 1 dphy_rdly_dq_bitslip_wr
h# 0 dphy_dly_sel_wr
;
: lfsr ( bits prev -- res )
dup 1 and not ( bits prev -- bits prev ~{prev&1} )
swap 1 >> ( bits prev ~{prev&1} -- bits ~{prev&1} {prev>>1} )
swap ( bits prev ~{prev&1} -- bits {prev>>1} ~{prev&1} )
rot ( bits {prev>>1} ~{prev&1} -- {prev>>1} ~{prev&1} bits )
\ assume bits is 32, 'cause it is
drop h# 80200003 ( {prev>>1} ~{prev&1} bits -- {prev>>1} ~{prev&1} lfsr_taps[bits] )
and
xor
;
: sdram_activate_test_row ( -- )
h# 0 sdr_dfii_pi0_address_wr
h# 0 sdr_dfii_pi0_baddress_wr
h# 9 command_p0
15 cdelay
;
: sdram_precharge_test_row ( -- )
h# 0 sdr_dfii_pi0_address_wr
h# 0 sdr_dfii_pi0_baddress_wr
h# b command_p0
15 cdelay
;
: command_px ( phase value -- )
over 3 = if dup command_p3 then
over 2 = if dup command_p2 then
over 1 = if dup command_p1 then
over 0 = if dup command_p0 then
2drop
;
: command_prd ( value -- )
dphy_rdphase_rd
swap command_px
;
: command_pwr ( value -- )
dphy_wrphase_rd
swap command_px
;
: sdr_dfii_pix_address_wr ( phase value -- )
over 3 = if dup sdr_dfii_pi3_address_wr then
over 2 = if dup sdr_dfii_pi2_address_wr then
over 1 = if dup sdr_dfii_pi1_address_wr then
over 0 = if dup sdr_dfii_pi0_address_wr then
2drop
;
: sdr_dfii_pird_address_wr ( value -- )
dphy_rdphase_rd
swap sdr_dfii_pix_address_wr
;
: sdr_dfii_piwr_address_wr ( value -- )
dphy_wrphase_rd
swap sdr_dfii_pix_address_wr
;
: sdr_dfii_pix_baddress_wr ( phase value -- )
over 3 = if dup sdr_dfii_pi3_baddress_wr then
over 2 = if dup sdr_dfii_pi2_baddress_wr then
over 1 = if dup sdr_dfii_pi1_baddress_wr then
over 0 = if dup sdr_dfii_pi0_baddress_wr then
2drop
;
: sdr_dfii_pird_baddress_wr ( value -- )
dphy_rdphase_rd
swap sdr_dfii_pix_baddress_wr
;
: sdr_dfii_piwr_baddress_wr ( value -- )
dphy_wrphase_rd
swap sdr_dfii_pix_baddress_wr
;
: sdr_wr_rd_chk_tst_pat_get ( seed -- A B C D )
\ .( sdr_wr_rd_chk_tst_pat_get ) cr
dup 42 = if h# 00000080 swap then
dup 42 = if h# 00000000 swap then
dup 42 = if h# 00000000 swap then
dup 42 = if h# 15090700 swap then
dup 84 = if h# 00000000 swap then
dup 84 = if h# 00000000 swap then
dup 84 = if h# 00000000 swap then
dup 84 = if h# 2a150907 swap then
drop
;
: sdr_wr_rd_check_test_pattern ( modulenum seed -- errors )
\ .( sdr_wr_rd_check_test_pattern ) cr
sdram_activate_test_row
dup sdr_wr_rd_chk_tst_pat_get
\ should have the 4 patterns on top of the stack: modulenum seed p0 p1 p2 p3
sdr_dfii_pi0_wrdata_wr
sdr_dfii_pi1_wrdata_wr
sdr_dfii_pi2_wrdata_wr
sdr_dfii_pi3_wrdata_wr
\ should be back at modulenum seed
h# 0 sdr_dfii_piwr_address_wr
h# 0 sdr_dfii_piwr_baddress_wr
h# 17 command_pwr
15 cdelay
h# 0 sdr_dfii_pird_address_wr
h# 0 sdr_dfii_pird_baddress_wr
h# 25 command_prd
15 cdelay
sdram_precharge_test_row
sdr_wr_rd_chk_tst_pat_get
\ should have the 4 patterns on top of the stack: modulenum p0 p1 p2 p3
sdr_dfii_pi0_rddata_rd xor popcnt
\ should be at modulenum p0 p1 p2 errors
swap sdr_dfii_pi0_rddata_rd xor popcnt +
\ should be at modulenum p0 p1 errors
swap sdr_dfii_pi0_rddata_rd xor popcnt +
\ should be at modulenum p0 errors
swap sdr_dfii_pi0_rddata_rd xor popcnt +
\ should be at modulenum errors
\ drop modulenum
nip
;
: sdram_read_leveling_scan_module ( modulenum bitslip -- score )
\ .( sdram_read_leveling_scan_module ) cr
over sdram_read_leveling_rst_delay
\ push score
0
\ we should be at 'modulenum bitslip score'
32 0 do
\ .( starting rd_lvl_scan loop with stack: ) .s cr
2 pick 42 sdr_wr_rd_check_test_pattern
\ now we have an error count at the top
3 pick 84 sdr_wr_rd_check_test_pattern
\ merge both error count
+
\ we should be at 'modulenum bitslip score errorcount'
dup 0=
\ we should be at 'modulenum bitslip score errorcount working?'
if 16384 else 0 then
\ we should be at 'modulenum bitslip score errorcount (0|16384)'
swap 512 swap -
\ we should be at 'modulenum bitslip score (0|16384) (512-errorcount)'
+
+
\ we should be at 'modulenum bitslip score'
2 pick sdram_read_leveling_inc_delay
loop
nip
nip
;
: sdr_wr_lat_cal_bitslip_loop ( modulenum bestbitslip bestscore bitslip -- modulenum bestbitslip bestscore )
\ .( sdr_wr_lat_cal_bitslip_loop for module: ) 3 pick . .( bitslip: ) dup . cr
\ .( sdr_wr_lat_cal_bitslip_loop, stack: ) .s cr
1 4 pick << dphy_dly_sel_wr ( '4 pick' will extract modulenum, needed as we're stacking the '1' )
1 dphy_wdly_dq_bitslip_rst_wr
\ Forth loop always have a least one iteration
dup 0<> if
dup 0 do
1 dphy_wdly_dq_bitslip_wr
loop
then
0 dphy_dly_sel_wr
\ .( sdr_wr_lat_cal_bitslip_loop after bitslip init loop, stack: ) .s cr
\ push current score
0 ( we should be at 'modulenum bestbitslip bestscore bitslip score' )
4 pick sdram_read_leveling_rst_bitslip
8 0 do
4 pick over sdram_read_leveling_scan_module
\ we should be at 'modulenum bestbitslip bestscore bitslip score score', max will merge scores
max
\ we should be at 'modulenum bestbitslip bestscore bitslip score' again
4 pick sdram_read_leveling_inc_bitslip
loop
.( sdr_wr_lat_cal_bitslip_loop after bitslip check loop, stack: ) .s cr
dup 3 pick >
if
\ .( lat_cal best bitslip was: ) 3 pick . .( with score: ) 2 pick . cr
2swap
.( lat_cal best bitslip now: ) 3 pick . .( with score: ) 2 pick . cr
then
2drop
\ .( sdr_wr_lat_cal_bitslip_loop end, stack: ) .s cr
;
: sdr_wr_lat_cal_module_loop ( modulenum -- )
.( sdr_wr_lat_cal_module_loop for module: ) dup . cr
\ push best_bitslip
-1
\ push best_score
0
\ we should have 'modulenum 1 0'
8 0 do
i sdr_wr_lat_cal_bitslip_loop
2 +loop
\ we should be at 'modulenum bestbitslip bestscore'
\ we don't need score anymore
drop
\ we should be at 'modulenum bestbitslip'
1 2 pick << dphy_dly_sel_wr
1 dphy_wdly_dq_bitslip_rst_wr
.( sdr_wr_lat_cal_module_loop: best bitslip: ) dup . cr
\ loop that consumes bestbitslip as the upper bound
\ Forth loop always have a least one iteration
dup 0<> if
0 do
1 dphy_wdly_dq_bitslip_wr
loop
else drop then
0 dphy_dly_sel_wr
\ drop the modulenum
drop
;
: sdram_write_latency_calibration ( -- )
.( sdram_write_latency_calibration ) cr
2 0 do
i sdr_wr_lat_cal_module_loop
loop
;
: sdram_leveling_center_module ( modulenum -- )
.( sdram_leveling_center_module ) cr
dup sdram_read_leveling_rst_delay
\ push delay_min
-1
\ push delay
0
\ we should be at 'modulenum delay_min delay'
begin
\ .( starting lvl_center loop with stack: ) .s cr
2 pick 42 sdr_wr_rd_check_test_pattern
.( we should be at 'modulenum delay_min delay error' stack: ) .s cr
3 pick 84 sdr_wr_rd_check_test_pattern
.( we should be at 'modulenum delay_min delay error error' stack: ) .s cr
+
\ we should be at 'modulenum delay_min delay error'
\ .( we should be at 'modulenum delay_min delay error' stack: ) .s cr
0=
\ we should be at 'modulenum delay_min delay working'
\ .( we should be at 'modulenum delay_min delay working' stack: ) .s cr
2 pick 0< and
\ we should be at 'modulenum delay_min delay {working&delay_min<0}'
\ .( we should be at 'modulenum delay_min delay {working&delay_min<0}' stack: ) .s cr
dup if rot drop 2dup rot drop then
not
\ we should be at 'modulenum new_delay_min delay !{working&delay_min<0}'
\ .( we should be at 'modulenum new_delay_min delay !{working&delay_min<0}' stack: ) .s cr
\ test delay before incrementing, if already 31 no point in continuing/incrementing
over 31 <
\ .( we should be at 'modulenum new_delay_min delay !{working&delay_min<0} <31' stack: ) .s cr
dup if rot 1+ -rot then
dup if 4 pick sdram_read_leveling_inc_delay then
\ and the conditions to signal end-of-loop
and
\ .( we should be at 'modulenum new_delay_min delay !{working&delay_min<0}&<31' stack: ) .s cr
\ .( finishing lvl_center loop with stack: ) .s cr
not until
\ we should be at 'modulenum new_delay_min delay', the while has consumed the condition
.( we should be at 'modulenum new_delay_min delay' stack: ) .s cr
1+
2 pick sdram_read_leveling_inc_delay
\ build a clean stack, startin with a copy of modulenum
2 pick
\ push delay_max
-1
\ we're at 'modulenum new_delay_min delay modulenum delay_max'
\ push delay
2 pick
\ we're at 'modulenum new_delay_min delay modulenum delay_max delay'
.( we should be at 'modulenum new_delay_min delay modulenum delay_max delay ' stack: ) .s cr
\ this is almost the same loop, except with !working instead of working and delay_max instead of delay_min
begin
2 pick 42 sdr_wr_rd_check_test_pattern
3 pick 84 sdr_wr_rd_check_test_pattern
+
\ we should be at 'modulenum delay_max delay error'
0<>
\ we should be at 'modulenum delay_max delay !working'
2 pick 0< and
\ we should be at 'modulenum delay_max delay {!working&delay_max<0}'
dup if rot drop 2dup rot drop then
not
\ we should be at 'modulenum new_delay_max delay !{!working&delay_max<0}'
\ test delay before incrementing, if already 31 no point in continuing/incrementing
over 31 <
dup not if rot 1+ -rot then
dup not if 4 pick sdram_read_leveling_inc_delay then
\ and the conditions to signal end-of-loop
and
not until
\ we should be at 'modulenum new_delay_min delay modulenum new_delay_max delay', the while has consumed the condition
.( we should be at 'modulenum new_delay_min delay modulenum new_delay_max delay ' stack: ) .s cr
\ keep delay if new_delay_max<0, new_delay_max otherwise
over 0< if nip else drop then
\ we should be at 'modulenum new_delay_min delay modulenum new_delay_max'
nip
nip
\ we should be at 'modulenum new_delay_min new_delay_max'
.( we should be at 'modulenum new_delay_min new_delay_max' stack: ) .s cr
\ compute delay_mid
2dup + 2/ 32 mod
\ we should be at 'modulenum new_delay_min new_delay_max {{new_delay_min+new_delay_max}/2%32}'
\ compute delay_range
3dup drop swap - 2/
\ we should be at 'modulenum new_delay_min new_delay_max {{new_delay_min+new_delay_max}/2%32} {{new_delay_max-new_delay_min}/2}'
.( we should be at 'modulenum new_delay_min new_delay_max delay_mid delay_range ' stack: ) .s cr
4 pick sdram_read_leveling_rst_delay
100 cdelay
\ Forth loop always have a least one iteration
over 0<> if
over 0 do
4 pick sdram_read_leveling_inc_delay
100 cdelay
loop
then
drop
drop
drop
drop
drop
;
: sdr_rd_lvl_bitslip_loop ( modulenum bestbitslip bestscore bitslip -- modulenum bestbitslip bestscore )
\ .( sdr_rd_lvl_bitslip_loop, stack: ) .s cr
3 pick over sdram_read_leveling_scan_module
\ we should be at 'modulenum bestbitslip bestscore bitslip score'
4 pick sdram_leveling_center_module
\ preserve a bitslip for the later test
over
\ (we should be at 'modulenum bestbitslip bestscore bitslip score bitslip') move it out of the way
.( we should be at 'modulenum bestbitslip bestscore bitslip score bitslip' stack: ) .s cr
5 roll ( 'modulenum bestscore bitslip score bitslip bestbitslip' )
5 roll ( 'modulenum bitslip score bitslip bestbitslip bestscore' )
5 roll ( 'modulenum score bitslip bestbitslip bestscore bitslip' )
5 roll ( 'modulenum bitslip bestbitslip bestscore bitslip score' )
.( we should be at 'modulenum bitslip bestbitslip bestscore bitslip score' stack: ) .s cr
\ compare the score and bestcore
dup 3 pick >
if
2swap
.( rd_lvl best bitslip now: ) 3 pick . .( with score: ) 2 pick . cr
then
2drop
\ we should be at 'modulenum bitslip bestbitslip bestscore'
rot
\ we should be at 'modulenum bestbitslip bestscore bitslip'
.( we should be at 'modulenum bestbitslip bestscore bitslip' stack: ) .s cr
7 <> if 2 pick sdram_read_leveling_inc_bitslip then
;
: sdr_rd_lvl_module_loop ( modulenum -- )
.( sdr_rd_lvl_module_loop ) cr
1 over << sdram_read_leveling_rst_bitslip
\ push best_bitslip
0
\ push best_score
0
\ we should have 'modulenum 0 0'
8 0 do
i sdr_rd_lvl_bitslip_loop
loop
\ don't need the score anymore
drop
2 pick sdram_read_leveling_rst_bitslip
.( sdr_rd_lvl_module_loop, best bitslip: ) dup . cr
\ Forth loop always have a least one iteration
dup 0<> if
\ consume best_bitslip as loop upper bound
0 do
dup sdram_leveling_center_module
loop
else drop then
drop
;
: sdram_read_leveling ( -- )
.( sdram_read_leveling ) cr
2 0 do
i sdr_rd_lvl_module_loop
loop
;
: sdram_leveling ( -- )
.( sdram_leveling ) cr
sdram_software_control_on
2 0 do
i sdram_read_leveling_rst_delay
i sdram_read_leveling_rst_bitslip
loop
sdram_write_latency_calibration
sdram_read_leveling
sdram_software_control_off
;
external
: init_sdram ( -- )
.( init_sdram ) cr
2 dphy_rdphase_wr
3 dphy_wrphase_wr
sdram_software_control_on
1 dphy_rst_wr
1 ms
0 dphy_rst_wr
1 ms
.( going to init_sequence ) cr
init_sequence
.( going to sdram_leveling ) cr
sdram_leveling
\ redundant
sdram_software_control_off
;
: init! ( -- )
.( init ) cr
map-in-mregs
init_sdram
map-out-mregs
;

View File

@ -0,0 +1,237 @@
#
# This file is part of LiteX-Boards.
#
# Support for the ZTEX USB-FGPA Module 2.13:
# <https://www.ztex.de/usb-fpga-2/usb-fpga-2.13.e.html>
# With (no-so-optional) expansion, either the ZTEX Debug board:
# <https://www.ztex.de/usb-fpga-2/debug.e.html>
# Or the SBusFPGA adapter board:
# <https://github.com/rdolbeau/SBusFPGA>
#
# Copyright (c) 2015 Yann Sionneau <yann.sionneau@gmail.com>
# Copyright (c) 2015-2019 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2020-2021 Romain Dolbeau <romain@dolbeau.org>
# SPDX-License-Identifier: BSD-2-Clause
from litex.build.generic_platform import *
from litex.build.xilinx import XilinxPlatform
from litex.build.openocd import OpenOCD
# IOs ----------------------------------------------------------------------------------------------
# FPGA daughterboard I/O
_io = [
## 48 MHz clock reference
("clk48", 0, Pins("P15"), IOStandard("LVCMOS33")),
## embedded 256 MiB DDR3 DRAM
("ddram", 0,
Subsignal("a", Pins("C5 B6 C7 D5 A3 E7 A4 C6", "A6 D8 B2 A5 B3 B7"),
IOStandard("SSTL135")),
Subsignal("ba", Pins("E5 A1 E6"), IOStandard("SSTL135")),
Subsignal("ras_n", Pins("E3"), IOStandard("SSTL135")),
Subsignal("cas_n", Pins("D3"), IOStandard("SSTL135")),
Subsignal("we_n", Pins("D4"), IOStandard("SSTL135")),
# Subsignal("cs_n", Pins(""), IOStandard("SSTL135")),
Subsignal("dm", Pins("G1 G6"), IOStandard("SSTL135")),
Subsignal("dq", Pins(
"H1 F1 E2 E1 F4 C1 F3 D2",
"G4 H5 G3 H6 J2 J3 K1 K2"),
IOStandard("SSTL135"),
Misc("IN_TERM=UNTUNED_SPLIT_40")),
Subsignal("dqs_p", Pins("H2 J4"),
IOStandard("DIFF_SSTL135"),
Misc("IN_TERM=UNTUNED_SPLIT_40")),
Subsignal("dqs_n", Pins("G2 H4"),
IOStandard("DIFF_SSTL135"),
Misc("IN_TERM=UNTUNED_SPLIT_40")),
Subsignal("clk_p", Pins("C4"), IOStandard("DIFF_SSTL135")),
Subsignal("clk_n", Pins("B4"), IOStandard("DIFF_SSTL135")),
Subsignal("cke", Pins("B1"), IOStandard("SSTL135")),
Subsignal("odt", Pins("F5"), IOStandard("SSTL135")),
Subsignal("reset_n", Pins("J5"), IOStandard("SSTL135")),
Misc("SLEW=FAST"),
),
]
# SBusFPGA I/O
_sbus_io_v1_0 = [
## leds on the SBus board
("user_led", 0, Pins("U8"), IOStandard("lvcmos33")), #LED0
("user_led", 1, Pins("U7"), IOStandard("lvcmos33")), #LED1
("user_led", 2, Pins("U6"), IOStandard("lvcmos33")), #LED2
("user_led", 3, Pins("T8"), IOStandard("lvcmos33")), #LED3
("user_led", 4, Pins("P4"), IOStandard("lvcmos33")), #LED4
("user_led", 5, Pins("P3"), IOStandard("lvcmos33")), #LED5
("user_led", 6, Pins("T1"), IOStandard("lvcmos33")), #LED6
("user_led", 7, Pins("R1"), IOStandard("lvcmos33")), #LED7
#("user_led", 8, Pins("U1"), IOStandard("lvcmos33")), #SBUS_DATA_OE_LED
#("user_led", 9, Pins("T3"), IOStandard("lvcmos33")), #SBUS_DATA_OE_LED_2
## serial header for console
("serial", 0,
Subsignal("tx", Pins("V9")), # FIXME: might be the other way round
Subsignal("rx", Pins("U9")),
IOStandard("LVCMOS33")
),
## sdcard connector
("spisdcard", 0,
Subsignal("clk", Pins("R8")),
Subsignal("mosi", Pins("T5"), Misc("PULLUP")),
Subsignal("cs_n", Pins("V6"), Misc("PULLUP")),
Subsignal("miso", Pins("V5"), Misc("PULLUP")),
Misc("SLEW=FAST"),
IOStandard("LVCMOS33"),
),
("sdcard", 0,
Subsignal("data", Pins("V5 V4 V7 V6"), Misc("PULLUP")),
Subsignal("cmd", Pins("T5"), Misc("PULLUP")),
Subsignal("clk", Pins("R8")),
#Subsignal("cd", Pins("V6")),
Misc("SLEW=FAST"),
IOStandard("LVCMOS33"),
),
]
_sbus_io_v1_2 = [
## leds on the SBus board
## serial header for console
("serial", 0,
Subsignal("tx", Pins("V9")), # FIXME: might be the other way round
Subsignal("rx", Pins("U9")),
IOStandard("LVCMOS33")
),
## sdcard connector
("spisdcard", 0,
Subsignal("clk", Pins("R8")),
Subsignal("mosi", Pins("T5"), Misc("PULLUP")),
Subsignal("cs_n", Pins("V6"), Misc("PULLUP")),
Subsignal("miso", Pins("V5"), Misc("PULLUP")),
Misc("SLEW=FAST"),
IOStandard("LVCMOS33"),
),
("sdcard", 0,
Subsignal("data", Pins("V5 V4 V7 V6"), Misc("PULLUP")),
Subsignal("cmd", Pins("T5"), Misc("PULLUP")),
Subsignal("clk", Pins("R8")),
#Subsignal("cd", Pins("V6")),
Misc("SLEW=FAST"),
IOStandard("LVCMOS33"),
),
## USB
("usb", 0,
Subsignal("dp", Pins("U8")), # Serial TX
Subsignal("dm", Pins("U7")), # Serial RX
IOStandard("LVCMOS33"))
]
_sbus_sbus_v1_0 = [
("SBUS_3V3_CLK", 0, Pins("D15"), IOStandard("lvttl")),
("SBUS_3V3_ASs", 0, Pins("T4"), IOStandard("lvttl")),
("SBUS_3V3_BGs", 0, Pins("T6"), IOStandard("lvttl")),
("SBUS_3V3_BRs", 0, Pins("R6"), IOStandard("lvttl")),
("SBUS_3V3_ERRs", 0, Pins("V2"), IOStandard("lvttl")),
("SBUS_DATA_OE_LED", 0, Pins("U1"), IOStandard("lvttl")),
("SBUS_DATA_OE_LED_2", 0, Pins("T3"), IOStandard("lvttl")),
("SBUS_3V3_RSTs", 0, Pins("U2"), IOStandard("lvttl")),
("SBUS_3V3_SELs", 0, Pins("K6"), IOStandard("lvttl")),
("SBUS_3V3_INT1s", 0, Pins("R3"), IOStandard("lvttl")),
("SBUS_3V3_INT7s", 0, Pins("N5"), IOStandard("lvttl")),
("SBUS_3V3_PPRD", 0, Pins("N6"), IOStandard("lvttl")),
("SBUS_OE", 0, Pins("P5"), IOStandard("lvttl")),
("SBUS_3V3_ACKs", 0, Pins("M6 L6 N4"), IOStandard("lvttl")),
("SBUS_3V3_SIZ", 0, Pins("R7 U3 V1"), IOStandard("lvttl")),
("SBUS_3V3_D", 0, Pins("J18 K16 J17 K15 K13 J15 J13 J14 H14 H17 G14 G17 G16 G18 H16 F18 F16 E18 F15 D18 E17 G13 D17 F13 F14 E16 E15 C17 C16 A18 B18 C15"), IOStandard("lvttl")),
("SBUS_3V3_PA", 0, Pins("B16 B17 D14 C14 D12 A16 A15 B14 B13 B12 C12 A14 A13 B11 A11 M4 R2 M3 P2 M2 N2 K5 N1 L4 M1 L3 L1 K3"), IOStandard("lvttl")),
]
_sbus_sbus_v1_2 = [
("SBUS_3V3_CLK", 0, Pins("D15"), IOStandard("lvttl")),
("SBUS_3V3_ASs", 0, Pins("T4"), IOStandard("lvttl")),
("SBUS_3V3_BGs", 0, Pins("R7"), IOStandard("lvttl")), # moved
("SBUS_3V3_BRs", 0, Pins("R6"), IOStandard("lvttl")),
("SBUS_3V3_ERRs", 0, Pins("D13"), IOStandard("lvttl")), # moved
("SBUS_DATA_OE_LED", 0, Pins("U1"), IOStandard("lvttl")),
#("SBUS_DATA_OE_LED_2", 0, Pins("T3"), IOStandard("lvttl")),
("SBUS_3V3_RSTs", 0, Pins("U2"), IOStandard("lvttl")),
("SBUS_3V3_SELs", 0, Pins("K6"), IOStandard("lvttl")),
("SBUS_3V3_INT1s", 0, Pins("R5"), IOStandard("lvttl")), # moved
("SBUS_3V3_INT2s", 0, Pins("H15"), IOStandard("lvttl")), # added
("SBUS_3V3_INT3s", 0, Pins("R3"), IOStandard("lvttl")), # added
("SBUS_3V3_INT4s", 0, Pins("N5"), IOStandard("lvttl")), # added
("SBUS_3V3_INT5s", 0, Pins("L5"), IOStandard("lvttl")), # added
("SBUS_3V3_INT6s", 0, Pins("V2"), IOStandard("lvttl")), # added
#("SBUS_3V3_INT7s", 0, Pins("N5"), IOStandard("lvttl")),
("SBUS_3V3_PPRD", 0, Pins("N6"), IOStandard("lvttl")),
("SBUS_OE", 0, Pins("P5"), IOStandard("lvttl")),
("SBUS_3V3_ACKs", 0, Pins("M6 L6 N4"), IOStandard("lvttl")),
("SBUS_3V3_SIZ", 0, Pins("T6 U3 V1"), IOStandard("lvttl")), # 0 moved
("SBUS_3V3_D", 0, Pins("J18 K16 J17 K15 K13 J15 J13 J14 H14 H17 G14 G17 G16 G18 H16 F18 F16 E18 F15 D18 E17 G13 D17 F13 F14 E16 E15 C17 C16 A18 B18 C15"), IOStandard("lvttl")),
("SBUS_3V3_PA", 0, Pins("B16 B17 D14 C14 D12 A16 A15 B14 B13 B12 C12 A14 A13 B11 A11 M4 R2 M3 P2 M2 N2 K5 N1 L4 M1 L3 L1 K3"), IOStandard("lvttl")),
]
# reusing the UART pins !!!
_usb_io_v1_0 = [
("usb", 0,
Subsignal("dp", Pins("V9")), # Serial TX
Subsignal("dm", Pins("U9")), # Serial RX
IOStandard("LVCMOS33"))
]
# Connectors ---------------------------------------------------------------------------------------
_connectors_v1_0 = [
]
_connectors_v1_2 = [
("P1", "T8 U6 P3 P4 T1 U4 R1 T3"),
]
# Platform -----------------------------------------------------------------------------------------
class Platform(XilinxPlatform):
default_clk_name = "clk48"
default_clk_period = 1e9/48e6
def __init__(self, variant="ztex2.13a", version="V1.0"):
device = {
"ztex2.13a": "xc7a35tcsg324-1",
"ztex2.13b": "xc7a50tcsg324-1", #untested
"ztex2.13b2": "xc7a50tcsg324-1", #untested
"ztex2.13c": "xc7a75tcsg324-2", #untested
"ztex2.13d": "xc7a100tcsg324-2" #untested
}[variant]
sbus_io = {
"V1.0" : _sbus_io_v1_0,
"V1.2" : _sbus_io_v1_2,
}[version]
sbus_sbus = {
"V1.0" : _sbus_sbus_v1_0,
"V1.2" : _sbus_sbus_v1_2,
}[version]
connectors = {
"V1.0" : _connectors_v1_0,
"V1.2" : _connectors_v1_2,
}[version]
XilinxPlatform.__init__(self, device, _io, connectors, toolchain="vivado")
self.add_extension(sbus_io)
self.add_extension(sbus_sbus)
self.toolchain.bitstream_commands = \
["set_property BITSTREAM.CONFIG.SPI_32BIT_ADDR No [current_design]",
"set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 2 [current_design]",
"set_property BITSTREAM.CONFIG.CONFIGRATE 66 [current_design]",
"set_property BITSTREAM.GENERAL.COMPRESS true [current_design]",
"set_property BITSTREAM.GENERAL.CRC DISABLE [current_design]",
"set_property STEPS.SYNTH_DESIGN.ARGS.RETIMING true [get_runs synth_1]",
"set_property CONFIG_VOLTAGE 3.3 [current_design]",
"set_property CFGBVS VCCO [current_design]"
# , "set_property STEPS.SYNTH_DESIGN.ARGS.DIRECTIVE AreaOptimized_high [get_runs synth_1]"
]
def create_programmer(self):
bscan_spi = "bscan_spi_xc7a35t.bit"
return OpenOCD("openocd_xc7_ft2232.cfg", bscan_spi) #FIXME
def do_finalize(self, fragment):
XilinxPlatform.do_finalize(self, fragment)
#self.add_period_constraint(self.lookup_request("clk48", loose=True), 1e9/48e6)

View File

@ -0,0 +1,9 @@
## Current status
2021-03-21: The adapter board seems to work fine in two different SS20. Currently the embedded PROM code exposes three devices in the FPGA:
* "RDOL,cryptoengine": exposes a (way too large) polynomial multiplier to implement GCM mode and a AES block. Currently used to implement DMA-based acceleration of AES-256-CBC through /dev/crypto. Unfortunately OpenSSL doesn't support AES-256-GCM in the cryptodev engine, and disagree with NetBSD's /dev/crypto on how to implement AES-256-CTR. And the default SSH cannot use cryptodev, it closes all file descriptors after cryptodev has opened /dev/crypto... still WiP.
* "RDOL,trng": exposes a 5 MHz counter (didn't realize the SS20 already had a good counter) and a so-far-not-true TRNG (implemented by a PRNG). The 'true' random generators I've found make Vivado screams very loudly when synthesizing... anyway both works fine in NetBSD 9.0 as a timecounter and an entropy source (which a PRNG really isn't, I know). still WiP.
* "RDOL,sdcard": trying to expose the micro-sd card slot as a storage device, at first using SPI mode. So far reading seems to work, and NetBSD can see a Sun disklabel on the micro-sd card if it has been partitioned that way. Mounting a FAT filesystem read-only now works (with very little testing as of yet). Writing not working yet. Very much WiP.