diff --git a/src/main/c/common/ram.ld b/src/main/c/common/ram.ld index 19dc3d9..2ebf858 100755 --- a/src/main/c/common/ram.ld +++ b/src/main/c/common/ram.ld @@ -4,7 +4,7 @@ ENTRY( _start ) MEMORY { - ram : ORIGIN = 0x80000000, LENGTH = 64k + ram : ORIGIN = DEFINED(__ram_origin) ? __ram_origin : 0x80000000, LENGTH = 64k } diff --git a/src/main/c/emulator/makefile b/src/main/c/emulator/makefile index e861785..7534d08 100755 --- a/src/main/c/emulator/makefile +++ b/src/main/c/emulator/makefile @@ -12,6 +12,17 @@ SRCS = $(wildcard src/*.c) \ LDSCRIPT = ${STANDALONE}/common/ram.ld +sim: CFLAGS += -DSIM +sim: all + +qemu: CFLAGS += -DQEMU +qemu: all + +litex: CFLAGS += -DLITEX -I${LITEX_BASE}/software/include +litex: | check_litex_base all +check_litex_base: + @[ "${LITEX_BASE}" ] || ( echo ">> LITEX_BASE is not set"; exit 1 ) + include ${STANDALONE}/common/riscv64-unknown-elf.mk include ${STANDALONE}/common/standalone.mk diff --git a/src/main/c/emulator/src/config.h b/src/main/c/emulator/src/config.h index 191ae9f..afce2d5 100644 --- a/src/main/c/emulator/src/config.h +++ b/src/main/c/emulator/src/config.h @@ -1,9 +1,12 @@ #ifndef CONFIG_H #define CONFIG_H -//#define QEMU -#define SIM +#ifndef OS_CALL #define OS_CALL 0xC0000000 +#endif + +#ifndef DTB #define DTB 0xC3000000 +#endif #endif diff --git a/src/main/c/emulator/src/hal.c b/src/main/c/emulator/src/hal.c index 52bb0e4..5a151bb 100644 --- a/src/main/c/emulator/src/hal.c +++ b/src/main/c/emulator/src/hal.c @@ -144,5 +144,92 @@ void halInit(){ #endif +#ifdef LITEX + +// this is copied from LiteX +#define CSR_ACCESSORS_DEFINED +static inline void csr_writeb(uint8_t value, unsigned long addr) +{ + *((volatile uint8_t *)addr) = value; +} + +static inline uint8_t csr_readb(unsigned long addr) +{ + return *(volatile uint8_t *)addr; +} + +static inline void csr_writew(uint16_t value, unsigned long addr) +{ + *((volatile uint16_t *)addr) = value; +} + +static inline uint16_t csr_readw(unsigned long addr) +{ + return *(volatile uint16_t *)addr; +} + +static inline void csr_writel(uint32_t value, unsigned long addr) +{ + *((volatile uint32_t *)addr) = value; +} + +static inline uint32_t csr_readl(unsigned long addr) +{ + return *(volatile uint32_t *)addr; +} + +// this is a file generated by LiteX +#include + +#if !defined(CSR_UART_BASE) || !defined(CSR_CPU_BASE) + #error LiteX configuration with uart and cpu_timer is required. +#endif + +void stopSim(){ + while(1); +} + +void putC(char ch){ + // protect against writing to a full tx fifo + while(uart_txfull_read()); + uart_rxtx_write(ch); +} + +int32_t getC(){ + if(uart_rxempty_read()) + { + return -1; + } + + // this is required to refresh rexempty status + uart_ev_pending_write(1 << 1); + return uart_rxtx_read(); +} + +uint32_t rdtime(){ + cpu_timer_latch_write(0); + uint32_t result = (uint32_t)cpu_timer_time_read(); + cpu_timer_latch_write(1); + return result; +} + +uint32_t rdtimeh(){ + cpu_timer_latch_write(0); + uint32_t result = (uint32_t)(cpu_timer_time_read() >> 32); + cpu_timer_latch_write(1); + return result; +} + +void setMachineTimerCmp(uint32_t low, uint32_t high){ + cpu_timer_latch_write(0); + cpu_timer_time_cmp_write((((unsigned long long int)high) << 32) | low); + cpu_timer_latch_write(1); +} + +void halInit(){ + cpu_timer_latch_write(1); +} + +#endif diff --git a/src/main/c/emulator/src/main.c b/src/main/c/emulator/src/main.c index 2102818..dc12fb8 100755 --- a/src/main/c/emulator/src/main.c +++ b/src/main/c/emulator/src/main.c @@ -161,7 +161,7 @@ void trap(){ #ifdef SIM uint32_t instruction = csr_read(mbadaddr); #endif -#ifdef QEMU +#if defined(QEMU) || defined(LITEX) uint32_t instruction = 0; uint32_t i; if (mepc & 2) { diff --git a/src/main/scala/vexriscv/demo/GenSmallAndProductiveCfu.scala b/src/main/scala/vexriscv/demo/GenSmallAndProductiveCfu.scala new file mode 100644 index 0000000..9983814 --- /dev/null +++ b/src/main/scala/vexriscv/demo/GenSmallAndProductiveCfu.scala @@ -0,0 +1,76 @@ +package vexriscv.demo + +import spinal.core._ +import vexriscv.plugin._ +import vexriscv.{VexRiscv, VexRiscvConfig, plugin} + +/** + * Created by spinalvm on 15.06.17. + */ +object GenSmallAndProductiveCfu extends App{ + def cpu() = new VexRiscv( + config = VexRiscvConfig( + plugins = List( + new IBusSimplePlugin( + resetVector = 0x80000000l, + cmdForkOnSecondStage = false, + cmdForkPersistence = false, + prediction = NONE, + catchAccessFault = false, + compressedGen = false + ), + new DBusSimplePlugin( + catchAddressMisaligned = false, + catchAccessFault = false + ), + new CsrPlugin(CsrPluginConfig.smallest), + new DecoderSimplePlugin( + catchIllegalInstruction = false + ), + new RegFilePlugin( + regFileReadyKind = plugin.SYNC, + zeroBoot = false + ), + new IntAluPlugin, + new SrcPlugin( + separatedAddSub = false, + executeInsertion = true + ), + new LightShifterPlugin, + new HazardSimplePlugin( + bypassExecute = true, + bypassMemory = true, + bypassWriteBack = true, + bypassWriteBackBuffer = true, + pessimisticUseSrc = false, + pessimisticWriteRegFile = false, + pessimisticAddressMatch = false + ), + new BranchPlugin( + earlyBranch = false, + catchAddressMisaligned = false + ), + new CfuPlugin( + p = CfuParameter( + stageCount = 1, + allowZeroLatency = true, + CFU_VERSION = 0, + CFU_INTERFACE_ID_W = 0, + CFU_FUNCTION_ID_W = 2, + CFU_REORDER_ID_W = 0, + CFU_REQ_RESP_ID_W = 0, + CFU_INPUTS = 2, + CFU_INPUT_DATA_W = 32, + CFU_OUTPUTS = 1, + CFU_OUTPUT_DATA_W = 32, + CFU_FLOW_REQ_READY_ALWAYS = false, + CFU_FLOW_RESP_READY_ALWAYS = false + ) + ), + new YamlPlugin("cpu0.yaml") + ) + ) + ) + + SpinalVerilog(cpu()) +} diff --git a/src/main/scala/vexriscv/plugin/CfuPlugin.scala b/src/main/scala/vexriscv/plugin/CfuPlugin.scala new file mode 100644 index 0000000..334fc25 --- /dev/null +++ b/src/main/scala/vexriscv/plugin/CfuPlugin.scala @@ -0,0 +1,144 @@ +package vexriscv.plugin + +import vexriscv.{DecoderService, ExceptionCause, ExceptionService, Stage, Stageable, VexRiscv} +import spinal.core._ +import spinal.lib._ + +case class CfuParameter(stageCount : Int, + allowZeroLatency : Boolean, + CFU_VERSION : Int, + CFU_INTERFACE_ID_W : Int, + CFU_FUNCTION_ID_W : Int, + CFU_REORDER_ID_W : Int, + CFU_REQ_RESP_ID_W : Int, + CFU_INPUTS : Int, + CFU_INPUT_DATA_W : Int, + CFU_OUTPUTS : Int, + CFU_OUTPUT_DATA_W : Int, + CFU_FLOW_REQ_READY_ALWAYS : Boolean, + CFU_FLOW_RESP_READY_ALWAYS : Boolean) + +case class CfuCmd(p : CfuParameter) extends Bundle{ + val function_id = UInt(p.CFU_FUNCTION_ID_W bits) + val reorder_id = UInt(p.CFU_REORDER_ID_W bits) + val request_id = UInt(p.CFU_REQ_RESP_ID_W bits) + val inputs = Vec(Bits(p.CFU_INPUT_DATA_W bits), p.CFU_INPUTS) +} + +case class CfuRsp(p : CfuParameter) extends Bundle{ + val response_ok = Bool() + val response_id = UInt(p.CFU_REQ_RESP_ID_W bits) + val outputs = Vec(Bits(p.CFU_OUTPUT_DATA_W bits), p.CFU_OUTPUTS) +} + +case class CfuBus(p : CfuParameter) extends Bundle with IMasterSlave{ + val cmd = Stream(CfuCmd(p)) + val rsp = Stream(CfuRsp(p)) + + override def asMaster(): Unit = { + master(cmd) + slave(rsp) + } +} + +class CfuPlugin(val p: CfuParameter) extends Plugin[VexRiscv]{ + assert(p.CFU_INPUTS <= 2) + assert(p.CFU_OUTPUTS == 1) + + var bus : CfuBus = null + var joinException : Flow[ExceptionCause] = null + + lazy val forkStage = pipeline.execute + lazy val joinStage = pipeline.stages(Math.min(pipeline.stages.length - 1, pipeline.indexOf(forkStage) + p.stageCount - 1)) + + + object CFU_ENABLE extends Stageable(Bool()) + object CFU_FUNCTION extends Stageable(UInt(p.CFU_FUNCTION_ID_W bits)) + object CFU_IN_FLIGHT extends Stageable(Bool()) + + override def setup(pipeline: VexRiscv): Unit = { + import pipeline._ + import pipeline.config._ + + bus = master(CfuBus(p)) + joinException = pipeline.service(classOf[ExceptionService]).newExceptionPort(joinStage) + + val decoderService = pipeline.service(classOf[DecoderService]) + + //custom-0 + decoderService.add(List( + M"000000-----------000-----0001011" -> List( + CFU_ENABLE -> True, + CFU_FUNCTION -> U"00", + REGFILE_WRITE_VALID -> True, + BYPASSABLE_EXECUTE_STAGE -> Bool(p.stageCount == 0), + BYPASSABLE_MEMORY_STAGE -> Bool(p.stageCount <= 1), + RS1_USE -> True, + RS2_USE -> True + ), + M"000000-----------001-----0001011" -> List( + CFU_ENABLE -> True, + CFU_FUNCTION -> U"01", + REGFILE_WRITE_VALID -> True, + BYPASSABLE_EXECUTE_STAGE -> Bool(p.stageCount == 0), + BYPASSABLE_MEMORY_STAGE -> Bool(p.stageCount <= 1), + RS1_USE -> True, + RS2_USE -> True + ) + )) + } + + override def build(pipeline: VexRiscv): Unit = { + import pipeline._ + import pipeline.config._ + + forkStage plug new Area{ + import forkStage._ + val schedule = arbitration.isValid && input(CFU_ENABLE) + val hold = RegInit(False) setWhen(schedule) clearWhen(bus.cmd.ready) + val fired = RegInit(False) setWhen(bus.cmd.fire) clearWhen(!arbitration.isStuckByOthers) + insert(CFU_IN_FLIGHT) := schedule || hold || fired + + bus.cmd.valid := (schedule || hold) && !fired + arbitration.haltItself setWhen(bus.cmd.valid && !bus.cmd.ready) + + bus.cmd.function_id := input(CFU_FUNCTION) + bus.cmd.reorder_id := 0 + bus.cmd.request_id := 0 + if(p.CFU_INPUTS >= 1) bus.cmd.inputs(0) := input(RS1) + if(p.CFU_INPUTS >= 2) bus.cmd.inputs(1) := input(RS2) + } + + joinStage plug new Area{ + import joinStage._ + + //If the CFU interface can produce a result combinatorialy and the fork stage isn't the same than the join stage + //Then it is required to add a buffer on rsp to not propagate the fork stage ready := False in the CPU pipeline. + val rsp = if(p.CFU_FLOW_RESP_READY_ALWAYS){ + bus.rsp.toFlow.toStream.queueLowLatency( + size = p.stageCount + 1, + latency = 0 + ) + } else if(forkStage != joinStage && p.allowZeroLatency) { + bus.rsp.m2sPipe() + } else { + bus.rsp.combStage() + } + + joinException.valid := False + joinException.code := 15 + joinException.badAddr := 0 + + rsp.ready := False + when(input(CFU_IN_FLIGHT)){ + arbitration.haltItself setWhen(!rsp.valid) + rsp.ready := arbitration.isStuckByOthers + output(REGFILE_WRITE_DATA) := rsp.outputs(0) + + when(arbitration.isValid){ + joinException.valid := !rsp.response_ok + } + } + } + } +}