A FPGA friendly 32 bit RISC-V CPU implementation
Go to file
Charles Papon cc875d1c0b Add TCP server socket to manage debug access from openOCD (as instance) 2017-05-22 00:42:19 +02:00
project boot 2017-03-08 22:17:48 +01:00
src Add TCP server socket to manage debug access from openOCD (as instance) 2017-05-22 00:42:19 +02:00
.gitignore Add self checked dhrystone test 2017-03-18 12:32:14 +01:00
README.md Update README.md 2017-05-19 17:13:33 +02:00
backup boot 2017-03-08 22:17:48 +01:00
build.sbt WIP 2017-03-11 00:34:49 +01:00

README.md

This repository host an RISC-V implementation written in SpinalHDL. There is some specs :

  • RV32IM instruction set
  • Interrupts and exception handling with the Machine mode from the riscv-privileged-v1.9.1 specification.
  • Pipelined on 5 stages (Fetch, Decode, Execute, Memory, WriteBack)
  • 1.17 DMIPS/Mhz with all extension
  • Optimized for FPGA
  • Optional MUL/DIV/REM extension
  • Optional instruction and data caches
  • Optional MMU
  • Two implementation of shift instructions, Single cycle / shiftNumber cycle
  • Each stage could have bypass or interlock hazard logic
  • FreeRTOS port https://github.com/Dolu1990/FreeRTOS-RISCV

The hardware description of this CPU is done by using an very software oriented approach (without any overhead in the generated hardware). There is a list of software concepts used :

  • There is very few fixed things. Nearly everything is plugin based. The PC manager is a plugin, the register file is a plugin, the hazard controller is a plugin ...
  • There is an automatic a tool which allow plugins to insert data in the pipeline at a given stage, and allow other plugins to read it in another stages through automatic pipelining.
  • There is an service system which provide a very dynamic framework. As instance, a plugin could provide an exception service which could then be used by others plugins to emit exceptions from the pipeline.

CPU instantiation

There is an example of instantiation of the CPU

//Define the cpu configuraiton
val config = VexRiscvConfig(
    pcWidth = 32
)

//Define the CSR configuration (riscv-privileged-v1.9.1)
val csrConfig = MachineCsrConfig(
    mvendorid      = 11,
    marchid        = 22,
    mimpid         = 33,
    mhartid        = 0,
    misaExtensionsInit = 66,
    misaAccess     = CsrAccess.READ_WRITE,
    mtvecAccess    = CsrAccess.READ_WRITE,
    mtvecInit      = 0x00000020l,
    mepcAccess     = CsrAccess.READ_WRITE,
    mscratchGen    = true,
    mcauseAccess   = CsrAccess.READ_WRITE,
    mbadaddrAccess = CsrAccess.READ_WRITE,
    mcycleAccess   = CsrAccess.READ_WRITE,
    minstretAccess = CsrAccess.READ_WRITE,
    ecallGen       = true,
    wfiGen         = true
)

//Add plugins into the cpu configuration
config.plugins ++= List(
    new PcManagerSimplePlugin(0x00000000l, false),
    new IBusSimplePlugin(
        interfaceKeepData = true
    ),
    new DecoderSimplePlugin(
        catchIllegalInstruction = true
    ),
    new RegFilePlugin(
        regFileReadyKind = Plugin.SYNC,
        zeroBoot = false
    ),
    new IntAluPlugin,
    new SrcPlugin,
    new FullBarrielShifterPlugin,
    new DBusSimplePlugin(
        catchUnalignedException = true
    ),
    new HazardSimplePlugin(true, true, true, true),
    new MulPlugin,
    new DivPlugin,
    new MachineCsr(csrConfig),
    new BranchPlugin(
        earlyBranch = false,
        catchUnalignedException = true,
        prediction = DYNAMIC
    )
)

//Instanciate the CPU
val toplevel = new VexRiscv(config)

Plugin structure

There is an example of an pseudo ALU plugin :


//Define an signal name/type which could be used in the pipeline
object ALU_ENABLE extends Stageable(Bool)
object ALU_OP     extends Stageable(Bits(2  bits))  // ADD, SUB, AND, OR
object ALU_SRC1   extends Stageable(UInt(32 bits))
object ALU_SRC2   extends Stageable(UInt(32 bits))
object ALU_RESULT extends Stageable(UInt(32 bits))

class AluPlugin() extends Plugin[VexRiscv]{

  //Callback to setup the plugin and ask for different services
  override def setup(pipeline: VexRiscv): Unit = {
    import pipeline.config._
    //Do some setups as for example specifying some instruction decoding by using the Decoding service
    val decoderService = pipeline.service(classOf[DecoderService])

    decoderService.addDefault(ALU_ENABLE,False)
    decodingService.add(List(
        M"0100----------" -> List(ALU_ENABLE -> True, ALU_OP -> B"01"),
        M"0110---11-----" -> List(ALU_ENABLE -> True, ...)
    ))
  }


  //Callback to build the hardware logic
  override def build(pipeline: VexRiscv): Unit = {
    import pipeline._

    execute plug new Area {
      import execute._
      //Add some logic in the execute stage
      insert(ALU_RESULT) := input(ALU_OP).mux(
        B"00" -> input(ALU_SRC1) + input(ALU_SRC2),
        B"01" -> input(ALU_SRC1) - input(ALU_SRC2),
        B"10" -> input(ALU_SRC1) & input(ALU_SRC2),
        B"11" -> input(ALU_SRC1) | input(ALU_SRC2),
      )
    }

    writeBack plug new Area {
      import writeBack._
      //Add some logic in the execute stage
      when(input(ALU_ENABLE)){
        input(REGFILE_WRITE_DATA) := input(ALU_RESULT)
      }
    }
  }
}