Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing environment for AXKU040 dev board #38

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,12 @@ object sanitytests extends ScalaModule {
)
override def moduleDeps = super.moduleDeps ++ Seq(myrocketchip, shells)
}
object axku040 extends Tests with CommonModule with TestModule.Utest {
override def ivyDeps = Agg(
ivys.utest
)
override def moduleDeps = super.moduleDeps ++ Seq(myrocketchip, shells)
}
}

object spike extends Module {
Expand Down
617 changes: 617 additions & 0 deletions patches/rocket-chip-fpga-shells/4.diff

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ rocket-chip-inclusive-cache https://github.com/chipsalliance/rocket-chip-inclusi
rocket-chip-blocks https://github.com/chipsalliance/rocket-chip-blocks/pull/2.diff
rocket-dsp-utils https://github.com/ucb-bar/rocket-dsp-utils/pull/6.diff
testchipip https://github.com/ucb-bar/testchipip/pull/149.diff
rocket-chip-fpga-shells https://github.com/chipsalliance/rocket-chip-fpga-shells/pull/4.diff
<!-- END-PATCH -->

## Why not Chipyard
Expand Down
54 changes: 54 additions & 0 deletions sanitytests/axku040/resources/bootrom.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "i2c.h"

static char volatile * const I2C_PRESCALER_LO = (char *)0x10002000;
static char volatile * const I2C_PRESCALER_HI = (char *)0x10002004;
static char volatile * const I2C_CTRL = (char *)0x10002008;
static char volatile * const I2C_DATA = (char *)0x1000200C;
static char volatile * const I2C_STAT = (char *)0x10002010;
static char volatile * const I2C_CMD = (char *)0x10002010;

// These two I2C registers are set for correct Vref for FMC2 IO
static char const __attribute__((section(".rodata"))) i2c_reg[] = {0x06, 0xfc, 0x07, 0xfc};

void i2c_write(char const * buf, unsigned int size);

__attribute__((naked, section(".text.hang")))
void _hang(void) {
__asm__ __volatile__ (
"li sp, 0xFFFFFF00\n\t"
"jal _start\n\t"
);
}

void _start(void) {
// Program the FMC voltage
long const prescaler = METAL_SIFIVE_I2C_GET_PRESCALER(200000, 100000000);
*I2C_PRESCALER_LO = (char)prescaler;
*I2C_PRESCALER_HI = (char)(prescaler >> 8);

*I2C_CTRL |= METAL_I2C_CONTROL_EN;

i2c_write(i2c_reg, 2);
i2c_write(i2c_reg + 2, 2);

while(1);
}

void i2c_write(char const * buf, unsigned int size) {
while((*I2C_STAT) & METAL_I2C_STATUS_TIP);
*I2C_DATA = METAL_SIFIVE_I2C_INSERT_RW_BIT(0xC2, METAL_I2C_WRITE);
*I2C_CMD = METAL_I2C_CMD_WRITE | METAL_I2C_CMD_START;
while((*I2C_STAT) & METAL_I2C_STATUS_TIP);
if ((*I2C_STAT) & METAL_I2C_STATUS_RXACK) { while(1); }

for (int i = 0; i < size; i++) {
char cmd = METAL_I2C_CMD_WRITE;
*I2C_DATA = buf[i];
if (i == (size - 1)) {
cmd |= METAL_I2C_CMD_STOP;
}
*I2C_CMD = cmd;
while((*I2C_STAT) & METAL_I2C_STATUS_TIP);
if ((*I2C_STAT) & METAL_I2C_STATUS_RXACK) { while(1); }
}
}
34 changes: 34 additions & 0 deletions sanitytests/axku040/resources/i2c.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* Macro definition from freedom-metal i2c driver */
/* Copyright 2019 SiFive, Inc */
/* SPDX-License-Identifier: Apache-2.0 */

/* Register fields */
#define METAL_I2C_CONTROL_EN (1UL << 7)
#define METAL_I2C_CONTROL_IE (1UL << 6)
#define METAL_I2C_WRITE (0UL << 0)
#define METAL_I2C_READ (1UL << 0)
#define METAL_I2C_CMD_START (1UL << 7)
#define METAL_I2C_CMD_STOP (1UL << 6)
#define METAL_I2C_CMD_READ (1UL << 5)
#define METAL_I2C_CMD_WRITE (1UL << 4)
#define METAL_I2C_CMD_ACK (1UL << 3)
#define METAL_I2C_CMD_IACK (1UL << 0)
#define METAL_I2C_STATUS_RXACK (1UL << 7)
#define METAL_I2C_STATUS_BUSY (1UL << 6)
#define METAL_I2C_STATUS_AL (1UL << 5)
#define METAL_I2C_STATUS_TIP (1UL << 1)
#define METAL_I2C_STATUS_IP (1UL << 0)

/* Prescaler max value */
#define METAL_I2C_PRESCALE_MAX 0xFFFF

/* Check endianess */
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
#error *** Unsupported endianess ***
#endif

#define METAL_SIFIVE_I2C_INSERT_STOP(stop_flag) ((stop_flag & 0x01UL) << 6)
#define METAL_SIFIVE_I2C_INSERT_RW_BIT(addr, rw) \
((addr & 0x7FUL) << 1 | (rw & 0x01UL))
#define METAL_SIFIVE_I2C_GET_PRESCALER(baud, clock_rate) \
((clock_rate / (baud * 5)) - 1)
10 changes: 10 additions & 0 deletions sanitytests/axku040/resources/linker.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SECTIONS
{
ROM_BASE = 0x10000; /* ... but actually position independent */

. = ROM_BASE;
.rodata : { *(.rodata) }
. = ROM_BASE + 0x40;
.text.hang : { *(.text.hang) }
.text : { *(.text) }
}
88 changes: 88 additions & 0 deletions sanitytests/axku040/src/DesignKeyWrapper.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package sanitytests.axku040

import chisel3._
import chipsalliance.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink.TLInwardNode
import freechips.rocketchip.subsystem._
import freechips.rocketchip.devices.debug._
import freechips.rocketchip.devices.tilelink.{BootROM, BootROMLocated}
import sifive.blocks.devices.gpio._
import sifive.blocks.devices.i2c._
import sifive.blocks.devices.spi._
import sifive.blocks.devices.uart._
import sifive.fpgashells.clocks._
import sifive.fpgashells.ip.xilinx.PowerOnResetFPGAOnly
import sifive.fpgashells.shell._
import sifive.fpgashells.shell.xilinx.DDRAlinxAxku040PlacedOverlay

class RocketFPGASubsystem(resetWrangler: ClockAdapterNode, pll: PLLNode, ddr: TLInwardNode)(implicit p: Parameters)
extends RocketSubsystem
with HasPeripheryDebug with HasPeripheryGPIO with HasPeripheryUART with HasPeripheryI2C
{
override lazy val module = new RocketFPGASubsystemImp(this)

p(BootROMLocated(location)).map { BootROM.attach(_, this, CBUS) }

ddr := mbus.toDRAMController(Some("xilinx_mig"))()
}
class RocketFPGASubsystemImp(val out: RocketFPGASubsystem)
extends RocketSubsystemModuleImp(out)
with HasPeripheryDebugModuleImp
with HasPeripheryGPIOModuleImp
with HasPeripheryUARTModuleImp
with HasPeripheryI2CModuleImp

class DesignKeyWrapper()(implicit p: Parameters) extends LazyModule {
private val fpgaClockSource = p(ClockInputOverlayKey).head.place(ClockInputDesignInput()).overlayOutput.node
private val pll = p(PLLFactoryKey)()
private val coreClock = ClockSinkNode(freqMHz = p(FPGAFrequencyKey), jitterPS = 500.0)
private val resetWrangler = LazyModule(new ResetWrangler()).node
private val led1 = p(LEDOverlayKey).head.place(LEDDesignInput()).overlayOutput.led
private val led2 = p(LEDOverlayKey).head.place(LEDDesignInput()).overlayOutput.led
private val led3 = p(LEDOverlayKey).head.place(LEDDesignInput()).overlayOutput.led
private val led4 = p(LEDOverlayKey).head.place(LEDDesignInput()).overlayOutput.led
private val jtag = p(JTAGDebugOverlayKey).head.place(JTAGDebugDesignInput()).overlayOutput.jtag
private val ddr = p(DDROverlayKey).head
.place(DDRDesignInput(0x80000000L, resetWrangler, pll))
.asInstanceOf[DDRAlinxAxku040PlacedOverlay]
private val rocket = LazyModule(new RocketFPGASubsystem(resetWrangler, pll, ddr.overlayOutput.ddr))
private val uartBundleBridgeSrcNode = BundleBridgeSource(() => new UARTPortIO(p(PeripheryUARTKey).head))
p(UARTOverlayKey).head.place(UARTDesignInput(uartBundleBridgeSrcNode))

private val i2cBundleBridgeSrcNode = BundleBridgeSource(() => new I2CPort())
p(I2COverlayKey).head.place(I2CDesignInput(i2cBundleBridgeSrcNode))

override lazy val module = new LazyRawModuleImp(this) {
val clockBundle = coreClock.in.head._1
childClock := clockBundle.clock
childReset := clockBundle.reset | rocket.module.debug.map(_.ndreset).getOrElse(false.B)

uartBundleBridgeSrcNode.bundle <> rocket.module.uart.head
i2cBundleBridgeSrcNode.bundle <> rocket.module.i2c.head

val systemJtag = rocket.module.debug.head.systemjtag.head
systemJtag.jtag.TCK := jtag.TCK
systemJtag.jtag.TDI := jtag.TDI
jtag.TDO.data := systemJtag.jtag.TDO.data
jtag.TDO.driven := 1.B
systemJtag.jtag.TMS := jtag.TMS
systemJtag.reset := PowerOnResetFPGAOnly(childClock).asAsyncReset
systemJtag.mfr_id := 0x233.U(11.W)
systemJtag.part_number := 0xde.U(12.W)
systemJtag.version := 0x1.U(4.W)
Debug.connectDebugClockAndReset(rocket.module.debug, childClock)
rocket.module.resetctrl.foreach { rc =>
rc.hartIsInReset.foreach { _ := childReset.asBool }
}

withClockAndReset(childClock, childReset) {
led1 := rocket.module.gpio.head.asInstanceOf[GPIOPortIO].pins(0).o.oval
led2 := rocket.module.gpio.head.asInstanceOf[GPIOPortIO].pins(1).o.oval
led3 := rocket.module.gpio.head.asInstanceOf[GPIOPortIO].pins(2).o.oval
led4 := ddr.placedAuxIO.MIGCalibComplete
}

}
coreClock := resetWrangler := ClockGroup() := pll := fpgaClockSource
}
142 changes: 142 additions & 0 deletions sanitytests/axku040/src/FPGATest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package sanitytests.axku040

import chisel3._
import chipsalliance.rocketchip.config.{Config, Parameters}
import firrtl.AnnotationSeq
import firrtl.options.TargetDirAnnotation
import firrtl.stage.{FirrtlStage, RunFirrtlTransformAnnotation}
import freechips.rocketchip.devices.debug._
import freechips.rocketchip.devices.tilelink.BootROMLocated
import freechips.rocketchip.stage.{ConfigsAnnotation, OutputBaseNameAnnotation, TopModuleAnnotation}
import freechips.rocketchip.system.RocketChipStage
import logger.LazyLogging
import sanitytests.xcku040.resource
import sifive.blocks.devices.gpio.{GPIOParams, PeripheryGPIOKey}
import sifive.blocks.devices.i2c.{I2CParams, PeripheryI2CKey}
import sifive.blocks.devices.uart.{UARTParams, PeripheryUARTKey}
import sifive.fpgashells.shell.{DesignKey, FPGAFrequencyKey}
import os._
import utest._

class FPGATestConfig extends Config((site, here, up) => {
case DesignKey => (p: Parameters) => new DesignKeyWrapper()(p)
case FPGAFrequencyKey => 100.0
case PeripheryGPIOKey => Seq(
GPIOParams(address = 0x10000000, width = 4)
)
case PeripheryUARTKey => Seq(
UARTParams(address = 0x10001000)
)
case PeripheryI2CKey => Seq(
I2CParams(address = 0x10002000)
)
case ExportDebug => up(ExportDebug, site).copy(protocols = Set(JTAG))
case DebugModuleKey => up(DebugModuleKey, site).map(_.copy(clockGate = false))
case BootROMLocated(x) =>
up(BootROMLocated(x), site).map(_.copy(contentFileName = {
val tmp = os.temp.dir()
val elf = tmp / "bootrom.elf"
val bin = tmp / "bootrom.bin"
val img = tmp / "bootrom.img"
// format: off
proc(
"clang",
"--target=riscv64", "-march=rv64gc",
"-mno-relax",
"-static",
"-nostdlib",
"-Wl,--no-gc-sections",
"-fuse-ld=lld", s"-T${resource("linker.ld")}",
s"${resource("bootrom.c")}",
"-o", elf
).call()
proc(
"llvm-objcopy",
"-O", "binary",
elf,
bin
).call()
// format: on
bin.toString()
}))
})

case class TestHarness[M <: RawModule](
configs: Seq[Class[_ <: Config]],
targetDir: Option[os.Path] = None)
extends LazyLogging {
lazy val filelist: os.Path = {
logger.warn(s"start to elaborate fpga designs in $outputDirectory")
Seq(
new RocketChipStage,
new FirrtlStage
).foldLeft(
AnnotationSeq(
Seq(
TargetDirAnnotation(outputDirectory.toString),
new TopModuleAnnotation(testHarness),
new ConfigsAnnotation(configs.map(_.getName)),
RunFirrtlTransformAnnotation(new firrtl.passes.InlineInstances),
new OutputBaseNameAnnotation("TestHarness")
)
)
) { case (annos, stage) => stage.transform(annos) }
logger.warn(s"$testHarness with configs: ${configs.mkString("_")} generated.")
val filelist = outputDirectory / "filelist"
os.write(filelist, os.walk(outputDirectory).filter(_.ext == "v").map(_.toString).mkString("\n"))
filelist
}
lazy val bitstreamScript: os.Path = {
val script = outputDirectory / "bitstream.sh"
os.write(
script,
f"""
|#!/usr/bin/env bash
|cd $outputDirectory
|vivado -nojournal -mode batch \\
| -source ${os.pwd / "dependencies" / "rocket-chip-fpga-shells" / "xilinx" / "common" / "tcl" / "vivado.tcl"} \\
| -tclargs -top-module AlinxAxku040Shell \\
| -F $filelist \\
| -ip-vivado-tcls "${os.walk(outputDirectory).filter(_.ext == "tcl").mkString(" ")}" \\
| -board alinx_axku040
|""".stripMargin
)
os.perms.set(script, "rwx------")
script
}
lazy val rerunFromSynthesisScript: os.Path = {
// depend on bitstream generation script
bitstreamScript
val script = outputDirectory / "rerunFromSynthesis.sh"
os.write(
script,
f"""
|#!/usr/bin/env bash
|cd $outputDirectory
|vivado -nojournal -mode batch \\
| -source ${os.pwd / "dependencies" / "chipyard" / "fpga" / "scripts" / "run_impl_bitstream.tcl"} \\
| -tclargs \\
| ${outputDirectory / "obj" / "post_synth.dcp"} \\
| alinx_axku040 \\
| ${outputDirectory / "debug_obj"} \\
| ${os.pwd / "dependencies" / "rocket-chip-fpga-shells" / "xilinx" / "common" / "tcl"} \\
|""".stripMargin
)
os.perms.set(script, "rwx------")
script
}
val testHarness = classOf[sifive.fpgashells.shell.xilinx.AlinxAxku040Shell]
val outputDirectory: os.Path = targetDir.getOrElse(os.temp.dir(deleteOnExit = false))
}

object FPGATest extends TestSuite {
val outputDirectory = os.pwd / "out" / "FPGATest"
os.remove.all(outputDirectory)
os.makeDir(outputDirectory)
val tests = Tests {
test("FPGA build script") {
val configs = Seq(classOf[FPGATestConfig], classOf[freechips.rocketchip.system.DefaultConfig])
TestHarness(configs, Some(outputDirectory)).rerunFromSynthesisScript
}
}
}
7 changes: 7 additions & 0 deletions sanitytests/axku040/src/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package sanitytests

import os._

package object xcku040 {
def resource(file: String): Path = Path(java.nio.file.Paths.get(getClass().getClassLoader().getResource(file).toURI))
}