VexRiscv设计支持5级可配置流水线构架,5级流水线的结构为IF=>DEC=>EXE=>MEM=>WB
其中IF, MEM, WB为可选配置, 因此,实现2级或者3级流水线的结构为:
两级: DEC => EXE
三级: IF => DEC => EXE (传统结构)
VexRiscv的流水线配置分散到Plugin和Configuration中实现, 其中MEM/WB可以通过Configuration配置,
IF可以通过IBUS总线的InjectorStage属性配置。
由于其他pipeline plugin插件基本上也是基于pipeline设计, 因为对pipeline有一定的依赖性。在我们禁止
掉某一级pipeline,某些plugin也需要做响应的调整,否则将会导致VexRiscv编译失败。
下面详细列出为实现2/3级流水线设计,plugin需要做的配置调整。
流水线配置
val cpuConfig = VexRiscvConfig(
withMemoryStage = false,
withWriteBackStage = false,
plugins = List(
new IBusSimplePlugin(
resetVector = 0x80000000l,
withMemoryStage: 配置MEM流水级
withWriteBackStage: 配置WB流水级
plugins = List(
new IBusSimplePlugin(
resetVector = 0x80000000l,
cmdForkOnSecondStage = true, //if(args.contains("--tripp")) true else false,
cmdForkPersistence = true, //if(args.contains("--tripp")) true else false, //
prediction = NONE,
catchAccessFault = false,
compressedGen = true,
injectorStage = if(args.contains("--tripp")) true else false
),
cmdForkOnSecondStage: 跳转地址更新配置,设置true, 多一个更新周期,但会获得更好的pipeline时序
cmdForkPersistence: 总线访问信号保持,需要增加寄存器,但有更好的总线协议兼容性;
prediction: 我们采用2/3级流水,目的是获得最小的设计,预测功能此处禁用。
compressGen: 支持RVC压缩指令,为了和cortex-m0+对比,我们这里打开16位压缩指令的支持(对比Thumb2指令)
injectorStage: 这里可以关闭 IF 流水线级,两级结构的实现这里设置 false以禁用IF
new DBusSimplePlugin(
catchAddressMisaligned = false,
catchAccessFault = false
),
我们采用简单的DBUS实现, 后面会重新配置输出DBUS的AhbLite接口转换实现
此处没有需要特别针对流水线的配置
new DecoderSimplePlugin(
catchIllegalInstruction = true
),
简单的指令解码实现, RVC到RV的转换在IBUS中实现。
此处也没有针对流水线的配置
new RegFilePlugin(
regFileReadyKind = plugin.SYNC,
readInExecute = if(args.contains("--tripp")) false else true, // for Two-stage (DEC/EXE) support
zeroBoot = true,
x0Init = false
),
regFileReadKind : 我们采用的是同步读时序,这样对SRAM有更好的兼容性,也能获得更好的时序,但会一定的面积成本;
如果采用ASYNC模式,需要根据物理实现做调整,在VexRiscv.git的RegFilePlugin描述部分有 相关说明;
readInExecute: 默认SRC寄存器在DEC级读取,但可以根据时序情况调整到EXE级读。实测是在2级流水线实现时,
这里需要配置为true, 否则无法通过编译;
new SrcPlugin(
separatedAddSub = false,
executeInsertion = true
),
exectueInsertion: 这里的情况和RegFilePlugin.readInExecute类似,需要保持一致
new FullBarrelShifterPlugin (
earlyInjection = true // cause we have no MEM stage
),
默认FullBarrelShifterPlugin的结果是插到MEM级,我们禁用了MEM,要使用earlyInjection
配置讲结果插入到EXE级
//new MulPlugin,
new MulSimplePlugin,
new DivPlugin,
new HazardSimplePlugin(
bypassExecute = true,
bypassMemory = true,
bypassWriteBack = true,
bypassWriteBackBuffer = true,
pessimisticUseSrc = false,
pessimisticWriteRegFile = false,
pessimisticAddressMatch = false
),
MulPlugin的实现依赖EXE/MEM/WB流水级,结果插入到WB级。
我们禁用了MEM/WB,因此不能使用MulPlugin, 这里采用一个简单的乘法器实现替代
new DebugPlugin(ClockDomain.current.clone(reset = Bool().setName("debugReset")), 2),
new BranchPlugin(
earlyBranch = true, // move branch out of MEM stage to EXE stage
catchAddressMisaligned = false
),
DebugPlugin这里我们加入2个硬件断点支持;(用于对比Sifive E2x/E3x系列)
BranchPlugin为了优化时序, 是在MEM级执行,这里我们使用earlyBranch将执行提前到EXE级
new CsrPlugin(
config = CsrPluginConfig(
catchIllegalAccess = false,
mvendorid = null,
marchid = null,
mimpid = null,
mhartid = null,
misaExtensionsInit = 66,
misaAccess = CsrAccess.NONE,
mtvecAccess = CsrAccess.NONE,
mtvecInit = 0x00000020l,
mepcAccess = CsrAccess.READ_WRITE,
mscratchGen = false,
mcauseAccess = CsrAccess.READ_ONLY,
mbadaddrAccess = CsrAccess.READ_ONLY,
mcycleAccess = CsrAccess.NONE,
minstretAccess = CsrAccess.NONE,
ecallGen = false,
wfiGenAsWait = false,
ucycleAccess = CsrAccess.NONE,
uinstretAccess = CsrAccess.NONE
)
CSR可以根据具体实现配置,这里是一个比较简单的实现, 用于支持硬件断点等...
VexRiscvConfig结束,下面是对SOC接口的实现:
//CPU instanciation
val cpu = new VexRiscv(cpuConfig)
cpu.rework {
for (plugin <- cpuConfig.plugins) plugin match {
case plugin: IBusSimplePlugin => {
plugin.iBus.setAsDirectionLess() //Unset IO properties of iBus
master(plugin.iBus.toAhbLite3Master()).setName("iBusAhbLite3")
}
case plugin: DBusSimplePlugin => {
plugin.dBus.setAsDirectionLess()
master(plugin.dBus.toAhbLite3Master(avoidWriteToReadHazard = true)).setName("dBusAhbLite3")
}
case plugin: DebugPlugin if args.contains("--jtag")=> plugin.debugClockDomain {
plugin.io.bus.setAsDirectionLess()
val jtag = slave(new Jtag()).setName("jtag")
jtag <> plugin.io.bus.fromJtag()
}
case _ =>
}
}
首先采用上面的配置,例化一个VexRiscv CPU
需要特别注意的是,由于Spinal
hdl的实现限制,所有spinalhdl的object instance都必须申明在Spinal
verilog/SpinalVHDL内,如下所示:
val report = SpinalConfig(mode = if(args.contains("--vhdl")) VHDL else Verilog).generate {
//CPU configuration
val cpuConfig = VexRiscvConfig(
withMemoryStage = false,
withWriteBackStage = false,
plugins = List(.....)
否则, 编译器spinalhdl 将会报告java.lang.NullPointerExecption的异常。
具体原因请见SpinalHDL作者的解释:
Dolu1990 commented on May 30
B
asicaly, hardware statements realy need to be nested into a SpinalVerilog{ } / SpinalVhdl { } and idealy in a component.
As far i can see, i think that doing all of that in a shell will not be practical, as you realy need to define a full body