1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
|
package vexriscv.plugin
import vexriscv._
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba3.ahblite.{AhbLite3, AhbLite3Config, AhbLite3Master}
import spinal.lib.bus.amba4.axi._
import spinal.lib.bus.avalon.{AvalonMM, AvalonMMConfig}
import spinal.lib.bus.bmb.{Bmb, BmbParameter}
import spinal.lib.bus.wishbone.{Wishbone, WishboneConfig}
import spinal.lib.bus.simple._
import vexriscv.Riscv.{FENCE, FENCE_I}
case class IBusSimpleCmd() extends Bundle{
val pc = UInt(32 bits)
}
case class IBusSimpleRsp() extends Bundle with IMasterSlave{
val error = Bool
val inst = Bits(32 bits)
override def asMaster(): Unit = {
out(error,inst)
}
}
object IBusSimpleBus{
def getAxi4Config() = Axi4Config(
addressWidth = 32,
dataWidth = 32,
useId = false,
useRegion = false,
useBurst = false,
useLock = false,
useQos = false,
useLen = false,
useResp = true,
useSize = false
)
def getAvalonConfig() = AvalonMMConfig.pipelined(
addressWidth = 32,
dataWidth = 32
).getReadOnlyConfig.copy(
useResponse = true,
maximumPendingReadTransactions = 8
)
def getWishboneConfig() = WishboneConfig(
addressWidth = 30,
dataWidth = 32,
selWidth = 4,
useSTALL = false,
useLOCK = false,
useERR = true,
useRTY = false,
tgaWidth = 0,
tgcWidth = 0,
tgdWidth = 0,
useBTE = true,
useCTI = true
)
def getPipelinedMemoryBusConfig() = PipelinedMemoryBusConfig(
addressWidth = 32,
dataWidth = 32
)
def getAhbLite3Config() = AhbLite3Config(
addressWidth = 32,
dataWidth = 32
)
def getBmbParameter(plugin : IBusSimplePlugin = null) = BmbParameter(
addressWidth = 32,
dataWidth = 32,
lengthWidth = 2,
sourceWidth = 0,
contextWidth = 0,
canRead = true,
canWrite = false,
alignment = BmbParameter.BurstAlignement.LENGTH,
maximumPendingTransaction = if(plugin != null) plugin.pendingMax else Int.MaxValue
)
}
case class IBusSimpleBus(plugin: IBusSimplePlugin) extends Bundle with IMasterSlave {
var cmd = Stream(IBusSimpleCmd())
var rsp = Flow(IBusSimpleRsp())
override def asMaster(): Unit = {
master(cmd)
slave(rsp)
}
def cmdS2mPipe() : IBusSimpleBus = {
val s = IBusSimpleBus(plugin)
s.cmd << this.cmd.s2mPipe()
this.rsp << s.rsp
s
}
def toAxi4ReadOnly(): Axi4ReadOnly = {
assert(plugin.cmdForkPersistence)
val axi = Axi4ReadOnly(IBusSimpleBus.getAxi4Config())
axi.ar.valid := cmd.valid
axi.ar.addr := cmd.pc(axi.readCmd.addr.getWidth -1 downto 2) @@ U"00"
axi.ar.prot := "110"
axi.ar.cache := "1111"
cmd.ready := axi.ar.ready
rsp.valid := axi.r.valid
rsp.inst := axi.r.data
rsp.error := !axi.r.isOKAY()
axi.r.ready := True
axi
}
def toAvalon(): AvalonMM = {
assert(plugin.cmdForkPersistence)
val avalonConfig = IBusSimpleBus.getAvalonConfig()
val mm = AvalonMM(avalonConfig)
mm.read := cmd.valid
mm.address := (cmd.pc >> 2) @@ U"00"
cmd.ready := mm.waitRequestn
rsp.valid := mm.readDataValid
rsp.inst := mm.readData
rsp.error := mm.response =/= AvalonMM.Response.OKAY
mm
}
def toWishbone(): Wishbone = {
val wishboneConfig = IBusSimpleBus.getWishboneConfig()
val bus = Wishbone(wishboneConfig)
val cmdPipe = cmd.stage()
bus.ADR := (cmdPipe.pc >> 2)
bus.CTI := B"000"
bus.BTE := "00"
bus.SEL := "1111"
bus.WE := False
bus.DAT_MOSI.assignDontCare()
bus.CYC := cmdPipe.valid
bus.STB := cmdPipe.valid
cmdPipe.ready := cmdPipe.valid && bus.ACK
rsp.valid := bus.CYC && bus.ACK
rsp.inst := bus.DAT_MISO
rsp.error := False //TODO
bus
}
def toPipelinedMemoryBus(): PipelinedMemoryBus = {
val pipelinedMemoryBusConfig = IBusSimpleBus.getPipelinedMemoryBusConfig()
val bus = PipelinedMemoryBus(pipelinedMemoryBusConfig)
bus.cmd.arbitrationFrom(cmd)
bus.cmd.address := cmd.pc.resized
bus.cmd.write := False
bus.cmd.mask.assignDontCare()
bus.cmd.data.assignDontCare()
rsp.valid := bus.rsp.valid
rsp.inst := bus.rsp.payload.data
rsp.error := False
bus
}
//cmdForkPersistence need to bet set
def toAhbLite3Master(): AhbLite3Master = {
assert(plugin.cmdForkPersistence)
val bus = AhbLite3Master(IBusSimpleBus.getAhbLite3Config())
bus.HADDR := this.cmd.pc
bus.HWRITE := False
bus.HSIZE := 2
bus.HBURST := 0
bus.HPROT := "1110"
bus.HTRANS := this.cmd.valid ## B"0"
bus.HMASTLOCK := False
bus.HWDATA.assignDontCare()
this.cmd.ready := bus.HREADY
val pending = RegInit(False) clearWhen(bus.HREADY) setWhen(this.cmd.fire)
this.rsp.valid := bus.HREADY && pending
this.rsp.inst := bus.HRDATA
this.rsp.error := bus.HRESP
bus
}
def toBmb() : Bmb = {
val pipelinedMemoryBusConfig = IBusSimpleBus.getBmbParameter(plugin)
val bus = Bmb(pipelinedMemoryBusConfig)
bus.cmd.arbitrationFrom(cmd)
bus.cmd.opcode := Bmb.Cmd.Opcode.READ
bus.cmd.address := cmd.pc.resized
bus.cmd.length := 3
bus.cmd.last := True
rsp.valid := bus.rsp.valid
rsp.inst := bus.rsp.data
rsp.error := bus.rsp.isError
bus.rsp.ready := True
bus
}
}
class IBusSimplePlugin( resetVector : BigInt,
val cmdForkOnSecondStage : Boolean,
val cmdForkPersistence : Boolean,
val catchAccessFault : Boolean = false,
prediction : BranchPrediction = NONE,
historyRamSizeLog2 : Int = 10,
keepPcPlus4 : Boolean = false,
compressedGen : Boolean = false,
val busLatencyMin : Int = 1,
val pendingMax : Int = 7,
injectorStage : Boolean = true,
val rspHoldValue : Boolean = false,
val singleInstructionPipeline : Boolean = false,
val memoryTranslatorPortConfig : Any = null,
relaxPredictorAddress : Boolean = true,
predictionBuffer : Boolean = true,
bigEndian : Boolean = false,
vecRspBuffer : Boolean = false
) extends IBusFetcherImpl(
resetVector = resetVector,
keepPcPlus4 = keepPcPlus4,
decodePcGen = compressedGen,
compressedGen = compressedGen,
cmdToRspStageCount = busLatencyMin + (if(cmdForkOnSecondStage) 1 else 0),
allowPcRegReusedForSecondStage = !(cmdForkOnSecondStage && cmdForkPersistence),
injectorReadyCutGen = false,
prediction = prediction,
historyRamSizeLog2 = historyRamSizeLog2,
injectorStage = injectorStage,
relaxPredictorAddress = relaxPredictorAddress,
fetchRedoGen = memoryTranslatorPortConfig != null,
predictionBuffer = predictionBuffer){
var iBus : IBusSimpleBus = null
var decodeExceptionPort : Flow[ExceptionCause] = null
val catchSomething = memoryTranslatorPortConfig != null || catchAccessFault
var mmuBus : MemoryTranslatorBus = null
// if(rspHoldValue) assert(busLatencyMin <= 1)
assert(!rspHoldValue, "rspHoldValue not supported yet")
assert(!singleInstructionPipeline)
override def setup(pipeline: VexRiscv): Unit = {
super.setup(pipeline)
iBus = master(IBusSimpleBus(this)).setName("iBus")
val decoderService = pipeline.service(classOf[DecoderService])
decoderService.add(FENCE_I, Nil)
if(catchSomething) {
decodeExceptionPort = pipeline.service(classOf[ExceptionService]).newExceptionPort(pipeline.decode,1)
}
if(memoryTranslatorPortConfig != null) {
mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(MemoryTranslatorPort.PRIORITY_INSTRUCTION, memoryTranslatorPortConfig)
}
}
override def build(pipeline: VexRiscv): Unit = {
import pipeline._
import pipeline.config._
pipeline plug new FetchArea(pipeline) {
var cmd = Stream(IBusSimpleCmd())
val cmdWithS2mPipe = cmdForkPersistence && (!cmdForkOnSecondStage || mmuBus != null)
iBus.cmd << (if(cmdWithS2mPipe) cmd.s2mPipe() else cmd)
//Avoid sending to many iBus cmd
val pending = new Area{
val inc, dec = Bool()
val value = Reg(UInt(log2Up(pendingMax + 1) bits)) init (0)
val next = value + U(inc) - U(dec)
value := next
}
val secondStagePersistence = cmdForkPersistence && cmdForkOnSecondStage && !cmdWithS2mPipe
def cmdForkStage = if(!secondStagePersistence) iBusRsp.stages(if(cmdForkOnSecondStage) 1 else 0) else iBusRsp.stages(1)
val cmdFork = if(!secondStagePersistence) new Area {
//This implementation keep the cmd on the bus until it's executed or the the pipeline is flushed
def stage = cmdForkStage
val canEmit = stage.output.ready && pending.value =/= pendingMax
stage.halt setWhen(stage.input.valid && (!canEmit || !cmd.ready))
cmd.valid := stage.input.valid && canEmit
pending.inc := cmd.fire
} else new Area{
//This implementation keep the cmd on the bus until it's executed, even if the pipeline is flushed
def stage = cmdForkStage
val pendingFull = pending.value === pendingMax
val enterTheMarket = Bool()
val cmdKeep = RegInit(False) setWhen(enterTheMarket) clearWhen(cmd.ready)
val cmdFired = RegInit(False) setWhen(cmd.fire) clearWhen(stage.input.ready)
enterTheMarket := stage.input.valid && !pendingFull && !cmdFired && !cmdKeep
// stage.halt setWhen(cmd.isStall || (pendingFull && !cmdFired)) //(cmd.isStall)
stage.halt setWhen(pendingFull && !cmdFired && !cmdKeep)
stage.halt setWhen(!cmd.ready && !cmdFired)
cmd.valid := enterTheMarket || cmdKeep
pending.inc := enterTheMarket
}
val mmu = (mmuBus != null) generate new Area {
mmuBus.cmd.last.isValid := cmdForkStage.input.valid
mmuBus.cmd.last.virtualAddress := cmdForkStage.input.payload
mmuBus.cmd.last.bypassTranslation := False
mmuBus.end := cmdForkStage.output.fire || externalFlush
cmd.pc := mmuBus.rsp.physicalAddress(31 downto 2) @@ U"00"
//do not emit memory request if MMU had issues
when(cmdForkStage.input.valid) {
when(mmuBus.rsp.refilling) {
cmdForkStage.halt := True
cmd.valid := False
}
when(mmuBus.rsp.exception) {
cmdForkStage.halt := False
cmd.valid := False
}
}
val joinCtx = stageXToIBusRsp(cmdForkStage, mmuBus.rsp)
}
val mmuLess = (mmuBus == null) generate new Area{
cmd.pc := cmdForkStage.input.payload(31 downto 2) @@ U"00"
}
val rspJoin = new Area {
import iBusRsp._
//Manage flush for iBus transactions in flight
val rspBuffer = new Area {
val output = Stream(IBusSimpleRsp())
val c = new StreamFifoLowLatency(IBusSimpleRsp(), busLatencyMin + (if(cmdForkOnSecondStage && cmdForkPersistence) 1 else 0), useVec = vecRspBuffer)
val discardCounter = Reg(UInt(log2Up(pendingMax + 1) bits)) init (0)
discardCounter := discardCounter - (c.io.pop.valid && discardCounter =/= 0).asUInt
when(iBusRsp.flush) {
discardCounter := (if(cmdForkOnSecondStage) pending.next else pending.value - U(pending.dec))
}
c.io.push << iBus.rsp.toStream
// if(compressedGen) c.io.flush setWhen(decompressor.consumeCurrent)
// if(!compressedGen && isDrivingDecode(IBUS_RSP)) c.io.flush setWhen(decode.arbitration.flushNext && iBusRsp.output.ready)
val flush = discardCounter =/= 0 || iBusRsp.flush
output.valid := c.io.pop.valid && discardCounter === 0
output.payload := c.io.pop.payload
c.io.pop.ready := output.ready || flush
pending.dec := c.io.pop.fire // iBus.rsp.valid && flush || c.io.pop.valid && output.ready instead to avoid unecessary dependancies ?
}
val fetchRsp = FetchRsp()
fetchRsp.pc := stages.last.output.payload
fetchRsp.rsp := rspBuffer.output.payload
fetchRsp.rsp.error.clearWhen(!rspBuffer.output.valid) //Avoid interference with instruction injection from the debug plugin
if(bigEndian){
// instructions are stored in little endian byteorder
fetchRsp.rsp.inst.allowOverride
fetchRsp.rsp.inst := EndiannessSwap(rspBuffer.output.payload.inst)
}
val join = Stream(FetchRsp())
val exceptionDetected = False
join.valid := stages.last.output.valid && rspBuffer.output.valid
join.payload := fetchRsp
stages.last.output.ready := stages.last.output.valid ? join.fire | join.ready
rspBuffer.output.ready := join.fire
output << join.haltWhen(exceptionDetected)
if(memoryTranslatorPortConfig != null){
when(stages.last.input.valid && mmu.joinCtx.refilling) {
iBusRsp.redoFetch := True
}
}
if(catchSomething){
decodeExceptionPort.code.assignDontCare()
decodeExceptionPort.badAddr := join.pc(31 downto 2) @@ U"00"
if(catchAccessFault) when(join.valid && join.rsp.error){
decodeExceptionPort.code := 1
exceptionDetected := True
}
if(memoryTranslatorPortConfig != null) {
val privilegeService = pipeline.serviceElse(classOf[PrivilegeService], PrivilegeServiceDefault())
when(stages.last.input.valid && !mmu.joinCtx.refilling && (mmu.joinCtx.exception || !mmu.joinCtx.allowExecute)){
decodeExceptionPort.code := 12
exceptionDetected := True
}
}
decodeExceptionPort.valid := exceptionDetected && iBusRsp.readyForError
}
}
}
}
}
|