aboutsummaryrefslogtreecommitdiff
path: root/VexRiscv/src/main/scala/vexriscv/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'VexRiscv/src/main/scala/vexriscv/plugin')
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/AesPlugin.scala329
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/BranchPlugin.scala386
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala357
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala1335
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala554
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala614
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/DebugPlugin.scala364
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala402
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/DivPlugin.scala75
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/DummyFencePlugin.scala22
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/ExternalInterruptArrayPlugin.scala30
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala637
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/FormalPlugin.scala135
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/FpuPlugin.scala314
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala44
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/HazardPessimisticPlugin.scala24
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/HazardSimplePlugin.scala125
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala290
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala418
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/IntAluPlugin.scala100
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/MemoryTranslatorPlugin.scala159
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/Misc.scala214
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/MmuPlugin.scala313
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/Mul16Plugin.scala119
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/MulDivIterativePlugin.scala188
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/MulPlugin.scala159
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/MulSimplePlugin.scala92
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/NoPipeliningPlugin.scala23
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/PcManagerSimplePlugin.scala145
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/Plugin.scala25
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/PmpPlugin.scala307
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/PmpPluginOld.scala244
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/RegFilePlugin.scala122
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/ShiftPlugins.scala193
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/SingleInstructionLimiterPlugin.scala17
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/SrcPlugin.scala83
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/StaticMemoryTranslatorPlugin.scala41
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/VfuPlugin.scala136
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/YamlPlugin.scala32
39 files changed, 9167 insertions, 0 deletions
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/AesPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/AesPlugin.scala
new file mode 100644
index 0000000..0d4556a
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/AesPlugin.scala
@@ -0,0 +1,329 @@
+package vexriscv.plugin
+
+import spinal.core._
+import spinal.lib._
+import vexriscv.{DecoderService, Stageable, VexRiscv}
+
+/**
+ * The AesPlugin allow to reduce the instruction count of each AES round by providing the following instruction :
+ * 1) aes_enc_round(rs1, rs2, sel). rd = rs1 ^ quad_mul(sel, sbox(byte_sel(rs2, sel)))
+ * 2) aes_enc_round_last(rs1, rs2, sel). rd = rs1 ^ quad_sbox(byte_sel(rs2, sel))
+ * 3) aes_dec_round(rs1, rs2, sel). rd = rs1 ^ quad_inv_sbox(quad_mul(sel,byte_sel(rs2, sel)))
+ * 4) aes_dec_round_last(rs1, rs2, sel). rd = rs1 ^ quad_inv_sbox(byte_sel(rs2, sel))
+ *
+ * Here is what those inner functions mean:
+ * - sbox apply the sbox transformation on the 'sel' byte of the 32 bits word
+ * - quad_mul multiply (Galois field) each byte of 32 bits word by a constant (which depend of sel)
+ * - quad_inv_sbox apply the inverse sbox transformation on each byte of 32 bits word
+ *
+ * You can find a complet example of those instruction usage in aes_cusom.h in vexriscv_aes_encrypt and
+ * vexriscv_aes_decrypt. Those function are made to work on little endian as in the linux kernel default AES
+ * implementation, but unlike libressl, libopenssl and dropbear ones (swapping the byte of the expended key can fix that).
+ *
+ * This plugin implement the processing using a single 32_bits * 512_words rom to fetch the sbox/inv_sbox/multiplication
+ * results already combined. This rom is formated as following :
+ *
+ * From word 0x000 to 0x0FF, it is formatted as follow : (note multiplication are in Galois field)
+ * [ 7 : 0] : SBox[word_address & 0xFF] * 1
+ * [15 : 8] : SBox[word_address & 0xFF] * 2
+ * [23 : 16] : SBox[word_address & 0xFF] * 3
+ * [31 : 24] : inverse SBox[word_address & 0xFF] * 1 (Used for the last round of the decryption)
+ *
+ * From word 0x100 to 0x1FF, it is formatted as follow :
+ * [ 7 : 0] : inverse SBox[word_address & 0xFF * 14]
+ * [15 : 8] : inverse SBox[word_address & 0xFF * 9]
+ * [23 : 16] : inverse SBox[word_address & 0xFF * 13]
+ * [31 : 24] : inverse SBox[word_address & 0xFF * 11]
+ *
+ * So, on each instruction, the following is done (in order)
+ * 1) Select the 'sel' byte of RS2
+ * 2) Read the rom at a address which depend of the RS2 selected byte and the instruction
+ * 3) Permute the rom read data depending the instruction and the 'sel' argument
+ * 4) Xor the result with RS1 and return that as instruction result
+ *
+ * The instructions are encoded by default as following :
+ * --SS-LDXXXXXYYYYY000ZZZZZ0001011
+ *
+ * Where :
+ * - XXXXX is the register file source 2 (RS2)
+ * - YYYYY is the register file source 1 (RS1)
+ * - ZZZZZ is the register file destination
+ * - D=1 mean decrypt, D=0 mean encrypt
+ * - L=1 mean last round, L=0 mean full round
+ * - SS specify which byte should be used from RS2 for the processing
+ *
+ * In practice the aes-256-cbc performances should improve by a factor 4. See the following results from libopenssl
+ * from a SoC running linux at 100 Mhz
+ * type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes
+ * aes-256-cbc SW 492.58k 700.22k 796.41k 831.49k 830.09k 832.81k
+ * aes-256 cbc HW 1781.52k 2834.07k 3323.07k 3486.72k 3465.22k 3440.10k
+ */
+
+case class AesPlugin(encoding : MaskedLiteral = M"-----------------000-----0001011") extends Plugin[VexRiscv]{
+
+ object IS_AES extends Stageable(Bool)
+ object CALC extends Stageable(Bits(32 bits))
+
+ val mapping = new {
+ def DECRYPT = 25 // 0/1 => encrypt/decrypt
+ def LAST_ROUND = 26
+ def BYTE_SEL = 28 //Which byte should be used in RS2
+ }
+
+ //Callback to setup the plugin and ask for different services
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+
+ decoderService.addDefault(IS_AES, False)
+ decoderService.add(
+ key = encoding,
+ List(
+ IS_AES -> True,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> False, //Late result
+ RS1_USE -> True,
+ RS2_USE -> True
+ )
+ )
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+
+
+ def BANK0 = (TE0, SBOX_INV).zipped.map((te0, inv) => (te0.toLong) | (inv.toLong << 24))
+ def BANK1 = TD0
+
+
+
+ val onExecute = execute plug new Area{
+ import execute._
+ val byteSel = input(INSTRUCTION)(mapping.BYTE_SEL, 2 bits).asUInt
+ val bankSel = input(INSTRUCTION)(mapping.DECRYPT) && !input(INSTRUCTION)(mapping.LAST_ROUND)
+ val romAddress = U(bankSel ## input(RS2).subdivideIn(8 bits).read(byteSel))
+ }
+
+ memory plug new Area{
+ import memory._
+
+ //Decode the rom data
+ val rom = new Area {
+ val storage = Mem(Bits(32 bits), 512) initBigInt((BANK0 ++ BANK1).map(BigInt(_)))
+
+ val data = storage.readSync(onExecute.romAddress, !arbitration.isStuck)
+ val bytes = data.subdivideIn(8 bits)
+
+ def VecUInt(l: Int*) = Vec(l.map(U(_, 2 bits)))
+ // remap will be used to decode the rom
+ val remap = Vec(
+ VecUInt(2, 0, 0, 1),
+ VecUInt(0, 0, 0, 0),
+ VecUInt(3, 2, 1, 0),
+ VecUInt(3, 3, 3, 3)
+ )
+
+ val address = U(input(INSTRUCTION)(mapping.DECRYPT) ## input(INSTRUCTION)(mapping.LAST_ROUND))
+ val output = remap(address)
+ }
+
+ val wordDesuffle = new Area{
+ val zero = B"0000"
+ val byteSel = input(INSTRUCTION)(mapping.BYTE_SEL, 2 bits).asUInt
+ val output = Vec(Bits(8 bits), 4)
+
+ def remap(l : Int*) = Vec(l.map(rom.output(_)))
+ val sel = byteSel.mux(
+ 0 -> remap(3, 2, 1, 0),
+ 1 -> remap(0, 3, 2, 1),
+ 2 -> remap(1, 0, 3, 2),
+ 3 -> remap(2, 1, 0, 3)
+ )
+ when(input(INSTRUCTION)(mapping.LAST_ROUND)){
+ zero := B"1111"
+ zero(byteSel) := False
+ }
+
+ //Finaly, mux the rom data
+ for(byteId <- 0 to 3){
+ output(byteId) := rom.bytes(sel(byteId))
+ when(zero(byteId)){
+ output(byteId) := 0
+ }
+ }
+ }
+
+ val xored = wordDesuffle.output.asBits ^ input(RS1)
+ insert(CALC) := xored
+ }
+
+ writeBack plug new Area {
+ import writeBack._
+
+ when(input(IS_AES)) {
+ output(REGFILE_WRITE_DATA) := input(CALC)
+ }
+ }
+ }
+
+ // Encryption table which solve a single byte sbox + column mix. Used for all rounds
+ def TE0 = List(
+ 0xa5c663, 0x84f87c, 0x99ee77, 0x8df67b,
+ 0x0dfff2, 0xbdd66b, 0xb1de6f, 0x5491c5,
+ 0x506030, 0x030201, 0xa9ce67, 0x7d562b,
+ 0x19e7fe, 0x62b5d7, 0xe64dab, 0x9aec76,
+ 0x458fca, 0x9d1f82, 0x4089c9, 0x87fa7d,
+ 0x15effa, 0xebb259, 0xc98e47, 0x0bfbf0,
+ 0xec41ad, 0x67b3d4, 0xfd5fa2, 0xea45af,
+ 0xbf239c, 0xf753a4, 0x96e472, 0x5b9bc0,
+ 0xc275b7, 0x1ce1fd, 0xae3d93, 0x6a4c26,
+ 0x5a6c36, 0x417e3f, 0x02f5f7, 0x4f83cc,
+ 0x5c6834, 0xf451a5, 0x34d1e5, 0x08f9f1,
+ 0x93e271, 0x73abd8, 0x536231, 0x3f2a15,
+ 0x0c0804, 0x5295c7, 0x654623, 0x5e9dc3,
+ 0x283018, 0xa13796, 0x0f0a05, 0xb52f9a,
+ 0x090e07, 0x362412, 0x9b1b80, 0x3ddfe2,
+ 0x26cdeb, 0x694e27, 0xcd7fb2, 0x9fea75,
+ 0x1b1209, 0x9e1d83, 0x74582c, 0x2e341a,
+ 0x2d361b, 0xb2dc6e, 0xeeb45a, 0xfb5ba0,
+ 0xf6a452, 0x4d763b, 0x61b7d6, 0xce7db3,
+ 0x7b5229, 0x3edde3, 0x715e2f, 0x971384,
+ 0xf5a653, 0x68b9d1, 0x000000, 0x2cc1ed,
+ 0x604020, 0x1fe3fc, 0xc879b1, 0xedb65b,
+ 0xbed46a, 0x468dcb, 0xd967be, 0x4b7239,
+ 0xde944a, 0xd4984c, 0xe8b058, 0x4a85cf,
+ 0x6bbbd0, 0x2ac5ef, 0xe54faa, 0x16edfb,
+ 0xc58643, 0xd79a4d, 0x556633, 0x941185,
+ 0xcf8a45, 0x10e9f9, 0x060402, 0x81fe7f,
+ 0xf0a050, 0x44783c, 0xba259f, 0xe34ba8,
+ 0xf3a251, 0xfe5da3, 0xc08040, 0x8a058f,
+ 0xad3f92, 0xbc219d, 0x487038, 0x04f1f5,
+ 0xdf63bc, 0xc177b6, 0x75afda, 0x634221,
+ 0x302010, 0x1ae5ff, 0x0efdf3, 0x6dbfd2,
+ 0x4c81cd, 0x14180c, 0x352613, 0x2fc3ec,
+ 0xe1be5f, 0xa23597, 0xcc8844, 0x392e17,
+ 0x5793c4, 0xf255a7, 0x82fc7e, 0x477a3d,
+ 0xacc864, 0xe7ba5d, 0x2b3219, 0x95e673,
+ 0xa0c060, 0x981981, 0xd19e4f, 0x7fa3dc,
+ 0x664422, 0x7e542a, 0xab3b90, 0x830b88,
+ 0xca8c46, 0x29c7ee, 0xd36bb8, 0x3c2814,
+ 0x79a7de, 0xe2bc5e, 0x1d160b, 0x76addb,
+ 0x3bdbe0, 0x566432, 0x4e743a, 0x1e140a,
+ 0xdb9249, 0x0a0c06, 0x6c4824, 0xe4b85c,
+ 0x5d9fc2, 0x6ebdd3, 0xef43ac, 0xa6c462,
+ 0xa83991, 0xa43195, 0x37d3e4, 0x8bf279,
+ 0x32d5e7, 0x438bc8, 0x596e37, 0xb7da6d,
+ 0x8c018d, 0x64b1d5, 0xd29c4e, 0xe049a9,
+ 0xb4d86c, 0xfaac56, 0x07f3f4, 0x25cfea,
+ 0xafca65, 0x8ef47a, 0xe947ae, 0x181008,
+ 0xd56fba, 0x88f078, 0x6f4a25, 0x725c2e,
+ 0x24381c, 0xf157a6, 0xc773b4, 0x5197c6,
+ 0x23cbe8, 0x7ca1dd, 0x9ce874, 0x213e1f,
+ 0xdd964b, 0xdc61bd, 0x860d8b, 0x850f8a,
+ 0x90e070, 0x427c3e, 0xc471b5, 0xaacc66,
+ 0xd89048, 0x050603, 0x01f7f6, 0x121c0e,
+ 0xa3c261, 0x5f6a35, 0xf9ae57, 0xd069b9,
+ 0x911786, 0x5899c1, 0x273a1d, 0xb9279e,
+ 0x38d9e1, 0x13ebf8, 0xb32b98, 0x332211,
+ 0xbbd269, 0x70a9d9, 0x89078e, 0xa73394,
+ 0xb62d9b, 0x223c1e, 0x921587, 0x20c9e9,
+ 0x4987ce, 0xffaa55, 0x785028, 0x7aa5df,
+ 0x8f038c, 0xf859a1, 0x800989, 0x171a0d,
+ 0xda65bf, 0x31d7e6, 0xc68442, 0xb8d068,
+ 0xc38241, 0xb02999, 0x775a2d, 0x111e0f,
+ 0xcb7bb0, 0xfca854, 0xd66dbb, 0x3a2c16
+ )
+
+
+ // Decryption table which solve a single byte sbox + column mix. Not used in the last round
+ def TD0 = List(
+ 0x50a7f451l, 0x5365417el, 0xc3a4171al, 0x965e273al,
+ 0xcb6bab3bl, 0xf1459d1fl, 0xab58faacl, 0x9303e34bl,
+ 0x55fa3020l, 0xf66d76adl, 0x9176cc88l, 0x254c02f5l,
+ 0xfcd7e54fl, 0xd7cb2ac5l, 0x80443526l, 0x8fa362b5l,
+ 0x495ab1del, 0x671bba25l, 0x980eea45l, 0xe1c0fe5dl,
+ 0x02752fc3l, 0x12f04c81l, 0xa397468dl, 0xc6f9d36bl,
+ 0xe75f8f03l, 0x959c9215l, 0xeb7a6dbfl, 0xda595295l,
+ 0x2d83bed4l, 0xd3217458l, 0x2969e049l, 0x44c8c98el,
+ 0x6a89c275l, 0x78798ef4l, 0x6b3e5899l, 0xdd71b927l,
+ 0xb64fe1bel, 0x17ad88f0l, 0x66ac20c9l, 0xb43ace7dl,
+ 0x184adf63l, 0x82311ae5l, 0x60335197l, 0x457f5362l,
+ 0xe07764b1l, 0x84ae6bbbl, 0x1ca081fel, 0x942b08f9l,
+ 0x58684870l, 0x19fd458fl, 0x876cde94l, 0xb7f87b52l,
+ 0x23d373abl, 0xe2024b72l, 0x578f1fe3l, 0x2aab5566l,
+ 0x0728ebb2l, 0x03c2b52fl, 0x9a7bc586l, 0xa50837d3l,
+ 0xf2872830l, 0xb2a5bf23l, 0xba6a0302l, 0x5c8216edl,
+ 0x2b1ccf8al, 0x92b479a7l, 0xf0f207f3l, 0xa1e2694el,
+ 0xcdf4da65l, 0xd5be0506l, 0x1f6234d1l, 0x8afea6c4l,
+ 0x9d532e34l, 0xa055f3a2l, 0x32e18a05l, 0x75ebf6a4l,
+ 0x39ec830bl, 0xaaef6040l, 0x069f715el, 0x51106ebdl,
+ 0xf98a213el, 0x3d06dd96l, 0xae053eddl, 0x46bde64dl,
+ 0xb58d5491l, 0x055dc471l, 0x6fd40604l, 0xff155060l,
+ 0x24fb9819l, 0x97e9bdd6l, 0xcc434089l, 0x779ed967l,
+ 0xbd42e8b0l, 0x888b8907l, 0x385b19e7l, 0xdbeec879l,
+ 0x470a7ca1l, 0xe90f427cl, 0xc91e84f8l, 0x00000000l,
+ 0x83868009l, 0x48ed2b32l, 0xac70111el, 0x4e725a6cl,
+ 0xfbff0efdl, 0x5638850fl, 0x1ed5ae3dl, 0x27392d36l,
+ 0x64d90f0al, 0x21a65c68l, 0xd1545b9bl, 0x3a2e3624l,
+ 0xb1670a0cl, 0x0fe75793l, 0xd296eeb4l, 0x9e919b1bl,
+ 0x4fc5c080l, 0xa220dc61l, 0x694b775al, 0x161a121cl,
+ 0x0aba93e2l, 0xe52aa0c0l, 0x43e0223cl, 0x1d171b12l,
+ 0x0b0d090el, 0xadc78bf2l, 0xb9a8b62dl, 0xc8a91e14l,
+ 0x8519f157l, 0x4c0775afl, 0xbbdd99eel, 0xfd607fa3l,
+ 0x9f2601f7l, 0xbcf5725cl, 0xc53b6644l, 0x347efb5bl,
+ 0x7629438bl, 0xdcc623cbl, 0x68fcedb6l, 0x63f1e4b8l,
+ 0xcadc31d7l, 0x10856342l, 0x40229713l, 0x2011c684l,
+ 0x7d244a85l, 0xf83dbbd2l, 0x1132f9ael, 0x6da129c7l,
+ 0x4b2f9e1dl, 0xf330b2dcl, 0xec52860dl, 0xd0e3c177l,
+ 0x6c16b32bl, 0x99b970a9l, 0xfa489411l, 0x2264e947l,
+ 0xc48cfca8l, 0x1a3ff0a0l, 0xd82c7d56l, 0xef903322l,
+ 0xc74e4987l, 0xc1d138d9l, 0xfea2ca8cl, 0x360bd498l,
+ 0xcf81f5a6l, 0x28de7aa5l, 0x268eb7dal, 0xa4bfad3fl,
+ 0xe49d3a2cl, 0x0d927850l, 0x9bcc5f6al, 0x62467e54l,
+ 0xc2138df6l, 0xe8b8d890l, 0x5ef7392el, 0xf5afc382l,
+ 0xbe805d9fl, 0x7c93d069l, 0xa92dd56fl, 0xb31225cfl,
+ 0x3b99acc8l, 0xa77d1810l, 0x6e639ce8l, 0x7bbb3bdbl,
+ 0x097826cdl, 0xf418596el, 0x01b79aecl, 0xa89a4f83l,
+ 0x656e95e6l, 0x7ee6ffaal, 0x08cfbc21l, 0xe6e815efl,
+ 0xd99be7bal, 0xce366f4al, 0xd4099feal, 0xd67cb029l,
+ 0xafb2a431l, 0x31233f2al, 0x3094a5c6l, 0xc066a235l,
+ 0x37bc4e74l, 0xa6ca82fcl, 0xb0d090e0l, 0x15d8a733l,
+ 0x4a9804f1l, 0xf7daec41l, 0x0e50cd7fl, 0x2ff69117l,
+ 0x8dd64d76l, 0x4db0ef43l, 0x544daaccl, 0xdf0496e4l,
+ 0xe3b5d19el, 0x1b886a4cl, 0xb81f2cc1l, 0x7f516546l,
+ 0x04ea5e9dl, 0x5d358c01l, 0x737487fal, 0x2e410bfbl,
+ 0x5a1d67b3l, 0x52d2db92l, 0x335610e9l, 0x1347d66dl,
+ 0x8c61d79al, 0x7a0ca137l, 0x8e14f859l, 0x893c13ebl,
+ 0xee27a9cel, 0x35c961b7l, 0xede51ce1l, 0x3cb1477al,
+ 0x59dfd29cl, 0x3f73f255l, 0x79ce1418l, 0xbf37c773l,
+ 0xeacdf753l, 0x5baafd5fl, 0x146f3ddfl, 0x86db4478l,
+ 0x81f3afcal, 0x3ec468b9l, 0x2c342438l, 0x5f40a3c2l,
+ 0x72c31d16l, 0x0c25e2bcl, 0x8b493c28l, 0x41950dffl,
+ 0x7101a839l, 0xdeb30c08l, 0x9ce4b4d8l, 0x90c15664l,
+ 0x6184cb7bl, 0x70b632d5l, 0x745c6c48l, 0x4257b8d0l
+ )
+
+ // Last round decryption sbox
+ def SBOX_INV = List(
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+ )
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/BranchPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/BranchPlugin.scala
new file mode 100644
index 0000000..24d42fa
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/BranchPlugin.scala
@@ -0,0 +1,386 @@
+package vexriscv.plugin
+
+import vexriscv.Riscv._
+import vexriscv._
+import spinal.core._
+import spinal.lib._
+
+trait BranchPrediction
+object NONE extends BranchPrediction
+object STATIC extends BranchPrediction
+object DYNAMIC extends BranchPrediction
+object DYNAMIC_TARGET extends BranchPrediction
+
+object BranchCtrlEnum extends SpinalEnum(binarySequential){
+ val INC,B,JAL,JALR = newElement()
+}
+object BRANCH_CTRL extends Stageable(BranchCtrlEnum())
+
+
+case class DecodePredictionCmd() extends Bundle {
+ val hadBranch = Bool
+}
+case class DecodePredictionRsp(stage : Stage) extends Bundle {
+ val wasWrong = Bool
+}
+case class DecodePredictionBus(stage : Stage) extends Bundle {
+ val cmd = DecodePredictionCmd()
+ val rsp = DecodePredictionRsp(stage)
+}
+
+case class FetchPredictionCmd() extends Bundle{
+ val hadBranch = Bool
+ val targetPc = UInt(32 bits)
+}
+case class FetchPredictionRsp() extends Bundle{
+ val wasRight = Bool
+ val finalPc = UInt(32 bits)
+ val sourceLastWord = UInt(32 bits)
+}
+case class FetchPredictionBus(stage : Stage) extends Bundle {
+ val cmd = FetchPredictionCmd()
+ val rsp = FetchPredictionRsp()
+}
+
+
+trait PredictionInterface{
+ def askFetchPrediction() : FetchPredictionBus
+ def askDecodePrediction() : DecodePredictionBus
+ def inDebugNoFetch() : Unit
+}
+
+
+
+class BranchPlugin(earlyBranch : Boolean,
+ catchAddressMisaligned : Boolean = false,
+ fenceiGenAsAJump : Boolean = false,
+ fenceiGenAsANop : Boolean = false,
+ decodeBranchSrc2 : Boolean = false) extends Plugin[VexRiscv] with PredictionInterface{
+
+
+ def catchAddressMisalignedForReal = catchAddressMisaligned && !pipeline.config.withRvc
+ lazy val branchStage = if(earlyBranch) pipeline.execute else pipeline.memory
+
+ object BRANCH_CALC extends Stageable(UInt(32 bits))
+ object BRANCH_DO extends Stageable(Bool)
+ object BRANCH_COND_RESULT extends Stageable(Bool)
+ object IS_FENCEI extends Stageable(Bool)
+
+ var jumpInterface : Flow[UInt] = null
+ var predictionExceptionPort : Flow[ExceptionCause] = null
+ var branchExceptionPort : Flow[ExceptionCause] = null
+ var inDebugNoFetchFlag : Bool = null
+
+
+ var decodePrediction : DecodePredictionBus = null
+ var fetchPrediction : FetchPredictionBus = null
+
+
+ override def askFetchPrediction() = {
+ fetchPrediction = FetchPredictionBus(branchStage)
+ fetchPrediction
+ }
+
+ override def askDecodePrediction() = {
+ decodePrediction = DecodePredictionBus(branchStage)
+ decodePrediction
+ }
+
+
+ override def inDebugNoFetch(): Unit = inDebugNoFetchFlag := True
+
+ def hasHazardOnBranch = if(earlyBranch) pipeline.service(classOf[HazardService]).hazardOnExecuteRS else False
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+ import IntAluPlugin._
+
+ assert(earlyBranch || withMemoryStage, "earlyBranch must be true when memory stage is disabled!")
+
+ val bActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC2_CTRL -> Src2CtrlEnum.RS,
+ SRC_USE_SUB_LESS -> True,
+ RS1_USE -> True,
+ RS2_USE -> True,
+ HAS_SIDE_EFFECT -> True
+ )
+
+ val jActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.PC_INCREMENT,
+ SRC2_CTRL -> Src2CtrlEnum.PC,
+ SRC_USE_SUB_LESS -> False,
+ REGFILE_WRITE_VALID -> True,
+ HAS_SIDE_EFFECT -> True
+ )
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+
+
+ decoderService.addDefault(BRANCH_CTRL, BranchCtrlEnum.INC)
+ decoderService.add(List(
+ JAL(true) -> (jActions ++ List(BRANCH_CTRL -> BranchCtrlEnum.JAL, ALU_CTRL -> AluCtrlEnum.ADD_SUB)),
+ JALR -> (jActions ++ List(BRANCH_CTRL -> BranchCtrlEnum.JALR, ALU_CTRL -> AluCtrlEnum.ADD_SUB, RS1_USE -> True)),
+ BEQ(true) -> (bActions ++ List(BRANCH_CTRL -> BranchCtrlEnum.B)),
+ BNE(true) -> (bActions ++ List(BRANCH_CTRL -> BranchCtrlEnum.B)),
+ BLT(true) -> (bActions ++ List(BRANCH_CTRL -> BranchCtrlEnum.B, SRC_LESS_UNSIGNED -> False)),
+ BGE(true) -> (bActions ++ List(BRANCH_CTRL -> BranchCtrlEnum.B, SRC_LESS_UNSIGNED -> False)),
+ BLTU(true) -> (bActions ++ List(BRANCH_CTRL -> BranchCtrlEnum.B, SRC_LESS_UNSIGNED -> True)),
+ BGEU(true) -> (bActions ++ List(BRANCH_CTRL -> BranchCtrlEnum.B, SRC_LESS_UNSIGNED -> True))
+ ))
+
+ if(fenceiGenAsAJump) {
+ decoderService.addDefault(IS_FENCEI, False)
+ decoderService.add(List(
+ FENCEI -> (List(IS_FENCEI -> True,HAS_SIDE_EFFECT -> True, BRANCH_CTRL -> BranchCtrlEnum.JAL))
+ ))
+ }
+
+ if(fenceiGenAsANop){
+ decoderService.add(List(FENCEI -> List()))
+ }
+
+ val pcManagerService = pipeline.service(classOf[JumpService])
+
+ //Priority -1, as DYNAMIC_TARGET misspredicted on non branch instruction should lose against other instructions
+ //legitim branches, as MRET for instance
+ jumpInterface = pcManagerService.createJumpInterface(branchStage, priority = -10)
+
+
+ if (catchAddressMisalignedForReal) {
+ val exceptionService = pipeline.service(classOf[ExceptionService])
+ branchExceptionPort = exceptionService.newExceptionPort(branchStage)
+ }
+ inDebugNoFetchFlag = False.setCompositeName(this, "inDebugNoFetchFlag")
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ (fetchPrediction,decodePrediction) match {
+ case (null, null) => buildWithoutPrediction(pipeline)
+ case (_ , null) => buildFetchPrediction(pipeline)
+ case (null, _) => buildDecodePrediction(pipeline)
+ }
+ if(fenceiGenAsAJump) {
+ import pipeline._
+ import pipeline.config._
+ when(decode.input(IS_FENCEI)) {
+ decode.output(INSTRUCTION)(12) := False
+ decode.output(INSTRUCTION)(22) := True
+ }
+ execute.arbitration.haltByOther setWhen(execute.arbitration.isValid && execute.input(IS_FENCEI) && stagesFromExecute.tail.map(_.arbitration.isValid).asBits.orR)
+ }
+ }
+
+ def buildWithoutPrediction(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ //Do branch calculations (conditions + target PC)
+ execute plug new Area {
+ import execute._
+
+ val less = input(SRC_LESS)
+ val eq = input(SRC1) === input(SRC2)
+
+ insert(BRANCH_DO) := input(BRANCH_CTRL).mux(
+ BranchCtrlEnum.INC -> False,
+ BranchCtrlEnum.JAL -> True,
+ BranchCtrlEnum.JALR -> True,
+ BranchCtrlEnum.B -> input(INSTRUCTION)(14 downto 12).mux(
+ B"000" -> eq ,
+ B"001" -> !eq ,
+ M"1-1" -> !less,
+ default -> less
+ )
+ )
+
+ val imm = IMM(input(INSTRUCTION))
+ val branch_src1 = (input(BRANCH_CTRL) === BranchCtrlEnum.JALR) ? input(RS1).asUInt | input(PC)
+ val branch_src2 = input(BRANCH_CTRL).mux(
+ BranchCtrlEnum.JAL -> imm.j_sext,
+ BranchCtrlEnum.JALR -> imm.i_sext,
+ default -> imm.b_sext
+ ).asUInt
+
+ val branchAdder = branch_src1 + branch_src2
+ insert(BRANCH_CALC) := branchAdder(31 downto 1) @@ U"0"
+ }
+
+ //Apply branchs (JAL,JALR, Bxx)
+ branchStage plug new Area {
+ import branchStage._
+ jumpInterface.valid := arbitration.isValid && input(BRANCH_DO) && !hasHazardOnBranch
+ jumpInterface.payload := input(BRANCH_CALC)
+ arbitration.flushNext setWhen(jumpInterface.valid)
+
+ if(catchAddressMisalignedForReal) {
+ branchExceptionPort.valid := arbitration.isValid && input(BRANCH_DO) && jumpInterface.payload(1)
+ branchExceptionPort.code := 0
+ branchExceptionPort.badAddr := jumpInterface.payload
+
+ if(branchStage == execute) branchExceptionPort.valid clearWhen(service(classOf[HazardService]).hazardOnExecuteRS)
+ }
+ }
+ }
+
+
+ def buildDecodePrediction(pipeline: VexRiscv): Unit = {
+ object PREDICTION_HAD_BRANCHED extends Stageable(Bool)
+
+ import pipeline._
+ import pipeline.config._
+
+
+ decode plug new Area {
+ import decode._
+ insert(PREDICTION_HAD_BRANCHED) := (if(fenceiGenAsAJump) decodePrediction.cmd.hadBranch && !decode.input(IS_FENCEI) else decodePrediction.cmd.hadBranch)
+ }
+
+ //Do real branch calculation
+ execute plug new Area {
+ import execute._
+
+ val less = input(SRC_LESS)
+ val eq = input(SRC1) === input(SRC2)
+
+ insert(BRANCH_COND_RESULT) := input(BRANCH_CTRL).mux(
+ BranchCtrlEnum.INC -> False,
+ BranchCtrlEnum.JAL -> True,
+ BranchCtrlEnum.JALR -> True,
+ BranchCtrlEnum.B -> input(INSTRUCTION)(14 downto 12).mux(
+ B"000" -> eq ,
+ B"001" -> !eq ,
+ M"1-1" -> !less,
+ default -> less
+ )
+ )
+
+ val imm = IMM(input(INSTRUCTION))
+ val missAlignedTarget = if(pipeline.config.withRvc) False else (input(BRANCH_COND_RESULT) && input(BRANCH_CTRL).mux(
+ BranchCtrlEnum.JALR -> (imm.i_sext(1) ^ input(RS1)(1)),
+ BranchCtrlEnum.JAL -> imm.j_sext(1),
+ default -> imm.b_sext(1)
+ ))
+
+ insert(BRANCH_DO) := input(PREDICTION_HAD_BRANCHED) =/= input(BRANCH_COND_RESULT) || missAlignedTarget
+
+ //Calculation of the branch target / correction
+ val branch_src1,branch_src2 = UInt(32 bits)
+ switch(input(BRANCH_CTRL)){
+ is(BranchCtrlEnum.JALR){
+ branch_src1 := input(RS1).asUInt
+ branch_src2 := imm.i_sext.asUInt
+ }
+ default{
+ branch_src1 := input(PC)
+ branch_src2 := ((input(BRANCH_CTRL) === BranchCtrlEnum.JAL) ? imm.j_sext | imm.b_sext).asUInt
+ when(input(PREDICTION_HAD_BRANCHED)){ //Assume the predictor never predict missaligned stuff, this avoid the need to know if the instruction should branch or not
+ branch_src2 := (if(pipeline.config.withRvc) Mux(input(IS_RVC), B(2), B(4)) else B(4)).asUInt.resized
+ }
+ }
+ }
+ val branchAdder = branch_src1 + branch_src2
+ insert(BRANCH_CALC) := branchAdder(31 downto 1) @@ U"0"
+ }
+
+
+ // branch JALR or JAL/Bxx prediction miss corrections
+ val branchStage = if(earlyBranch) execute else memory
+ branchStage plug new Area {
+ import branchStage._
+ jumpInterface.valid := arbitration.isValid && input(BRANCH_DO) && !hasHazardOnBranch
+ jumpInterface.payload := input(BRANCH_CALC)
+ arbitration.flushNext setWhen(jumpInterface.valid)
+
+ if(catchAddressMisalignedForReal) {
+ val unalignedJump = input(BRANCH_DO) && input(BRANCH_CALC)(1)
+ branchExceptionPort.valid := arbitration.isValid && unalignedJump
+ branchExceptionPort.code := 0
+ branchExceptionPort.badAddr := input(BRANCH_CALC) //pipeline.stages(pipeline.indexOf(branchStage)-1).input
+
+ if(branchStage == execute) branchExceptionPort.valid clearWhen(service(classOf[HazardService]).hazardOnExecuteRS)
+ }
+ }
+
+ decodePrediction.rsp.wasWrong := jumpInterface.valid
+ }
+
+
+
+
+
+ def buildFetchPrediction(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+
+ //Do branch calculations (conditions + target PC)
+ object NEXT_PC extends Stageable(UInt(32 bits))
+ object TARGET_MISSMATCH extends Stageable(Bool)
+ object BRANCH_SRC2 extends Stageable(UInt(32 bits))
+ val branchSrc2Stage = if(decodeBranchSrc2) decode else execute
+ execute plug new Area {
+ import execute._
+
+ val less = input(SRC_LESS)
+ val eq = input(SRC1) === input(SRC2)
+
+ insert(BRANCH_DO) := input(BRANCH_CTRL).mux(
+ BranchCtrlEnum.INC -> False,
+ BranchCtrlEnum.JAL -> True,
+ BranchCtrlEnum.JALR -> True,
+ BranchCtrlEnum.B -> input(INSTRUCTION)(14 downto 12).mux(
+ B"000" -> eq ,
+ B"001" -> !eq ,
+ M"1-1" -> !less,
+ default -> less
+ )
+ )
+
+ val branch_src1 = (input(BRANCH_CTRL) === BranchCtrlEnum.JALR) ? input(RS1).asUInt | input(PC)
+
+ val imm = IMM(branchSrc2Stage.input(INSTRUCTION))
+ branchSrc2Stage.insert(BRANCH_SRC2) := branchSrc2Stage.input(BRANCH_CTRL).mux(
+ BranchCtrlEnum.JAL -> imm.j_sext,
+ BranchCtrlEnum.JALR -> imm.i_sext,
+ default -> imm.b_sext
+ ).asUInt
+
+ val branchAdder = branch_src1 + input(BRANCH_SRC2)
+ insert(BRANCH_CALC) := branchAdder(31 downto 1) @@ U"0"
+ insert(NEXT_PC) := input(PC) + (if(pipeline.config.withRvc) ((input(IS_RVC)) ? U(2) | U(4)) else 4)
+ insert(TARGET_MISSMATCH) := decode.input(PC) =/= input(BRANCH_CALC)
+ }
+
+ //Apply branchs (JAL,JALR, Bxx)
+ val branchStage = if(earlyBranch) execute else memory
+ branchStage plug new Area {
+ import branchStage._
+
+ val predictionMissmatch = fetchPrediction.cmd.hadBranch =/= input(BRANCH_DO) || (input(BRANCH_DO) && input(TARGET_MISSMATCH))
+ when(inDebugNoFetchFlag) { predictionMissmatch := input(BRANCH_DO)}
+ fetchPrediction.rsp.wasRight := ! predictionMissmatch
+ fetchPrediction.rsp.finalPc := input(BRANCH_CALC)
+ fetchPrediction.rsp.sourceLastWord := {
+ if(pipeline.config.withRvc)
+ ((!input(IS_RVC) && input(PC)(1)) ? input(NEXT_PC) | input(PC))
+ else
+ input(PC)
+ }
+
+ jumpInterface.valid := arbitration.isValid && predictionMissmatch && !hasHazardOnBranch
+ jumpInterface.payload := (input(BRANCH_DO) ? input(BRANCH_CALC) | input(NEXT_PC))
+ arbitration.flushNext setWhen(jumpInterface.valid)
+
+
+ if(catchAddressMisalignedForReal) {
+ branchExceptionPort.valid := arbitration.isValid && input(BRANCH_DO) && input(BRANCH_CALC)(1)
+ branchExceptionPort.code := 0
+ branchExceptionPort.badAddr := input(BRANCH_CALC)
+
+ if(branchStage == execute) branchExceptionPort.valid clearWhen(service(classOf[HazardService]).hazardOnExecuteRS)
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala
new file mode 100644
index 0000000..d343640
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala
@@ -0,0 +1,357 @@
+package vexriscv.plugin
+
+import vexriscv.{DecoderService, ExceptionCause, ExceptionService, Stage, Stageable, VexRiscv}
+import spinal.core._
+import spinal.lib._
+import spinal.lib.bus.bmb.WeakConnector
+import spinal.lib.bus.misc.{AddressMapping, DefaultMapping}
+import vexriscv.Riscv.IMM
+
+case class CfuPluginParameter(
+ 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 CfuBusParameter(CFU_VERSION : Int = 0,
+ CFU_INTERFACE_ID_W : Int = 0,
+ CFU_FUNCTION_ID_W : Int,
+ CFU_CFU_ID_W : Int = 0,
+ CFU_REORDER_ID_W : Int = 0,
+ CFU_REQ_RESP_ID_W : Int = 0,
+ CFU_STATE_INDEX_NUM : Int = 0,
+ 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,
+ CFU_WITH_STATUS : Boolean = false,
+ CFU_RAW_INSN_W : Int = 0)
+
+case class CfuCmd( p : CfuBusParameter ) 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)
+ val state_index = UInt(log2Up(p.CFU_STATE_INDEX_NUM) bits)
+ val cfu_index = UInt(p.CFU_CFU_ID_W bits)
+ val raw_insn = Bits(p.CFU_RAW_INSN_W bits)
+ def weakAssignFrom(m : CfuCmd): Unit ={
+ def s = this
+ WeakConnector(m, s, m.function_id, s.function_id, defaultValue = null, allowUpSize = false, allowDownSize = true , allowDrop = true)
+ WeakConnector(m, s, m.reorder_id, s.reorder_id, defaultValue = null, allowUpSize = false , allowDownSize = false, allowDrop = false)
+ WeakConnector(m, s, m.request_id, s.request_id, defaultValue = null, allowUpSize = false, allowDownSize = false, allowDrop = false)
+ s.inputs := m.inputs
+ }
+}
+
+case class CfuRsp(p : CfuBusParameter) extends Bundle{
+ val response_id = UInt(p.CFU_REQ_RESP_ID_W bits)
+ val outputs = Vec(Bits(p.CFU_OUTPUT_DATA_W bits), p.CFU_OUTPUTS)
+ val status = p.CFU_WITH_STATUS generate Bits(3 bits)
+
+ def weakAssignFrom(m : CfuRsp): Unit ={
+ def s = this
+ s.response_id := m.response_id
+ s.outputs := m.outputs
+ }
+}
+
+case class CfuBus(p : CfuBusParameter) extends Bundle with IMasterSlave{
+ val cmd = Stream(CfuCmd(p))
+ val rsp = Stream(CfuRsp(p))
+
+ def <<(m : CfuBus) : Unit = {
+ val s = this
+ s.cmd.arbitrationFrom(m.cmd)
+ m.rsp.arbitrationFrom(s.rsp)
+
+ s.cmd.weakAssignFrom(m.cmd)
+ m.rsp.weakAssignFrom(s.rsp)
+ }
+
+ override def asMaster(): Unit = {
+ master(cmd)
+ slave(rsp)
+ }
+}
+
+object CfuPlugin{
+ object Input2Kind extends SpinalEnum{
+ val RS, IMM_I = newElement()
+ }
+}
+
+case class CfuPluginEncoding(instruction : MaskedLiteral,
+ functionId : List[Range],
+ input2Kind : CfuPlugin.Input2Kind.E){
+ val functionIdWidth = functionId.map(_.size).sum
+}
+
+class CfuPlugin(val stageCount : Int,
+ val allowZeroLatency : Boolean,
+ val busParameter : CfuBusParameter,
+ val encodings : List[CfuPluginEncoding] = null,
+ val stateAndIndexCsrOffset : Int = 0xBC0,
+ val statusCsrOffset : Int = 0x801,
+ val withEnable : Boolean = true) extends Plugin[VexRiscv]{
+ def p = busParameter
+
+ assert(p.CFU_INPUTS <= 2)
+ assert(p.CFU_OUTPUTS == 1)
+// assert(p.CFU_FUNCTION_ID_W == 3)
+
+ var bus : CfuBus = null
+
+ lazy val forkStage = pipeline.execute
+ lazy val joinStage = pipeline.stages(Math.min(pipeline.stages.length - 1, pipeline.indexOf(forkStage) + stageCount))
+
+
+ val CFU_ENABLE = new Stageable(Bool()).setCompositeName(this, "CFU_ENABLE")
+ val CFU_IN_FLIGHT = new Stageable(Bool()).setCompositeName(this, "CFU_IN_FLIGHT")
+ val CFU_ENCODING = new Stageable(UInt(log2Up(encodings.size) bits)).setCompositeName(this, "CFU_ENCODING")
+ val CFU_INPUT_2_KIND = new Stageable(CfuPlugin.Input2Kind()).setCompositeName(this, "CFU_INPUT_2_KIND")
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ bus = master(CfuBus(p))
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(CFU_ENABLE, False)
+
+ for((encoding, id) <- encodings.zipWithIndex){
+ var actions = List(
+ CFU_ENABLE -> True,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> Bool(stageCount == 0),
+ BYPASSABLE_MEMORY_STAGE -> Bool(stageCount <= 1),
+ RS1_USE -> True,
+ CFU_ENCODING -> U(id),
+ CFU_INPUT_2_KIND -> encoding.input2Kind()
+ )
+
+ encoding.input2Kind match {
+ case CfuPlugin.Input2Kind.RS =>
+ actions :+= RS2_USE -> True
+ case CfuPlugin.Input2Kind.IMM_I =>
+ }
+
+ decoderService.add(
+ key = encoding.instruction,
+ values = actions
+ )
+ }
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ val csr = pipeline plug new Area{
+ val factory = pipeline.service(classOf[CsrInterface])
+ val en = withEnable generate (Reg(Bool()) init(False))
+ if(withEnable) factory.rw(stateAndIndexCsrOffset, 31, en)
+
+ val stateId = Reg(UInt(log2Up(p.CFU_STATE_INDEX_NUM) bits)) init(0)
+ if(p.CFU_STATE_INDEX_NUM > 1) {
+ assert(stateAndIndexCsrOffset != -1, "CfuPlugin stateCsrIndex need to be set in the parameters")
+ factory.rw(stateAndIndexCsrOffset, 16, stateId)
+ }
+
+ val cfuIndex = Reg(UInt(p.CFU_CFU_ID_W bits)) init(0)
+ if(p.CFU_CFU_ID_W != 0){
+ factory.rw(stateAndIndexCsrOffset, 0, cfuIndex)
+ }
+ val status = p.CFU_WITH_STATUS generate new Area{
+ val CU, OP, FI, OF, SI, CI = RegInit(False)
+ val flags = List(CU, OP, FI, OF, SI, CI).reverse
+ factory.rw(statusCsrOffset, flags.zipWithIndex.map(_.swap) :_*)
+ factory.duringWrite(statusCsrOffset){
+ decode.arbitration.haltByOther := True //Handle CSRW to decode
+ }
+ }
+ }
+
+ if(withEnable) when(decode.input(CFU_ENABLE) && !csr.en){
+ pipeline.service(classOf[DecoderService]).forceIllegal()
+ }
+
+ forkStage plug new Area{
+ import forkStage._
+ val hazard = stages.dropWhile(_ != forkStage).tail.map(s => s.arbitration.isValid && s.input(HAS_SIDE_EFFECT)).orR
+ val scheduleWish = arbitration.isValid && input(CFU_ENABLE)
+ val schedule = scheduleWish && !hazard
+ arbitration.haltItself setWhen(scheduleWish && hazard)
+
+ val hold = RegInit(False) setWhen(schedule) clearWhen(bus.cmd.ready)
+ val fired = RegInit(False) setWhen(bus.cmd.fire) clearWhen(!arbitration.isStuck)
+ 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 := U(input(INSTRUCTION)(14 downto 12)).resized
+ val functionIdFromInstructinoWidth = encodings.map(_.functionIdWidth).max
+ val functionsIds = encodings.map(e => U(Cat(e.functionId.map(r => input(INSTRUCTION)(r))), functionIdFromInstructinoWidth bits))
+ bus.cmd.cfu_index := csr.cfuIndex
+ bus.cmd.state_index := csr.stateId
+ bus.cmd.function_id := functionsIds.read(input(CFU_ENCODING))
+ bus.cmd.reorder_id := 0
+ bus.cmd.request_id := 0
+ bus.cmd.raw_insn := input(INSTRUCTION).resized
+ if(p.CFU_INPUTS >= 1) bus.cmd.inputs(0) := input(RS1)
+ if(p.CFU_INPUTS >= 2) bus.cmd.inputs(1) := input(CFU_INPUT_2_KIND).mux(
+ CfuPlugin.Input2Kind.RS -> input(RS2),
+ CfuPlugin.Input2Kind.IMM_I -> IMM(input(INSTRUCTION)).h_sext
+ )
+ }
+
+ 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 = stageCount + 1,
+ latency = 0
+ )
+ } else if(forkStage != joinStage && allowZeroLatency) {
+ bus.rsp.s2mPipe()
+ } else {
+ bus.rsp.combStage()
+ }
+
+ rsp.ready := False
+ when(input(CFU_IN_FLIGHT)){
+ arbitration.haltItself setWhen(!rsp.valid)
+ rsp.ready := !arbitration.isStuckByOthers
+ output(REGFILE_WRITE_DATA) := rsp.outputs(0)
+ if(p.CFU_WITH_STATUS) when(arbitration.isFiring){
+ switch(rsp.status) {
+ for (i <- 1 to 6) is(i) {
+ csr.status.flags(i-1) := True
+ }
+ }
+ }
+ }
+ }
+
+ pipeline.stages.drop(1).foreach(s => s.output(CFU_IN_FLIGHT) clearWhen(s.arbitration.isStuck))
+ addPrePopTask(() => stages.dropWhile(_ != memory).reverse.dropWhile(_ != joinStage).foreach(s => s.input(CFU_IN_FLIGHT).init(False)))
+ }
+}
+
+
+object CfuTest{
+
+// stageCount = 0,
+// allowZeroLatency = true,
+ def getCfuParameter() = CfuBusParameter(
+ CFU_VERSION = 0,
+ CFU_INTERFACE_ID_W = 0,
+ CFU_FUNCTION_ID_W = 3,
+ 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
+ )
+}
+case class CfuTest() extends Component{
+ val io = new Bundle {
+ val bus = slave(CfuBus(CfuTest.getCfuParameter()))
+ }
+ io.bus.rsp.arbitrationFrom(io.bus.cmd)
+ io.bus.rsp.response_id := io.bus.cmd.request_id
+ io.bus.rsp.outputs(0) := ~(io.bus.cmd.inputs(0) & io.bus.cmd.inputs(1))
+}
+
+
+case class CfuBb(p : CfuBusParameter) extends BlackBox{
+ val io = new Bundle {
+ val clk, reset = in Bool()
+ val bus = slave(CfuBus(p))
+ }
+
+ mapCurrentClockDomain(io.clk, io.reset)
+}
+
+//case class CfuGray(p : CfuBusParameter) extends BlackBox{
+// val req_function_id = in Bits(p.CFU_FUNCTION_ID_W)
+// val req_data = in Bits(p.CFU_REQ_INPUTS)
+// val resp_data = in Bits(p.CFU_FUNCTION_ID_W)
+// input `CFU_FUNCTION_ID req_function_id,
+// input [CFU_REQ_INPUTS-1:0]`CFU_REQ_DATA req_data,
+// output [CFU_RESP_OUTPUTS-1:0]`CFU_RESP_DATA resp_data
+// io.bus.rsp.arbitrationFrom(io.bus.cmd)
+// io.bus.rsp.response_ok := True
+// io.bus.rsp.response_id := io.bus.cmd.request_id
+// io.bus.rsp.outputs(0) := ~(io.bus.cmd.inputs(0) & io.bus.cmd.inputs(1))
+//}
+
+
+case class CfuDecoder(p : CfuBusParameter,
+ mappings : Seq[AddressMapping],
+ pendingMax : Int = 3) extends Component{
+ val io = new Bundle {
+ val input = slave(CfuBus(p))
+ val outputs = Vec(master(CfuBus(p)), mappings.size)
+ }
+ val hasDefault = mappings.contains(DefaultMapping)
+ val logic = if(hasDefault && mappings.size == 1){
+ io.outputs(0) << io.input
+ } else new Area {
+ val hits = Vec(Bool, mappings.size)
+ for (portId <- 0 until mappings.length) yield {
+ val slaveBus = io.outputs(portId)
+ val memorySpace = mappings(portId)
+ val hit = hits(portId)
+ hit := (memorySpace match {
+ case DefaultMapping => !hits.filterNot(_ == hit).orR
+ case _ => memorySpace.hit(io.input.cmd.function_id)
+ })
+ slaveBus.cmd.valid := io.input.cmd.valid && hit
+ slaveBus.cmd.payload := io.input.cmd.payload.resized
+ }
+ val noHit = if (!hasDefault) !hits.orR else False
+ io.input.cmd.ready := (hits, io.outputs).zipped.map(_ && _.cmd.ready).orR || noHit
+
+ val rspPendingCounter = Reg(UInt(log2Up(pendingMax + 1) bits)) init(0)
+ rspPendingCounter := rspPendingCounter + U(io.input.cmd.fire) - U(io.input.rsp.fire)
+ val rspHits = RegNextWhen(hits, io.input.cmd.fire)
+ val rspPending = rspPendingCounter =/= 0
+ val rspNoHitValid = if (!hasDefault) !rspHits.orR else False
+ val rspNoHit = !hasDefault generate new Area{
+ val doIt = RegInit(False) clearWhen(io.input.rsp.fire) setWhen(io.input.cmd.fire && noHit)
+ val response_id = RegNextWhen(io.input.cmd.request_id, io.input.cmd.fire)
+ }
+
+ io.input.rsp.valid := io.outputs.map(_.rsp.valid).orR || (rspPending && rspNoHitValid)
+ io.input.rsp.payload := io.outputs.map(_.rsp.payload).read(OHToUInt(rspHits))
+ if(!hasDefault) when(rspNoHit.doIt) {
+ io.input.rsp.valid := True
+ io.input.rsp.response_id := rspNoHit.response_id
+ }
+ for(output <- io.outputs) output.rsp.ready := io.input.rsp.ready
+
+ val cmdWait = (rspPending && (hits =/= rspHits || rspNoHitValid)) || rspPendingCounter === pendingMax
+ when(cmdWait) {
+ io.input.cmd.ready := False
+ io.outputs.foreach(_.cmd.valid := False)
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala
new file mode 100644
index 0000000..7731a41
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala
@@ -0,0 +1,1335 @@
+package vexriscv.plugin
+
+import spinal.core._
+import spinal.lib._
+import vexriscv._
+import vexriscv.Riscv._
+import vexriscv.plugin.IntAluPlugin.{ALU_BITWISE_CTRL, ALU_CTRL, AluBitwiseCtrlEnum, AluCtrlEnum}
+
+import scala.collection.mutable.ArrayBuffer
+import scala.collection.mutable
+import spinal.core.sim._
+
+/**
+ * Created by spinalvm on 21.03.17.
+ */
+
+trait CsrAccess{
+ def canWrite : Boolean = false
+ def canRead : Boolean = false
+}
+object CsrAccess {
+ object WRITE_ONLY extends CsrAccess{
+ override def canWrite : Boolean = true
+ }
+ object READ_ONLY extends CsrAccess{
+ override def canRead : Boolean = true
+ }
+ object READ_WRITE extends CsrAccess{
+ override def canWrite : Boolean = true
+ override def canRead : Boolean = true
+ }
+ object NONE extends CsrAccess
+}
+
+
+
+case class ExceptionPortInfo(port : Flow[ExceptionCause],stage : Stage, priority : Int, codeWidth : Int)
+case class CsrPluginConfig(
+ catchIllegalAccess : Boolean,
+ mvendorid : BigInt,
+ marchid : BigInt,
+ mimpid : BigInt,
+ mhartid : BigInt,
+ misaExtensionsInit : Int,
+ misaAccess : CsrAccess,
+ mtvecAccess : CsrAccess,
+ mtvecInit : BigInt,
+ mepcAccess : CsrAccess,
+ mscratchGen : Boolean,
+ mcauseAccess : CsrAccess,
+ mbadaddrAccess : CsrAccess,
+ mcycleAccess : CsrAccess,
+ minstretAccess : CsrAccess,
+ ucycleAccess : CsrAccess,
+ uinstretAccess : CsrAccess = CsrAccess.NONE,
+ wfiGenAsWait : Boolean,
+ ecallGen : Boolean,
+ xtvecModeGen : Boolean = false,
+ noCsrAlu : Boolean = false,
+ wfiGenAsNop : Boolean = false,
+ ebreakGen : Boolean = false,
+ userGen : Boolean = false,
+ supervisorGen : Boolean = false,
+ sscratchGen : Boolean = false,
+ stvecAccess : CsrAccess = CsrAccess.NONE,
+ sepcAccess : CsrAccess = CsrAccess.NONE,
+ scauseAccess : CsrAccess = CsrAccess.NONE,
+ sbadaddrAccess : CsrAccess = CsrAccess.NONE,
+ scycleAccess : CsrAccess = CsrAccess.NONE,
+ sinstretAccess : CsrAccess = CsrAccess.NONE,
+ satpAccess : CsrAccess = CsrAccess.NONE,
+ utimeAccess :CsrAccess = CsrAccess.NONE,
+ medelegAccess : CsrAccess = CsrAccess.NONE,
+ midelegAccess : CsrAccess = CsrAccess.NONE,
+ withExternalMhartid : Boolean = false,
+ mhartidWidth : Int = 0,
+ pipelineCsrRead : Boolean = false,
+ pipelinedInterrupt : Boolean = true,
+ csrOhDecoder : Boolean = true,
+ deterministicInteruptionEntry : Boolean = false, //Only used for simulatation purposes
+ wfiOutput : Boolean = false
+ ){
+ assert(!ucycleAccess.canWrite)
+ def privilegeGen = userGen || supervisorGen
+ def noException = this.copy(ecallGen = false, ebreakGen = false, catchIllegalAccess = false)
+ def noExceptionButEcall = this.copy(ecallGen = true, ebreakGen = false, catchIllegalAccess = false)
+}
+
+object CsrPluginConfig{
+ def all : CsrPluginConfig = all(0x00000020l)
+ def small : CsrPluginConfig = small(0x00000020l)
+ def smallest : CsrPluginConfig = smallest(0x00000020l)
+
+ def openSbi(mhartid : Int, misa : Int) = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 0,
+ marchid = 0,
+ mimpid = 0,
+ mhartid = mhartid,
+ misaExtensionsInit = misa,
+ misaAccess = CsrAccess.READ_ONLY,
+ mtvecAccess = CsrAccess.READ_WRITE, //Could have been WRITE_ONLY :(
+ mtvecInit = null,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_ONLY,
+ mbadaddrAccess = CsrAccess.READ_ONLY,
+ mcycleAccess = CsrAccess.NONE,
+ minstretAccess = CsrAccess.NONE,
+ ucycleAccess = CsrAccess.NONE,
+ wfiGenAsWait = true,
+ ecallGen = true,
+ xtvecModeGen = false,
+ noCsrAlu = false,
+ wfiGenAsNop = false,
+ ebreakGen = true,
+ userGen = true,
+ supervisorGen = true,
+ sscratchGen = true,
+ stvecAccess = CsrAccess.READ_WRITE,
+ sepcAccess = CsrAccess.READ_WRITE,
+ scauseAccess = CsrAccess.READ_WRITE,
+ sbadaddrAccess = CsrAccess.READ_WRITE,
+ scycleAccess = CsrAccess.NONE,
+ sinstretAccess = CsrAccess.NONE,
+ satpAccess = CsrAccess.NONE,
+ medelegAccess = CsrAccess.READ_WRITE, //Could have been WRITE_ONLY :(
+ midelegAccess = CsrAccess.READ_WRITE, //Could have been WRITE_ONLY :(
+ pipelineCsrRead = false,
+ deterministicInteruptionEntry = false
+ )
+
+ def linuxMinimal(mtVecInit : BigInt) = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 1,
+ marchid = 2,
+ mimpid = 3,
+ mhartid = 0,
+ misaExtensionsInit = 0, //TODO
+ misaAccess = CsrAccess.NONE, //Read required by some regressions
+ mtvecAccess = CsrAccess.WRITE_ONLY, //Read required by some regressions
+ mtvecInit = mtVecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_ONLY,
+ mbadaddrAccess = CsrAccess.READ_ONLY,
+ mcycleAccess = CsrAccess.NONE,
+ minstretAccess = CsrAccess.NONE,
+ ucycleAccess = CsrAccess.NONE,
+ uinstretAccess = CsrAccess.NONE,
+ wfiGenAsWait = true,
+ ecallGen = true,
+ xtvecModeGen = false,
+ noCsrAlu = false,
+ wfiGenAsNop = false,
+ ebreakGen = true,
+ userGen = true,
+ supervisorGen = true,
+ sscratchGen = true,
+ stvecAccess = CsrAccess.READ_WRITE,
+ sepcAccess = CsrAccess.READ_WRITE,
+ scauseAccess = CsrAccess.READ_WRITE,
+ sbadaddrAccess = CsrAccess.READ_WRITE,
+ scycleAccess = CsrAccess.NONE,
+ sinstretAccess = CsrAccess.NONE,
+ satpAccess = CsrAccess.NONE, //Implemented into the MMU plugin
+ medelegAccess = CsrAccess.WRITE_ONLY,
+ midelegAccess = CsrAccess.WRITE_ONLY,
+ pipelineCsrRead = false,
+ deterministicInteruptionEntry = false
+ )
+
+
+ def linuxFull(mtVecInit : BigInt) = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 1,
+ marchid = 2,
+ mimpid = 3,
+ mhartid = 0,
+ misaExtensionsInit = 0, //TODO
+ misaAccess = CsrAccess.READ_WRITE,
+ mtvecAccess = CsrAccess.READ_WRITE,
+ mtvecInit = mtVecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_WRITE,
+ mbadaddrAccess = CsrAccess.READ_WRITE,
+ mcycleAccess = CsrAccess.READ_WRITE,
+ minstretAccess = CsrAccess.READ_WRITE,
+ ucycleAccess = CsrAccess.READ_ONLY,
+ uinstretAccess = CsrAccess.READ_ONLY,
+ wfiGenAsWait = true,
+ ecallGen = true,
+ xtvecModeGen = false,
+ noCsrAlu = false,
+ wfiGenAsNop = false,
+ ebreakGen = false,
+ userGen = true,
+ supervisorGen = true,
+ sscratchGen = true,
+ stvecAccess = CsrAccess.READ_WRITE,
+ sepcAccess = CsrAccess.READ_WRITE,
+ scauseAccess = CsrAccess.READ_WRITE,
+ sbadaddrAccess = CsrAccess.READ_WRITE,
+ scycleAccess = CsrAccess.READ_WRITE,
+ sinstretAccess = CsrAccess.READ_WRITE,
+ satpAccess = CsrAccess.NONE, //Implemented into the MMU plugin
+ medelegAccess = CsrAccess.READ_WRITE,
+ midelegAccess = CsrAccess.READ_WRITE,
+ pipelineCsrRead = false,
+ deterministicInteruptionEntry = false
+ )
+
+ def all(mtvecInit : BigInt) : CsrPluginConfig = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 11,
+ marchid = 22,
+ mimpid = 33,
+ mhartid = 0,
+ misaExtensionsInit = 66,
+ misaAccess = CsrAccess.READ_WRITE,
+ mtvecAccess = CsrAccess.READ_WRITE,
+ mtvecInit = mtvecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_WRITE,
+ mbadaddrAccess = CsrAccess.READ_WRITE,
+ mcycleAccess = CsrAccess.READ_WRITE,
+ minstretAccess = CsrAccess.READ_WRITE,
+ ecallGen = true,
+ wfiGenAsWait = true,
+ ucycleAccess = CsrAccess.READ_ONLY,
+ uinstretAccess = CsrAccess.READ_ONLY
+ )
+
+ def all2(mtvecInit : BigInt) : CsrPluginConfig = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 11,
+ marchid = 22,
+ mimpid = 33,
+ mhartid = 0,
+ misaExtensionsInit = 66,
+ misaAccess = CsrAccess.READ_WRITE,
+ mtvecAccess = CsrAccess.READ_WRITE,
+ mtvecInit = mtvecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_WRITE,
+ mbadaddrAccess = CsrAccess.READ_WRITE,
+ mcycleAccess = CsrAccess.READ_WRITE,
+ minstretAccess = CsrAccess.READ_WRITE,
+ ecallGen = true,
+ wfiGenAsWait = true,
+ ucycleAccess = CsrAccess.READ_ONLY,
+ uinstretAccess = CsrAccess.READ_ONLY,
+ supervisorGen = true,
+ sscratchGen = true,
+ stvecAccess = CsrAccess.READ_WRITE,
+ sepcAccess = CsrAccess.READ_WRITE,
+ scauseAccess = CsrAccess.READ_WRITE,
+ sbadaddrAccess = CsrAccess.READ_WRITE,
+ scycleAccess = CsrAccess.READ_WRITE,
+ sinstretAccess = CsrAccess.READ_WRITE,
+ satpAccess = CsrAccess.READ_WRITE,
+ medelegAccess = CsrAccess.READ_WRITE,
+ midelegAccess = CsrAccess.READ_WRITE
+ )
+
+ def small(mtvecInit : BigInt) = CsrPluginConfig(
+ catchIllegalAccess = false,
+ mvendorid = null,
+ marchid = null,
+ mimpid = null,
+ mhartid = null,
+ misaExtensionsInit = 66,
+ misaAccess = CsrAccess.NONE,
+ mtvecAccess = CsrAccess.NONE,
+ mtvecInit = mtvecInit,
+ 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
+ )
+
+ def smallest(mtvecInit : BigInt) = CsrPluginConfig(
+ catchIllegalAccess = false,
+ mvendorid = null,
+ marchid = null,
+ mimpid = null,
+ mhartid = null,
+ misaExtensionsInit = 66,
+ misaAccess = CsrAccess.NONE,
+ mtvecAccess = CsrAccess.NONE,
+ mtvecInit = mtvecInit,
+ mepcAccess = CsrAccess.NONE,
+ mscratchGen = false,
+ mcauseAccess = CsrAccess.READ_ONLY,
+ mbadaddrAccess = CsrAccess.NONE,
+ mcycleAccess = CsrAccess.NONE,
+ minstretAccess = CsrAccess.NONE,
+ ecallGen = false,
+ wfiGenAsWait = false,
+ ucycleAccess = CsrAccess.NONE,
+ uinstretAccess = CsrAccess.NONE
+ )
+
+ def secure(mtvecInit : BigInt) = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 1,
+ marchid = 2,
+ mimpid = 3,
+ mhartid = 0,
+ misaExtensionsInit = 0x101064, // RV32GCFMU
+ misaAccess = CsrAccess.READ_WRITE,
+ mtvecAccess = CsrAccess.READ_WRITE,
+ mtvecInit = mtvecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_WRITE,
+ mbadaddrAccess = CsrAccess.READ_WRITE,
+ mcycleAccess = CsrAccess.READ_WRITE,
+ minstretAccess = CsrAccess.READ_WRITE,
+ ucycleAccess = CsrAccess.READ_ONLY,
+ uinstretAccess = CsrAccess.READ_ONLY,
+ wfiGenAsWait = true,
+ ecallGen = true,
+ userGen = true,
+ medelegAccess = CsrAccess.READ_WRITE,
+ midelegAccess = CsrAccess.READ_WRITE
+ )
+
+}
+case class CsrWrite(that : Data, bitOffset : Int)
+case class CsrRead(that : Data , bitOffset : Int)
+case class CsrReadToWriteOverride(that : Data, bitOffset : Int) //Used for special cases, as MIP where there shadow stuff
+case class CsrOnWrite(doThat :() => Unit)
+case class CsrDuringWrite(doThat :() => Unit)
+case class CsrDuringRead(doThat :() => Unit)
+case class CsrDuring(doThat :() => Unit)
+case class CsrOnRead(doThat : () => Unit)
+
+
+case class CsrMapping() extends Area with CsrInterface {
+ val mapping = mutable.LinkedHashMap[Int,ArrayBuffer[Any]]()
+ val always = ArrayBuffer[Any]()
+ val readDataSignal, readDataInit, writeDataSignal = Bits(32 bits)
+ val allowCsrSignal = False
+ val hazardFree = Bool()
+
+ readDataSignal := readDataInit
+ def addMappingAt(address : Int,that : Any) = mapping.getOrElseUpdate(address,new ArrayBuffer[Any]) += that
+ override def r(csrAddress : Int, bitOffset : Int, that : Data): Unit = addMappingAt(csrAddress, CsrRead(that,bitOffset))
+ override def w(csrAddress : Int, bitOffset : Int, that : Data): Unit = addMappingAt(csrAddress, CsrWrite(that,bitOffset))
+ override def r2w(csrAddress : Int, bitOffset : Int, that : Data): Unit = addMappingAt(csrAddress, CsrReadToWriteOverride(that,bitOffset))
+ override def onWrite(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrOnWrite(() => body))
+ override def duringWrite(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrDuringWrite(() => body))
+ override def duringRead(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrDuringRead(() => body))
+ override def during(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrDuring(() => body))
+ override def onRead(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrOnRead(() => {body}))
+ override def duringAny(): Bool = ???
+ override def duringAnyRead(body: => Unit) : Unit = always += CsrDuringRead(() => body)
+ override def duringAnyWrite(body: => Unit) : Unit = always += CsrDuringWrite(() => body)
+ override def onAnyRead(body: => Unit) : Unit = always += CsrOnRead(() => body)
+ override def onAnyWrite(body: => Unit) : Unit = always += CsrOnWrite(() => body)
+ override def readData() = readDataSignal
+ override def writeData() = writeDataSignal
+ override def allowCsr() = allowCsrSignal := True
+ override def isHazardFree() = hazardFree
+}
+
+
+trait CsrInterface{
+ def onWrite(csrAddress : Int)(doThat : => Unit) : Unit
+ def onRead(csrAddress : Int)(doThat : => Unit) : Unit
+ def duringWrite(csrAddress: Int)(body: => Unit): Unit
+ def duringRead(csrAddress: Int)(body: => Unit): Unit
+ def during(csrAddress: Int)(body: => Unit): Unit
+ def duringAny(): Bool
+ def r(csrAddress : Int, bitOffset : Int, that : Data): Unit
+ def w(csrAddress : Int, bitOffset : Int, that : Data): Unit
+ def rw(csrAddress : Int, bitOffset : Int,that : Data): Unit ={
+ r(csrAddress,bitOffset,that)
+ w(csrAddress,bitOffset,that)
+ }
+ def duringAnyRead(body: => Unit) : Unit //Called all the durration of a Csr write instruction in the execute stage
+ def duringAnyWrite(body: => Unit) : Unit //same than above for read
+ def onAnyRead(body: => Unit) : Unit
+ def onAnyWrite(body: => Unit) : Unit
+ def allowCsr() : Unit //In case your csr do not use the regular API with csrAddress but is implemented using "side channels", you can call that if the current csr is implemented
+ def isHazardFree() : Bool // You should not have any side effect nor use readData() until this return True
+
+ def r2w(csrAddress : Int, bitOffset : Int,that : Data): Unit
+
+ def rw(csrAddress : Int, thats : (Int, Data)*) : Unit = for(that <- thats) rw(csrAddress,that._1, that._2)
+ def w(csrAddress : Int, thats : (Int, Data)*) : Unit = for(that <- thats) w(csrAddress,that._1, that._2)
+ def r(csrAddress : Int, thats : (Int, Data)*) : Unit = for(that <- thats) r(csrAddress,that._1, that._2)
+ def rw[T <: Data](csrAddress : Int, that : T): Unit = rw(csrAddress,0,that)
+ def w[T <: Data](csrAddress : Int, that : T): Unit = w(csrAddress,0,that)
+ def r [T <: Data](csrAddress : Int, that : T): Unit = r(csrAddress,0,that)
+ def isWriting(csrAddress : Int) : Bool = {
+ val ret = False
+ onWrite(csrAddress){
+ ret := True
+ }
+ ret
+ }
+
+ def isReading(csrAddress : Int) : Bool = {
+ val ret = False
+ onRead(csrAddress){
+ ret := True
+ }
+ ret
+ }
+
+ def readData() : Bits //Return the 32 bits internal signal of the CsrPlugin for you to override (if you want)
+ def writeData() : Bits //Return the 32 bits value that the CsrPlugin want to write in the CSR (depend on readData combinatorialy)
+}
+
+
+trait IContextSwitching{
+ def isContextSwitching : Bool
+}
+trait IWake{
+ def askWake() : Unit
+}
+
+class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with ExceptionService with PrivilegeService with InterruptionInhibitor with ExceptionInhibitor with IContextSwitching with CsrInterface with IWake{
+ import config._
+ import CsrAccess._
+
+ assert(!(wfiGenAsNop && wfiGenAsWait))
+
+ def xlen = 32
+
+ //Mannage ExceptionService calls
+ val exceptionPortsInfos = ArrayBuffer[ExceptionPortInfo]()
+ override def newExceptionPort(stage : Stage, priority : Int = 0, codeWidth : Int = 4) = {
+ val interface = Flow(ExceptionCause(codeWidth))
+ exceptionPortsInfos += ExceptionPortInfo(interface,stage,priority,codeWidth)
+ interface
+ }
+
+
+
+ var exceptionPendings : Vec[Bool] = null
+ override def isExceptionPending(stage : Stage): Bool = exceptionPendings(pipeline.stages.indexOf(stage))
+
+ var redoInterface : Flow[UInt] = null
+ var jumpInterface : Flow[UInt] = null
+ var timerInterrupt, externalInterrupt, softwareInterrupt : Bool = null
+ var externalInterruptS : Bool = null
+ var forceMachineWire : Bool = null
+ var privilege : UInt = null
+ var selfException : Flow[ExceptionCause] = null
+ var contextSwitching : Bool = null
+ var thirdPartyWake : Bool = null
+ var inWfi : Bool = null
+ var externalMhartId : UInt = null
+ var utime : UInt = null
+
+ override def askWake(): Unit = thirdPartyWake := True
+
+ override def isContextSwitching = contextSwitching
+
+ object EnvCtrlEnum extends SpinalEnum(binarySequential){
+ val NONE, XRET = newElement()
+ val WFI = if(wfiGenAsWait) newElement() else null
+ val ECALL = if(ecallGen) newElement() else null
+ val EBREAK = if(ebreakGen) newElement() else null
+ }
+
+ object ENV_CTRL extends Stageable(EnvCtrlEnum())
+ object IS_CSR extends Stageable(Bool)
+ object IS_SFENCE_VMA extends Stageable(Bool)
+ object CSR_WRITE_OPCODE extends Stageable(Bool)
+ object CSR_READ_OPCODE extends Stageable(Bool)
+ object PIPELINED_CSR_READ extends Stageable(Bits(32 bits))
+
+ var allowInterrupts : Bool = null
+ var allowException : Bool = null
+ var allowEbreakException : Bool = null
+
+ var csrMapping : CsrMapping = null
+
+ //Print CSR mapping
+ def printCsr() {
+ for ((address, things) <- csrMapping.mapping) {
+ println("0x" + address.toHexString + " => ")
+ for (thing <- things) {
+ println(" - " + thing)
+ }
+ }
+ }
+
+
+ //Interruption and exception data model
+ case class Delegator(var enable : Bool, privilege : Int)
+ case class InterruptSpec(var cond : Bool, id : Int, privilege : Int, delegators : List[Delegator])
+ case class ExceptionSpec(id : Int, delegators : List[Delegator])
+ var interruptSpecs = ArrayBuffer[InterruptSpec]()
+ var exceptionSpecs = ArrayBuffer[ExceptionSpec]()
+
+ def addInterrupt(cond : Bool, id : Int, privilege : Int, delegators : List[Delegator]): Unit = {
+ interruptSpecs += InterruptSpec(cond, id, privilege, delegators)
+ }
+
+ override def r(csrAddress: Int, bitOffset: Int, that: Data): Unit = csrMapping.r(csrAddress, bitOffset, that)
+ override def w(csrAddress: Int, bitOffset: Int, that: Data): Unit = csrMapping.w(csrAddress, bitOffset, that)
+ override def r2w(csrAddress: Int, bitOffset: Int, that: Data): Unit = csrMapping.r2w(csrAddress, bitOffset, that)
+ override def onWrite(csrAddress: Int)(body: => Unit): Unit = csrMapping.onWrite(csrAddress)(body)
+ override def duringWrite(csrAddress: Int)(body: => Unit): Unit = csrMapping.duringWrite(csrAddress)(body)
+ override def onRead(csrAddress: Int)(body: => Unit): Unit = csrMapping.onRead(csrAddress)(body)
+ override def duringRead(csrAddress: Int)(body: => Unit): Unit = csrMapping.duringRead(csrAddress)(body)
+ override def during(csrAddress: Int)(body: => Unit): Unit = csrMapping.during(csrAddress)(body)
+ override def duringAny(): Bool = pipeline.execute.arbitration.isValid && pipeline.execute.input(IS_CSR)
+ override def duringAnyRead(body: => Unit) = csrMapping.duringAnyRead(body)
+ override def duringAnyWrite(body: => Unit) = csrMapping.duringAnyWrite(body)
+ override def onAnyRead(body: => Unit) = csrMapping.onAnyRead(body)
+ override def onAnyWrite(body: => Unit) = csrMapping.onAnyWrite(body)
+ override def allowCsr() = csrMapping.allowCsr()
+ override def readData() = csrMapping.readData()
+ override def writeData() = csrMapping.writeData()
+ override def isHazardFree() = csrMapping.isHazardFree()
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+
+ if(!config.ebreakGen) {
+ SpinalWarning("This VexRiscv configuration is set without software ebreak instruction support. Some software may rely on it (ex: Rust). (This isn't related to JTAG ebreak)")
+ }
+
+ csrMapping = new CsrMapping()
+
+ inWfi = False.addTag(Verilator.public)
+
+ thirdPartyWake = False
+
+ val defaultEnv = List[(Stageable[_ <: BaseType],Any)](
+ )
+
+ val defaultCsrActions = List[(Stageable[_ <: BaseType],Any)](
+ IS_CSR -> True,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> True
+ ) ++ (if(catchIllegalAccess) List(HAS_SIDE_EFFECT -> True) else Nil)
+
+ val nonImmediatActions = defaultCsrActions ++ List(
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ RS1_USE -> True
+ )
+
+ val immediatActions = defaultCsrActions ++ List(
+ SRC1_CTRL -> Src1CtrlEnum.URS1
+ )
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+
+ decoderService.addDefault(ENV_CTRL, EnvCtrlEnum.NONE)
+ decoderService.addDefault(IS_CSR, False)
+ decoderService.add(List(
+ CSRRW -> nonImmediatActions,
+ CSRRS -> nonImmediatActions,
+ CSRRC -> nonImmediatActions,
+ CSRRWI -> immediatActions,
+ CSRRSI -> immediatActions,
+ CSRRCI -> immediatActions,
+ MRET -> (defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.XRET, HAS_SIDE_EFFECT -> True)),
+ SRET -> (defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.XRET, HAS_SIDE_EFFECT -> True))
+ ))
+ if(wfiGenAsWait) decoderService.add(WFI, defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.WFI))
+ if(wfiGenAsNop) decoderService.add(WFI, Nil)
+ if(ecallGen) decoderService.add(ECALL, defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.ECALL, HAS_SIDE_EFFECT -> True))
+ if(ebreakGen) decoderService.add(EBREAK, defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.EBREAK, HAS_SIDE_EFFECT -> True))
+
+ val pcManagerService = pipeline.service(classOf[JumpService])
+ jumpInterface = pcManagerService.createJumpInterface(pipeline.stages.last)
+ jumpInterface.valid := False
+ jumpInterface.payload.assignDontCare()
+
+
+ if(supervisorGen) {
+ redoInterface = pcManagerService.createJumpInterface(pipeline.execute, -20) //Should lose against dynamic_target branch prediction correction
+ }
+
+ exceptionPendings = Vec(Bool, pipeline.stages.length)
+ timerInterrupt = in Bool() setName("timerInterrupt")
+ externalInterrupt = in Bool() setName("externalInterrupt")
+ softwareInterrupt = in Bool() setName("softwareInterrupt") default(False)
+ if(supervisorGen){
+// timerInterruptS = in Bool() setName("timerInterruptS")
+ externalInterruptS = in Bool() setName("externalInterruptS")
+ }
+ contextSwitching = Bool().setName("contextSwitching")
+
+ privilege = UInt(2 bits).setName("CsrPlugin_privilege")
+ forceMachineWire = False
+
+ if(catchIllegalAccess || ecallGen || ebreakGen)
+ selfException = newExceptionPort(pipeline.execute)
+
+ allowInterrupts = True
+ allowException = True
+ allowEbreakException = True
+
+ for (i <- interruptSpecs) i.cond = i.cond.pull()
+
+
+ pipeline.update(MPP, UInt(2 bits))
+
+ if(withExternalMhartid) externalMhartId = in UInt(mhartidWidth bits)
+ if(utimeAccess != CsrAccess.NONE) utime = in UInt(64 bits) setName("utime")
+
+ if(supervisorGen) {
+ decoderService.addDefault(IS_SFENCE_VMA, False)
+ decoderService.add(SFENCE_VMA, List(IS_SFENCE_VMA -> True))
+ }
+ }
+
+ def inhibateInterrupts() : Unit = allowInterrupts := False
+ def inhibateException() : Unit = allowException := False
+ def inhibateEbreakException() : Unit = allowEbreakException := False
+
+ override def isUser() : Bool = privilege === 0
+ override def isSupervisor(): Bool = privilege === 1
+ override def isMachine(): Bool = privilege === 3
+ override def forceMachine(): Unit = forceMachineWire := True
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ val fetcher = service(classOf[IBusFetcher])
+ val trapCodeWidth = log2Up((List(16) ++ interruptSpecs.map(_.id + 1) ++ exceptionPortsInfos.map(p => 1 << widthOf(p.port.code))).max)
+
+ //Define CSR mapping utilities
+ implicit class CsrAccessPimper(csrAccess : CsrAccess){
+ def apply(csrAddress : Int, thats : (Int, Data)*) : Unit = {
+ if(csrAccess == `WRITE_ONLY` || csrAccess == `READ_WRITE`) for(that <- thats) csrMapping.w(csrAddress,that._1, that._2)
+ if(csrAccess == `READ_ONLY` || csrAccess == `READ_WRITE`) for(that <- thats) csrMapping.r(csrAddress,that._1, that._2)
+ }
+ def apply(csrAddress : Int, that : Data) : Unit = {
+ if(csrAccess == `WRITE_ONLY` || csrAccess == `READ_WRITE`) csrMapping.w(csrAddress, 0, that)
+ if(csrAccess == `READ_ONLY` || csrAccess == `READ_WRITE`) csrMapping.r(csrAddress, 0, that)
+ }
+ }
+
+
+ case class Xtvec() extends Bundle {
+ val mode = Bits(2 bits)
+ val base = UInt(xlen-2 bits)
+ }
+
+ val privilegeReg = privilegeGen generate RegInit(U"11")
+ privilege := (if(privilegeGen) privilegeReg else U"11")
+
+ when(forceMachineWire) { privilege := 3 }
+
+ val machineCsr = pipeline plug new Area{
+ //Define CSR registers
+ // Status => MXR, SUM, TVM, TW, TSE ?
+ val misa = new Area{
+ val base = Reg(UInt(2 bits)) init(U"01") allowUnsetRegToAvoidLatch
+ val extensions = Reg(Bits(26 bits)) init(misaExtensionsInit) allowUnsetRegToAvoidLatch
+ }
+
+ val mtvec = Reg(Xtvec()).allowUnsetRegToAvoidLatch
+
+ if(mtvecInit != null) mtvec.mode init(mtvecInit & 0x3)
+ if(mtvecInit != null) mtvec.base init(mtvecInit / 4)
+ val mepc = Reg(UInt(xlen bits))
+ val mstatus = new Area{
+ val MIE, MPIE = RegInit(False)
+ val MPP = RegInit(U"11")
+ }
+ val mip = new Area{
+ val MEIP = RegNext(externalInterrupt)
+ val MTIP = RegNext(timerInterrupt)
+ val MSIP = RegNext(softwareInterrupt)
+ }
+ val mie = new Area{
+ val MEIE, MTIE, MSIE = RegInit(False)
+ }
+ val mscratch = if(mscratchGen) Reg(Bits(xlen bits)) else null
+ val mcause = new Area{
+ val interrupt = Reg(Bool)
+ val exceptionCode = Reg(UInt(trapCodeWidth bits))
+ }
+ val mtval = Reg(UInt(xlen bits))
+ val mcycle = Reg(UInt(64 bits)) init(0)
+ val minstret = Reg(UInt(64 bits)) init(0)
+
+
+ val medeleg = supervisorGen generate new Area {
+ val IAM, IAF, II, LAM, LAF, SAM, SAF, EU, ES, IPF, LPF, SPF = RegInit(False)
+ val mapping = mutable.LinkedHashMap(0 -> IAM, 1 -> IAF, 2 -> II, 4 -> LAM, 5 -> LAF, 6 -> SAM, 7 -> SAF, 8 -> EU, 9 -> ES, 12 -> IPF, 13 -> LPF, 15 -> SPF)
+ }
+ val mideleg = supervisorGen generate new Area {
+ val ST, SE, SS = RegInit(False)
+ }
+
+ if(mvendorid != null) READ_ONLY(CSR.MVENDORID, U(mvendorid))
+ if(marchid != null) READ_ONLY(CSR.MARCHID , U(marchid ))
+ if(mimpid != null) READ_ONLY(CSR.MIMPID , U(mimpid ))
+ if(mhartid != null && !withExternalMhartid) READ_ONLY(CSR.MHARTID , U(mhartid ))
+ if(withExternalMhartid) READ_ONLY(CSR.MHARTID , externalMhartId)
+ misaAccess(CSR.MISA, xlen-2 -> misa.base , 0 -> misa.extensions)
+
+ //Machine CSR
+ READ_WRITE(CSR.MSTATUS, 7 -> mstatus.MPIE, 3 -> mstatus.MIE)
+ READ_ONLY(CSR.MIP, 11 -> mip.MEIP, 7 -> mip.MTIP)
+ READ_WRITE(CSR.MIP, 3 -> mip.MSIP)
+ READ_WRITE(CSR.MIE, 11 -> mie.MEIE, 7 -> mie.MTIE, 3 -> mie.MSIE)
+
+ r(CSR.MSTATUS, 11 -> mstatus.MPP)
+ onWrite(CSR.MSTATUS){
+ switch(writeData()(12 downto 11)){
+ is(3){ mstatus.MPP := 3 }
+ if(supervisorGen) is(1){ mstatus.MPP := 1 }
+ if(userGen) is(0){ mstatus.MPP := 0 }
+ }
+ }
+
+ mtvecAccess(CSR.MTVEC, 2 -> mtvec.base, 0 -> mtvec.mode)
+ mepcAccess(CSR.MEPC, mepc)
+ if(mscratchGen) READ_WRITE(CSR.MSCRATCH, mscratch)
+ mcauseAccess(CSR.MCAUSE, xlen-1 -> mcause.interrupt, 0 -> mcause.exceptionCode)
+ mbadaddrAccess(CSR.MBADADDR, mtval)
+ mcycleAccess(CSR.MCYCLE, mcycle(31 downto 0))
+ mcycleAccess(CSR.MCYCLEH, mcycle(63 downto 32))
+ minstretAccess(CSR.MINSTRET, minstret(31 downto 0))
+ minstretAccess(CSR.MINSTRETH, minstret(63 downto 32))
+
+ if(supervisorGen) {
+ for((id, enable) <- medeleg.mapping) medelegAccess(CSR.MEDELEG, id -> enable)
+ midelegAccess(CSR.MIDELEG, 9 -> mideleg.SE, 5 -> mideleg.ST, 1 -> mideleg.SS)
+ }
+
+ //User CSR
+ ucycleAccess(CSR.UCYCLE, mcycle(31 downto 0))
+ ucycleAccess(CSR.UCYCLEH, mcycle(63 downto 32))
+ uinstretAccess(CSR.UINSTRET, minstret(31 downto 0))
+ uinstretAccess(CSR.UINSTRETH, minstret(63 downto 32))
+
+ if(utimeAccess != CsrAccess.NONE) {
+ utimeAccess(CSR.UTIME, utime(31 downto 0))
+ utimeAccess(CSR.UTIMEH, utime(63 downto 32))
+ }
+
+ pipeline(MPP) := mstatus.MPP
+ }
+
+ val supervisorCsr = ifGen(supervisorGen) {
+ pipeline plug new Area {
+ val sstatus = new Area {
+ val SIE, SPIE = RegInit(False)
+ val SPP = RegInit(U"1")
+ }
+
+ val sip = new Area {
+ val SEIP_SOFT = RegInit(False)
+ val SEIP_INPUT = RegNext(externalInterruptS)
+ val SEIP_OR = SEIP_SOFT || SEIP_INPUT
+ val STIP = RegInit(False)
+ val SSIP = RegInit(False)
+ }
+ val sie = new Area {
+ val SEIE, STIE, SSIE = RegInit(False)
+ }
+ val stvec = Reg(Xtvec()).allowUnsetRegToAvoidLatch
+ val sscratch = if (sscratchGen) Reg(Bits(xlen bits)) else null
+
+ val scause = new Area {
+ val interrupt = Reg(Bool)
+ val exceptionCode = Reg(UInt(trapCodeWidth bits))
+ }
+ val stval = Reg(UInt(xlen bits))
+ val sepc = Reg(UInt(xlen bits))
+ val satp = new Area {
+ val PPN = Reg(Bits(22 bits))
+ val ASID = Reg(Bits(9 bits))
+ val MODE = Reg(Bits(1 bits))
+ }
+
+ //Supervisor CSR
+ for(offset <- List(CSR.MSTATUS, CSR.SSTATUS)) READ_WRITE(offset,8 -> sstatus.SPP, 5 -> sstatus.SPIE, 1 -> sstatus.SIE)
+ for(offset <- List(CSR.MIP, CSR.SIP)) {
+ READ_WRITE(offset, 5 -> sip.STIP, 1 -> sip.SSIP)
+ READ_ONLY(offset, 9 -> sip.SEIP_OR)
+ WRITE_ONLY(offset, 9 -> sip.SEIP_SOFT)
+ r2w(offset, 9, sip.SEIP_SOFT)
+ }
+
+ for(offset <- List(CSR.MIE, CSR.SIE)) READ_WRITE(offset, 9 -> sie.SEIE, 5 -> sie.STIE, 1 -> sie.SSIE)
+
+
+ stvecAccess(CSR.STVEC, 2 -> stvec.base, 0 -> stvec.mode)
+ sepcAccess(CSR.SEPC, sepc)
+ if(sscratchGen) READ_WRITE(CSR.SSCRATCH, sscratch)
+ scauseAccess(CSR.SCAUSE, xlen-1 -> scause.interrupt, 0 -> scause.exceptionCode)
+ sbadaddrAccess(CSR.SBADADDR, stval)
+ satpAccess(CSR.SATP, 31 -> satp.MODE, 22 -> satp.ASID, 0 -> satp.PPN)
+
+
+ val rescheduleLogic = supervisorGen generate new Area {
+ redoInterface.valid := False
+ redoInterface.payload := decode.input(PC)
+
+ val rescheduleNext = False
+ when(execute.arbitration.isValid && execute.input(IS_SFENCE_VMA)) { rescheduleNext := True }
+ duringWrite(CSR.SATP) { rescheduleNext := True }
+
+ when(rescheduleNext){
+ redoInterface.valid := True
+ execute.arbitration.flushNext := True
+ decode.arbitration.haltByOther := True
+ }
+ }
+ }
+ }
+
+
+
+ pipeline plug new Area{
+ import machineCsr._
+ import supervisorCsr._
+
+ val lastStage = pipeline.stages.last
+ val beforeLastStage = pipeline.stages(pipeline.stages.size-2)
+ val stagesFromExecute = pipeline.stages.dropWhile(_ != execute)
+
+ //Manage counters
+ mcycle := mcycle + 1
+ when(lastStage.arbitration.isFiring) {
+ minstret := minstret + 1
+ }
+
+
+ if(supervisorGen) {
+ addInterrupt(sip.STIP && sie.STIE, id = 5, privilege = 1, delegators = List(Delegator(mideleg.ST, 3)))
+ addInterrupt(sip.SSIP && sie.SSIE, id = 1, privilege = 1, delegators = List(Delegator(mideleg.SS, 3)))
+ addInterrupt(sip.SEIP_OR && sie.SEIE, id = 9, privilege = 1, delegators = List(Delegator(mideleg.SE, 3)))
+
+ for((id, enable) <- medeleg.mapping) exceptionSpecs += ExceptionSpec(id, List(Delegator(enable, 3)))
+ }
+
+ addInterrupt(mip.MTIP && mie.MTIE, id = 7, privilege = 3, delegators = Nil)
+ addInterrupt(mip.MSIP && mie.MSIE, id = 3, privilege = 3, delegators = Nil)
+ addInterrupt(mip.MEIP && mie.MEIE, id = 11, privilege = 3, delegators = Nil)
+
+
+ val mepcCaptureStage = if(exceptionPortsInfos.nonEmpty) lastStage else decode
+
+
+ //Aggregate all exception port and remove required instructions
+ val exceptionPortCtrl = exceptionPortsInfos.nonEmpty generate new Area{
+ val codeWidth = exceptionPortsInfos.map(_.codeWidth).max
+ val firstStageIndexWithExceptionPort = exceptionPortsInfos.map(i => indexOf(i.stage)).min
+ val exceptionValids = Vec(stages.map(s => Bool().setPartialName(s.getName())))
+ val exceptionValidsRegs = Vec(stages.map(s => Reg(Bool).init(False).setPartialName(s.getName()))).allowUnsetRegToAvoidLatch
+ val exceptionContext = Reg(ExceptionCause(codeWidth))
+ val exceptionTargetPrivilegeUncapped = U"11"
+
+ switch(exceptionContext.code){
+ for(s <- exceptionSpecs){
+ is(s.id){
+ var exceptionPrivilegs = if (supervisorGen) List(1, 3) else List(3)
+ while(exceptionPrivilegs.length != 1){
+ val p = exceptionPrivilegs.head
+ if (exceptionPrivilegs.tail.forall(e => s.delegators.exists(_.privilege == e))) {
+ val delegUpOn = s.delegators.filter(_.privilege > p).map(_.enable).fold(True)(_ && _)
+ val delegDownOff = !s.delegators.filter(_.privilege <= p).map(_.enable).orR
+ when(delegUpOn && delegDownOff) {
+ exceptionTargetPrivilegeUncapped := p
+ }
+ }
+ exceptionPrivilegs = exceptionPrivilegs.tail
+ }
+ }
+ }
+ }
+ val exceptionTargetPrivilege = privilege.max(exceptionTargetPrivilegeUncapped)
+
+ val groupedByStage = exceptionPortsInfos.map(_.stage).distinct.map(s => {
+ val stagePortsInfos = exceptionPortsInfos.filter(_.stage == s).sortWith(_.priority > _.priority)
+ val stagePort = stagePortsInfos.length match{
+ case 1 => {
+ stagePortsInfos.head.port.translateWith(stagePortsInfos.head.port.payload.resizeCode(codeWidth))
+ }
+ case _ => {
+ val groupedPort = Flow(ExceptionCause(codeWidth))
+ val valids = stagePortsInfos.map(_.port.valid)
+ val codes = stagePortsInfos.map(_.port.payload.resizeCode(codeWidth))
+ groupedPort.valid := valids.orR
+ groupedPort.payload := MuxOH(OHMasking.first(stagePortsInfos.map(_.port.valid).asBits), codes)
+ groupedPort
+ }
+ }
+ ExceptionPortInfo(stagePort,s,0, codeWidth)
+ })
+
+ val sortedByStage = groupedByStage.sortWith((a, b) => pipeline.indexOf(a.stage) < pipeline.indexOf(b.stage))
+// sortedByStage.zipWithIndex.foreach(e => e._1.port.setName(e._1.stage.getName() + "_exception_agregat"))
+ exceptionValids := exceptionValidsRegs
+ for(portInfo <- sortedByStage; port = portInfo.port ; stage = portInfo.stage; stageId = indexOf(portInfo.stage)) {
+ when(port.valid) {
+ stage.arbitration.flushNext := True
+ stage.arbitration.removeIt := True
+ exceptionValids(stageId) := True
+ exceptionContext := port.payload
+ }
+ }
+
+ for(stageId <- firstStageIndexWithExceptionPort until stages.length; stage = stages(stageId) ){
+ val previousStage = if(stageId == firstStageIndexWithExceptionPort) stage else stages(stageId-1)
+ when(!stage.arbitration.isStuck){
+ exceptionValidsRegs(stageId) := (if(stageId != firstStageIndexWithExceptionPort) exceptionValids(stageId-1) && !previousStage.arbitration.isStuck else False)
+ }otherwise{
+ if(stage != stages.last)
+ exceptionValidsRegs(stageId) := exceptionValids(stageId)
+ else
+ exceptionValidsRegs(stageId) := False
+ }
+ when(stage.arbitration.isFlushed){
+ exceptionValids(stageId) := False
+ }
+ }
+
+ when(exceptionValids.orR){
+ fetcher.haltIt()
+ }
+
+ //Avoid the PC register of the last stage to change durring an exception handleing (Used to fill Xepc)
+ stages.last.dontSample.getOrElseUpdate(PC, ArrayBuffer[Bool]()) += exceptionValids.last
+ exceptionPendings := exceptionValidsRegs
+ }
+
+
+
+
+
+ //Process interrupt request, code and privilege
+ val interrupt = new Area {
+ val valid = if(pipelinedInterrupt) RegNext(False) init(False) else False
+ val code = if(pipelinedInterrupt) Reg(UInt(trapCodeWidth bits)) else UInt(trapCodeWidth bits).assignDontCare()
+ var privilegs = if (supervisorGen) List(1, 3) else List(3)
+ val targetPrivilege = if(pipelinedInterrupt) Reg(UInt(2 bits)) else UInt(2 bits).assignDontCare()
+ val privilegeAllowInterrupts = mutable.LinkedHashMap[Int, Bool]()
+ if (supervisorGen) privilegeAllowInterrupts += 1 -> ((sstatus.SIE && privilege === U"01") || privilege < U"01")
+ privilegeAllowInterrupts += 3 -> (mstatus.MIE || privilege < U"11")
+ while (privilegs.nonEmpty) {
+ val p = privilegs.head
+ when(privilegeAllowInterrupts(p)) {
+ for (i <- interruptSpecs
+ if i.privilege <= p //EX : Machine timer interrupt can't go into supervisor mode
+ if privilegs.tail.forall(e => i.delegators.exists(_.privilege == e))) { // EX : Supervisor timer need to have machine mode delegator
+ val delegUpOn = i.delegators.filter(_.privilege > p).map(_.enable).fold(True)(_ && _)
+ val delegDownOff = !i.delegators.filter(_.privilege <= p).map(_.enable).orR
+ when(i.cond && delegUpOn && delegDownOff) {
+ valid := True
+ code := i.id
+ targetPrivilege := p
+ }
+ }
+ }
+ privilegs = privilegs.tail
+ }
+
+ code.addTag(Verilator.public)
+ }
+
+
+
+
+ val exception = if(exceptionPortCtrl != null) exceptionPortCtrl.exceptionValids.last && allowException else False
+ val lastStageWasWfi = if(wfiGenAsWait) RegNext(lastStage.arbitration.isFiring && lastStage.input(ENV_CTRL) === EnvCtrlEnum.WFI) init(False) else False
+
+
+
+ //Used to make the pipeline empty softly (for interrupts)
+ val pipelineLiberator = new Area{
+ val pcValids = Vec(RegInit(False), stagesFromExecute.length)
+ val active = interrupt.valid && allowInterrupts && decode.arbitration.isValid
+ when(active){
+ decode.arbitration.haltByOther := True
+ for((stage, reg, previous) <- (stagesFromExecute, pcValids, True :: pcValids.toList).zipped){
+ when(!stage.arbitration.isStuck){
+ reg := previous
+ }
+ }
+ }
+ when(!active || decode.arbitration.isRemoved) {
+ pcValids.foreach(_ := False)
+ }
+
+// val pcValids = for(stage <- stagesFromExecute) yield RegInit(False) clearWhen(!started) setWhen(!stage.arbitration.isValid)
+ val done = CombInit(pcValids.last)
+ if(exceptionPortCtrl != null) done.clearWhen(exceptionPortCtrl.exceptionValidsRegs.tail.orR)
+ }
+
+ //Interrupt/Exception entry logic
+ val interruptJump = Bool.addTag(Verilator.public)
+ interruptJump := interrupt.valid && pipelineLiberator.done && allowInterrupts
+ if(pipelinedInterrupt) interrupt.valid clearWhen(interruptJump) //avoid double fireing
+
+ val hadException = RegNext(exception) init(False) addTag(Verilator.public)
+ pipelineLiberator.done.clearWhen(hadException)
+
+
+ val targetPrivilege = CombInit(interrupt.targetPrivilege)
+ if(exceptionPortCtrl != null) when(hadException) {
+ targetPrivilege := exceptionPortCtrl.exceptionTargetPrivilege
+ }
+
+ val trapCause = CombInit(interrupt.code.resize(trapCodeWidth))
+ if(exceptionPortCtrl != null) when( hadException){
+ trapCause := exceptionPortCtrl.exceptionContext.code.resized
+ }
+
+ val xtvec = Xtvec().assignDontCare()
+ switch(targetPrivilege){
+ if(supervisorGen) is(1) { xtvec := supervisorCsr.stvec }
+ is(3){ xtvec := machineCsr.mtvec }
+ }
+
+ when(hadException || interruptJump){
+ fetcher.haltIt() //Avoid having the fetch confused by the incomming privilege switch
+
+ jumpInterface.valid := True
+ jumpInterface.payload := (if(!xtvecModeGen) xtvec.base @@ U"00" else (xtvec.mode === 0 || hadException) ? (xtvec.base @@ U"00") | ((xtvec.base + trapCause) @@ U"00") )
+ lastStage.arbitration.flushNext := True
+
+ if(privilegeGen) privilegeReg := targetPrivilege
+
+ switch(targetPrivilege){
+ if(supervisorGen) is(1) {
+ sstatus.SIE := False
+ sstatus.SPIE := sstatus.SIE
+ sstatus.SPP := privilege(0 downto 0)
+ scause.interrupt := !hadException
+ scause.exceptionCode := trapCause
+ sepc := mepcCaptureStage.input(PC)
+ if (exceptionPortCtrl != null) when(hadException){
+ stval := exceptionPortCtrl.exceptionContext.badAddr
+ }
+ }
+
+ is(3){
+ mstatus.MIE := False
+ mstatus.MPIE := mstatus.MIE
+ mstatus.MPP := privilege
+ mcause.interrupt := !hadException
+ mcause.exceptionCode := trapCause
+ mepc := mepcCaptureStage.input(PC)
+ if(exceptionPortCtrl != null) when(hadException){
+ mtval := exceptionPortCtrl.exceptionContext.badAddr
+ }
+ }
+ }
+ }
+
+ if(exceptionPortCtrl == null){
+ if(mbadaddrAccess == CsrAccess.READ_ONLY) mtval := 0
+ if(sbadaddrAccess == CsrAccess.READ_ONLY) stval := 0
+ }
+
+ lastStage plug new Area{
+ import lastStage._
+
+ //Manage MRET / SRET instructions
+ when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.XRET) {
+ fetcher.haltIt()
+ jumpInterface.valid := True
+ lastStage.arbitration.flushNext := True
+ switch(input(INSTRUCTION)(29 downto 28)){
+ is(3){
+ mstatus.MPP := U"00"
+ mstatus.MIE := mstatus.MPIE
+ mstatus.MPIE := True
+ jumpInterface.payload := mepc
+ if(privilegeGen) privilegeReg := mstatus.MPP
+ }
+ if(supervisorGen) is(1){
+ sstatus.SPP := U"0"
+ sstatus.SIE := sstatus.SPIE
+ sstatus.SPIE := True
+ jumpInterface.payload := sepc
+ if(privilegeGen) privilegeReg := U"0" @@ sstatus.SPP
+ }
+ }
+ }
+ }
+
+
+ contextSwitching := jumpInterface.valid
+
+ //CSR read/write instructions management
+ decode plug new Area{
+ import decode._
+
+ val imm = IMM(input(INSTRUCTION))
+ insert(CSR_WRITE_OPCODE) := ! (
+ (input(INSTRUCTION)(14 downto 13) === B"01" && input(INSTRUCTION)(rs1Range) === 0)
+ || (input(INSTRUCTION)(14 downto 13) === B"11" && imm.z === 0)
+ )
+ insert(CSR_READ_OPCODE) := input(INSTRUCTION)(13 downto 7) =/= B"0100000"
+ }
+
+
+ execute plug new Area{
+ import execute._
+ //Manage WFI instructions
+ if(wfiOutput) out(inWfi)
+ val wfiWake = RegNext(interruptSpecs.map(_.cond).orR || thirdPartyWake) init(False)
+ if(wfiGenAsWait) when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.WFI){
+ inWfi := True
+ when(!wfiWake){
+ arbitration.haltItself := True
+ }
+ }
+ }
+
+ decode.arbitration.haltByOther setWhen(stagesFromExecute.map(s => s.arbitration.isValid && s.input(ENV_CTRL) === EnvCtrlEnum.XRET).asBits.orR)
+
+ execute plug new Area {
+ import execute._
+ def previousStage = decode
+ val blockedBySideEffects = stagesFromExecute.tail.map(s => s.arbitration.isValid).asBits().orR || pipeline.service(classOf[HazardService]).hazardOnExecuteRS// && s.input(HAS_SIDE_EFFECT) to improve be less pessimistic
+
+ val illegalAccess = True
+ val illegalInstruction = False
+ if(selfException != null) {
+ selfException.valid := False
+ selfException.code.assignDontCare()
+ selfException.badAddr := input(INSTRUCTION).asUInt
+ if(catchIllegalAccess) when(illegalAccess || illegalInstruction){
+ selfException.valid := True
+ selfException.code := 2
+ }
+ }
+
+ //Manage MRET / SRET instructions
+ when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.XRET) {
+ when(input(INSTRUCTION)(29 downto 28).asUInt > privilege) {
+ illegalInstruction := True
+ }
+ }
+
+
+ //Manage ECALL instructions
+ if(ecallGen) when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.ECALL){
+ selfException.valid := True
+ switch(privilege) {
+ is(0) { selfException.code := 8 }
+ if(supervisorGen) is(1) { selfException.code := 9 }
+ default { selfException.code := 11 }
+ }
+ }
+
+
+ if(ebreakGen) when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.EBREAK && allowEbreakException){
+ selfException.valid := True
+ selfException.code := 3
+ }
+
+
+ val imm = IMM(input(INSTRUCTION))
+ def writeSrc = input(SRC1)
+ def readData = csrMapping.readDataSignal
+ def writeData = csrMapping.writeDataSignal
+ val writeInstruction = arbitration.isValid && input(IS_CSR) && input(CSR_WRITE_OPCODE)
+ val readInstruction = arbitration.isValid && input(IS_CSR) && input(CSR_READ_OPCODE)
+ val writeEnable = writeInstruction && !arbitration.isStuck
+ val readEnable = readInstruction && !arbitration.isStuck
+ csrMapping.hazardFree := !blockedBySideEffects
+
+ val readToWriteData = CombInit(readData)
+ writeData := (if(noCsrAlu) writeSrc else input(INSTRUCTION)(13).mux(
+ False -> writeSrc,
+ True -> Mux(input(INSTRUCTION)(12), readToWriteData & ~writeSrc, readToWriteData | writeSrc)
+ ))
+
+ when(arbitration.isValid && input(IS_CSR)) {
+ if(!pipelineCsrRead) output(REGFILE_WRITE_DATA) := readData
+ }
+
+ when(arbitration.isValid && (input(IS_CSR) || (if(supervisorGen) input(IS_SFENCE_VMA) else False))) {
+ arbitration.haltItself setWhen(blockedBySideEffects)
+ }
+
+ if(pipelineCsrRead){
+ insert(PIPELINED_CSR_READ) := readData
+ when(memory.arbitration.isValid && memory.input(IS_CSR)) {
+ memory.output(REGFILE_WRITE_DATA) := memory.input(PIPELINED_CSR_READ)
+ }
+ }
+//
+// Component.current.rework{
+// when(arbitration.isFiring && input(IS_CSR)) {
+// memory.input(REGFILE_WRITE_DATA).getDrivingReg := readData
+// }
+// }
+
+ //Translation of the csrMapping into real logic
+ val csrAddress = input(INSTRUCTION)(csrRange)
+ Component.current.afterElaboration{
+ def doJobs(jobs : ArrayBuffer[Any]): Unit ={
+ val withWrite = jobs.exists(j => j.isInstanceOf[CsrWrite] || j.isInstanceOf[CsrOnWrite] || j.isInstanceOf[CsrDuringWrite])
+ val withRead = jobs.exists(j => j.isInstanceOf[CsrRead] || j.isInstanceOf[CsrOnRead])
+ if(withRead && withWrite) {
+ illegalAccess := False
+ } else {
+ if (withWrite) illegalAccess.clearWhen(input(CSR_WRITE_OPCODE))
+ if (withRead) illegalAccess.clearWhen(input(CSR_READ_OPCODE))
+ }
+
+
+ for (element <- jobs) element match {
+ case element : CsrDuringWrite => when(writeInstruction){element.doThat()}
+ case element : CsrDuringRead => when(readInstruction){element.doThat()}
+ case element : CsrDuring => {element.doThat()}
+ case _ =>
+ }
+ when(writeEnable) {
+ for (element <- jobs) element match {
+ case element: CsrWrite => element.that.assignFromBits(writeData(element.bitOffset, element.that.getBitsWidth bits))
+ case element: CsrOnWrite => element.doThat()
+ case _ =>
+ }
+ }
+
+ when(readEnable) {
+ for (element <- jobs) element match {
+ case element: CsrOnRead =>
+ element.doThat()
+ case _ =>
+ }
+ }
+ }
+
+ def doJobsOverride(jobs : ArrayBuffer[Any]): Unit ={
+ for (element <- jobs) element match {
+ case element: CsrReadToWriteOverride if element.that.getBitsWidth != 0 => readToWriteData(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
+ case _ =>
+ }
+ }
+
+ csrOhDecoder match {
+ case false => {
+ csrMapping.readDataInit := 0
+ switch(csrAddress) {
+ for ((address, jobs) <- csrMapping.mapping) {
+ is(address) {
+ doJobs(jobs)
+ for (element <- jobs) element match {
+ case element: CsrRead if element.that.getBitsWidth != 0 => csrMapping.readDataInit (element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
+ case _ =>
+ }
+ }
+ }
+ }
+ switch(csrAddress) {
+ for ((address, jobs) <- csrMapping.mapping if jobs.exists(_.isInstanceOf[CsrReadToWriteOverride])) {
+ is(address) {
+ doJobsOverride(jobs)
+ }
+ }
+ }
+ }
+ case true => {
+ val oh = csrMapping.mapping.keys.toList.distinct.map(address => address -> RegNextWhen(decode.input(INSTRUCTION)(csrRange) === address, !execute.arbitration.isStuck).setCompositeName(this, "csr_" + address)).toMap
+ val readDatas = ArrayBuffer[Bits]()
+ for ((address, jobs) <- csrMapping.mapping) {
+ when(oh(address)){
+ doJobs(jobs)
+ }
+ if(jobs.exists(_.isInstanceOf[CsrRead])) {
+ val masked = B(0, 32 bits)
+ when(oh(address)) (for (element <- jobs) element match {
+ case element: CsrRead if element.that.getBitsWidth != 0 => masked(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
+ case _ =>
+ })
+ readDatas += masked
+ }
+ }
+ csrMapping.readDataInit := readDatas.reduceBalancedTree(_ | _)
+ for ((address, jobs) <- csrMapping.mapping) {
+ when(oh(address)){
+ doJobsOverride(jobs)
+ }
+ }
+ }
+ }
+
+ csrMapping.always.foreach {
+ case element : CsrDuringWrite => when(writeInstruction){element.doThat()}
+ case element : CsrDuringRead => when(readInstruction){element.doThat()}
+ case element : CsrOnWrite => when(writeEnable){element.doThat()}
+ case element : CsrOnRead => when(readEnable){element.doThat()}
+ }
+
+ illegalAccess clearWhen(csrMapping.allowCsrSignal)
+
+ when(privilege < csrAddress(9 downto 8).asUInt){
+ illegalAccess := True
+ readInstruction := False
+ writeInstruction := False
+ }
+ illegalAccess clearWhen(!arbitration.isValid || !input(IS_CSR))
+ }
+ }
+ }
+ }
+}
+
+
+class UserInterruptPlugin(interruptName : String, code : Int, privilege : Int = 3) extends Plugin[VexRiscv]{
+ var interrupt, interruptEnable : Bool = null
+ override def setup(pipeline: VexRiscv): Unit = {
+ val csr = pipeline.service(classOf[CsrPlugin])
+ interrupt = in.Bool().setName(interruptName)
+ val interruptPending = RegNext(interrupt) init(False)
+ val interruptEnable = RegInit(False).setName(interruptName + "_enable")
+ csr.addInterrupt(interruptPending && interruptEnable, code, privilege, Nil)
+ csr.r(csrAddress = CSR.MIP, bitOffset = code,interruptPending)
+ csr.rw(csrAddress = CSR.MIE, bitOffset = code, interruptEnable)
+ }
+ override def build(pipeline: VexRiscv): Unit = {}
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala
new file mode 100644
index 0000000..80d4409
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala
@@ -0,0 +1,554 @@
+package vexriscv.plugin
+
+import vexriscv.ip._
+import vexriscv._
+import spinal.core._
+import spinal.lib._
+import spinal.lib.bus.amba4.axi.Axi4
+
+import scala.collection.mutable.ArrayBuffer
+
+
+class DAxiCachedPlugin(config : DataCacheConfig, memoryTranslatorPortConfig : Any = null) extends DBusCachedPlugin(config, memoryTranslatorPortConfig) {
+ var dAxi : Axi4 = null
+
+ override def build(pipeline: VexRiscv): Unit = {
+ super.build(pipeline)
+ dBus.setAsDirectionLess()
+ dAxi = master(dBus.toAxi4Shared().toAxi4()).setName("dAxi")
+ dBus = null //For safety, as nobody should use it anymore :)
+ }
+}
+
+trait DBusEncodingService {
+ def addLoadWordEncoding(key: MaskedLiteral): Unit
+ def addStoreWordEncoding(key: MaskedLiteral): Unit
+ def bypassStore(data : Bits) : Unit
+ def loadData() : Bits
+}
+
+class DBusCachedPlugin(val config : DataCacheConfig,
+ memoryTranslatorPortConfig : Any = null,
+ dBusCmdMasterPipe : Boolean = false,
+ dBusCmdSlavePipe : Boolean = false,
+ dBusRspSlavePipe : Boolean = false,
+ relaxedMemoryTranslationRegister : Boolean = false,
+ csrInfo : Boolean = false) extends Plugin[VexRiscv] with DBusAccessService with DBusEncodingService with VexRiscvRegressionArg {
+ import config._
+ assert(!(config.withExternalAmo && !dBusRspSlavePipe))
+ assert(isPow2(cacheSize))
+ assert(!(memoryTranslatorPortConfig != null && config.cacheSize/config.wayCount > 4096), "When the D$ is used with MMU, each way can't be bigger than a page (4096 bytes)")
+
+ var dBus : DataCacheMemBus = null
+ var mmuBus : MemoryTranslatorBus = null
+ var exceptionBus : Flow[ExceptionCause] = null
+ var privilegeService : PrivilegeService = null
+ var redoBranch : Flow[UInt] = null
+
+ @dontName var dBusAccess : DBusAccess = null
+ override def newDBusAccess(): DBusAccess = {
+ assert(dBusAccess == null)
+ dBusAccess = DBusAccess()
+ dBusAccess
+ }
+
+ override def getVexRiscvRegressionArgs(): Seq[String] = {
+ var args = List[String]()
+ args :+= "DBUS=CACHED"
+ args :+= s"DBUS_LOAD_DATA_WIDTH=$memDataWidth"
+ args :+= s"DBUS_STORE_DATA_WIDTH=$cpuDataWidth"
+ if(withLrSc) args :+= "LRSC=yes"
+ if(withAmo) args :+= "AMO=yes"
+ if(config.withExclusive && config.withInvalidate) args ++= List("DBUS_EXCLUSIVE=yes", "DBUS_INVALIDATE=yes")
+ args
+ }
+
+
+ override def addLoadWordEncoding(key : MaskedLiteral): Unit = {
+ val decoderService = pipeline.service(classOf[DecoderService])
+ val cfg = pipeline.config
+ import cfg._
+
+ decoderService.add(
+ key,
+ List(
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC_USE_SUB_LESS -> False,
+ MEMORY_ENABLE -> True,
+ RS1_USE -> True,
+ IntAluPlugin.ALU_CTRL -> IntAluPlugin.AluCtrlEnum.ADD_SUB,
+ SRC2_CTRL -> Src2CtrlEnum.IMI,
+ // REGFILE_WRITE_VALID -> True,
+ // BYPASSABLE_EXECUTE_STAGE -> False,
+ // BYPASSABLE_MEMORY_STAGE -> False,
+ MEMORY_WR -> False,
+ HAS_SIDE_EFFECT -> True
+ )
+ )
+
+ if(withLrSc) decoderService.add(key, Seq(MEMORY_LRSC -> False))
+ if(withAmo) decoderService.add(key, Seq(MEMORY_AMO -> False))
+ }
+ override def addStoreWordEncoding(key : MaskedLiteral): Unit = {
+ val decoderService = pipeline.service(classOf[DecoderService])
+ val cfg = pipeline.config
+ import cfg._
+
+ decoderService.add(
+ key,
+ List(
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC_USE_SUB_LESS -> False,
+ MEMORY_ENABLE -> True,
+ RS1_USE -> True,
+ IntAluPlugin.ALU_CTRL -> IntAluPlugin.AluCtrlEnum.ADD_SUB,
+ SRC2_CTRL -> Src2CtrlEnum.IMS,
+// RS2_USE -> True,
+ MEMORY_WR -> True,
+ HAS_SIDE_EFFECT -> True
+ )
+ )
+
+ if(withLrSc) decoderService.add(key, Seq(MEMORY_LRSC -> False))
+ if(withAmo) decoderService.add(key, Seq(MEMORY_AMO -> False))
+ }
+
+ val bypassStoreList = ArrayBuffer[(Bool, Bits)]()
+
+ override def bypassStore(data: Bits): Unit = {
+ val prefix = s"DBusBypass${bypassStoreList.size}"
+ bypassStoreList += ConditionalContext.isTrue().setName(prefix + "_cond") -> CombInit(data).setName(prefix + "_value")
+ assert(config.cpuDataWidth >= data.getWidth, "Data cache word width is too small for that")
+ }
+
+
+ override def loadData(): Bits = pipeline.stages.last.output(MEMORY_LOAD_DATA)
+
+ object MEMORY_ENABLE extends Stageable(Bool)
+ object MEMORY_MANAGMENT extends Stageable(Bool)
+ object MEMORY_WR extends Stageable(Bool)
+ object MEMORY_LRSC extends Stageable(Bool)
+ object MEMORY_AMO extends Stageable(Bool)
+ object MEMORY_FENCE extends Stageable(Bool)
+ object MEMORY_FORCE_CONSTISTENCY extends Stageable(Bool)
+ object IS_DBUS_SHARING extends Stageable(Bool())
+ object MEMORY_VIRTUAL_ADDRESS extends Stageable(UInt(32 bits))
+ object MEMORY_STORE_DATA_RF extends Stageable(Bits(32 bits))
+// object MEMORY_STORE_DATA_CPU extends Stageable(Bits(config.cpuDataWidth bits))
+ object MEMORY_LOAD_DATA extends Stageable(Bits(config.cpuDataWidth bits))
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+
+ dBus = master(DataCacheMemBus(this.config)).setName("dBus")
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+
+ val stdActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC_USE_SUB_LESS -> False,
+ MEMORY_ENABLE -> True,
+ RS1_USE -> True,
+ IntAluPlugin.ALU_CTRL -> IntAluPlugin.AluCtrlEnum.ADD_SUB
+ )
+
+ val loadActions = stdActions ++ List(
+ SRC2_CTRL -> Src2CtrlEnum.IMI,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> False,
+ MEMORY_WR -> False,
+ HAS_SIDE_EFFECT -> True
+ )
+
+ val storeActions = stdActions ++ List(
+ SRC2_CTRL -> Src2CtrlEnum.IMS,
+ RS2_USE -> True,
+ MEMORY_WR -> True,
+ HAS_SIDE_EFFECT -> True
+ )
+
+ decoderService.addDefault(MEMORY_ENABLE, False)
+ decoderService.add(
+ List(LB, LH, LW, LBU, LHU, LWU).map(_ -> loadActions) ++
+ List(SB, SH, SW).map(_ -> storeActions)
+ )
+
+ if(withLrSc){
+ List(LB, LH, LW, LBU, LHU, LWU, SB, SH, SW).foreach(e =>
+ decoderService.add(e, Seq(MEMORY_LRSC -> False))
+ )
+ decoderService.add(
+ key = LR,
+ values = loadActions.filter(_._1 != SRC2_CTRL) ++ Seq(
+ SRC_ADD_ZERO -> True,
+ MEMORY_LRSC -> True
+ )
+ )
+ decoderService.add(
+ key = SC,
+ values = storeActions.filter(_._1 != SRC2_CTRL) ++ Seq(
+ SRC_ADD_ZERO -> True,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> False,
+ MEMORY_LRSC -> True
+ )
+ )
+ }
+
+ if(withAmo){
+ List(LB, LH, LW, LBU, LHU, LWU, SB, SH, SW).foreach(e =>
+ decoderService.add(e, Seq(MEMORY_AMO -> False))
+ )
+ val amoActions = storeActions.filter(_._1 != SRC2_CTRL) ++ Seq(
+ SRC_ADD_ZERO -> True,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> False,
+ MEMORY_AMO -> True
+ )
+
+ for(i <- List(AMOSWAP, AMOADD, AMOXOR, AMOAND, AMOOR, AMOMIN, AMOMAX, AMOMINU, AMOMAXU)){
+ decoderService.add(i, amoActions)
+ }
+ }
+
+ if(withAmo && withLrSc){
+ for(i <- List(AMOSWAP, AMOADD, AMOXOR, AMOAND, AMOOR, AMOMIN, AMOMAX, AMOMINU, AMOMAXU)){
+ decoderService.add(i, List(MEMORY_LRSC -> False))
+ }
+ for(i <- List(LR, SC)){
+ decoderService.add(i, List(MEMORY_AMO -> False))
+ }
+ }
+
+ def MANAGEMENT = M"-------00000-----101-----0001111"
+
+ decoderService.addDefault(MEMORY_MANAGMENT, False)
+ decoderService.add(MANAGEMENT, List(
+ MEMORY_MANAGMENT -> True,
+ RS1_USE -> True
+ ))
+
+ withWriteResponse match {
+ case false => decoderService.add(FENCE, Nil)
+ case true => {
+ decoderService.addDefault(MEMORY_FENCE, False)
+ decoderService.add(FENCE, List(MEMORY_FENCE -> True))
+ }
+ }
+
+ mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(MemoryTranslatorPort.PRIORITY_DATA ,memoryTranslatorPortConfig)
+ redoBranch = pipeline.service(classOf[JumpService]).createJumpInterface(if(pipeline.writeBack != null) pipeline.writeBack else pipeline.memory)
+
+ if(catchSomething)
+ exceptionBus = pipeline.service(classOf[ExceptionService]).newExceptionPort(if(pipeline.writeBack == null) pipeline.memory else pipeline.writeBack)
+
+ if(pipeline.serviceExist(classOf[PrivilegeService]))
+ privilegeService = pipeline.service(classOf[PrivilegeService])
+
+ pipeline.update(DEBUG_BYPASS_CACHE, False)
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ val twoStageMmu = mmuBus.p.latency match {
+ case 0 => false
+ case 1 => true
+ }
+
+ val cache = new DataCache(
+ this.config.copy(
+ mergeExecuteMemory = writeBack == null,
+ rfDataWidth = 32
+ ),
+ mmuParameter = mmuBus.p
+ )
+
+ //Interconnect the plugin dBus with the cache dBus with some optional pipelining
+ def optionPipe[T](cond : Boolean, on : T)(f : T => T) : T = if(cond) f(on) else on
+ def cmdBuf = optionPipe(dBusCmdSlavePipe, cache.io.mem.cmd)(_.s2mPipe())
+ dBus.cmd << optionPipe(dBusCmdMasterPipe, cmdBuf)(_.m2sPipe())
+ cache.io.mem.rsp << (dBusRspSlavePipe match {
+ case false => dBus.rsp
+ case true if !withExternalAmo => dBus.rsp.m2sPipe()
+ case true if withExternalAmo => {
+ val rsp = Flow (DataCacheMemRsp(cache.p))
+ rsp.valid := RegNext(dBus.rsp.valid) init(False)
+ rsp.exclusive := RegNext(dBus.rsp.exclusive)
+ rsp.error := RegNext(dBus.rsp.error)
+ rsp.last := RegNext(dBus.rsp.last)
+ rsp.aggregated := RegNext(dBus.rsp.aggregated)
+ rsp.data := RegNextWhen(dBus.rsp.data, dBus.rsp.valid && !cache.io.cpu.writeBack.keepMemRspData)
+ rsp
+ }
+ })
+
+ if(withInvalidate) {
+ cache.io.mem.inv << dBus.inv
+ cache.io.mem.ack >> dBus.ack
+ cache.io.mem.sync << dBus.sync
+ }
+
+ pipeline plug new Area{
+ //Memory bandwidth counter
+ val rspCounter = Reg(UInt(32 bits)) init(0)
+ when(dBus.rsp.valid){
+ rspCounter := rspCounter + 1
+ }
+ }
+
+ decode plug new Area {
+ import decode._
+
+ when(mmuBus.busy && arbitration.isValid && input(MEMORY_ENABLE)) {
+ arbitration.haltItself := True
+ }
+
+
+ //Manage write to read hit ordering (ensure invalidation timings)
+ val fence = new Area {
+ insert(MEMORY_FORCE_CONSTISTENCY) := False
+ when(input(INSTRUCTION)(25)) { //RL
+ if (withLrSc) insert(MEMORY_FORCE_CONSTISTENCY) setWhen (input(MEMORY_LRSC))
+ if (withAmo) insert(MEMORY_FORCE_CONSTISTENCY) setWhen (input(MEMORY_AMO))
+ }
+ }
+ }
+
+ execute plug new Area {
+ import execute._
+
+ val size = input(INSTRUCTION)(13 downto 12).asUInt
+ cache.io.cpu.execute.isValid := arbitration.isValid && input(MEMORY_ENABLE)
+ cache.io.cpu.execute.address := input(SRC_ADD).asUInt
+ cache.io.cpu.execute.args.wr := input(MEMORY_WR)
+ insert(MEMORY_STORE_DATA_RF) := size.mux(
+ U(0) -> input(RS2)( 7 downto 0) ## input(RS2)( 7 downto 0) ## input(RS2)(7 downto 0) ## input(RS2)(7 downto 0),
+ U(1) -> input(RS2)(15 downto 0) ## input(RS2)(15 downto 0),
+ default -> input(RS2)(31 downto 0)
+ )
+ cache.io.cpu.execute.args.size := size.resized
+
+ if(twoStageMmu) {
+ mmuBus.cmd(0).isValid := cache.io.cpu.execute.isValid
+ mmuBus.cmd(0).isStuck := arbitration.isStuck
+ mmuBus.cmd(0).virtualAddress := input(SRC_ADD).asUInt
+ mmuBus.cmd(0).bypassTranslation := False
+// KeepAttribute(mmuBus.cmd(0))
+// KeepAttribute(mmuBus.cmd(1))
+ }
+
+ cache.io.cpu.flush.valid := arbitration.isValid && input(MEMORY_MANAGMENT)
+ cache.io.cpu.flush.singleLine := input(INSTRUCTION)(Riscv.rs1Range) =/= 0
+ cache.io.cpu.flush.lineId := U(input(RS1) >> log2Up(bytePerLine)).resized
+ cache.io.cpu.execute.args.totalyConsistent := input(MEMORY_FORCE_CONSTISTENCY)
+ arbitration.haltItself setWhen(cache.io.cpu.flush.isStall || cache.io.cpu.execute.haltIt)
+
+ if(withLrSc) {
+ cache.io.cpu.execute.args.isLrsc := False
+ when(input(MEMORY_LRSC)){
+ cache.io.cpu.execute.args.isLrsc := True
+ }
+ }
+
+ if(withAmo){
+ cache.io.cpu.execute.isAmo := input(MEMORY_AMO)
+ cache.io.cpu.execute.amoCtrl.alu := input(INSTRUCTION)(31 downto 29)
+ cache.io.cpu.execute.amoCtrl.swap := input(INSTRUCTION)(27)
+ }
+
+
+ when(cache.io.cpu.execute.refilling && arbitration.isValid){
+ arbitration.haltByOther := True
+ }
+
+ if(relaxedMemoryTranslationRegister) {
+ insert(MEMORY_VIRTUAL_ADDRESS) := cache.io.cpu.execute.address
+ memory.input(MEMORY_VIRTUAL_ADDRESS)
+ if(writeBack != null) addPrePopTask( () =>
+ KeepAttribute(memory.input(MEMORY_VIRTUAL_ADDRESS).getDrivingReg)
+ )
+ }
+ }
+
+ val mmuAndBufferStage = if(writeBack != null) memory else execute
+ mmuAndBufferStage plug new Area {
+ import mmuAndBufferStage._
+
+ cache.io.cpu.memory.isValid := arbitration.isValid && input(MEMORY_ENABLE)
+ cache.io.cpu.memory.isStuck := arbitration.isStuck
+ cache.io.cpu.memory.address := (if(relaxedMemoryTranslationRegister) input(MEMORY_VIRTUAL_ADDRESS) else if(mmuAndBufferStage == execute) cache.io.cpu.execute.address else U(input(REGFILE_WRITE_DATA)))
+
+ mmuBus.cmd.last.isValid := cache.io.cpu.memory.isValid
+ mmuBus.cmd.last.isStuck := cache.io.cpu.memory.isStuck
+ mmuBus.cmd.last.virtualAddress := cache.io.cpu.memory.address
+ mmuBus.cmd.last.bypassTranslation := False
+ mmuBus.end := !arbitration.isStuck || arbitration.removeIt
+ cache.io.cpu.memory.mmuRsp := mmuBus.rsp
+ cache.io.cpu.memory.mmuRsp.isIoAccess setWhen(pipeline(DEBUG_BYPASS_CACHE) && !cache.io.cpu.memory.isWrite)
+ }
+
+ val managementStage = stages.last
+ val mgs = managementStage plug new Area{
+ import managementStage._
+ cache.io.cpu.writeBack.isValid := arbitration.isValid && input(MEMORY_ENABLE)
+ cache.io.cpu.writeBack.isStuck := arbitration.isStuck
+ cache.io.cpu.writeBack.isFiring := arbitration.isFiring
+ cache.io.cpu.writeBack.isUser := (if(privilegeService != null) privilegeService.isUser() else False)
+ cache.io.cpu.writeBack.address := U(input(REGFILE_WRITE_DATA))
+ cache.io.cpu.writeBack.storeData.subdivideIn(32 bits).foreach(_ := input(MEMORY_STORE_DATA_RF))
+ afterElaboration(for((cond, value) <- bypassStoreList) when(cond){
+ cache.io.cpu.writeBack.storeData.subdivideIn(widthOf(value) bits).foreach(_ := value) //Not optimal, but ok
+ })
+
+ val fence = if(withInvalidate) new Area {
+ cache.io.cpu.writeBack.fence := input(INSTRUCTION)(31 downto 20).as(FenceFlags())
+ val aquire = False
+ if(withWriteResponse) when(input(MEMORY_ENABLE) && input(INSTRUCTION)(26)) { //AQ
+ if(withLrSc) when(input(MEMORY_LRSC)){
+ aquire := True
+ }
+ if(withAmo) when(input(MEMORY_AMO)){
+ aquire := True
+ }
+ }
+
+ when(aquire){
+ cache.io.cpu.writeBack.fence.forceAll()
+ }
+
+ when(!input(MEMORY_FENCE) || !arbitration.isFiring){
+ cache.io.cpu.writeBack.fence.clearAll()
+ }
+
+ when(arbitration.isValid && (input(MEMORY_FENCE) || aquire)){
+ mmuAndBufferStage.arbitration.haltByOther := True //Ensure that the fence affect the memory stage instruction by stoping it
+ }
+ }
+
+ redoBranch.valid := False
+ redoBranch.payload := input(PC)
+ arbitration.flushIt setWhen(redoBranch.valid)
+ arbitration.flushNext setWhen(redoBranch.valid)
+
+ if(catchSomething) {
+ exceptionBus.valid := False //cache.io.cpu.writeBack.mmuMiss || cache.io.cpu.writeBack.accessError || cache.io.cpu.writeBack.illegalAccess || cache.io.cpu.writeBack.unalignedAccess
+ exceptionBus.badAddr := U(input(REGFILE_WRITE_DATA))
+ exceptionBus.code.assignDontCare()
+ }
+
+
+ when(arbitration.isValid && input(MEMORY_ENABLE)) {
+ if (catchAccessError) when(cache.io.cpu.writeBack.accessError) {
+ exceptionBus.valid := True
+ exceptionBus.code := (input(MEMORY_WR) ? U(7) | U(5)).resized
+ }
+ if(catchIllegal) when (cache.io.cpu.writeBack.mmuException) {
+ exceptionBus.valid := True
+ exceptionBus.code := (input(MEMORY_WR) ? U(15) | U(13)).resized
+ }
+ if (catchUnaligned) when(cache.io.cpu.writeBack.unalignedAccess) {
+ exceptionBus.valid := True
+ exceptionBus.code := (input(MEMORY_WR) ? U(6) | U(4)).resized
+ }
+
+ when(cache.io.cpu.redo) {
+ redoBranch.valid := True
+ if(catchSomething) exceptionBus.valid := False
+ }
+ }
+
+ arbitration.haltItself.setWhen(cache.io.cpu.writeBack.isValid && cache.io.cpu.writeBack.haltIt)
+
+ val rspSplits = cache.io.cpu.writeBack.data.subdivideIn(8 bits)
+ val rspShifted = Bits(cpuDataWidth bits)
+ //Generate minimal mux to move from a wide aligned memory read to the register file shifter representation
+ for(i <- 0 until cpuDataWidth/8){
+ val srcSize = 1 << (log2Up(cpuDataBytes) - log2Up(i+1))
+ val srcZipped = rspSplits.zipWithIndex.filter{case (v, b) => b % (cpuDataBytes/srcSize) == i}
+ val src = srcZipped.map(_._1)
+ val range = cache.cpuWordToRfWordRange.high downto cache.cpuWordToRfWordRange.high+1-log2Up(srcSize)
+ val sel = cache.io.cpu.writeBack.address(range)
+// println(s"$i $srcSize $range ${srcZipped.map(_._2).mkString(",")}")
+ rspShifted(i*8, 8 bits) := src.read(sel)
+ }
+
+ val rspRf = CombInit(rspShifted(31 downto 0))
+ if(withLrSc) when(input(MEMORY_LRSC) && input(MEMORY_WR)){
+ rspRf := B(!cache.io.cpu.writeBack.exclusiveOk).resized
+ }
+
+ val rspFormated = input(INSTRUCTION)(13 downto 12).mux(
+ 0 -> B((31 downto 8) -> (rspRf(7) && !input(INSTRUCTION)(14)),(7 downto 0) -> rspRf(7 downto 0)),
+ 1 -> B((31 downto 16) -> (rspRf(15) && ! input(INSTRUCTION)(14)),(15 downto 0) -> rspRf(15 downto 0)),
+ default -> rspRf //W
+ )
+
+ when(arbitration.isValid && input(MEMORY_ENABLE)) {
+ output(REGFILE_WRITE_DATA) := rspFormated
+ }
+
+ insert(MEMORY_LOAD_DATA) := rspShifted
+ }
+
+ //Share access to the dBus (used by self refilled MMU)
+ if(dBusAccess != null) pipeline plug new Area{
+ dBusAccess.cmd.ready := False
+ val forceDatapath = False
+ when(dBusAccess.cmd.valid){
+ decode.arbitration.haltByOther := True
+ val exceptionService = pipeline.service(classOf[ExceptionService])
+ when(!stagesFromExecute.map(s => s.arbitration.isValid || exceptionService.isExceptionPending(s)).orR){
+ when(!cache.io.cpu.execute.refilling) {
+ cache.io.cpu.execute.isValid := True
+ dBusAccess.cmd.ready := !execute.arbitration.isStuck
+ }
+ cache.io.cpu.execute.args.wr := False //dBusAccess.cmd.write
+// execute.insert(MEMORY_STORE_DATA_RF) := dBusAccess.cmd.data //Not implemented
+ cache.io.cpu.execute.args.size := dBusAccess.cmd.size.resized
+ if(withLrSc) execute.input(MEMORY_LRSC) := False
+ if(withAmo) execute.input(MEMORY_AMO) := False
+ cache.io.cpu.execute.address := dBusAccess.cmd.address //Will only be 12 muxes
+ forceDatapath := True
+ }
+ }
+ execute.insert(IS_DBUS_SHARING) := dBusAccess.cmd.fire
+ mmuBus.cmd.last.bypassTranslation setWhen(mmuAndBufferStage.input(IS_DBUS_SHARING))
+ if(twoStageMmu) mmuBus.cmd(0).bypassTranslation setWhen(execute.input(IS_DBUS_SHARING))
+
+ if(mmuAndBufferStage != execute) (cache.io.cpu.memory.isValid setWhen(mmuAndBufferStage.input(IS_DBUS_SHARING)))
+ cache.io.cpu.writeBack.isValid setWhen(managementStage.input(IS_DBUS_SHARING))
+ dBusAccess.rsp.valid := managementStage.input(IS_DBUS_SHARING) && !cache.io.cpu.writeBack.isWrite && (cache.io.cpu.redo || !cache.io.cpu.writeBack.haltIt)
+ dBusAccess.rsp.data := mgs.rspRf
+ dBusAccess.rsp.error := cache.io.cpu.writeBack.unalignedAccess || cache.io.cpu.writeBack.accessError
+ dBusAccess.rsp.redo := cache.io.cpu.redo
+ component.addPrePopTask{() =>
+ managementStage.input(IS_DBUS_SHARING).getDrivingReg clearWhen(dBusAccess.rsp.fire)
+ when(forceDatapath){
+ execute.output(REGFILE_WRITE_DATA) := dBusAccess.cmd.address.asBits
+ }
+ if(mmuAndBufferStage != execute) mmuAndBufferStage.input(IS_DBUS_SHARING) init(False)
+ managementStage.input(IS_DBUS_SHARING) init(False)
+ when(dBusAccess.rsp.valid){
+ managementStage.input(IS_DBUS_SHARING).getDrivingReg := False
+ }
+ }
+ }
+
+ when(stages.last.arbitration.haltByOther){
+ cache.io.cpu.writeBack.isValid := False
+ }
+
+ if(csrInfo){
+ val csr = service(classOf[CsrPlugin])
+ csr.r(0xCC0, 0 -> U(cacheSize/wayCount), 20 -> U(bytePerLine))
+ }
+ }
+}
+
+
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala
new file mode 100644
index 0000000..372cfcc
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala
@@ -0,0 +1,614 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+import spinal.lib._
+import spinal.lib.bus.amba3.ahblite.{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.ip.DataCacheMemCmd
+
+import scala.collection.mutable.ArrayBuffer
+
+
+case class DBusSimpleCmd() extends Bundle{
+ val wr = Bool
+ val address = UInt(32 bits)
+ val data = Bits(32 bit)
+ val size = UInt(2 bit)
+}
+
+case class DBusSimpleRsp() extends Bundle with IMasterSlave{
+ val ready = Bool
+ val error = Bool
+ val data = Bits(32 bit)
+
+ override def asMaster(): Unit = {
+ out(ready,error,data)
+ }
+}
+
+
+object DBusSimpleBus{
+ def getAxi4Config() = Axi4Config(
+ addressWidth = 32,
+ dataWidth = 32,
+ useId = false,
+ useRegion = false,
+ useBurst = false,
+ useLock = false,
+ useQos = false,
+ useLen = false,
+ useResp = true
+ )
+
+ def getAvalonConfig() = AvalonMMConfig.pipelined(
+ addressWidth = 32,
+ dataWidth = 32).copy(
+ useByteEnable = true,
+ useResponse = true,
+ maximumPendingReadTransactions = 1
+ )
+
+ 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() = BmbParameter(
+ addressWidth = 32,
+ dataWidth = 32,
+ lengthWidth = 2,
+ sourceWidth = 0,
+ contextWidth = 1,
+ alignment = BmbParameter.BurstAlignement.LENGTH
+ )
+}
+
+case class DBusSimpleBus(bigEndian : Boolean = false) extends Bundle with IMasterSlave{
+ val cmd = Stream(DBusSimpleCmd())
+ val rsp = DBusSimpleRsp()
+
+ override def asMaster(): Unit = {
+ master(cmd)
+ slave(rsp)
+ }
+
+ def cmdS2mPipe() : DBusSimpleBus = {
+ val s = DBusSimpleBus(bigEndian)
+ s.cmd << this.cmd.s2mPipe()
+ this.rsp := s.rsp
+ s
+ }
+
+ def genMask(cmd : DBusSimpleCmd) = {
+ if(bigEndian)
+ cmd.size.mux(
+ U(0) -> B"1000",
+ U(1) -> B"1100",
+ default -> B"1111"
+ ) |>> cmd.address(1 downto 0)
+ else
+ cmd.size.mux(
+ U(0) -> B"0001",
+ U(1) -> B"0011",
+ default -> B"1111"
+ ) |<< cmd.address(1 downto 0)
+ }
+
+ def toAxi4Shared(stageCmd : Boolean = false, pendingWritesMax : Int = 7): Axi4Shared = {
+ val axi = Axi4Shared(DBusSimpleBus.getAxi4Config())
+
+ val cmdPreFork = if (stageCmd) cmd.stage.stage().s2mPipe() else cmd
+
+ val pendingWrites = CounterUpDown(
+ stateCount = pendingWritesMax + 1,
+ incWhen = cmdPreFork.fire && cmdPreFork.wr,
+ decWhen = axi.writeRsp.fire
+ )
+
+ val hazard = (pendingWrites =/= 0 && cmdPreFork.valid && !cmdPreFork.wr) || pendingWrites === pendingWritesMax
+ val (cmdFork, dataFork) = StreamFork2(cmdPreFork.haltWhen(hazard))
+ axi.sharedCmd.arbitrationFrom(cmdFork)
+ axi.sharedCmd.write := cmdFork.wr
+ axi.sharedCmd.prot := "010"
+ axi.sharedCmd.cache := "1111"
+ axi.sharedCmd.size := cmdFork.size.resized
+ axi.sharedCmd.addr := cmdFork.address
+
+ val dataStage = dataFork.throwWhen(!dataFork.wr)
+ axi.writeData.arbitrationFrom(dataStage)
+ axi.writeData.last := True
+ axi.writeData.data := dataStage.data
+ axi.writeData.strb := genMask(dataStage).resized
+
+
+ rsp.ready := axi.r.valid
+ rsp.error := !axi.r.isOKAY()
+ rsp.data := axi.r.data
+
+ axi.r.ready := True
+ axi.b.ready := True
+ axi
+ }
+
+ def toAxi4(stageCmd : Boolean = true) = this.toAxi4Shared(stageCmd).toAxi4()
+
+
+
+ def toAvalon(stageCmd : Boolean = true): AvalonMM = {
+ val avalonConfig = DBusSimpleBus.getAvalonConfig()
+ val mm = AvalonMM(avalonConfig)
+ val cmdStage = if(stageCmd) cmd.stage else cmd
+ mm.read := cmdStage.valid && !cmdStage.wr
+ mm.write := cmdStage.valid && cmdStage.wr
+ mm.address := (cmdStage.address >> 2) @@ U"00"
+ mm.writeData := cmdStage.data(31 downto 0)
+ mm.byteEnable := genMask(cmdStage).resized
+
+
+ cmdStage.ready := mm.waitRequestn
+ rsp.ready :=mm.readDataValid
+ rsp.error := mm.response =/= AvalonMM.Response.OKAY
+ rsp.data := mm.readData
+
+ mm
+ }
+
+ def toWishbone(): Wishbone = {
+ val wishboneConfig = DBusSimpleBus.getWishboneConfig()
+ val bus = Wishbone(wishboneConfig)
+ val cmdStage = cmd.halfPipe()
+
+ bus.ADR := cmdStage.address >> 2
+ bus.CTI :=B"000"
+ bus.BTE := "00"
+ bus.SEL := genMask(cmdStage).resized
+ when(!cmdStage.wr) {
+ bus.SEL := "1111"
+ }
+ bus.WE := cmdStage.wr
+ bus.DAT_MOSI := cmdStage.data
+
+ cmdStage.ready := cmdStage.valid && bus.ACK
+ bus.CYC := cmdStage.valid
+ bus.STB := cmdStage.valid
+
+ rsp.ready := cmdStage.valid && !bus.WE && bus.ACK
+ rsp.data := bus.DAT_MISO
+ rsp.error := False //TODO
+ bus
+ }
+
+ def toPipelinedMemoryBus() : PipelinedMemoryBus = {
+ val pipelinedMemoryBusConfig = DBusSimpleBus.getPipelinedMemoryBusConfig()
+ val bus = PipelinedMemoryBus(pipelinedMemoryBusConfig)
+ bus.cmd.valid := cmd.valid
+ bus.cmd.write := cmd.wr
+ bus.cmd.address := cmd.address.resized
+ bus.cmd.data := cmd.data
+ bus.cmd.mask := genMask(cmd)
+ cmd.ready := bus.cmd.ready
+
+ rsp.ready := bus.rsp.valid
+ rsp.data := bus.rsp.data
+
+ bus
+ }
+
+ def toAhbLite3Master(avoidWriteToReadHazard : Boolean): AhbLite3Master = {
+ val bus = AhbLite3Master(DBusSimpleBus.getAhbLite3Config())
+ bus.HADDR := this.cmd.address
+ bus.HWRITE := this.cmd.wr
+ bus.HSIZE := B(this.cmd.size, 3 bits)
+ bus.HBURST := 0
+ bus.HPROT := "1111"
+ bus.HTRANS := this.cmd.valid ## B"0"
+ bus.HMASTLOCK := False
+ bus.HWDATA := RegNextWhen(this.cmd.data, bus.HREADY)
+ this.cmd.ready := bus.HREADY
+
+ val pending = RegInit(False) clearWhen(bus.HREADY) setWhen(this.cmd.fire && !this.cmd.wr)
+ this.rsp.ready := bus.HREADY && pending
+ this.rsp.data := bus.HRDATA
+ this.rsp.error := bus.HRESP
+
+ if(avoidWriteToReadHazard) {
+ val writeDataPhase = RegNextWhen(bus.HTRANS === 2 && bus.HWRITE, bus.HREADY) init (False)
+ val potentialHazard = this.cmd.valid && !this.cmd.wr && writeDataPhase
+ when(potentialHazard) {
+ bus.HTRANS := 0
+ this.cmd.ready := False
+ }
+ }
+ bus
+ }
+
+ def toBmb() : Bmb = {
+ val pipelinedMemoryBusConfig = DBusSimpleBus.getBmbParameter()
+ val bus = Bmb(pipelinedMemoryBusConfig)
+
+ bus.cmd.valid := cmd.valid
+ bus.cmd.last := True
+ bus.cmd.context(0) := cmd.wr
+ bus.cmd.opcode := (cmd.wr ? B(Bmb.Cmd.Opcode.WRITE) | B(Bmb.Cmd.Opcode.READ))
+ bus.cmd.address := cmd.address.resized
+ bus.cmd.data := cmd.data
+ bus.cmd.length := cmd.size.mux(
+ 0 -> U"00",
+ 1 -> U"01",
+ default -> U"11"
+ )
+ bus.cmd.mask := genMask(cmd)
+
+ cmd.ready := bus.cmd.ready
+
+ rsp.ready := bus.rsp.valid && !bus.rsp.context(0)
+ rsp.data := bus.rsp.data
+ rsp.error := bus.rsp.isError
+ bus.rsp.ready := True
+
+ bus
+ }
+}
+
+
+class DBusSimplePlugin(catchAddressMisaligned : Boolean = false,
+ catchAccessFault : Boolean = false,
+ earlyInjection : Boolean = false, /*, idempotentRegions : (UInt) => Bool = (x) => False*/
+ emitCmdInMemoryStage : Boolean = false,
+ onlyLoadWords : Boolean = false,
+ withLrSc : Boolean = false,
+ val bigEndian : Boolean = false,
+ memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv] with DBusAccessService {
+
+ var dBus : DBusSimpleBus = null
+ assert(!(emitCmdInMemoryStage && earlyInjection))
+ object MEMORY_ENABLE extends Stageable(Bool)
+ object MEMORY_READ_DATA extends Stageable(Bits(32 bits))
+ object MEMORY_ADDRESS_LOW extends Stageable(UInt(2 bits))
+ object ALIGNEMENT_FAULT extends Stageable(Bool)
+ object MMU_FAULT extends Stageable(Bool)
+ object MEMORY_ATOMIC extends Stageable(Bool)
+ object ATOMIC_HIT extends Stageable(Bool)
+ object MEMORY_STORE extends Stageable(Bool)
+
+ var memoryExceptionPort : Flow[ExceptionCause] = null
+ var rspStage : Stage = null
+ var mmuBus : MemoryTranslatorBus = null
+ var redoBranch : Flow[UInt] = null
+ val catchSomething = catchAccessFault || catchAddressMisaligned || memoryTranslatorPortConfig != null
+
+ @dontName var dBusAccess : DBusAccess = null
+ override def newDBusAccess(): DBusAccess = {
+ assert(dBusAccess == null)
+ dBusAccess = DBusAccess()
+ dBusAccess
+ }
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+ import pipeline._
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+
+ val stdActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC_USE_SUB_LESS -> False,
+ MEMORY_ENABLE -> True,
+ RS1_USE -> True
+ ) ++ (if(catchAccessFault || catchAddressMisaligned) List(IntAluPlugin.ALU_CTRL -> IntAluPlugin.AluCtrlEnum.ADD_SUB) else Nil) //Used for access fault bad address in memory stage
+
+ val loadActions = stdActions ++ List(
+ SRC2_CTRL -> Src2CtrlEnum.IMI,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> Bool(earlyInjection),
+ MEMORY_STORE -> False,
+ HAS_SIDE_EFFECT -> True
+ )
+
+ val storeActions = stdActions ++ List(
+ SRC2_CTRL -> Src2CtrlEnum.IMS,
+ RS2_USE -> True,
+ MEMORY_STORE -> True,
+ HAS_SIDE_EFFECT -> True
+ )
+
+ decoderService.addDefault(MEMORY_ENABLE, False)
+ decoderService.add(
+ (if(onlyLoadWords) List(LW) else List(LB, LH, LW, LBU, LHU, LWU)).map(_ -> loadActions) ++
+ List(SB, SH, SW).map(_ -> storeActions)
+ )
+
+
+ if(withLrSc){
+ List(LB, LH, LW, LBU, LHU, LWU, SB, SH, SW).foreach(e =>
+ decoderService.add(e, Seq(MEMORY_ATOMIC -> False))
+ )
+ decoderService.add(
+ key = LR,
+ values = loadActions.filter(_._1 != SRC2_CTRL) ++ Seq(
+ SRC_ADD_ZERO -> True,
+ MEMORY_ATOMIC -> True
+ )
+ )
+
+ decoderService.add(
+ key = SC,
+ values = storeActions.filter(_._1 != SRC2_CTRL) ++ Seq(
+ SRC_ADD_ZERO -> True,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> False,
+ MEMORY_ATOMIC -> True
+ )
+ )
+ }
+
+ decoderService.add(FENCE, Nil)
+
+ rspStage = if(stages.last == execute) execute else (if(emitCmdInMemoryStage) writeBack else memory)
+ if(catchSomething) {
+ val exceptionService = pipeline.service(classOf[ExceptionService])
+ memoryExceptionPort = exceptionService.newExceptionPort(rspStage)
+ }
+
+ if(memoryTranslatorPortConfig != null) {
+ mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(MemoryTranslatorPort.PRIORITY_DATA, memoryTranslatorPortConfig)
+ redoBranch = pipeline.service(classOf[JumpService]).createJumpInterface(if(pipeline.memory != null) pipeline.memory else pipeline.execute)
+ }
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ object MMU_RSP extends Stageable(MemoryTranslatorRsp(mmuBus.p))
+
+ dBus = master(DBusSimpleBus(bigEndian)).setName("dBus")
+
+
+ decode plug new Area {
+ import decode._
+
+ if(mmuBus != null) when(mmuBus.busy && arbitration.isValid && input(MEMORY_ENABLE)) {
+ arbitration.haltItself := True
+ }
+ }
+
+ //Emit dBus.cmd request
+ val cmdSent = if(rspStage == execute) RegInit(False) setWhen(dBus.cmd.fire) clearWhen(!execute.arbitration.isStuck) else False
+ val cmdStage = if(emitCmdInMemoryStage) memory else execute
+ cmdStage plug new Area{
+ import cmdStage._
+ val privilegeService = pipeline.serviceElse(classOf[PrivilegeService], PrivilegeServiceDefault())
+
+
+ if (catchAddressMisaligned)
+ insert(ALIGNEMENT_FAULT) := (dBus.cmd.size === 2 && dBus.cmd.address(1 downto 0) =/= 0) || (dBus.cmd.size === 1 && dBus.cmd.address(0 downto 0) =/= 0)
+ else
+ insert(ALIGNEMENT_FAULT) := False
+
+
+ val skipCmd = False
+ skipCmd setWhen(input(ALIGNEMENT_FAULT))
+
+ dBus.cmd.valid := arbitration.isValid && input(MEMORY_ENABLE) && !arbitration.isStuckByOthers && !arbitration.isFlushed && !skipCmd && !cmdSent
+ dBus.cmd.wr := input(MEMORY_STORE)
+ dBus.cmd.size := input(INSTRUCTION)(13 downto 12).asUInt
+ dBus.cmd.payload.data := dBus.cmd.size.mux (
+ U(0) -> input(RS2)(7 downto 0) ## input(RS2)(7 downto 0) ## input(RS2)(7 downto 0) ## input(RS2)(7 downto 0),
+ U(1) -> input(RS2)(15 downto 0) ## input(RS2)(15 downto 0),
+ default -> input(RS2)(31 downto 0)
+ )
+ when(arbitration.isValid && input(MEMORY_ENABLE) && !dBus.cmd.ready && !skipCmd && !cmdSent){
+ arbitration.haltItself := True
+ }
+
+ insert(MEMORY_ADDRESS_LOW) := dBus.cmd.address(1 downto 0)
+
+ //formal
+ val formalMask = dBus.genMask(dBus.cmd)
+
+ insert(FORMAL_MEM_ADDR) := dBus.cmd.address & U"xFFFFFFFC"
+ insert(FORMAL_MEM_WMASK) := (dBus.cmd.valid && dBus.cmd.wr) ? formalMask | B"0000"
+ insert(FORMAL_MEM_RMASK) := (dBus.cmd.valid && !dBus.cmd.wr) ? formalMask | B"0000"
+ insert(FORMAL_MEM_WDATA) := dBus.cmd.payload.data
+
+ val mmu = (mmuBus != null) generate new Area {
+ mmuBus.cmd.last.isValid := arbitration.isValid && input(MEMORY_ENABLE)
+ mmuBus.cmd.last.isStuck := arbitration.isStuck
+ mmuBus.cmd.last.virtualAddress := input(SRC_ADD).asUInt
+ mmuBus.cmd.last.bypassTranslation := False
+ mmuBus.end := !arbitration.isStuck || arbitration.isRemoved
+ dBus.cmd.address := mmuBus.rsp.physicalAddress
+
+ //do not emit memory request if MMU refilling
+ insert(MMU_FAULT) := input(MMU_RSP).exception || (!input(MMU_RSP).allowWrite && input(MEMORY_STORE)) || (!input(MMU_RSP).allowRead && !input(MEMORY_STORE))
+ skipCmd.setWhen(input(MMU_FAULT) || input(MMU_RSP).refilling)
+
+ insert(MMU_RSP) := mmuBus.rsp
+ }
+
+ val mmuLess = (mmuBus == null) generate new Area{
+ dBus.cmd.address := input(SRC_ADD).asUInt
+ }
+
+
+ val atomic = withLrSc generate new Area{
+ val reserved = RegInit(False)
+ insert(ATOMIC_HIT) := reserved
+ when(arbitration.isFiring && input(MEMORY_ENABLE) && (if(mmuBus != null) !input(MMU_FAULT) else True) && !skipCmd){
+ reserved setWhen(input(MEMORY_ATOMIC))
+ reserved clearWhen(input(MEMORY_STORE))
+ }
+ when(input(MEMORY_STORE) && input(MEMORY_ATOMIC) && !input(ATOMIC_HIT)){
+ skipCmd := True
+ }
+ }
+ }
+
+ //Collect dBus.rsp read responses
+ rspStage plug new Area {
+ val s = rspStage; import s._
+
+
+ insert(MEMORY_READ_DATA) := dBus.rsp.data
+
+ arbitration.haltItself setWhen(arbitration.isValid && input(MEMORY_ENABLE) && !input(MEMORY_STORE) && (!dBus.rsp.ready || (if(rspStage == execute) !cmdSent else False)))
+
+ if(catchSomething) {
+ memoryExceptionPort.valid := False
+ memoryExceptionPort.code.assignDontCare()
+ memoryExceptionPort.badAddr := input(REGFILE_WRITE_DATA).asUInt
+
+ if(catchAccessFault) when(dBus.rsp.ready && dBus.rsp.error && !input(MEMORY_STORE)) {
+ memoryExceptionPort.valid := True
+ memoryExceptionPort.code := 5
+ }
+
+ if(catchAddressMisaligned) when(input(ALIGNEMENT_FAULT)){
+ memoryExceptionPort.code := (input(MEMORY_STORE) ? U(6) | U(4)).resized
+ memoryExceptionPort.valid := True
+ }
+
+ if(memoryTranslatorPortConfig != null) {
+ redoBranch.valid := False
+ redoBranch.payload := input(PC)
+
+ when(input(MMU_RSP).refilling){
+ redoBranch.valid := True
+ memoryExceptionPort.valid := False
+ } elsewhen(input(MMU_FAULT)) {
+ memoryExceptionPort.valid := True
+ memoryExceptionPort.code := (input(MEMORY_STORE) ? U(15) | U(13)).resized
+ }
+
+ arbitration.flushIt setWhen(redoBranch.valid)
+ arbitration.flushNext setWhen(redoBranch.valid)
+ }
+
+ when(!(arbitration.isValid && input(MEMORY_ENABLE) && (Bool(cmdStage != rspStage) || !arbitration.isStuckByOthers))){
+ if(catchSomething) memoryExceptionPort.valid := False
+ if(memoryTranslatorPortConfig != null) redoBranch.valid := False
+ }
+
+ }
+ }
+
+ //Reformat read responses, REGFILE_WRITE_DATA overriding
+ val injectionStage = if(earlyInjection) memory else stages.last
+ injectionStage plug new Area {
+ import injectionStage._
+
+
+ val rspShifted = MEMORY_READ_DATA()
+ rspShifted := input(MEMORY_READ_DATA)
+ if(bigEndian)
+ switch(input(MEMORY_ADDRESS_LOW)){
+ is(1){rspShifted(31 downto 24) := input(MEMORY_READ_DATA)(23 downto 16)}
+ is(2){rspShifted(31 downto 16) := input(MEMORY_READ_DATA)(15 downto 0)}
+ is(3){rspShifted(31 downto 24) := input(MEMORY_READ_DATA)(7 downto 0)}
+ }
+ else
+ switch(input(MEMORY_ADDRESS_LOW)){
+ is(1){rspShifted(7 downto 0) := input(MEMORY_READ_DATA)(15 downto 8)}
+ is(2){rspShifted(15 downto 0) := input(MEMORY_READ_DATA)(31 downto 16)}
+ is(3){rspShifted(7 downto 0) := input(MEMORY_READ_DATA)(31 downto 24)}
+ }
+
+ val rspFormated =
+ if(bigEndian)
+ input(INSTRUCTION)(13 downto 12).mux(
+ 0 -> B((31 downto 8) -> (rspShifted(31) && !input(INSTRUCTION)(14)),(7 downto 0) -> rspShifted(31 downto 24)),
+ 1 -> B((31 downto 16) -> (rspShifted(31) && ! input(INSTRUCTION)(14)),(15 downto 0) -> rspShifted(31 downto 16)),
+ default -> rspShifted //W
+ )
+ else
+ input(INSTRUCTION)(13 downto 12).mux(
+ 0 -> B((31 downto 8) -> (rspShifted(7) && !input(INSTRUCTION)(14)),(7 downto 0) -> rspShifted(7 downto 0)),
+ 1 -> B((31 downto 16) -> (rspShifted(15) && ! input(INSTRUCTION)(14)),(15 downto 0) -> rspShifted(15 downto 0)),
+ default -> rspShifted //W
+ )
+
+ when(arbitration.isValid && input(MEMORY_ENABLE)) {
+ output(REGFILE_WRITE_DATA) := (if(!onlyLoadWords) rspFormated else input(MEMORY_READ_DATA))
+ if(withLrSc){
+ when(input(MEMORY_ATOMIC) && input(MEMORY_STORE)){
+ output(REGFILE_WRITE_DATA) := (!input(ATOMIC_HIT)).asBits.resized
+ }
+ }
+ }
+
+// if(!earlyInjection && !emitCmdInMemoryStage && config.withWriteBackStage)
+// assert(!(arbitration.isValid && input(MEMORY_ENABLE) && !input(MEMORY_STORE) && arbitration.isStuck),"DBusSimplePlugin doesn't allow writeback stage stall when read happend")
+
+ //formal
+ insert(FORMAL_MEM_RDATA) := input(MEMORY_READ_DATA)
+ }
+
+ //Share access to the dBus (used by self refilled MMU)
+ val dBusSharing = (dBusAccess != null) generate new Area{
+ val state = Reg(UInt(2 bits)) init(0)
+ dBusAccess.cmd.ready := False
+ dBusAccess.rsp.valid := False
+ dBusAccess.rsp.data := dBus.rsp.data
+ dBusAccess.rsp.error := dBus.rsp.error
+ dBusAccess.rsp.redo := False
+
+ switch(state){
+ is(0){
+ when(dBusAccess.cmd.valid){
+ decode.arbitration.haltItself := True
+ when(!stages.dropWhile(_ != execute).map(_.arbitration.isValid).orR){
+ state := 1
+ }
+ }
+ }
+ is(1){
+ decode.arbitration.haltItself := True
+ dBus.cmd.valid := True
+ dBus.cmd.address := dBusAccess.cmd.address
+ dBus.cmd.wr := dBusAccess.cmd.write
+ dBus.cmd.data := dBusAccess.cmd.data
+ dBus.cmd.size := dBusAccess.cmd.size
+ when(dBus.cmd.ready){
+ state := (dBusAccess.cmd.write ? U(0) | U(2))
+ dBusAccess.cmd.ready := True
+ }
+ }
+ is(2){
+ decode.arbitration.haltItself := True
+ when(dBus.rsp.ready){
+ dBusAccess.rsp.valid := True
+ state := 0
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/DebugPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/DebugPlugin.scala
new file mode 100644
index 0000000..01c2acd
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/DebugPlugin.scala
@@ -0,0 +1,364 @@
+package vexriscv.plugin
+
+import spinal.lib.com.jtag.{Jtag, JtagTapInstructionCtrl}
+import spinal.lib.system.debugger.{JtagBridge, JtagBridgeNoTap, SystemDebugger, SystemDebuggerConfig, SystemDebuggerMemBus}
+import vexriscv.plugin.IntAluPlugin.{ALU_CTRL, AluCtrlEnum}
+import vexriscv._
+import vexriscv.ip._
+import spinal.core._
+import spinal.lib._
+import spinal.lib.blackbox.xilinx.s7.BSCANE2
+import spinal.lib.bus.amba3.apb.{Apb3, Apb3Config}
+import spinal.lib.bus.avalon.{AvalonMM, AvalonMMConfig}
+import spinal.lib.bus.bmb.{Bmb, BmbAccessCapabilities, BmbAccessParameter, BmbParameter}
+import spinal.lib.bus.simple.PipelinedMemoryBus
+
+import scala.collection.mutable.ArrayBuffer
+
+
+case class DebugExtensionCmd() extends Bundle{
+ val wr = Bool
+ val address = UInt(8 bit)
+ val data = Bits(32 bit)
+}
+case class DebugExtensionRsp() extends Bundle{
+ val data = Bits(32 bit)
+}
+
+object DebugExtensionBus{
+ def getBmbAccessParameter(source : BmbAccessCapabilities) = source.copy(
+ addressWidth = 8,
+ dataWidth = 32,
+ lengthWidthMax = 2,
+ alignment = BmbParameter.BurstAlignement.LENGTH
+ )
+}
+
+case class DebugExtensionBus() extends Bundle with IMasterSlave{
+ val cmd = Stream(DebugExtensionCmd())
+ val rsp = DebugExtensionRsp() //one cycle latency
+
+ override def asMaster(): Unit = {
+ master(cmd)
+ in(rsp)
+ }
+
+ def fromApb3(): Apb3 ={
+ val apb = Apb3(Apb3Config(
+ addressWidth = 8,
+ dataWidth = 32,
+ useSlaveError = false
+ ))
+
+ cmd.valid := apb.PSEL(0) && apb.PENABLE
+ cmd.wr := apb.PWRITE
+ cmd.address := apb.PADDR
+ cmd.data := apb.PWDATA
+
+ apb.PREADY := cmd.ready
+ apb.PRDATA := rsp.data
+
+ apb
+ }
+
+ def fromAvalon(): AvalonMM ={
+ val bus = AvalonMM(AvalonMMConfig.fixed(addressWidth = 8,dataWidth = 32, readLatency = 1))
+
+ cmd.valid := bus.read || bus.write
+ cmd.wr := bus.write
+ cmd.address := bus.address
+ cmd.data := bus.writeData
+
+ bus.waitRequestn := cmd.ready
+ bus.readData := rsp.data
+
+ bus
+ }
+
+ def fromPipelinedMemoryBus(): PipelinedMemoryBus ={
+ val bus = PipelinedMemoryBus(32, 32)
+
+ cmd.arbitrationFrom(bus.cmd)
+ cmd.wr := bus.cmd.write
+ cmd.address := bus.cmd.address.resized
+ cmd.data := bus.cmd.data
+
+ bus.rsp.valid := RegNext(cmd.fire) init(False)
+ bus.rsp.data := rsp.data
+
+ bus
+ }
+
+ def fromBmb(): Bmb ={
+ val bus = Bmb(BmbParameter(
+ addressWidth = 8,
+ dataWidth = 32,
+ lengthWidth = 2,
+ sourceWidth = 0,
+ contextWidth = 0
+ ))
+
+ cmd.arbitrationFrom(bus.cmd)
+ cmd.wr := bus.cmd.isWrite
+ cmd.address := bus.cmd.address
+ cmd.data := bus.cmd.data
+
+ bus.rsp.valid := RegNext(cmd.fire) init(False)
+ bus.rsp.data := rsp.data
+ bus.rsp.last := True
+ bus.rsp.setSuccess()
+
+ bus
+ }
+
+ def from(c : SystemDebuggerConfig) : SystemDebuggerMemBus = {
+ val mem = SystemDebuggerMemBus(c)
+ cmd.valid := mem.cmd.valid
+ cmd.wr := mem.cmd.wr
+ cmd.data := mem.cmd.data
+ cmd.address := mem.cmd.address.resized
+ mem.cmd.ready := cmd.ready
+ mem.rsp.valid := RegNext(cmd.fire).init(False)
+ mem.rsp.payload := rsp.data
+ mem
+ }
+
+ def fromJtag(): Jtag ={
+ val jtagConfig = SystemDebuggerConfig(
+ memAddressWidth = 32,
+ memDataWidth = 32,
+ remoteCmdWidth = 1
+ )
+ val jtagBridge = new JtagBridge(jtagConfig)
+ val debugger = new SystemDebugger(jtagConfig)
+ debugger.io.remote <> jtagBridge.io.remote
+ debugger.io.mem <> this.from(jtagConfig)
+
+ jtagBridge.io.jtag
+ }
+
+ def fromJtagInstructionCtrl(jtagClockDomain : ClockDomain, jtagHeaderIgnoreWidth : Int): JtagTapInstructionCtrl ={
+ val jtagConfig = SystemDebuggerConfig(
+ memAddressWidth = 32,
+ memDataWidth = 32,
+ remoteCmdWidth = 1
+ )
+ val jtagBridge = new JtagBridgeNoTap(jtagConfig, jtagClockDomain, jtagHeaderIgnoreWidth)
+ val debugger = new SystemDebugger(jtagConfig)
+ debugger.io.remote <> jtagBridge.io.remote
+ debugger.io.mem <> this.from(jtagConfig)
+
+ jtagBridge.io.ctrl
+ }
+
+ def fromBscane2(usedId : Int, jtagHeaderIgnoreWidth : Int): Unit ={
+ val jtagConfig = SystemDebuggerConfig()
+
+ val bscane2 = BSCANE2(usedId)
+ val jtagClockDomain = ClockDomain(bscane2.TCK)
+
+ val jtagBridge = new JtagBridgeNoTap(jtagConfig, jtagClockDomain, jtagHeaderIgnoreWidth)
+ jtagBridge.io.ctrl << bscane2.toJtagTapInstructionCtrl()
+
+ val debugger = new SystemDebugger(jtagConfig)
+ debugger.io.remote <> jtagBridge.io.remote
+ debugger.io.mem <> this.from(debugger.io.mem.c)
+ }
+}
+
+case class DebugExtensionIo() extends Bundle with IMasterSlave{
+ val bus = DebugExtensionBus()
+ val resetOut = Bool
+
+ override def asMaster(): Unit = {
+ master(bus)
+ in(resetOut)
+ }
+}
+
+class DebugPlugin(var debugClockDomain : ClockDomain, hardwareBreakpointCount : Int = 0, BreakpointReadback : Boolean = false) extends Plugin[VexRiscv] {
+
+ var io : DebugExtensionIo = null
+ val injectionAsks = ArrayBuffer[(Stage, Bool)]()
+ var injectionPort : Stream[Bits] = null
+
+
+ object IS_EBREAK extends Stageable(Bool)
+ object DO_EBREAK extends Stageable(Bool)
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+
+ io = slave(DebugExtensionIo()).setName("debug")
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+
+ decoderService.addDefault(IS_EBREAK, False)
+ decoderService.add(EBREAK,List(IS_EBREAK -> True))
+
+ injectionPort = pipeline.service(classOf[IBusFetcher]).getInjectionPort()
+
+ if(pipeline.serviceExist(classOf[ReportService])){
+ val report = pipeline.service(classOf[ReportService])
+ report.add("debug" -> {
+ val e = new DebugReport()
+ e.hardwareBreakpointCount = hardwareBreakpointCount
+ e
+ })
+ }
+ }
+
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ val logic = debugClockDomain {pipeline plug new Area{
+ val iBusFetcher = service(classOf[IBusFetcher])
+ val firstCycle = RegNext(False) setWhen (io.bus.cmd.ready)
+ val secondCycle = RegNext(firstCycle)
+ val resetIt = RegInit(False)
+ val haltIt = RegInit(False)
+ val stepIt = RegInit(False)
+
+ val isPipBusy = RegNext(stages.map(_.arbitration.isValid).orR || iBusFetcher.incoming())
+ val godmode = RegInit(False) setWhen(haltIt && !isPipBusy)
+ val haltedByBreak = RegInit(False)
+ val debugUsed = RegInit(False) setWhen(io.bus.cmd.valid) addAttribute(Verilator.public)
+ val disableEbreak = RegInit(False)
+
+ val allowEBreak = debugUsed && !disableEbreak
+
+ val hardwareBreakpoints = Vec(Reg(new Bundle{
+ val valid = Bool()
+ val pc = UInt(31 bits)
+ }), hardwareBreakpointCount)
+ hardwareBreakpoints.foreach(_.valid init(False))
+
+ val busReadDataReg = Reg(Bits(32 bit))
+ when(stages.last.arbitration.isValid) {
+ busReadDataReg := stages.last.output(REGFILE_WRITE_DATA)
+ }
+ io.bus.cmd.ready := True
+ io.bus.rsp.data := busReadDataReg
+ when(!RegNext(io.bus.cmd.address(2))){
+ io.bus.rsp.data(0) := resetIt
+ io.bus.rsp.data(1) := haltIt
+ io.bus.rsp.data(2) := isPipBusy
+ io.bus.rsp.data(3) := haltedByBreak
+ io.bus.rsp.data(4) := stepIt
+ }
+ if (BreakpointReadback) {
+ switch(RegNext(io.bus.cmd.address(7 downto 2))) {
+ for(i <- 0 until hardwareBreakpointCount){
+ is(0x10 + i){
+ io.bus.rsp.data(31 downto 1) := hardwareBreakpoints(i).pc.asBits
+ io.bus.rsp.data(0) := hardwareBreakpoints(i).valid
+ }
+ }
+ }
+ }
+
+
+ injectionPort.valid := False
+ injectionPort.payload := io.bus.cmd.data
+
+ when(io.bus.cmd.valid) {
+ switch(io.bus.cmd.address(7 downto 2)) {
+ is(0x0) {
+ when(io.bus.cmd.wr) {
+ stepIt := io.bus.cmd.data(4)
+ resetIt setWhen (io.bus.cmd.data(16)) clearWhen (io.bus.cmd.data(24))
+ haltIt setWhen (io.bus.cmd.data(17)) clearWhen (io.bus.cmd.data(25))
+ haltedByBreak clearWhen (io.bus.cmd.data(25))
+ godmode clearWhen(io.bus.cmd.data(25))
+ disableEbreak setWhen (io.bus.cmd.data(18)) clearWhen (io.bus.cmd.data(26))
+ }
+ }
+ is(0x1) {
+ when(io.bus.cmd.wr) {
+ injectionPort.valid := True
+ io.bus.cmd.ready := injectionPort.ready
+ }
+ }
+ for(i <- 0 until hardwareBreakpointCount){
+ is(0x10 + i){
+ when(io.bus.cmd.wr){
+ hardwareBreakpoints(i).assignFromBits(io.bus.cmd.data)
+ }
+ }
+ }
+ }
+ }
+
+ decode.insert(DO_EBREAK) := !haltIt && (decode.input(IS_EBREAK) || hardwareBreakpoints.map(hb => hb.valid && hb.pc === (decode.input(PC) >> 1)).foldLeft(False)(_ || _)) && allowEBreak
+ when(execute.arbitration.isValid && execute.input(DO_EBREAK)){
+ execute.arbitration.haltByOther := True
+ busReadDataReg := execute.input(PC).asBits
+ when(stagesFromExecute.tail.map(_.arbitration.isValid).orR === False){
+ iBusFetcher.haltIt()
+ execute.arbitration.flushIt := True
+ execute.arbitration.flushNext := True
+ haltIt := True
+ haltedByBreak := True
+ }
+ }
+
+ when(haltIt) {
+ iBusFetcher.haltIt()
+ }
+
+ when(stepIt && iBusFetcher.incoming()) {
+ iBusFetcher.haltIt()
+ when(decode.arbitration.isValid) {
+ haltIt := True
+ }
+ }
+
+ //Avoid having two C instruction executed in a single step
+ if(pipeline.config.withRvc){
+ val cleanStep = RegNext(stepIt && decode.arbitration.isFiring) init(False)
+ execute.arbitration.flushNext setWhen(cleanStep)
+ when(cleanStep){
+ execute.arbitration.flushNext := True
+ iBusFetcher.forceNoDecode()
+ }
+ }
+
+ io.resetOut := RegNext(resetIt)
+
+ if(serviceExist(classOf[InterruptionInhibitor])) {
+ when(haltIt || stepIt) {
+ service(classOf[InterruptionInhibitor]).inhibateInterrupts()
+ }
+ }
+
+ when(godmode) {
+ pipeline.plugins.foreach{
+ case p : ExceptionInhibitor => p.inhibateException()
+ case _ =>
+ }
+ pipeline.plugins.foreach{
+ case p : PrivilegeService => p.forceMachine()
+ case _ =>
+ }
+ pipeline.plugins.foreach{
+ case p : PredictionInterface => p.inDebugNoFetch()
+ case _ =>
+ }
+ if(pipeline.things.contains(DEBUG_BYPASS_CACHE)) pipeline(DEBUG_BYPASS_CACHE) := True
+ }
+ when(allowEBreak) {
+ pipeline.plugins.foreach {
+ case p: ExceptionInhibitor => p.inhibateEbreakException()
+ case _ =>
+ }
+ }
+
+ val wakeService = serviceElse(classOf[IWake], null)
+ if(wakeService != null) when(haltIt){
+ wakeService.askWake()
+ }
+ }}
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala
new file mode 100644
index 0000000..a525b77
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala
@@ -0,0 +1,402 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+import spinal.core.internals.Literal
+import spinal.lib._
+import vexriscv.demo.GenFull
+
+import scala.collection.mutable
+import scala.collection.mutable.ArrayBuffer
+
+
+case class Masked(value : BigInt,care : BigInt){
+ assert((value & ~care) == 0)
+ var isPrime = true
+
+ def < (that: Masked) = value < that.value || value == that.value && ~care < ~that.care
+
+ def intersects(x: Masked) = ((value ^ x.value) & care & x.care) == 0
+
+ def covers(x: Masked) = ((value ^ x.value) & care | (~x.care) & care) == 0
+
+ def setPrime(value : Boolean) = {
+ isPrime = value
+ this
+ }
+
+ def mergeOneBitDifSmaller(x: Masked) = {
+ val bit = value - x.value
+ val ret = new Masked(value &~ bit, care & ~bit)
+ // ret.isPrime = isPrime || x.isPrime
+ isPrime = false
+ x.isPrime = false
+ ret
+ }
+ def isSimilarOneBitDifSmaller(x: Masked) = {
+ val diff = value - x.value
+ care == x.care && value > x.value && (diff & diff - 1) == 0
+ }
+
+
+ def === (hard : Bits) : Bool = (hard & care) === (value & care)
+
+ def toString(bitCount : Int) = (0 until bitCount).map(i => if(care.testBit(i)) (if(value.testBit(i)) "1" else "0") else "-").reverseIterator.reduce(_+_)
+}
+
+class DecoderSimplePlugin(catchIllegalInstruction : Boolean = false,
+ throwIllegalInstruction : Boolean = false,
+ assertIllegalInstruction : Boolean = false,
+ forceLegalInstructionComputation : Boolean = false,
+ decoderIsolationBench : Boolean = false,
+ stupidDecoder : Boolean = false) extends Plugin[VexRiscv] with DecoderService {
+ override def add(encoding: Seq[(MaskedLiteral, Seq[(Stageable[_ <: BaseType], Any)])]): Unit = encoding.foreach(e => this.add(e._1,e._2))
+ override def add(key: MaskedLiteral, values: Seq[(Stageable[_ <: BaseType], Any)]): Unit = {
+ val instructionModel = encodings.getOrElseUpdate(key,ArrayBuffer[(Stageable[_ <: BaseType], BaseType)]())
+ values.map{case (a,b) => {
+ assert(!instructionModel.contains(a), s"Over specification of $a")
+ val value = b match {
+ case e: SpinalEnumElement[_] => e()
+ case e: BaseType => e
+ }
+ instructionModel += (a->value)
+ }}
+ }
+
+ override def addDefault(key: Stageable[_ <: BaseType], value: Any): Unit = {
+ assert(!defaults.contains(key))
+ defaults(key) = value match{
+ case e : SpinalEnumElement[_] => e()
+ case e : BaseType => e
+ }
+ }
+
+ def forceIllegal() : Unit = if(catchIllegalInstruction) pipeline.decode.input(pipeline.config.LEGAL_INSTRUCTION) := False
+
+ val defaults = mutable.LinkedHashMap[Stageable[_ <: BaseType], BaseType]()
+ val encodings = mutable.LinkedHashMap[MaskedLiteral,ArrayBuffer[(Stageable[_ <: BaseType], BaseType)]]()
+ var decodeExceptionPort : Flow[ExceptionCause] = null
+
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ if(!catchIllegalInstruction) {
+ SpinalWarning("This VexRiscv configuration is set without illegal instruction catch support. Some software may rely on it (ex: Rust)")
+ }
+ if(catchIllegalInstruction) {
+ val exceptionService = pipeline.plugins.filter(_.isInstanceOf[ExceptionService]).head.asInstanceOf[ExceptionService]
+ decodeExceptionPort = exceptionService.newExceptionPort(pipeline.decode).setName("decodeExceptionPort")
+ }
+ }
+
+ val detectLegalInstructions = catchIllegalInstruction || throwIllegalInstruction || forceLegalInstructionComputation || assertIllegalInstruction
+
+ object ASSERT_ERROR extends Stageable(Bool)
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+ import pipeline.decode._
+
+ val stageables = (encodings.flatMap(_._2.map(_._1)) ++ defaults.map(_._1)).toList.distinct
+
+
+ if(stupidDecoder){
+ if (detectLegalInstructions) insert(LEGAL_INSTRUCTION) := False
+ for(stageable <- stageables){
+ if(defaults.contains(stageable)){
+ insert(stageable).assignFrom(defaults(stageable))
+ } else {
+ insert(stageable).assignDontCare()
+ }
+ }
+ for((key, tasks) <- encodings){
+ when(input(INSTRUCTION) === key){
+ if (detectLegalInstructions) insert(LEGAL_INSTRUCTION) := True
+ for((stageable, value) <- tasks){
+ insert(stageable).assignFrom(value)
+ }
+ }
+ }
+ } else {
+ var offset = 0
+ var defaultValue, defaultCare = BigInt(0)
+ val offsetOf = mutable.LinkedHashMap[Stageable[_ <: BaseType], Int]()
+
+ //Build defaults value and field offset map
+ stageables.foreach(e => {
+ defaults.get(e) match {
+ case Some(value) => {
+ value.head.source match {
+ case literal: EnumLiteral[_] => literal.fixEncoding(e.dataType.asInstanceOf[SpinalEnumCraft[_]].getEncoding)
+ case _ =>
+ }
+ defaultValue += value.head.source.asInstanceOf[Literal].getValue << offset
+ defaultCare += ((BigInt(1) << e.dataType.getBitsWidth) - 1) << offset
+
+ }
+ case _ =>
+ }
+ offsetOf(e) = offset
+ offset += e.dataType.getBitsWidth
+ })
+
+ //Build spec
+ val spec = encodings.map { case (key, values) =>
+ var decodedValue = defaultValue
+ var decodedCare = defaultCare
+ for ((e, literal) <- values) {
+ literal.head.source match {
+ case literal: EnumLiteral[_] => literal.fixEncoding(e.dataType.asInstanceOf[SpinalEnumCraft[_]].getEncoding)
+ case _ =>
+ }
+ val offset = offsetOf(e)
+ decodedValue |= literal.head.source.asInstanceOf[Literal].getValue << offset
+ decodedCare |= ((BigInt(1) << e.dataType.getBitsWidth) - 1) << offset
+ }
+ (Masked(key.value, key.careAbout), Masked(decodedValue, decodedCare))
+ }
+
+
+ // logic implementation
+ val decodedBits = Bits(stageables.foldLeft(0)(_ + _.dataType.getBitsWidth) bits)
+ decodedBits := Symplify(input(INSTRUCTION), spec, decodedBits.getWidth)
+ if (detectLegalInstructions) insert(LEGAL_INSTRUCTION) := Symplify.logicOf(input(INSTRUCTION), SymplifyBit.getPrimeImplicantsByTrueAndDontCare(spec.unzip._1.toSeq, Nil, 32))
+ if (throwIllegalInstruction) {
+ input(LEGAL_INSTRUCTION) //Fill the request for later (prePopTask)
+ Component.current.addPrePopTask(() => arbitration.isValid clearWhen(!input(LEGAL_INSTRUCTION)))
+ }
+ if(assertIllegalInstruction){
+ val reg = RegInit(False) setWhen(arbitration.isValid) clearWhen(arbitration.isRemoved || !arbitration.isStuck)
+ insert(ASSERT_ERROR) := arbitration.isValid || reg
+ }
+
+ if(decoderIsolationBench){
+ KeepAttribute(RegNext(KeepAttribute(RegNext(decodedBits.removeAssignments().asInput()))))
+ out(Bits(32 bits)).setName("instruction") := KeepAttribute(RegNext(KeepAttribute(RegNext(input(INSTRUCTION)))))
+ }
+
+ //Unpack decodedBits and insert fields in the pipeline
+ offset = 0
+ stageables.foreach(e => {
+ insert(e).assignFromBits(decodedBits(offset, e.dataType.getBitsWidth bits))
+ // insert(e).assignFromBits(RegNext(decodedBits(offset, e.dataType.getBitsWidth bits)))
+ offset += e.dataType.getBitsWidth
+ })
+ }
+
+ if(catchIllegalInstruction){
+ decodeExceptionPort.valid := arbitration.isValid && !input(LEGAL_INSTRUCTION) // ?? HalitIt to alow decoder stage to wait valid data from 2 stages cache cache ??
+ decodeExceptionPort.code := 2
+ decodeExceptionPort.badAddr := input(INSTRUCTION).asUInt
+ }
+ if(assertIllegalInstruction){
+ pipeline.stages.tail.foreach(s => s.output(ASSERT_ERROR) clearWhen(s.arbitration.isRemoved))
+ assert(!pipeline.stages.last.output(ASSERT_ERROR))
+ }
+ }
+
+ def bench(toplevel : VexRiscv): Unit ={
+ toplevel.rework{
+ import toplevel.config._
+ toplevel.getAllIo.toList.foreach{io =>
+ if(io.isInput) { io.assignDontCare()}
+ io.setAsDirectionLess()
+ }
+ toplevel.decode.input(INSTRUCTION).removeAssignments()
+ toplevel.decode.input(INSTRUCTION) := Delay((in Bits(32 bits)).setName("instruction"),2)
+ val stageables = encodings.flatMap(_._2.map(_._1)).toSet
+ stageables.foreach(e => out(RegNext(RegNext(toplevel.decode.insert(e)).setName(e.getName()))))
+ if(catchIllegalInstruction) out(RegNext(RegNext(toplevel.decode.insert(LEGAL_INSTRUCTION)).setName(LEGAL_INSTRUCTION.getName())))
+ // toplevel.getAdditionalNodesRoot.clear()
+ }
+ }
+}
+
+object DecodingBench extends App{
+ SpinalVerilog{
+ val top = GenFull.cpu()
+ top.service(classOf[DecoderSimplePlugin]).bench(top)
+ top
+ }
+}
+
+
+object Symplify{
+ val cache = mutable.LinkedHashMap[Bits,mutable.LinkedHashMap[Masked,Bool]]()
+ def getCache(addr : Bits) = cache.getOrElseUpdate(addr,mutable.LinkedHashMap[Masked,Bool]())
+
+ //Generate terms logic for the given input
+ def logicOf(input : Bits,terms : Seq[Masked]) = terms.map(t => getCache(input).getOrElseUpdate(t,t === input)).asBits.orR
+
+ //Decode 'input' b using an mapping[key, decoding] specification
+ def apply(input: Bits, mapping: Iterable[(Masked, Masked)],resultWidth : Int) : Bits = {
+ val addrWidth = widthOf(input)
+ (for(bitId <- 0 until resultWidth) yield{
+ val trueTerm = mapping.filter { case (k,t) => (t.care.testBit(bitId) && t.value.testBit(bitId))}.map(_._1)
+ val falseTerm = mapping.filter { case (k,t) => (t.care.testBit(bitId) && !t.value.testBit(bitId))}.map(_._1)
+ val symplifiedTerms = SymplifyBit.getPrimeImplicantsByTrueAndFalse(trueTerm.toSeq, falseTerm.toSeq, addrWidth)
+ logicOf(input, symplifiedTerms)
+ }).asBits
+ }
+}
+
+object SymplifyBit{
+
+ //Return a new term with only one bit difference with 'term' and not included in falseTerms. above => 0 to 1 dif, else 1 to 0 diff
+ def genImplicitDontCare(falseTerms: Seq[Masked], term: Masked, bits: Int, above: Boolean): Masked = {
+ for (i <- 0 until bits; if term.care.testBit(i)) {
+ var t: Masked = null
+ if(above) {
+ if (!term.value.testBit(i))
+ t = Masked(term.value.setBit(i), term.care)
+ } else {
+ if (term.value.testBit(i))
+ t = Masked(term.value.clearBit(i), term.care)
+ }
+ if (t != null && !falseTerms.exists(_.intersects(t))) {
+ t.isPrime = false
+ return t
+ }
+ }
+ null
+ }
+
+ //Return primes implicants for the trueTerms, falseTerms spec. Default value is don't care
+ def getPrimeImplicantsByTrueAndFalse(trueTerms: Seq[Masked], falseTerms: Seq[Masked], inputWidth : Int): Seq[Masked] = {
+ val primes = mutable.LinkedHashSet[Masked]()
+ trueTerms.foreach(_.isPrime = true)
+ falseTerms.foreach(_.isPrime = true)
+ val trueTermByCareCount = (inputWidth to 0 by -1).map(b => trueTerms.filter(b == _.care.bitCount))
+ //table[Vector[HashSet[Masked]]](careCount)(bitSetCount)
+ val table = trueTermByCareCount.map(c => (0 to inputWidth).map(b => collection.mutable.Set(c.filter(b == _.value.bitCount): _*)))
+ for (i <- 0 to inputWidth) {
+ //Expends explicit terms
+ for (j <- 0 until inputWidth - i){
+ for(term <- table(i)(j)){
+ table(i+1)(j) ++= table(i)(j+1).withFilter(_.isSimilarOneBitDifSmaller(term)).map(_.mergeOneBitDifSmaller(term))
+ }
+ }
+ //Expends implicit don't care terms
+ for (j <- 0 until inputWidth-i) {
+ for (prime <- table(i)(j).withFilter(_.isPrime)) {
+ val dc = genImplicitDontCare(falseTerms, prime, inputWidth, true)
+ if (dc != null)
+ table(i+1)(j) += dc mergeOneBitDifSmaller prime
+ }
+ for (prime <- table(i)(j+1).withFilter(_.isPrime)) {
+ val dc = genImplicitDontCare(falseTerms, prime, inputWidth, false)
+ if (dc != null)
+ table(i+1)(j) += prime mergeOneBitDifSmaller dc
+ }
+ }
+ for (r <- table(i))
+ for (p <- r; if p.isPrime)
+ primes += p
+ }
+
+ def optimise() {
+ val duplicateds = primes.filter(prime => verifyTrueFalse(primes.filterNot(_ == prime), trueTerms, falseTerms))
+ if(duplicateds.nonEmpty) {
+ primes -= duplicateds.maxBy(_.care.bitCount)
+ optimise()
+ }
+ }
+
+ optimise()
+
+ verifyTrueFalse(primes, trueTerms, falseTerms)
+ var duplication = 0
+ for(prime <- primes){
+ if(verifyTrueFalse(primes.filterNot(_ == prime), trueTerms, falseTerms)){
+ duplication += 1
+ }
+ }
+ if(duplication != 0){
+ PendingError(s"Duplicated primes : $duplication")
+ }
+ primes.toSeq
+ }
+
+ //Verify that the 'terms' doesn't violate the trueTerms ++ falseTerms spec
+ def verifyTrueFalse(terms : Iterable[Masked], trueTerms : Seq[Masked], falseTerms : Seq[Masked]): Boolean ={
+ return (trueTerms.forall(trueTerm => terms.exists(_ covers trueTerm))) && (falseTerms.forall(falseTerm => !terms.exists(_ covers falseTerm)))
+ }
+
+ def checkTrue(terms : Iterable[Masked], trueTerms : Seq[Masked]): Boolean ={
+ return trueTerms.forall(trueTerm => terms.exists(_ covers trueTerm))
+ }
+
+
+ def getPrimeImplicantsByTrue(trueTerms: Seq[Masked], inputWidth : Int) : Seq[Masked] = getPrimeImplicantsByTrueAndDontCare(trueTerms, Nil, inputWidth)
+
+ // Return primes implicants for the trueTerms, default value is False.
+ // You can insert don't care values by adding non-prime implicants in the trueTerms
+ // Will simplify the trueTerms from the most constrained ones to the least constrained ones
+ def getPrimeImplicantsByTrueAndDontCare(trueTerms: Seq[Masked],dontCareTerms: Seq[Masked], inputWidth : Int): Seq[Masked] = {
+ val primes = mutable.LinkedHashSet[Masked]()
+ trueTerms.foreach(_.isPrime = true)
+ dontCareTerms.foreach(_.isPrime = false)
+ val termsByCareCount = (inputWidth to 0 by -1).map(b => (trueTerms ++ dontCareTerms).filter(b == _.care.bitCount))
+ //table[Vector[HashSet[Masked]]](careCount)(bitSetCount)
+ val table = termsByCareCount.map(c => (0 to inputWidth).map(b => collection.mutable.Set(c.filter(m => b == m.value.bitCount): _*)))
+ for (i <- 0 to inputWidth) {
+ for (j <- 0 until inputWidth - i){
+ for(term <- table(i)(j)){
+ table(i+1)(j) ++= table(i)(j+1).withFilter(_.isSimilarOneBitDifSmaller(term)).map(_.mergeOneBitDifSmaller(term))
+ }
+ }
+ for (r <- table(i))
+ for (p <- r; if p.isPrime)
+ primes += p
+ }
+
+
+ def optimise() {
+ val duplicateds = primes.filter(prime => checkTrue(primes.filterNot(_ == prime), trueTerms))
+ if(duplicateds.nonEmpty) {
+ primes -= duplicateds.maxBy(_.care.bitCount)
+ optimise()
+ }
+ }
+
+ optimise()
+
+
+ var duplication = 0
+ for(prime <- primes){
+ if(checkTrue(primes.filterNot(_ == prime), trueTerms)){
+ duplication += 1
+ }
+ }
+ if(duplication != 0){
+ PendingError(s"Duplicated primes : $duplication")
+ }
+ primes.toSeq
+ }
+
+ def main(args: Array[String]) {
+ {
+ // val default = Masked(0, 0xF)
+ // val primeImplicants = List(4, 8, 10, 11, 12, 15).map(v => Masked(v, 0xF))
+ // val dcImplicants = List(9, 14).map(v => Masked(v, 0xF).setPrime(false))
+ // val reducedPrimeImplicants = getPrimeImplicantsByTrueAndDontCare(primeImplicants, dcImplicants, 4)
+ // println("UUT")
+ // println(reducedPrimeImplicants.map(_.toString(4)).mkString("\n"))
+ // println("REF")
+ // println("-100\n10--\n1--0\n1-1-")
+ }
+
+ {
+ val primeImplicants = List(0).map(v => Masked(v, 0xF))
+ val dcImplicants = (1 to 15).map(v => Masked(v, 0xF))
+ val reducedPrimeImplicants = getPrimeImplicantsByTrueAndDontCare(primeImplicants, dcImplicants, 4)
+ println("UUT")
+ println(reducedPrimeImplicants.map(_.toString(4)).mkString("\n"))
+ }
+ {
+ val trueTerms = List(0, 15).map(v => Masked(v, 0xF))
+ val falseTerms = List(3).map(v => Masked(v, 0xF))
+ val primes = getPrimeImplicantsByTrueAndFalse(trueTerms, falseTerms, 4)
+ println(primes.map(_.toString(4)).mkString("\n"))
+ }
+ }
+} \ No newline at end of file
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/DivPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/DivPlugin.scala
new file mode 100644
index 0000000..c20dcb3
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/DivPlugin.scala
@@ -0,0 +1,75 @@
+package vexriscv.plugin
+
+import vexriscv.{VexRiscv, _}
+import spinal.core._
+
+// DivPlugin was by the past a standalone plugin, but now it use the MulDivIterativePlugin implementation
+class DivPlugin extends MulDivIterativePlugin(genMul = false, genDiv = true, mulUnrollFactor = 1, divUnrollFactor = 1)
+
+//import spinal.lib.math.MixedDivider
+//
+//class DivPlugin extends Plugin[VexRiscv]{
+// object IS_DIV extends Stageable(Bool)
+//
+// override def setup(pipeline: VexRiscv): Unit = {
+// import Riscv._
+// import pipeline.config._
+//
+// val actions = List[(Stageable[_ <: BaseType],Any)](
+// SRC1_CTRL -> Src1CtrlEnum.RS,
+// SRC2_CTRL -> Src2CtrlEnum.RS,
+// REGFILE_WRITE_VALID -> True,
+// BYPASSABLE_EXECUTE_STAGE -> False,
+// BYPASSABLE_MEMORY_STAGE -> True,
+// RS1_USE -> True,
+// RS2_USE -> True,
+// IS_DIV -> True
+// )
+//
+// val decoderService = pipeline.service(classOf[DecoderService])
+// decoderService.addDefault(IS_DIV, False)
+// decoderService.add(List(
+// DIVX -> actions
+// ))
+//
+// }
+//
+// override def build(pipeline: VexRiscv): Unit = {
+// import pipeline._
+// import pipeline.config._
+//
+// val divider = new MixedDivider(32, 32, true) //cmd >-> rsp
+//
+// //Send request to the divider component
+// execute plug new Area {
+// import execute._
+//
+// divider.io.cmd.valid := False
+// divider.io.cmd.numerator := input(SRC1)
+// divider.io.cmd.denominator := input(SRC2)
+// divider.io.cmd.signed := !input(INSTRUCTION)(12)
+//
+// when(arbitration.isValid && input(IS_DIV)) {
+// divider.io.cmd.valid := !arbitration.isStuckByOthers && !arbitration.removeIt
+// arbitration.haltItself := memory.arbitration.isValid && memory.input(IS_DIV)
+// }
+// }
+//
+// //Collect response from the divider component, REGFILE_WRITE_DATA overriding
+// memory plug new Area{
+// import memory._
+//
+// divider.io.flush := memory.arbitration.removeIt
+// divider.io.rsp.ready := !arbitration.isStuckByOthers
+//
+// when(arbitration.isValid && input(IS_DIV)) {
+// arbitration.haltItself := !divider.io.rsp.valid
+//
+// output(REGFILE_WRITE_DATA) := Mux(input(INSTRUCTION)(13), divider.io.rsp.remainder, divider.io.rsp.quotient).asBits
+// }
+//
+//
+// divider.io.rsp.payload.error.allowPruning
+// }
+// }
+// }
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/DummyFencePlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/DummyFencePlugin.scala
new file mode 100644
index 0000000..7efbaac
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/DummyFencePlugin.scala
@@ -0,0 +1,22 @@
+package vexriscv.plugin
+
+import spinal.core._
+import vexriscv.{VexRiscv, _}
+
+class DummyFencePlugin extends Plugin[VexRiscv]{
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.add(FENCE_I, Nil)
+ decoderService.add(FENCE, Nil)
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ //Dummy
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/ExternalInterruptArrayPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/ExternalInterruptArrayPlugin.scala
new file mode 100644
index 0000000..43d32f0
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/ExternalInterruptArrayPlugin.scala
@@ -0,0 +1,30 @@
+package vexriscv.plugin
+
+import spinal.core._
+import vexriscv.VexRiscv
+
+class ExternalInterruptArrayPlugin(arrayWidth : Int = 32,
+ machineMaskCsrId : Int = 0xBC0,
+ machinePendingsCsrId : Int = 0xFC0,
+ supervisorMaskCsrId : Int = 0x9C0,
+ supervisorPendingsCsrId : Int = 0xDC0) extends Plugin[VexRiscv]{
+ var externalInterruptArray : Bits = null
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ externalInterruptArray = in(Bits(arrayWidth bits)).setName("externalInterruptArray")
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ val csr = pipeline.service(classOf[CsrPlugin])
+ val externalInterruptArrayBuffer = RegNext(externalInterruptArray)
+ def gen(maskCsrId : Int, pendingsCsrId : Int, interruptPin : Bool) = new Area {
+ val mask = Reg(Bits(arrayWidth bits)) init(0)
+ val pendings = mask & externalInterruptArrayBuffer
+ interruptPin.setAsDirectionLess() := pendings.orR
+ csr.rw(maskCsrId, mask)
+ csr.r(pendingsCsrId, pendings)
+ }
+ gen(machineMaskCsrId, machinePendingsCsrId, csr.externalInterrupt)
+ if(csr.config.supervisorGen) gen(supervisorMaskCsrId, supervisorPendingsCsrId, csr.externalInterruptS)
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala b/VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala
new file mode 100644
index 0000000..14450a1
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala
@@ -0,0 +1,637 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+import spinal.lib._
+import vexriscv.Riscv.IMM
+import StreamVexPimper._
+import scala.collection.mutable.ArrayBuffer
+
+
+//TODO val killLastStage = jump.pcLoad.valid || decode.arbitration.isRemoved
+// DBUSSimple check memory halt execute optimization
+
+abstract class IBusFetcherImpl(val resetVector : BigInt,
+ val keepPcPlus4 : Boolean,
+ val decodePcGen : Boolean,
+ val compressedGen : Boolean,
+ val cmdToRspStageCount : Int,
+ val allowPcRegReusedForSecondStage : Boolean,
+ val injectorReadyCutGen : Boolean,
+ val prediction : BranchPrediction,
+ val historyRamSizeLog2 : Int,
+ val injectorStage : Boolean,
+ val relaxPredictorAddress : Boolean,
+ val fetchRedoGen : Boolean,
+ val predictionBuffer : Boolean = true) extends Plugin[VexRiscv] with JumpService with IBusFetcher{
+ var prefetchExceptionPort : Flow[ExceptionCause] = null
+ var decodePrediction : DecodePredictionBus = null
+ var fetchPrediction : FetchPredictionBus = null
+ var dynamicTargetFailureCorrection : Flow[UInt] = null
+ var externalResetVector : UInt = null
+ assert(cmdToRspStageCount >= 1)
+// assert(!(cmdToRspStageCount == 1 && !injectorStage))
+ assert(!(compressedGen && !decodePcGen))
+ var fetcherHalt : Bool = null
+ var forceNoDecodeCond : Bool = null
+ var pcValids : Vec[Bool] = null
+ def pcValid(stage : Stage) = pcValids(pipeline.indexOf(stage))
+ var incomingInstruction : Bool = null
+ override def incoming() = incomingInstruction
+
+
+ override def withRvc(): Boolean = compressedGen
+
+ var injectionPort : Stream[Bits] = null
+ override def getInjectionPort() = {
+ injectionPort = Stream(Bits(32 bits))
+ injectionPort
+ }
+ def pcRegReusedForSecondStage = allowPcRegReusedForSecondStage && prediction != DYNAMIC_TARGET //TODO might not be required for DYNAMIC_TARGET
+ var predictionJumpInterface : Flow[UInt] = null
+
+ override def haltIt(): Unit = fetcherHalt := True
+ override def forceNoDecode(): Unit = forceNoDecodeCond := True
+ case class JumpInfo(interface : Flow[UInt], stage: Stage, priority : Int)
+ val jumpInfos = ArrayBuffer[JumpInfo]()
+ override def createJumpInterface(stage: Stage, priority : Int = 0): Flow[UInt] = {
+ assert(stage != null)
+ val interface = Flow(UInt(32 bits))
+ jumpInfos += JumpInfo(interface,stage, priority)
+ interface
+ }
+
+
+// var decodeExceptionPort : Flow[ExceptionCause] = null
+ override def setup(pipeline: VexRiscv): Unit = {
+ fetcherHalt = False
+ forceNoDecodeCond = False
+ incomingInstruction = False
+ if(resetVector == null) externalResetVector = in(UInt(32 bits).setName("externalResetVector"))
+
+ prediction match {
+ case NONE =>
+ case STATIC | DYNAMIC => {
+ predictionJumpInterface = createJumpInterface(pipeline.decode)
+ decodePrediction = pipeline.service(classOf[PredictionInterface]).askDecodePrediction()
+ }
+ case DYNAMIC_TARGET => {
+ fetchPrediction = pipeline.service(classOf[PredictionInterface]).askFetchPrediction()
+ }
+ }
+
+ pcValids = Vec(Bool, pipeline.stages.size)
+ }
+
+ object IBUS_RSP
+ object DECOMPRESSOR
+ object INJECTOR_M2S
+
+ def isDrivingDecode(s : Any): Boolean = {
+ if(injectorStage) return s == INJECTOR_M2S
+ s == IBUS_RSP || s == DECOMPRESSOR
+ }
+
+
+
+ class FetchArea(pipeline : VexRiscv) extends Area {
+ import pipeline._
+ import pipeline.config._
+ val externalFlush = stages.map(_.arbitration.flushNext).orR
+
+ def getFlushAt(s : Any, lastCond : Boolean = true): Bool = {
+ if(isDrivingDecode(s) && lastCond) pipeline.decode.arbitration.isRemoved else externalFlush
+ }
+
+ //Arbitrate jump requests into pcLoad
+ val jump = new Area {
+ val sortedByStage = jumpInfos.sortWith((a, b) => {
+ (pipeline.indexOf(a.stage) > pipeline.indexOf(b.stage)) ||
+ (pipeline.indexOf(a.stage) == pipeline.indexOf(b.stage) && a.priority > b.priority)
+ })
+ val valids = sortedByStage.map(_.interface.valid)
+ val pcs = sortedByStage.map(_.interface.payload)
+
+ val pcLoad = Flow(UInt(32 bits))
+ pcLoad.valid := jumpInfos.map(_.interface.valid).orR
+ pcLoad.payload := MuxOH(OHMasking.first(valids.asBits), pcs)
+ }
+
+
+
+ //The fetchPC pcReg can also be use for the second stage of the fetch
+ //When the fetcherHalt is set and the pipeline isn't stalled,, the pc is propagated to to the pcReg, which allow
+ //using the pc pipeline to get the next PC value for interrupts
+ val fetchPc = new Area{
+ //PC calculation without Jump
+ val output = Stream(UInt(32 bits))
+ val pcReg = Reg(UInt(32 bits)) init(if(resetVector != null) resetVector else externalResetVector) addAttribute(Verilator.public)
+ val correction = False
+ val correctionReg = RegInit(False) setWhen(correction) clearWhen(output.fire)
+ val corrected = correction || correctionReg
+ val pcRegPropagate = False
+ val booted = RegNext(True) init (False)
+ val inc = RegInit(False) clearWhen(correction || pcRegPropagate) setWhen(output.fire) clearWhen(!output.valid && output.ready)
+ val pc = pcReg + (inc ## B"00").asUInt
+ val predictionPcLoad = ifGen(prediction == DYNAMIC_TARGET) (Flow(UInt(32 bits)))
+ val redo = (fetchRedoGen || prediction == DYNAMIC_TARGET) generate Flow(UInt(32 bits))
+ val flushed = False
+
+ if(compressedGen) when(inc) {
+ pc(1) := False
+ }
+
+ if(predictionPcLoad != null) {
+ when(predictionPcLoad.valid) {
+ correction := True
+ pc := predictionPcLoad.payload
+ }
+ }
+ if(redo != null) when(redo.valid){
+ correction := True
+ pc := redo.payload
+ flushed := True
+ }
+ when(jump.pcLoad.valid) {
+ correction := True
+ pc := jump.pcLoad.payload
+ flushed := True
+ }
+
+ when(booted && (output.ready || correction || pcRegPropagate)){
+ pcReg := pc
+ }
+
+ pc(0) := False
+ if(!compressedGen) pc(1) := False
+
+ output.valid := !fetcherHalt && booted
+ output.payload := pc
+ }
+
+ val decodePc = ifGen(decodePcGen)(new Area {
+ //PC calculation without Jump
+ val flushed = False
+ val pcReg = Reg(UInt(32 bits)) init(if(resetVector != null) resetVector else externalResetVector) addAttribute(Verilator.public)
+ val pcPlus = if(compressedGen)
+ pcReg + ((decode.input(IS_RVC)) ? U(2) | U(4))
+ else
+ pcReg + 4
+
+ if (keepPcPlus4) KeepAttribute(pcPlus)
+ val injectedDecode = False
+ when(decode.arbitration.isFiring && !injectedDecode) {
+ pcReg := pcPlus
+ }
+
+ val predictionPcLoad = ifGen(prediction == DYNAMIC_TARGET) (Flow(UInt(32 bits)))
+ if(prediction == DYNAMIC_TARGET) {
+ when(predictionPcLoad.valid && !forceNoDecodeCond) {
+ pcReg := predictionPcLoad.payload
+ }
+ }
+
+ //application of the selected jump request
+ when(jump.pcLoad.valid && (!decode.arbitration.isStuck || decode.arbitration.isRemoved)) {
+ pcReg := jump.pcLoad.payload
+ flushed := True
+ }
+ })
+
+
+ case class FetchRsp() extends Bundle {
+ val pc = UInt(32 bits)
+ val rsp = IBusSimpleRsp()
+ val isRvc = Bool()
+ }
+
+
+ val iBusRsp = new Area {
+ val redoFetch = False
+ val stages = Array.fill(cmdToRspStageCount + 1)(new Bundle {
+ val input = Stream(UInt(32 bits))
+ val output = Stream(UInt(32 bits))
+ val halt = Bool()
+ })
+
+ stages(0).input << fetchPc.output
+ for(s <- stages) {
+ s.halt := False
+ s.output << s.input.haltWhen(s.halt)
+ }
+
+ if(fetchPc.redo != null) {
+ fetchPc.redo.valid := redoFetch
+ fetchPc.redo.payload := stages.last.input.payload
+ }
+
+ val flush = (if(isDrivingDecode(IBUS_RSP)) pipeline.decode.arbitration.isRemoved || decode.arbitration.flushNext && !decode.arbitration.isStuck else externalFlush) || redoFetch
+ for((s,sNext) <- (stages, stages.tail).zipped) {
+ val sFlushed = if(s != stages.head) flush else False
+ val sNextFlushed = flush
+ if(s == stages.head && pcRegReusedForSecondStage) {
+ sNext.input.arbitrationFrom(s.output.toEvent().m2sPipeWithFlush(sNextFlushed, false, collapsBubble = false, flushInput = sFlushed))
+ sNext.input.payload := fetchPc.pcReg
+ fetchPc.pcRegPropagate setWhen(sNext.input.ready)
+ } else {
+ sNext.input << s.output.m2sPipeWithFlush(sNextFlushed, false, collapsBubble = false, flushInput = sFlushed)
+ }
+ }
+
+ val readyForError = True
+ val output = Stream(FetchRsp())
+ incomingInstruction setWhen(stages.tail.map(_.input.valid).reduce(_ || _))
+ }
+
+ val decompressor = ifGen(decodePcGen)(new Area{
+ val input = iBusRsp.output.clearValidWhen(iBusRsp.redoFetch)
+ val output = Stream(FetchRsp())
+ val flush = getFlushAt(DECOMPRESSOR)
+ val flushNext = if(isDrivingDecode(DECOMPRESSOR)) decode.arbitration.flushNext else False
+ val consumeCurrent = if(isDrivingDecode(DECOMPRESSOR)) flushNext && output.ready else False
+
+ val bufferValid = RegInit(False)
+ val bufferData = Reg(Bits(16 bits))
+
+ val isInputLowRvc = input.rsp.inst(1 downto 0) =/= 3
+ val isInputHighRvc = input.rsp.inst(17 downto 16) =/= 3
+ val throw2BytesReg = RegInit(False)
+ val throw2Bytes = throw2BytesReg || input.pc(1)
+ val unaligned = throw2Bytes || bufferValid
+ def aligned = !unaligned
+
+ //Latch and patches are there to ensure that the decoded instruction do not mutate while being halted and unscheduled to ensure FpuPlugin cmd fork from consistancy
+ val bufferValidLatch = RegNextWhen(bufferValid, input.valid)
+ val throw2BytesLatch = RegNextWhen(throw2Bytes, input.valid)
+ val bufferValidPatched = input.valid ? bufferValid | bufferValidLatch
+ val throw2BytesPatched = input.valid ? throw2Bytes | throw2BytesLatch
+
+ val raw = Mux(
+ sel = bufferValidPatched,
+ whenTrue = input.rsp.inst(15 downto 0) ## bufferData,
+ whenFalse = input.rsp.inst(31 downto 16) ## (throw2BytesPatched ? input.rsp.inst(31 downto 16) | input.rsp.inst(15 downto 0))
+ )
+ val isRvc = raw(1 downto 0) =/= 3
+ val decompressed = RvcDecompressor(raw(15 downto 0), pipeline.config.withRvf, pipeline.config.withRvd)
+ output.valid := input.valid && !(throw2Bytes && !bufferValid && !isInputHighRvc)
+ output.pc := input.pc
+ output.isRvc := isRvc
+ output.rsp.inst := isRvc ? decompressed | raw
+ input.ready := output.ready && (!iBusRsp.stages.last.input.valid || flushNext || (!(bufferValid && isInputHighRvc) && !(aligned && isInputLowRvc && isInputHighRvc)))
+
+ when(output.fire){
+ throw2BytesReg := (aligned && isInputLowRvc && isInputHighRvc) || (bufferValid && isInputHighRvc)
+ }
+ val bufferFill = (aligned && isInputLowRvc && !isInputHighRvc) || (bufferValid && !isInputHighRvc) || (throw2Bytes && !isRvc && !isInputHighRvc)
+ when(output.ready && input.valid){
+ bufferValid := False
+ }
+ when(output.ready && input.valid){
+ bufferData := input.rsp.inst(31 downto 16)
+ bufferValid setWhen(bufferFill)
+ }
+
+ when(flush || consumeCurrent){
+ throw2BytesReg := False
+ bufferValid := False
+ }
+
+ if(fetchPc.redo != null) {
+ fetchPc.redo.payload(1) setWhen(throw2BytesReg)
+ }
+ })
+
+
+ def condApply[T](that : T, cond : Boolean)(func : (T) => T) = if(cond)func(that) else that
+ val injector = new Area {
+ val inputBeforeStage = condApply(if (decodePcGen) decompressor.output else iBusRsp.output, injectorReadyCutGen)(_.s2mPipe(externalFlush))
+ if (injectorReadyCutGen) {
+ iBusRsp.readyForError.clearWhen(inputBeforeStage.valid) //Can't emit error if there is a instruction pending in the s2mPipe
+ incomingInstruction setWhen (inputBeforeStage.valid)
+ }
+ val decodeInput = (if (injectorStage) {
+ val flushStage = getFlushAt(INJECTOR_M2S)
+ val decodeInput = inputBeforeStage.m2sPipeWithFlush(flushStage, false, collapsBubble = false, flushInput = externalFlush)
+ decode.insert(INSTRUCTION_ANTICIPATED) := Mux(decode.arbitration.isStuck, decode.input(INSTRUCTION), inputBeforeStage.rsp.inst)
+ iBusRsp.readyForError.clearWhen(decodeInput.valid) //Can't emit error when there is a instruction pending in the injector stage buffer
+ incomingInstruction setWhen (decodeInput.valid)
+ decodeInput
+ } else {
+ inputBeforeStage
+ })
+
+ if(!decodePcGen) iBusRsp.readyForError.clearWhen(!pcValid(decode)) //Need to wait a valid PC on the decode stage, as it is use to fill CSR xEPC
+
+
+ def pcUpdatedGen(input : Bool, stucks : Seq[Bool], relaxedInput : Boolean, flush : Bool) : Seq[Bool] = {
+ stucks.scanLeft(input)((i, stuck) => {
+ val reg = RegInit(False)
+ if(!relaxedInput) when(flush) {
+ reg := False
+ }
+ when(!stuck) {
+ reg := i
+ }
+ if(relaxedInput || i != input) when(flush) {
+ reg := False
+ }
+ reg
+ }).tail
+ }
+
+ val stagesFromExecute = stages.dropWhile(_ != execute).toList
+ val nextPcCalc = if (decodePcGen) new Area{
+ val valids = pcUpdatedGen(True, False :: stagesFromExecute.map(_.arbitration.isStuck), true, decodePc.flushed)
+ pcValids := Vec(valids.takeRight(stages.size))
+ } else new Area{
+ val valids = pcUpdatedGen(True, iBusRsp.stages.tail.map(!_.input.ready) ++ (if (injectorStage) List(!decodeInput.ready) else Nil) ++ stagesFromExecute.map(_.arbitration.isStuck), false, fetchPc.flushed)
+ pcValids := Vec(valids.takeRight(stages.size))
+ }
+
+ decodeInput.ready := !decode.arbitration.isStuck
+ decode.arbitration.isValid := decodeInput.valid
+ decode.insert(PC) := (if (decodePcGen) decodePc.pcReg else decodeInput.pc)
+ decode.insert(INSTRUCTION) := decodeInput.rsp.inst
+ if (compressedGen) decode.insert(IS_RVC) := decodeInput.isRvc
+
+ if (injectionPort != null) {
+ Component.current.addPrePopTask(() => {
+ val state = RegInit(U"000")
+
+ injectionPort.ready := False
+ if(decodePcGen){
+ decodePc.injectedDecode setWhen(state =/= 0)
+ }
+ switch(state) {
+ is(0) { //request pipelining
+ when(injectionPort.valid) {
+ state := 1
+ }
+ }
+ is(1) { //Give time to propagate the payload
+ state := 2
+ }
+ is(2){ //read regfile delay
+ decode.arbitration.isValid := True
+ decode.arbitration.haltItself := True
+ state := 3
+ }
+ is(3){ //Do instruction
+ decode.arbitration.isValid := True
+ when(!decode.arbitration.isStuck) {
+ state := 4
+ }
+ }
+ is(4){ //request pipelining
+ injectionPort.ready := True
+ state := 0
+ }
+ }
+
+ //Check if the decode instruction is driven by a register
+ val instructionDriver = try {
+ decode.input(INSTRUCTION).getDrivingReg
+ } catch {
+ case _: Throwable => null
+ }
+ if (instructionDriver != null) { //If yes =>
+ //Insert the instruction by writing the "fetch to decode instruction register",
+ // Work even if it need to cross some hierarchy (caches)
+ instructionDriver.component.rework {
+ when(state.pull() =/= 0) {
+ instructionDriver := injectionPort.payload.pull()
+ }
+ }
+ } else {
+ //Insert the instruction via a mux in the decode stage
+ when(state =/= 0) {
+ decode.input(INSTRUCTION) := RegNext(injectionPort.payload)
+ }
+ }
+ })
+ }
+
+ Component.current.addPrePopTask(() => {
+ decode.arbitration.isValid clearWhen(forceNoDecodeCond)
+ })
+
+ //Formal verification signals generation, miss prediction stuff ?
+ val formal = new Area {
+ val raw = if(compressedGen) decompressor.raw else inputBeforeStage.rsp.inst
+ val rawInDecode = Delay(raw, if(injectorStage) 1 else 0, when = decodeInput.ready)
+ decode.insert(FORMAL_INSTRUCTION) := rawInDecode
+
+ decode.insert(FORMAL_PC_NEXT) := (if (compressedGen)
+ decode.input(PC) + ((decode.input(IS_RVC)) ? U(2) | U(4))
+ else
+ decode.input(PC) + 4)
+
+ if(decodePc != null && decodePc.predictionPcLoad != null){
+ when(decodePc.predictionPcLoad.valid){
+ decode.insert(FORMAL_PC_NEXT) := decodePc.predictionPcLoad.payload
+ }
+ }
+
+ jumpInfos.foreach(info => {
+ when(info.interface.valid) {
+ info.stage.output(FORMAL_PC_NEXT) := info.interface.payload
+ }
+ })
+ }
+ }
+
+ def stage1ToInjectorPipe[T <: Data](input : T): (T, T, T) ={
+ val iBusRspContext = iBusRsp.stages.drop(1).dropRight(1).foldLeft(input)((data,stage) => RegNextWhen(data, stage.output.ready))
+
+ val iBusRspContextOutput = cloneOf(input)
+ iBusRspContextOutput := iBusRspContext
+ val injectorContext = Delay(iBusRspContextOutput, cycleCount=if(injectorStage) 1 else 0, when=injector.decodeInput.ready)
+ val injectorContextWire = cloneOf(input) //Allow combinatorial override
+ injectorContextWire := injectorContext
+ (iBusRspContext, iBusRspContextOutput, injectorContextWire)
+ }
+
+ val predictor = prediction match {
+ case NONE =>
+ case STATIC | DYNAMIC => {
+ def historyWidth = 2
+ val dynamic = ifGen(prediction == DYNAMIC) (new Area {
+ case class BranchPredictorLine() extends Bundle{
+ val history = SInt(historyWidth bits)
+ }
+
+ val historyCache = Mem(BranchPredictorLine(), 1 << historyRamSizeLog2)
+ val historyWrite = historyCache.writePort
+ val historyWriteLast = RegNextWhen(historyWrite, iBusRsp.stages(0).output.ready)
+ val hazard = historyWriteLast.valid && historyWriteLast.address === (iBusRsp.stages(0).input.payload >> 2).resized
+
+ case class DynamicContext() extends Bundle{
+ val hazard = Bool
+ val line = BranchPredictorLine()
+ }
+ val fetchContext = DynamicContext()
+ fetchContext.hazard := hazard
+ fetchContext.line := historyCache.readSync((fetchPc.output.payload >> 2).resized, iBusRsp.stages(0).output.ready || externalFlush)
+
+ object PREDICTION_CONTEXT extends Stageable(DynamicContext())
+ decode.insert(PREDICTION_CONTEXT) := stage1ToInjectorPipe(fetchContext)._3
+ val decodeContextPrediction = decode.input(PREDICTION_CONTEXT).line.history.msb
+
+ val branchStage = decodePrediction.stage
+ val branchContext = branchStage.input(PREDICTION_CONTEXT)
+ val moreJump = decodePrediction.rsp.wasWrong ^ branchContext.line.history.msb
+
+ historyWrite.address := branchStage.input(PC)(2, historyRamSizeLog2 bits) + (if(pipeline.config.withRvc)
+ ((!branchStage.input(IS_RVC) && branchStage.input(PC)(1)) ? U(1) | U(0))
+ else
+ U(0))
+
+ historyWrite.data.history := branchContext.line.history + (moreJump ? S(-1) | S(1))
+ val sat = (branchContext.line.history === (moreJump ? S(branchContext.line.history.minValue) | S(branchContext.line.history.maxValue)))
+ historyWrite.valid := !branchContext.hazard && branchStage.arbitration.isFiring && branchStage.input(BRANCH_CTRL) === BranchCtrlEnum.B && !sat
+ })
+
+
+ val imm = IMM(decode.input(INSTRUCTION))
+
+ val conditionalBranchPrediction = prediction match {
+ case STATIC => imm.b_sext.msb
+ case DYNAMIC => dynamic.decodeContextPrediction
+ }
+
+ decodePrediction.cmd.hadBranch := decode.input(BRANCH_CTRL) === BranchCtrlEnum.JAL || (decode.input(BRANCH_CTRL) === BranchCtrlEnum.B && conditionalBranchPrediction)
+
+ val noPredictionOnMissaligned = (!pipeline.config.withRvc) generate new Area{
+ val missaligned = decode.input(BRANCH_CTRL).mux(
+ BranchCtrlEnum.JAL -> imm.j_sext(1),
+ default -> imm.b_sext(1)
+ )
+ decodePrediction.cmd.hadBranch clearWhen(missaligned)
+ }
+
+ //TODO no more fireing depedancies
+ predictionJumpInterface.valid := decode.arbitration.isValid && decodePrediction.cmd.hadBranch
+ predictionJumpInterface.payload := decode.input(PC) + ((decode.input(BRANCH_CTRL) === BranchCtrlEnum.JAL) ? imm.j_sext | imm.b_sext).asUInt
+ decode.arbitration.flushNext setWhen(predictionJumpInterface.valid)
+
+ if(relaxPredictorAddress) KeepAttribute(predictionJumpInterface.payload)
+ }
+ case DYNAMIC_TARGET => new Area{
+// assert(!compressedGen || cmdToRspStageCount == 1, "Can't combine DYNAMIC_TARGET and RVC as it could stop the instruction fetch mid-air")
+
+ case class BranchPredictorLine() extends Bundle{
+ val source = Bits(30 - historyRamSizeLog2 bits)
+ val branchWish = UInt(2 bits)
+ val last2Bytes = ifGen(compressedGen)(Bool)
+ val target = UInt(32 bits)
+ }
+
+ val history = Mem(BranchPredictorLine(), 1 << historyRamSizeLog2)
+ val historyWriteDelayPatched = history.writePort
+ val historyWrite = cloneOf(historyWriteDelayPatched)
+ historyWriteDelayPatched.valid := historyWrite.valid
+ historyWriteDelayPatched.address := (if(predictionBuffer) historyWrite.address - 1 else historyWrite.address)
+ historyWriteDelayPatched.data := historyWrite.data
+
+
+ val writeLast = RegNextWhen(historyWriteDelayPatched, iBusRsp.stages(0).output.ready)
+
+ //Avoid write to read hazard
+ val buffer = predictionBuffer generate new Area{
+ val line = history.readSync((iBusRsp.stages(0).input.payload >> 2).resized, iBusRsp.stages(0).output.ready)
+ val pcCorrected = RegNextWhen(fetchPc.corrected, iBusRsp.stages(0).input.ready)
+ val hazard = (writeLast.valid && writeLast.address === (iBusRsp.stages(1).input.payload >> 2).resized)
+ }
+
+ val (line, hazard) = predictionBuffer match {
+ case true =>
+ (RegNextWhen(buffer.line, iBusRsp.stages(0).output.ready),
+ RegNextWhen(buffer.hazard, iBusRsp.stages(0).output.ready) || buffer.pcCorrected)
+ case false =>
+ (history.readSync((iBusRsp.stages(0).input.payload >> 2).resized,
+ iBusRsp.stages(0).output.ready), writeLast.valid && writeLast.address === (iBusRsp.stages(1).input.payload >> 2).resized)
+ }
+
+ val hit = line.source === (iBusRsp.stages(1).input.payload.asBits >> 2 + historyRamSizeLog2)
+ if(compressedGen) hit clearWhen(!line.last2Bytes && iBusRsp.stages(1).input.payload(1))
+
+ fetchPc.predictionPcLoad.valid := line.branchWish.msb && hit && !hazard && iBusRsp.stages(1).input.valid
+ fetchPc.predictionPcLoad.payload := line.target
+
+ case class PredictionResult() extends Bundle{
+ val hazard = Bool
+ val hit = Bool
+ val line = BranchPredictorLine()
+ }
+
+ val fetchContext = PredictionResult()
+ fetchContext.hazard := hazard
+ fetchContext.hit := hit
+ fetchContext.line := line
+
+ val (iBusRspContext, iBusRspContextOutput, injectorContext) = stage1ToInjectorPipe(fetchContext)
+
+ object PREDICTION_CONTEXT extends Stageable(PredictionResult())
+ pipeline.decode.insert(PREDICTION_CONTEXT) := injectorContext
+ val branchStage = fetchPrediction.stage
+ val branchContext = branchStage.input(PREDICTION_CONTEXT)
+
+ fetchPrediction.cmd.hadBranch := branchContext.hit && !branchContext.hazard && branchContext.line.branchWish.msb
+ fetchPrediction.cmd.targetPc := branchContext.line.target
+
+
+ historyWrite.valid := False
+ historyWrite.address := fetchPrediction.rsp.sourceLastWord(2, historyRamSizeLog2 bits)
+ historyWrite.data.source := fetchPrediction.rsp.sourceLastWord.asBits >> 2 + historyRamSizeLog2
+ historyWrite.data.target := fetchPrediction.rsp.finalPc
+ if(compressedGen) historyWrite.data.last2Bytes := fetchPrediction.stage.input(PC)(1) && fetchPrediction.stage.input(IS_RVC)
+
+ when(fetchPrediction.rsp.wasRight) {
+ historyWrite.valid := branchContext.hit
+ historyWrite.data.branchWish := branchContext.line.branchWish + (branchContext.line.branchWish === 2).asUInt - (branchContext.line.branchWish === 1).asUInt
+ } otherwise {
+ when(branchContext.hit) {
+ historyWrite.valid := True
+ historyWrite.data.branchWish := branchContext.line.branchWish - (branchContext.line.branchWish.msb).asUInt + (!branchContext.line.branchWish.msb).asUInt
+ } otherwise {
+ historyWrite.valid := True
+ historyWrite.data.branchWish := "10"
+ }
+ }
+
+ historyWrite.valid clearWhen(branchContext.hazard || !branchStage.arbitration.isFiring)
+
+ val compressor = compressedGen generate new Area{
+ val predictionBranch = iBusRspContext.hit && !iBusRspContext.hazard && iBusRspContext.line.branchWish(1)
+ val unalignedWordIssue = iBusRsp.output.valid && predictionBranch && iBusRspContext.line.last2Bytes && Mux(decompressor.unaligned, !decompressor.isInputHighRvc, decompressor.isInputLowRvc && !decompressor.isInputHighRvc)
+
+ when(unalignedWordIssue){
+ historyWrite.valid := True
+ historyWrite.address := (iBusRsp.stages(1).input.payload >> 2).resized
+ historyWrite.data.branchWish := 0
+
+ iBusRsp.redoFetch := True
+ }
+
+ //Do not trigger prediction hit when it is one for the upper RVC word and we aren't there yet
+ iBusRspContextOutput.hit clearWhen(iBusRspContext.line.last2Bytes && (decompressor.bufferValid || (!decompressor.throw2Bytes && decompressor.isInputLowRvc)))
+
+ decodePc.predictionPcLoad.valid := injectorContext.line.branchWish.msb && injectorContext.hit && !injectorContext.hazard && injector.decodeInput.fire
+ decodePc.predictionPcLoad.payload := injectorContext.line.target
+
+ //Clean the RVC buffer when a prediction was made
+ when(iBusRspContext.line.branchWish.msb && iBusRspContextOutput.hit && !iBusRspContext.hazard && decompressor.output.fire){
+ decompressor.bufferValid := False
+ decompressor.throw2BytesReg := False
+ decompressor.input.ready := True //Drop the remaining byte if any
+ }
+ }
+ }
+ }
+
+ def stageXToIBusRsp[T <: Data](stage : Any, input : T): (T) ={
+ iBusRsp.stages.dropWhile(_ != stage).tail.foldLeft(input)((data,stage) => RegNextWhen(data, stage.output.ready))
+ }
+
+ }
+} \ No newline at end of file
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/FormalPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/FormalPlugin.scala
new file mode 100644
index 0000000..2d70ebd
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/FormalPlugin.scala
@@ -0,0 +1,135 @@
+package vexriscv.plugin
+
+import spinal.core._
+import spinal.lib._
+import vexriscv.VexRiscv
+
+case class RvfiPortRsRead() extends Bundle{
+ val addr = UInt(5 bits)
+ val rdata = Bits(32 bits)
+}
+
+case class RvfiPortRsWrite() extends Bundle{
+ val addr = UInt(5 bits)
+ val wdata = Bits(32 bits)
+}
+
+case class RvfiPortPc() extends Bundle{
+ val rdata = UInt(32 bits)
+ val wdata = UInt(32 bits)
+}
+
+
+case class RvfiPortMem() extends Bundle{
+ val addr = UInt(32 bits)
+ val rmask = Bits(4 bits)
+ val wmask = Bits(4 bits)
+ val rdata = Bits(32 bits)
+ val wdata = Bits(32 bits)
+}
+
+case class RvfiPort() extends Bundle with IMasterSlave {
+ val valid = Bool
+ val order = UInt(64 bits)
+ val insn = Bits(32 bits)
+ val trap = Bool
+ val halt = Bool
+ val intr = Bool
+ val mode = Bits(2 bits)
+ val ixl = Bits(2 bits)
+ val rs1 = RvfiPortRsRead()
+ val rs2 = RvfiPortRsRead()
+ val rd = RvfiPortRsWrite()
+ val pc = RvfiPortPc()
+ val mem = RvfiPortMem()
+
+ override def asMaster(): Unit = out(this)
+}
+
+
+//Tool stuff
+//https://www.reddit.com/r/yosys/comments/77g5hn/unsupported_cell_type_error_adff/
+//rd_addr == 0 => no rd_wdata check
+//instruction that doesn't use RSx have to force the formal port address to zero
+
+//feature added
+//Halt CPU on decoding exception
+
+//VexRiscv changes
+//
+
+//VexRiscv bug
+//1) pcManagerService.createJumpInterface(pipeline.execute)
+// pcManagerService.createJumpInterface(if(earlyBranch) pipeline.execute else pipeline.memory)
+//2) JALR => clear PC(0)
+//3) input(INSTRUCTION)(5) REGFILE_WRITE_VALID memory read with exception would not fire properly
+
+class FormalPlugin extends Plugin[VexRiscv]{
+
+ var rvfi : RvfiPort = null
+
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ rvfi = master(RvfiPort()).setName("rvfi")
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ import vexriscv.Riscv._
+
+ writeBack plug new Area{
+ import writeBack._
+
+ val order = Reg(UInt(64 bits)) init(0)
+ when(arbitration.isFiring){
+ order := order + 1
+ }
+
+
+ rvfi.valid := arbitration.isFiring
+ rvfi.order := order
+ rvfi.insn := output(FORMAL_INSTRUCTION)
+ rvfi.trap := False
+ rvfi.halt := False
+ rvfi.intr := False
+ rvfi.mode := 3
+ rvfi.ixl := 1
+// rvfi.rs1.addr := output(INSTRUCTION)(rs1Range).asUInt
+// rvfi.rs2.addr := output(INSTRUCTION)(rs2Range).asUInt
+// rvfi.rs1.rdata := output(RS1)
+// rvfi.rs2.rdata := output(RS2)
+ rvfi.rs1.addr := output(RS1_USE) ? output(INSTRUCTION)(rs1Range).asUInt | U(0)
+ rvfi.rs2.addr := output(RS2_USE) ? output(INSTRUCTION)(rs2Range).asUInt | U(0)
+ rvfi.rs1.rdata := output(RS1_USE) ? output(RS1) | B(0)
+ rvfi.rs2.rdata := output(RS2_USE) ? output(RS2) | B(0)
+ rvfi.rd.addr := output(REGFILE_WRITE_VALID) ? (output(INSTRUCTION)(rdRange).asUInt) | U(0)
+ rvfi.rd.wdata := output(REGFILE_WRITE_VALID) ? output(REGFILE_WRITE_DATA) | B(0)
+ rvfi.pc.rdata := output(PC)
+ rvfi.pc.wdata := output(FORMAL_PC_NEXT)
+ rvfi.mem.addr := output(FORMAL_MEM_ADDR)
+ rvfi.mem.rmask := output(FORMAL_MEM_RMASK)
+ rvfi.mem.wmask := output(FORMAL_MEM_WMASK)
+ rvfi.mem.rdata := output(FORMAL_MEM_RDATA)
+ rvfi.mem.wdata := output(FORMAL_MEM_WDATA)
+
+ val haltRequest = False
+ stages.map(s => {
+ when(s.arbitration.isValid && s.output(FORMAL_HALT)){ //Stage is exception halted
+ when(stages.drop(indexOf(s) + 1).map(!_.arbitration.isValid).foldLeft(True)(_ && _)){ //There nothing in futher stages
+ haltRequest := True
+ }
+ }
+ })
+
+ when(Delay(haltRequest, 5, init=False)){ //Give time for value propagation from decode stage to writeback stage
+ rvfi.valid := True
+ rvfi.trap := True
+ rvfi.halt := True
+ }
+
+ val haltFired = RegInit(False) setWhen(rvfi.valid && rvfi.halt)
+ rvfi.valid clearWhen(haltFired)
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/FpuPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/FpuPlugin.scala
new file mode 100644
index 0000000..3e664f5
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/FpuPlugin.scala
@@ -0,0 +1,314 @@
+package vexriscv.plugin
+
+import spinal.core._
+import spinal.core.internals.{BoolLiteral, Literal}
+import spinal.lib._
+import vexriscv._
+import vexriscv.Riscv._
+import vexriscv.ip.fpu._
+
+import scala.collection.mutable.ArrayBuffer
+
+class FpuPlugin(externalFpu : Boolean = false,
+ simHalt : Boolean = false,
+ val p : FpuParameter) extends Plugin[VexRiscv] with VexRiscvRegressionArg {
+
+ object FPU_ENABLE extends Stageable(Bool())
+ object FPU_COMMIT extends Stageable(Bool())
+ object FPU_COMMIT_SYNC extends Stageable(Bool())
+ object FPU_COMMIT_LOAD extends Stageable(Bool())
+ object FPU_RSP extends Stageable(Bool())
+ object FPU_FORKED extends Stageable(Bool())
+ object FPU_OPCODE extends Stageable(FpuOpcode())
+ object FPU_ARG extends Stageable(Bits(2 bits))
+ object FPU_FORMAT extends Stageable(FpuFormat())
+
+ var port : FpuPort = null //Commit port is already isolated
+
+ override def getVexRiscvRegressionArgs(): Seq[String] = {
+ var args = List[String]()
+ args :+= "RVF=yes"
+ if(p.withDouble) args :+= "RVD=yes"
+ args
+ }
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+
+ type ENC = (Stageable[_ <: BaseType],Any)
+
+ val intRfWrite = List[ENC](
+ FPU_ENABLE -> True,
+ FPU_COMMIT -> False,
+ FPU_RSP -> True,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> False
+ )
+
+ val floatRfWrite = List[ENC](
+ FPU_ENABLE -> True,
+ FPU_COMMIT -> True,
+ FPU_RSP -> False
+ )
+
+ val addSub = floatRfWrite :+ FPU_OPCODE -> FpuOpcode.ADD
+ val mul = floatRfWrite :+ FPU_OPCODE -> FpuOpcode.MUL
+ val fma = floatRfWrite :+ FPU_OPCODE -> FpuOpcode.FMA
+ val div = floatRfWrite :+ FPU_OPCODE -> FpuOpcode.DIV
+ val sqrt = floatRfWrite :+ FPU_OPCODE -> FpuOpcode.SQRT
+ val fsgnj = floatRfWrite :+ FPU_OPCODE -> FpuOpcode.SGNJ
+ val fminMax = floatRfWrite :+ FPU_OPCODE -> FpuOpcode.MIN_MAX
+ val fmvWx = floatRfWrite :+ FPU_OPCODE -> FpuOpcode.FMV_W_X :+ RS1_USE -> True
+ val fcvtI2f = floatRfWrite :+ FPU_OPCODE -> FpuOpcode.I2F :+ RS1_USE -> True
+ val fcvtxx = floatRfWrite :+ FPU_OPCODE -> FpuOpcode.FCVT_X_X
+
+ val fcmp = intRfWrite :+ FPU_OPCODE -> FpuOpcode.CMP
+ val fclass = intRfWrite :+ FPU_OPCODE -> FpuOpcode.FCLASS
+ val fmvXw = intRfWrite :+ FPU_OPCODE -> FpuOpcode.FMV_X_W
+ val fcvtF2i = intRfWrite :+ FPU_OPCODE -> FpuOpcode.F2I
+
+ val fl = List[ENC](
+ FPU_ENABLE -> True,
+ FPU_OPCODE -> FpuOpcode.LOAD,
+ FPU_COMMIT -> True,
+ FPU_RSP -> False
+ )
+
+ val fs = List[ENC](
+ FPU_ENABLE -> True,
+ FPU_OPCODE -> FpuOpcode.STORE,
+ FPU_COMMIT -> False,
+ FPU_RSP -> True
+ )
+
+
+ def arg(v : Int) = FPU_ARG -> B(v, 2 bits)
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(FPU_ENABLE, False)
+
+ val f32 = FPU_FORMAT -> FpuFormat.FLOAT
+ val f64 = FPU_FORMAT -> FpuFormat.DOUBLE
+
+ decoderService.add(List(
+ FADD_S -> (addSub :+ f32 :+ arg(0)),
+ FSUB_S -> (addSub :+ f32 :+ arg(1)),
+ FMADD_S -> (fma :+ f32 :+ arg(0)),
+ FMSUB_S -> (fma :+ f32 :+ arg(2)),
+ FNMADD_S -> (fma :+ f32 :+ arg(3)),
+ FNMSUB_S -> (fma :+ f32 :+ arg(1)),
+ FMUL_S -> (mul :+ f32 :+ arg(0)),
+ FDIV_S -> (div :+ f32 ),
+ FSQRT_S -> (sqrt :+ f32 ),
+ FLW -> (fl :+ f32 ),
+ FSW -> (fs :+ f32 ),
+ FCVT_S_WU -> (fcvtI2f :+ f32 :+ arg(0)),
+ FCVT_S_W -> (fcvtI2f :+ f32 :+ arg(1)),
+ FCVT_WU_S -> (fcvtF2i :+ f32 :+ arg(0)),
+ FCVT_W_S -> (fcvtF2i :+ f32 :+ arg(1)),
+ FCLASS_S -> (fclass :+ f32 ),
+ FLE_S -> (fcmp :+ f32 :+ arg(0)),
+ FEQ_S -> (fcmp :+ f32 :+ arg(2)),
+ FLT_S -> (fcmp :+ f32 :+ arg(1)),
+ FSGNJ_S -> (fsgnj :+ f32 :+ arg(0)),
+ FSGNJN_S -> (fsgnj :+ f32 :+ arg(1)),
+ FSGNJX_S -> (fsgnj :+ f32 :+ arg(2)),
+ FMIN_S -> (fminMax :+ f32 :+ arg(0)),
+ FMAX_S -> (fminMax :+ f32 :+ arg(1)),
+ FMV_X_W -> (fmvXw :+ f32 ),
+ FMV_W_X -> (fmvWx :+ f32 )
+ ))
+
+ if(p.withDouble){
+ decoderService.add(List(
+ FADD_D -> (addSub :+ f64 :+ arg(0)),
+ FSUB_D -> (addSub :+ f64 :+ arg(1)),
+ FMADD_D -> (fma :+ f64 :+ arg(0)),
+ FMSUB_D -> (fma :+ f64 :+ arg(2)),
+ FNMADD_D -> (fma :+ f64 :+ arg(3)),
+ FNMSUB_D -> (fma :+ f64 :+ arg(1)),
+ FMUL_D -> (mul :+ f64 :+ arg(0)),
+ FDIV_D -> (div :+ f64 ),
+ FSQRT_D -> (sqrt :+ f64 ),
+ FLD -> (fl :+ f64 ),
+ FSD -> (fs :+ f64 ),
+ FCVT_D_WU -> (fcvtI2f :+ f64 :+ arg(0)),
+ FCVT_D_W -> (fcvtI2f :+ f64 :+ arg(1)),
+ FCVT_WU_D -> (fcvtF2i :+ f64 :+ arg(0)),
+ FCVT_W_D -> (fcvtF2i :+ f64 :+ arg(1)),
+ FCLASS_D -> (fclass :+ f64 ),
+ FLE_D -> (fcmp :+ f64 :+ arg(0)),
+ FEQ_D -> (fcmp :+ f64 :+ arg(2)),
+ FLT_D -> (fcmp :+ f64 :+ arg(1)),
+ FSGNJ_D -> (fsgnj :+ f64 :+ arg(0)),
+ FSGNJN_D -> (fsgnj :+ f64 :+ arg(1)),
+ FSGNJX_D -> (fsgnj :+ f64 :+ arg(2)),
+ FMIN_D -> (fminMax :+ f64 :+ arg(0)),
+ FMAX_D -> (fminMax :+ f64 :+ arg(1)),
+ FCVT_D_S -> (fcvtxx :+ f32),
+ FCVT_S_D -> (fcvtxx :+ f64)
+ ))
+ }
+ //TODO FMV_X_X + doubles
+
+ port = FpuPort(p).addTag(Verilator.public)
+ if(externalFpu) master(port)
+
+ val dBusEncoding = pipeline.service(classOf[DBusEncodingService])
+ dBusEncoding.addLoadWordEncoding(FLW)
+ dBusEncoding.addStoreWordEncoding(FSW)
+ if(p.withDouble) {
+ dBusEncoding.addLoadWordEncoding(FLD)
+ dBusEncoding.addStoreWordEncoding(FSD)
+ }
+
+// exposeEncoding()
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ import Riscv._
+
+ val internal = (!externalFpu).generate (pipeline plug new Area{
+ val fpu = FpuCore(1, p)
+ if(simHalt) {
+ val cmdHalt = in(Bool).setName("fpuCmdHalt").addAttribute(Verilator.public)
+ val commitHalt = in(Bool).setName("fpuCommitHalt").addAttribute(Verilator.public)
+ val rspHalt = in(Bool).setName("fpuRspHalt").addAttribute(Verilator.public)
+ fpu.io.port(0).cmd << port.cmd.haltWhen(cmdHalt)
+ fpu.io.port(0).commit << port.commit.haltWhen(commitHalt)
+ fpu.io.port(0).rsp.haltWhen(rspHalt) >> port.rsp
+ fpu.io.port(0).completion <> port.completion
+ } else {
+ fpu.io.port(0).cmd << port.cmd
+ fpu.io.port(0).commit << port.commit
+ fpu.io.port(0).rsp >> port.rsp
+ fpu.io.port(0).completion <> port.completion
+ }
+ })
+
+
+ val csr = pipeline plug new Area{
+ val pendings = Reg(UInt(6 bits)) init(0)
+ pendings := pendings + U(port.cmd.fire) - U(port.completion.fire) - U(port.rsp.fire)
+
+ val hasPending = pendings =/= 0
+
+ val flags = Reg(FpuFlags())
+ flags.NV init(False) setWhen(port.completion.fire && port.completion.flags.NV)
+ flags.DZ init(False) setWhen(port.completion.fire && port.completion.flags.DZ)
+ flags.OF init(False) setWhen(port.completion.fire && port.completion.flags.OF)
+ flags.UF init(False) setWhen(port.completion.fire && port.completion.flags.UF)
+ flags.NX init(False) setWhen(port.completion.fire && port.completion.flags.NX)
+
+ val service = pipeline.service(classOf[CsrInterface])
+ val rm = Reg(Bits(3 bits)) init(0)
+
+ service.rw(CSR.FCSR, 5, rm)
+ service.rw(CSR.FCSR, 0, flags)
+ service.rw(CSR.FRM, 0, rm)
+ service.rw(CSR.FFLAGS, 0, flags)
+
+ val csrActive = service.duringAny()
+ execute.arbitration.haltByOther setWhen(csrActive && hasPending) // pessimistic
+
+ val fs = Reg(Bits(2 bits)) init(1)
+ val sd = fs === 3
+
+ when(stages.last.arbitration.isFiring && stages.last.input(FPU_ENABLE) && stages.last.input(FPU_OPCODE) =/= FpuOpcode.STORE){
+ fs := 3 //DIRTY
+ }
+
+ service.rw(CSR.SSTATUS, 13, fs)
+ service.rw(CSR.MSTATUS, 13, fs)
+
+ service.r(CSR.SSTATUS, 31, sd)
+ service.r(CSR.MSTATUS, 31, sd)
+ }
+
+ decode plug new Area{
+ import decode._
+
+ //Maybe it might be better to not fork before fire to avoid RF stall on commits
+ val forked = Reg(Bool) setWhen(port.cmd.fire) clearWhen(!arbitration.isStuck) init(False)
+
+ val hazard = csr.pendings.msb || csr.csrActive
+
+ arbitration.haltItself setWhen(arbitration.isValid && input(FPU_ENABLE) && hazard)
+ arbitration.haltItself setWhen(port.cmd.isStall)
+
+ val iRoundMode = input(INSTRUCTION)(funct3Range)
+ val roundMode = (input(INSTRUCTION)(funct3Range) === B"111") ? csr.rm | input(INSTRUCTION)(funct3Range)
+
+ port.cmd.valid := arbitration.isValid && input(FPU_ENABLE) && !forked && !hazard
+ port.cmd.opcode := input(FPU_OPCODE)
+ port.cmd.arg := input(FPU_ARG)
+ port.cmd.rs1 := input(INSTRUCTION)(rs1Range).asUInt
+ port.cmd.rs2 := input(INSTRUCTION)(rs2Range).asUInt
+ port.cmd.rs3 := input(INSTRUCTION)(rs3Range).asUInt
+ port.cmd.rd := input(INSTRUCTION)(rdRange).asUInt
+ port.cmd.format := (if(p.withDouble) input(FPU_FORMAT) else FpuFormat.FLOAT())
+ port.cmd.roundMode := roundMode.as(FpuRoundMode())
+
+ insert(FPU_FORKED) := forked || port.cmd.fire
+
+ insert(FPU_COMMIT_SYNC) := List(FpuOpcode.LOAD, FpuOpcode.FMV_W_X, FpuOpcode.I2F).map(_ === input(FPU_OPCODE)).orR
+ insert(FPU_COMMIT_LOAD) := input(FPU_OPCODE) === FpuOpcode.LOAD
+
+ if(serviceExist(classOf[IWake])) when(forked){
+ service(classOf[IWake]).askWake() //Ensure that no WFI followed by a FPU stall the FPU interface for other CPU
+ }
+ }
+
+ writeBack plug new Area{ //WARNING IF STAGE CHANGE, update the regression rsp capture filter for the golden model (top->VexRiscv->lastStageIsFiring)
+ import writeBack._
+
+ val dBusEncoding = pipeline.service(classOf[DBusEncodingService])
+ val isRsp = input(FPU_FORKED) && input(FPU_RSP)
+ val isCommit = input(FPU_FORKED) && input(FPU_COMMIT)
+ val storeFormated = CombInit(port.rsp.value)
+ if(p.withDouble) when(!input(INSTRUCTION)(12)){
+ storeFormated(32, 32 bits) := port.rsp.value(0, 32 bits)
+ }
+ //Manage $store and port.rsp
+ port.rsp.ready := False
+ when(isRsp){
+ when(arbitration.isValid) {
+ dBusEncoding.bypassStore(storeFormated)
+ output(REGFILE_WRITE_DATA) := port.rsp.value(31 downto 0)
+ when(!arbitration.isStuck && !arbitration.isRemoved){
+ csr.flags.NV setWhen(port.rsp.NV)
+ csr.flags.NX setWhen(port.rsp.NX)
+ }
+ }
+ when(!port.rsp.valid){
+ arbitration.haltByOther := True
+ } elsewhen(!arbitration.haltItself){
+ port.rsp.ready := True
+ }
+ }
+
+ // Manage $load
+ val commit = Stream(FpuCommit(p)).addTag(Verilator.public)
+ commit.valid := isCommit && !arbitration.isStuck
+ commit.value(31 downto 0) := (input(FPU_COMMIT_LOAD) ? dBusEncoding.loadData()(31 downto 0) | input(RS1))
+ if(p.withDouble) commit.value(63 downto 32) := dBusEncoding.loadData()(63 downto 32)
+ commit.write := arbitration.isValid && !arbitration.removeIt
+ commit.opcode := input(FPU_OPCODE)
+ commit.rd := input(INSTRUCTION)(rdRange).asUInt
+
+ when(isCommit && !commit.ready){
+ arbitration.haltByOther := True
+ }
+
+ port.commit << commit.pipelined(s2m = true, m2s = false)
+ }
+
+ pipeline.stages.dropRight(1).foreach(s => s.output(FPU_FORKED) clearWhen(s.arbitration.isStuck))
+
+ Component.current.afterElaboration{
+ pipeline.stages.tail.foreach(_.input(FPU_FORKED).init(False))
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala
new file mode 100644
index 0000000..b104223
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala
@@ -0,0 +1,44 @@
+
+package vexriscv.plugin
+
+import spinal.core._
+import spinal.lib._
+import vexriscv._
+import vexriscv.Riscv._
+
+import scala.collection.mutable.ArrayBuffer
+import scala.collection.mutable
+
+
+class HaltOnExceptionPlugin() extends Plugin[VexRiscv] with ExceptionService {
+ def xlen = 32
+
+ //Mannage ExceptionService calls
+ val exceptionPortsInfos = ArrayBuffer[ExceptionPortInfo]()
+ def exceptionCodeWidth = 4
+ override def newExceptionPort(stage : Stage, priority : Int = 0, codeWidth : Int = 4) = {
+ val interface = Flow(ExceptionCause(4))
+ exceptionPortsInfos += ExceptionPortInfo(interface,stage,priority, codeWidth)
+ interface
+ }
+ override def isExceptionPending(stage : Stage): Bool = False
+
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ stages.head.insert(FORMAL_HALT) := False
+ stages.foreach(stage => {
+ val stagePorts = exceptionPortsInfos.filter(_.stage == stage)
+ if(stagePorts.nonEmpty) {
+ when(stagePorts.map(info => info.port.valid).orR) {
+ stage.output(FORMAL_HALT) := True
+ stage.arbitration.haltItself := True
+ }
+ for(stage <- stages){
+ stage.output(FORMAL_HALT) clearWhen(stage.arbitration.isFlushed)
+ }
+ }
+ })
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/HazardPessimisticPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/HazardPessimisticPlugin.scala
new file mode 100644
index 0000000..5a8f4d3
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/HazardPessimisticPlugin.scala
@@ -0,0 +1,24 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+import spinal.lib._
+
+
+class HazardPessimisticPlugin() extends Plugin[VexRiscv] {
+ import Riscv._
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(HAS_SIDE_EFFECT, False)
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ val writesInPipeline = stages.dropWhile(_ != execute).map(s => s.arbitration.isValid && s.input(REGFILE_WRITE_VALID)) :+ RegNext(stages.last.arbitration.isValid && stages.last.input(REGFILE_WRITE_VALID))
+ decode.arbitration.haltByOther.setWhen(decode.arbitration.isValid && writesInPipeline.orR)
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/HazardSimplePlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/HazardSimplePlugin.scala
new file mode 100644
index 0000000..1b650e3
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/HazardSimplePlugin.scala
@@ -0,0 +1,125 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+import spinal.lib._
+
+trait HazardService{
+ def hazardOnExecuteRS : Bool
+}
+
+class HazardSimplePlugin(bypassExecute : Boolean = false,
+ bypassMemory: Boolean = false,
+ bypassWriteBack: Boolean = false,
+ bypassWriteBackBuffer : Boolean = false,
+ pessimisticUseSrc : Boolean = false,
+ pessimisticWriteRegFile : Boolean = false,
+ pessimisticAddressMatch : Boolean = false) extends Plugin[VexRiscv] with HazardService{
+ import Riscv._
+
+
+ def hazardOnExecuteRS = {
+ if(pipeline.service(classOf[RegFileService]).readStage() == pipeline.execute) pipeline.execute.arbitration.isStuckByOthers else False //TODO not so nice
+ }
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(HAS_SIDE_EFFECT, False) //TODO implement it in each plugin
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ pipeline plug new Area {
+ val src0Hazard = False
+ val src1Hazard = False
+
+ val readStage = service(classOf[RegFileService]).readStage()
+
+ def trackHazardWithStage(stage: Stage, bypassable: Boolean, runtimeBypassable: Stageable[Bool]): Unit = {
+ val runtimeBypassableValue = if (runtimeBypassable != null) stage.input(runtimeBypassable) else True
+ val addr0Match = if (pessimisticAddressMatch) True else stage.input(INSTRUCTION)(rdRange) === readStage.input(INSTRUCTION)(rs1Range)
+ val addr1Match = if (pessimisticAddressMatch) True else stage.input(INSTRUCTION)(rdRange) === readStage.input(INSTRUCTION)(rs2Range)
+ when(stage.arbitration.isValid && stage.input(REGFILE_WRITE_VALID)) {
+ if (bypassable) {
+ when(runtimeBypassableValue) {
+ when(addr0Match) {
+ readStage.input(RS1) := stage.output(REGFILE_WRITE_DATA)
+ }
+ when(addr1Match) {
+ readStage.input(RS2) := stage.output(REGFILE_WRITE_DATA)
+ }
+ }
+ }
+ }
+ when(stage.arbitration.isValid && (if (pessimisticWriteRegFile) True else stage.input(REGFILE_WRITE_VALID))) {
+ when((Bool(!bypassable) || !runtimeBypassableValue)) {
+ when(addr0Match) {
+ src0Hazard := True
+ }
+ when(addr1Match) {
+ src1Hazard := True
+ }
+ }
+ }
+ }
+
+
+ val writeBackWrites = Flow(cloneable(new Bundle {
+ val address = Bits(5 bits)
+ val data = Bits(32 bits)
+ }))
+ writeBackWrites.valid := stages.last.output(REGFILE_WRITE_VALID) && stages.last.arbitration.isFiring
+ writeBackWrites.address := stages.last.output(INSTRUCTION)(rdRange)
+ writeBackWrites.data := stages.last.output(REGFILE_WRITE_DATA)
+ val writeBackBuffer = writeBackWrites.stage()
+
+ val addr0Match = if (pessimisticAddressMatch) True else writeBackBuffer.address === readStage.input(INSTRUCTION)(rs1Range)
+ val addr1Match = if (pessimisticAddressMatch) True else writeBackBuffer.address === readStage.input(INSTRUCTION)(rs2Range)
+ when(writeBackBuffer.valid) {
+ if (bypassWriteBackBuffer) {
+ when(addr0Match) {
+ readStage.input(RS1) := writeBackBuffer.data
+ }
+ when(addr1Match) {
+ readStage.input(RS2) := writeBackBuffer.data
+ }
+ } else {
+ when(addr0Match) {
+ src0Hazard := True
+ }
+ when(addr1Match) {
+ src1Hazard := True
+ }
+ }
+ }
+
+ if (withWriteBackStage) trackHazardWithStage(writeBack, bypassWriteBack, null)
+ if (withMemoryStage) trackHazardWithStage(memory, bypassMemory, if (stages.last == memory) null else BYPASSABLE_MEMORY_STAGE)
+ if (readStage != execute) trackHazardWithStage(execute, bypassExecute, if (stages.last == execute) null else BYPASSABLE_EXECUTE_STAGE)
+
+
+ if (!pessimisticUseSrc) {
+ when(!readStage.input(RS1_USE)) {
+ src0Hazard := False
+ }
+ when(!readStage.input(RS2_USE)) {
+ src1Hazard := False
+ }
+ }
+
+ when(readStage.arbitration.isValid && (src0Hazard || src1Hazard)) {
+ readStage.arbitration.haltByOther := True
+ }
+ }
+ }
+}
+
+
+class NoHazardPlugin extends Plugin[VexRiscv] with HazardService {
+ override def build(pipeline: VexRiscv): Unit = {}
+
+ def hazardOnExecuteRS = False
+} \ No newline at end of file
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala
new file mode 100644
index 0000000..035c5dc
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala
@@ -0,0 +1,290 @@
+package vexriscv.plugin
+
+import vexriscv.{plugin, _}
+import vexriscv.ip._
+import spinal.core._
+import spinal.lib._
+
+import scala.collection.mutable.ArrayBuffer
+
+//class IBusCachedPlugin(config : InstructionCacheConfig, memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv] {
+// var iBus : InstructionCacheMemBus = null
+// override def build(pipeline: VexRiscv): Unit = ???
+//}
+
+case class TightlyCoupledBus() extends Bundle with IMasterSlave {
+ val enable = Bool()
+ val address = UInt(32 bits)
+ val data = Bits(32 bits)
+
+ override def asMaster(): Unit = {
+ out(enable, address)
+ in(data)
+ }
+}
+
+case class TightlyCoupledPortParameter(name : String, hit : UInt => Bool)
+case class TightlyCoupledPort(p : TightlyCoupledPortParameter, var bus : TightlyCoupledBus)
+class IBusCachedPlugin(resetVector : BigInt = 0x80000000l,
+ relaxedPcCalculation : Boolean = false,
+ prediction : BranchPrediction = NONE,
+ historyRamSizeLog2 : Int = 10,
+ compressedGen : Boolean = false,
+ keepPcPlus4 : Boolean = false,
+ val config : InstructionCacheConfig,
+ memoryTranslatorPortConfig : Any = null,
+ injectorStage : Boolean = false,
+ withoutInjectorStage : Boolean = false,
+ relaxPredictorAddress : Boolean = true,
+ predictionBuffer : Boolean = true) extends IBusFetcherImpl(
+ resetVector = resetVector,
+ keepPcPlus4 = keepPcPlus4,
+ decodePcGen = compressedGen,
+ compressedGen = compressedGen,
+ cmdToRspStageCount = (if(config.twoCycleCache) 2 else 1) + (if(relaxedPcCalculation) 1 else 0),
+ allowPcRegReusedForSecondStage = true,
+ injectorReadyCutGen = false,
+ prediction = prediction,
+ historyRamSizeLog2 = historyRamSizeLog2,
+ injectorStage = (!config.twoCycleCache && !withoutInjectorStage) || injectorStage,
+ relaxPredictorAddress = relaxPredictorAddress,
+ fetchRedoGen = true,
+ predictionBuffer = predictionBuffer) with VexRiscvRegressionArg{
+ import config._
+
+
+
+ assert(isPow2(cacheSize))
+ assert(!(memoryTranslatorPortConfig != null && config.cacheSize/config.wayCount > 4096), "When the I$ is used with MMU, each way can't be bigger than a page (4096 bytes)")
+
+
+ assert(!(withoutInjectorStage && injectorStage))
+
+
+ override def getVexRiscvRegressionArgs(): Seq[String] = {
+ var args = List[String]()
+ args :+= "IBUS=CACHED"
+ args :+= s"IBUS_DATA_WIDTH=$memDataWidth"
+ args :+= s"COMPRESSED=${if(compressedGen) "yes" else "no"}"
+ args
+ }
+
+ var iBus : InstructionCacheMemBus = null
+ var mmuBus : MemoryTranslatorBus = null
+ var privilegeService : PrivilegeService = null
+ var decodeExceptionPort : Flow[ExceptionCause] = null
+ val tightlyCoupledPorts = ArrayBuffer[TightlyCoupledPort]()
+ def tightlyGen = tightlyCoupledPorts.nonEmpty
+
+ def newTightlyCoupledPort(p : TightlyCoupledPortParameter) = {
+ val port = TightlyCoupledPort(p, null)
+ tightlyCoupledPorts += port
+ this
+ }
+
+
+ object FLUSH_ALL extends Stageable(Bool)
+ object IBUS_ACCESS_ERROR extends Stageable(Bool)
+ object IBUS_MMU_MISS extends Stageable(Bool)
+ object IBUS_ILLEGAL_ACCESS extends Stageable(Bool)
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+
+ super.setup(pipeline)
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(FLUSH_ALL, False)
+ decoderService.add(FENCE_I, List(
+ FLUSH_ALL -> True
+ ))
+
+ if(catchSomething) {
+ val exceptionService = pipeline.service(classOf[ExceptionService])
+ decodeExceptionPort = exceptionService.newExceptionPort(pipeline.decode,1)
+ }
+
+ if(pipeline.serviceExist(classOf[MemoryTranslator]))
+ mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(MemoryTranslatorPort.PRIORITY_INSTRUCTION, memoryTranslatorPortConfig)
+
+ privilegeService = pipeline.serviceElse(classOf[PrivilegeService], PrivilegeServiceDefault())
+
+ if(pipeline.serviceExist(classOf[ReportService])){
+ val report = pipeline.service(classOf[ReportService])
+ report.add("iBus" -> {
+ val e = new BusReport()
+ val c = new CacheReport()
+ e.kind = "cached"
+ e.flushInstructions.add(0x100F) //FENCE.I
+ e.flushInstructions.add(0x13)
+ e.flushInstructions.add(0x13)
+ e.flushInstructions.add(0x13)
+
+ e.info = c
+ c.size = cacheSize
+ c.bytePerLine = bytePerLine
+
+ e
+ })
+ }
+ }
+
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ pipeline plug new FetchArea(pipeline) {
+ val cache = new InstructionCache(IBusCachedPlugin.this.config.copy(bypassGen = tightlyGen), if(mmuBus != null) mmuBus.p else MemoryTranslatorBusParameter(0,0))
+ iBus = master(new InstructionCacheMemBus(IBusCachedPlugin.this.config)).setName("iBus")
+ iBus <> cache.io.mem
+ iBus.cmd.address.allowOverride := cache.io.mem.cmd.address
+
+ //Memory bandwidth counter
+ val rspCounter = Reg(UInt(32 bits)) init(0)
+ when(iBus.rsp.valid){
+ rspCounter := rspCounter + 1
+ }
+
+ val stageOffset = if(relaxedPcCalculation) 1 else 0
+ def stages = iBusRsp.stages.drop(stageOffset)
+
+ tightlyCoupledPorts.foreach(p => p.bus = master(TightlyCoupledBus()).setName(p.p.name))
+
+ val s0 = new Area {
+ //address decoding
+ val tightlyCoupledHits = Vec(tightlyCoupledPorts.map(_.p.hit(stages(0).input.payload)))
+ val tightlyCoupledHit = tightlyCoupledHits.orR
+
+ for((port, hit) <- (tightlyCoupledPorts, tightlyCoupledHits).zipped){
+ port.bus.enable := stages(0).input.fire && hit
+ port.bus.address := stages(0).input.payload(31 downto 2) @@ U"00"
+ }
+
+ //Connect prefetch cache side
+ cache.io.cpu.prefetch.isValid := stages(0).input.valid && !tightlyCoupledHit
+ cache.io.cpu.prefetch.pc := stages(0).input.payload
+ stages(0).halt setWhen (cache.io.cpu.prefetch.haltIt)
+
+ if(mmuBus != null && mmuBus.p.latency == 1) {
+ stages(0).halt setWhen(mmuBus.busy)
+ mmuBus.cmd(0).isValid := cache.io.cpu.prefetch.isValid
+ mmuBus.cmd(0).isStuck := !stages(0).input.ready
+ mmuBus.cmd(0).virtualAddress := cache.io.cpu.prefetch.pc
+ mmuBus.cmd(0).bypassTranslation := False
+ }
+ }
+
+
+ val s1 = new Area {
+ val tightlyCoupledHits = RegNextWhen(s0.tightlyCoupledHits, stages(1).input.ready)
+ val tightlyCoupledHit = RegNextWhen(s0.tightlyCoupledHit, stages(1).input.ready)
+
+ if(tightlyGen) cache.io.cpu.fetch.dataBypassValid := tightlyCoupledHit
+ if(tightlyGen) cache.io.cpu.fetch.dataBypass := MuxOH(tightlyCoupledHits, tightlyCoupledPorts.map(e => CombInit(e.bus.data)))
+
+ //Connect fetch cache side
+ cache.io.cpu.fetch.isValid := stages(1).input.valid && !tightlyCoupledHit
+ cache.io.cpu.fetch.isStuck := !stages(1).input.ready
+ cache.io.cpu.fetch.pc := stages(1).input.payload
+
+ if(mmuBus != null) {
+ mmuBus.cmd.last.isValid := cache.io.cpu.fetch.isValid
+ mmuBus.cmd.last.isStuck := !stages(1).input.ready
+ mmuBus.cmd.last.virtualAddress := cache.io.cpu.fetch.pc
+ mmuBus.cmd.last.bypassTranslation := False
+ mmuBus.end := stages(1).input.ready || externalFlush
+ if (mmuBus.p.latency == 0) stages(1).halt setWhen (mmuBus.busy)
+ }
+
+
+ if (!twoCycleCache) {
+ cache.io.cpu.fetch.isUser := privilegeService.isUser()
+ }
+ }
+
+ val s2 = twoCycleCache generate new Area {
+ val tightlyCoupledHit = RegNextWhen(s1.tightlyCoupledHit, stages(2).input.ready)
+ cache.io.cpu.decode.isValid := stages(2).input.valid && !tightlyCoupledHit
+ cache.io.cpu.decode.isStuck := !stages(2).input.ready
+ cache.io.cpu.decode.pc := stages(2).input.payload
+ cache.io.cpu.decode.isUser := privilegeService.isUser()
+
+ if ((!twoCycleRam || wayCount == 1) && !compressedGen && !injectorStage) {
+ decode.insert(INSTRUCTION_ANTICIPATED) := Mux(decode.arbitration.isStuck, decode.input(INSTRUCTION), cache.io.cpu.fetch.data)
+ }
+ }
+
+ val rsp = new Area {
+ val iBusRspOutputHalt = False
+
+ val cacheRsp = if (twoCycleCache) cache.io.cpu.decode else cache.io.cpu.fetch
+ val cacheRspArbitration = stages(if (twoCycleCache) 2 else 1)
+ var issueDetected = False
+ val redoFetch = False
+
+
+ //Refill / redo
+ assert(decodePcGen == compressedGen)
+ cache.io.cpu.fill.valid := redoFetch && !cacheRsp.mmuRefilling
+ cache.io.cpu.fill.payload := cacheRsp.physicalAddress
+
+
+ if (catchSomething) {
+ decodeExceptionPort.valid := False
+ decodeExceptionPort.code.assignDontCare()
+ decodeExceptionPort.badAddr := cacheRsp.pc(31 downto 2) @@ U"00"
+ }
+
+ when(cacheRsp.isValid && cacheRsp.mmuRefilling && !issueDetected) {
+ issueDetected \= True
+ redoFetch := True
+ }
+
+ if(catchIllegalAccess) when(cacheRsp.isValid && cacheRsp.mmuException && !issueDetected) {
+ issueDetected \= True
+ decodeExceptionPort.valid := iBusRsp.readyForError
+ decodeExceptionPort.code := 12
+ }
+
+ when(cacheRsp.isValid && cacheRsp.cacheMiss && !issueDetected) {
+ issueDetected \= True
+ cache.io.cpu.fill.valid := True
+ redoFetch := True
+ }
+
+ if(catchAccessFault) when(cacheRsp.isValid && cacheRsp.error && !issueDetected) {
+ issueDetected \= True
+ decodeExceptionPort.valid := iBusRsp.readyForError
+ decodeExceptionPort.code := 1
+ }
+
+ when(redoFetch) {
+ iBusRsp.redoFetch := True
+ }
+
+
+ cacheRspArbitration.halt setWhen (issueDetected || iBusRspOutputHalt)
+ iBusRsp.output.valid := cacheRspArbitration.output.valid
+ cacheRspArbitration.output.ready := iBusRsp.output.ready
+ iBusRsp.output.rsp.inst := cacheRsp.data
+ iBusRsp.output.pc := cacheRspArbitration.output.payload
+ }
+
+ if (mmuBus != null) {
+ cache.io.cpu.fetch.mmuRsp <> mmuBus.rsp
+ } else {
+ cache.io.cpu.fetch.mmuRsp.physicalAddress := cache.io.cpu.fetch.pc
+ cache.io.cpu.fetch.mmuRsp.allowExecute := True
+ cache.io.cpu.fetch.mmuRsp.allowRead := True
+ cache.io.cpu.fetch.mmuRsp.allowWrite := True
+ cache.io.cpu.fetch.mmuRsp.isIoAccess := False
+ cache.io.cpu.fetch.mmuRsp.exception := False
+ cache.io.cpu.fetch.mmuRsp.refilling := False
+ }
+
+ val flushStage = decode
+ cache.io.flush := flushStage.arbitration.isValid && flushStage.input(FLUSH_ALL)
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala
new file mode 100644
index 0000000..1bb02bf
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala
@@ -0,0 +1,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
+ }
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/IntAluPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/IntAluPlugin.scala
new file mode 100644
index 0000000..0520c2f
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/IntAluPlugin.scala
@@ -0,0 +1,100 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+object IntAluPlugin{
+ object AluBitwiseCtrlEnum extends SpinalEnum(binarySequential){
+ val XOR, OR, AND = newElement()
+ }
+ object AluCtrlEnum extends SpinalEnum(binarySequential){
+ val ADD_SUB, SLT_SLTU, BITWISE = newElement()
+ }
+
+ object ALU_BITWISE_CTRL extends Stageable(AluBitwiseCtrlEnum())
+ object ALU_CTRL extends Stageable(AluCtrlEnum())
+}
+
+class IntAluPlugin extends Plugin[VexRiscv]{
+ import IntAluPlugin._
+
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+
+ val immediateActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC2_CTRL -> Src2CtrlEnum.IMI,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> True,
+ BYPASSABLE_MEMORY_STAGE -> True,
+ RS1_USE -> True
+ )
+
+ val nonImmediateActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC2_CTRL -> Src2CtrlEnum.RS,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> True,
+ BYPASSABLE_MEMORY_STAGE -> True,
+ RS1_USE -> True,
+ RS2_USE -> True
+ )
+
+ val otherAction = List[(Stageable[_ <: BaseType],Any)](
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> True,
+ BYPASSABLE_MEMORY_STAGE -> True
+ )
+
+
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.add(List(
+ ADD -> (nonImmediateActions ++ List(ALU_CTRL -> AluCtrlEnum.ADD_SUB, SRC_USE_SUB_LESS -> False)),
+ SUB -> (nonImmediateActions ++ List(ALU_CTRL -> AluCtrlEnum.ADD_SUB, SRC_USE_SUB_LESS -> True)),
+ SLT -> (nonImmediateActions ++ List(ALU_CTRL -> AluCtrlEnum.SLT_SLTU, SRC_USE_SUB_LESS -> True, SRC_LESS_UNSIGNED -> False)),
+ SLTU -> (nonImmediateActions ++ List(ALU_CTRL -> AluCtrlEnum.SLT_SLTU, SRC_USE_SUB_LESS -> True, SRC_LESS_UNSIGNED -> True)),
+ XOR -> (nonImmediateActions ++ List(ALU_CTRL -> AluCtrlEnum.BITWISE, ALU_BITWISE_CTRL -> AluBitwiseCtrlEnum.XOR)),
+ OR -> (nonImmediateActions ++ List(ALU_CTRL -> AluCtrlEnum.BITWISE, ALU_BITWISE_CTRL -> AluBitwiseCtrlEnum.OR)),
+ AND -> (nonImmediateActions ++ List(ALU_CTRL -> AluCtrlEnum.BITWISE, ALU_BITWISE_CTRL -> AluBitwiseCtrlEnum.AND))
+ ))
+
+ decoderService.add(List(
+ ADDI -> (immediateActions ++ List(ALU_CTRL -> AluCtrlEnum.ADD_SUB, SRC_USE_SUB_LESS -> False)),
+ SLTI -> (immediateActions ++ List(ALU_CTRL -> AluCtrlEnum.SLT_SLTU, SRC_USE_SUB_LESS -> True, SRC_LESS_UNSIGNED -> False)),
+ SLTIU -> (immediateActions ++ List(ALU_CTRL -> AluCtrlEnum.SLT_SLTU, SRC_USE_SUB_LESS -> True, SRC_LESS_UNSIGNED -> True)),
+ XORI -> (immediateActions ++ List(ALU_CTRL -> AluCtrlEnum.BITWISE, ALU_BITWISE_CTRL -> AluBitwiseCtrlEnum.XOR)),
+ ORI -> (immediateActions ++ List(ALU_CTRL -> AluCtrlEnum.BITWISE, ALU_BITWISE_CTRL -> AluBitwiseCtrlEnum.OR)),
+ ANDI -> (immediateActions ++ List(ALU_CTRL -> AluCtrlEnum.BITWISE, ALU_BITWISE_CTRL -> AluBitwiseCtrlEnum.AND))
+ ))
+
+ decoderService.add(List(
+ LUI -> (otherAction ++ List(ALU_CTRL -> AluCtrlEnum.ADD_SUB, SRC1_CTRL -> Src1CtrlEnum.IMU, SRC_USE_SUB_LESS -> False, SRC_ADD_ZERO -> True)),
+ AUIPC -> (otherAction ++ List(ALU_CTRL -> AluCtrlEnum.ADD_SUB, SRC_USE_SUB_LESS -> False, SRC1_CTRL -> Src1CtrlEnum.IMU, SRC2_CTRL -> Src2CtrlEnum.PC))
+ ))
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+
+ execute plug new Area{
+ import execute._
+
+ val bitwise = input(ALU_BITWISE_CTRL).mux(
+ AluBitwiseCtrlEnum.AND -> (input(SRC1) & input(SRC2)),
+ AluBitwiseCtrlEnum.OR -> (input(SRC1) | input(SRC2)),
+ AluBitwiseCtrlEnum.XOR -> (input(SRC1) ^ input(SRC2))
+ )
+
+ // mux results
+ insert(REGFILE_WRITE_DATA) := input(ALU_CTRL).mux(
+ AluCtrlEnum.BITWISE -> bitwise,
+ AluCtrlEnum.SLT_SLTU -> input(SRC_LESS).asBits(32 bit),
+ AluCtrlEnum.ADD_SUB -> input(SRC_ADD_SUB)
+ )
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/MemoryTranslatorPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/MemoryTranslatorPlugin.scala
new file mode 100644
index 0000000..081b11d
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/MemoryTranslatorPlugin.scala
@@ -0,0 +1,159 @@
+package vexriscv.plugin
+
+import vexriscv.{VexRiscv, _}
+import spinal.core._
+import spinal.lib._
+
+import scala.collection.mutable.ArrayBuffer
+
+object MemoryTranslatorPort{
+ val PRIORITY_DATA = 1
+ val PRIORITY_INSTRUCTION = 0
+}
+case class MemoryTranslatorPort(bus : MemoryTranslatorBus, priority : Int, args : MemoryTranslatorPortConfig/*, exceptionBus: Flow[ExceptionCause]*/)
+
+case class MemoryTranslatorPortConfig(portTlbSize : Int)
+
+class MemoryTranslatorPlugin(tlbSize : Int,
+ virtualRange : UInt => Bool,
+ ioRange : UInt => Bool) extends Plugin[VexRiscv] with MemoryTranslator {
+ assert(isPow2(tlbSize))
+
+ val portsInfo = ArrayBuffer[MemoryTranslatorPort]()
+
+ override def newTranslationPort(priority : Int,args : Any): MemoryTranslatorBus = {
+ val config = args.asInstanceOf[MemoryTranslatorPortConfig]
+ val port = MemoryTranslatorPort(MemoryTranslatorBus(MemoryTranslatorBusParameter(wayCount = 0)),priority, config/*,exceptionBus*/)
+ portsInfo += port
+ port.bus
+ }
+
+ object IS_TLB extends Stageable(Bool)
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+ def TLBW0 = M"0000000----------111-----0001111"
+ def TLBW1 = M"0000001----------111-----0001111"
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(IS_TLB, False)
+ decoderService.add(TLBW0, List(IS_TLB -> True, RS1_USE -> True, SRC1_CTRL -> Src1CtrlEnum.RS))
+ decoderService.add(TLBW1, List(IS_TLB -> True, RS1_USE -> True, RS2_USE -> True, SRC1_CTRL -> Src1CtrlEnum.RS))
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ import Riscv._
+
+ //Sorted by priority
+ val sortedPortsInfo = portsInfo.sortWith((a,b) => a.priority > b.priority)
+
+ case class CacheLine() extends Bundle {
+ val valid = Bool
+ val virtualAddress = UInt(20 bits)
+ val physicalAddress = UInt(20 bits)
+ val allowRead, allowWrite, allowExecute, allowUser = Bool
+
+ def init = {
+ valid init (False)
+ this
+ }
+ }
+
+ val core = pipeline plug new Area {
+ val shared = new Area {
+ val cache = Mem(CacheLine(), tlbSize)
+ var free = True
+ val readAddr = cache.addressType().assignDontCare()
+ val readData = RegNext(cache.readSync(readAddr))
+ }
+
+ val ports = for ((port, portId) <- sortedPortsInfo.zipWithIndex) yield new Area {
+ val cache = Vec(Reg(CacheLine()) init, port.args.portTlbSize)
+ val cacheHits = cache.map(line => line.valid && line.virtualAddress === port.bus.cmd.last.virtualAddress(31 downto 12))
+ val cacheHit = cacheHits.asBits.orR
+ val cacheLine = MuxOH(cacheHits, cache)
+ val isInMmuRange = virtualRange(port.bus.cmd.last.virtualAddress) && !port.bus.cmd.last.bypassTranslation
+
+ val sharedMiss = RegInit(False)
+ val sharedIterator = Reg(UInt(log2Up(tlbSize + 1) bits))
+ val sharedAccessed = RegInit(B"00")
+ val entryToReplace = Counter(port.args.portTlbSize)
+
+ val sharedAccessAsked = RegNext(port.bus.cmd.last.isValid && !cacheHit && sharedIterator < tlbSize && isInMmuRange)
+ val sharedAccessGranted = sharedAccessAsked && shared.free
+ when(sharedAccessGranted) {
+ shared.readAddr := sharedIterator.resized
+ sharedIterator := sharedIterator + 1
+ }
+ sharedAccessed := (sharedAccessed ## sharedAccessGranted).resized
+ when(sharedAccessAsked){
+ shared.free \= False
+ }
+
+ when(sharedAccessed.msb){
+ when(shared.readData.virtualAddress === port.bus.cmd.last.virtualAddress(31 downto 12)){
+ cache(entryToReplace) := shared.readData
+ entryToReplace.increment()
+ }
+ }
+
+ sharedMiss.setWhen(sharedIterator >= tlbSize && sharedAccessed === B"00")
+ when(port.bus.end){
+ sharedIterator := 0
+ sharedMiss.clear()
+ sharedAccessAsked.clear()
+ sharedAccessed := 0
+ }
+
+
+ when(isInMmuRange) {
+ port.bus.rsp.physicalAddress := cacheLine.physicalAddress @@ port.bus.cmd.last.virtualAddress(11 downto 0)
+ port.bus.rsp.allowRead := cacheLine.allowRead
+ port.bus.rsp.allowWrite := cacheLine.allowWrite
+ port.bus.rsp.allowExecute := cacheLine.allowExecute
+ ???
+// port.bus.rsp.hit := cacheHit
+// port.stage.arbitration.haltItself setWhen (port.bus.cmd.isValid && !cacheHit && !sharedMiss)
+ } otherwise {
+ port.bus.rsp.physicalAddress := port.bus.cmd.last.virtualAddress
+ port.bus.rsp.allowRead := True
+ port.bus.rsp.allowWrite := True
+ port.bus.rsp.allowExecute := True
+ ???
+// port.bus.rsp.hit := True
+ }
+ port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress)
+ ???
+// port.bus.rsp.miss := sharedMiss
+ }
+ }
+
+ //Manage TLBW0 and TLBW1 instructions
+ //TODO not exception safe (sideeffect)
+ execute plug new Area{
+ import execute._
+ val tlbWriteBuffer = Reg(UInt(20 bits))
+ when(arbitration.isFiring && input(IS_TLB)){
+ switch(input(INSTRUCTION)(25 downto 25)){
+ is(0){
+ tlbWriteBuffer := input(SRC1).asUInt.resized
+ }
+ is(1){
+ val line = CacheLine()
+ line.virtualAddress := tlbWriteBuffer
+ line.physicalAddress := input(RS2)(19 downto 0).asUInt
+ line.allowUser := input(RS2)(27)
+ line.allowRead := input(RS2)(28)
+ line.allowWrite := input(RS2)(29)
+ line.allowExecute := input(RS2)(30)
+ line.valid := input(RS2)(31)
+ core.shared.cache(input(SRC1)(log2Up(tlbSize)-1 downto 0).asUInt) := line
+
+ core.ports.foreach(_.cache.foreach(_.valid := False)) //Invalidate all ports caches
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/Misc.scala b/VexRiscv/src/main/scala/vexriscv/plugin/Misc.scala
new file mode 100644
index 0000000..979d246
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/Misc.scala
@@ -0,0 +1,214 @@
+package vexriscv.plugin
+
+import spinal.core._
+import spinal.lib._
+
+object RvcDecompressor{
+
+ def main(args: Array[String]): Unit = {
+ SpinalVerilog(new Component{
+ out(Delay((apply(Delay(in Bits(16 bits),2), false, false)),2))
+ }.setDefinitionName("Decompressor"))
+ }
+
+ def apply(i : Bits, rvf : Boolean, rvd : Boolean): Bits ={
+ val ret = Bits(32 bits).assignDontCare()
+
+ val rch = B"01" ## i(9 downto 7)
+ val rcl = B"01" ## i(4 downto 2)
+
+ val addi5spnImm = B"00" ## i(10 downto 7) ## i(12 downto 11) ## i(5) ## i(6) ## B"00"
+ val lwImm = B"00000" ## i(5) ## i(12 downto 10) ## i(6) ## B"00"
+ def swImm = lwImm
+ val ldImm = B"0000" ## i(6 downto 5) ## i(12 downto 10) ## B"000"
+ def sdImm = ldImm
+ val addImm = B((11 downto 5) -> i(12), (4 downto 0) -> i(6 downto 2))
+ def lImm = addImm
+ val jalImm = B((9 downto 0) -> i(12)) ## i(8) ## i(10 downto 9) ## i(6) ## i(7) ## i(2) ## i(11) ## i(5 downto 3) ## B"0"
+ val luiImm = B((14 downto 0) -> i(12)) ## i(6 downto 2) ## B"0000_0000_0000"
+ val shiftImm = i(6 downto 2)
+ val addi16spImm = B((2 downto 0) -> i(12)) ## i(4 downto 3) ## i(5) ## i(2) ## i(6) ## B"0000"
+ val jImm = B((9 downto 0) -> i(12)) ## i(8) ## i(10 downto 9) ## i(6) ## i(7) ## i(2) ## i(11) ## i(5 downto 3) ## B"0"
+ val bImm = B((4 downto 0) -> i(12)) ## i(6 downto 5) ## i(2) ## i(11 downto 10) ## i(4 downto 3) ## B"0"
+
+ def lwspImm = B"0000" ## i(3 downto 2) ## i(12) ## i(6 downto 4) ## B"00"
+ def swspImm = B"0000" ## i(8 downto 7) ## i(12 downto 9) ## B"00"
+ def ldspImm = B"000" ## i(4 downto 2) ## i(12) ## i(6 downto 5) ## B"000"
+ def sdspImm = B"000" ## i(9 downto 7) ## i(12 downto 10) ## B"000"
+
+
+ val x0 = B"00000"
+ val x1 = B"00001"
+ val x2 = B"00010"
+
+ switch(i(1 downto 0) ## i(15 downto 13)){
+ is(0){ret := addi5spnImm ## B"00010" ## B"000" ## rcl ## B"0010011"} //C.ADDI4SPN -> addi rd0, x2, nzuimm[9:2].
+ if(rvd) is(1){ret := ldImm ## rch ## B"011" ## rcl ## B"0000111"} // C.FLD
+ is(2){ret := lwImm ## rch ## B"010" ## rcl ## B"0000011"} //C.LW -> lw rd', offset[6:2](rs1')
+ if(rvf) is(3){ret := lwImm ## rch ## B"010" ## rcl ## B"0000111"} // C.FLW
+ if(rvd) is(5){ret := sdImm(11 downto 5) ## rcl ## rch ## B"011" ## sdImm(4 downto 0) ## B"0100111"} // C.FSD
+ is(6){ret := swImm(11 downto 5) ## rcl ## rch ## B"010" ## swImm(4 downto 0) ## B"0100011"} //C.SW -> sw rs2',offset[6:2](rs1')
+ if(rvf) is(7){ret := swImm(11 downto 5) ## rcl ## rch ## B"010" ## swImm(4 downto 0) ## B"0100111"} // C.FSW
+ is(8){ret := addImm ## i(11 downto 7) ## B"000" ## i(11 downto 7) ## B"0010011"} //C.ADDI -> addi rd, rd, nzimm[5:0].
+ is(9){ret := jalImm(20) ## jalImm(10 downto 1) ## jalImm(11) ## jalImm(19 downto 12) ## x1 ## B"1101111"} //C.JAL -> jalr x1, rs1, 0.
+ is(10){ret := lImm ## B"00000" ## B"000" ## i(11 downto 7) ## B"0010011"} //C.LI -> addi rd, x0, imm[5:0].
+ is(11){ //C.ADDI16SP C.LUI ->
+ val addi16sp = addi16spImm ## i(11 downto 7) ## B"000" ## i(11 downto 7) ## B"0010011"
+ val lui = luiImm(31 downto 12) ## i(11 downto 7) ## B"0110111"
+ ret := (i(11 downto 7) === 2) ? addi16sp | lui
+ }
+ is(12){
+ val isImmediate = i(11 downto 10) =/= B"11"
+ val isShift = !i(11)
+ val func3 = i(11 downto 10).mux(
+ 0 -> B"101",
+ 1 -> B"101",
+ 2 -> B"111",
+ 3 -> i(6 downto 5).mux(
+ 0 -> B"000",
+ 1 -> B"100",
+ 2 -> B"110",
+ 3 -> B"111"
+ )
+ )
+ val msbs = Mux(
+ sel = i(11 downto 10) === B"10",
+ whenTrue = B((6 downto 0) -> i(12)), //andi
+ whenFalse = B"0" ## (i(11 downto 10) === B"01" || (i(11 downto 10) === B"11" && i(6 downto 5) === B"00")) ## B"00000"
+ )
+ val rs2Shift = (isShift || isImmediate) ? shiftImm | rcl
+ val opc = (isImmediate ? B"0010011" | B"0110011")
+ ret := msbs ## rs2Shift ## rch ## func3 ## rch ## opc
+ }
+ is(13){ ret := jImm(20) ## jImm(10 downto 1) ## jImm(11) ## jImm(19 downto 12) ## x0 ## B"1101111"}
+ is(14){ ret := bImm(12) ## bImm(10 downto 5) ## x0 ## rch ## B"000" ## bImm(4 downto 1) ## bImm(11) ## B"1100011" }
+ is(15){ ret := bImm(12) ## bImm(10 downto 5) ## x0 ## rch ## B"001" ## bImm(4 downto 1) ## bImm(11) ## B"1100011" }
+ is(16){ ret := B"0000000" ## i(6 downto 2) ## i(11 downto 7) ## B"001" ## i(11 downto 7) ## B"0010011" }
+ if(rvd) is(17){ret := ldspImm ## x2 ## B"011" ## i(11 downto 7) ## B"0000111" } // C.FLDSP
+ is(18){ ret := lwspImm ## x2 ## B"010" ## i(11 downto 7) ## B"0000011" }
+ if(rvf) is(19){ret := lwspImm ## x2 ## B"010" ## i(11 downto 7) ## B"0000111" } // C.FLWSP
+ is(20) {
+ val add = B"000_0000" ## i(6 downto 2) ## (i(12) ? i(11 downto 7) | x0) ## B"000" ## i(11 downto 7) ## B"0110011" //add => add rd, rd, rs2 mv => add rd, x0, rs2
+ val j = B"0000_0000_0000" ## i(11 downto 7) ## B"000" ## (i(12) ? x1 | x0) ## B"1100111" //jr => jalr x0, rs1, 0. jalr => jalr x1, rs1, 0.
+ val ebreak = B"000000000001_00000_000_00000_1110011" //EBREAK
+ val addJ = (i(6 downto 2) === 0) ? j | add
+ ret := (i(12 downto 2) === B"100_0000_0000") ? ebreak | addJ
+ }
+
+ if(rvd) is(21){ret := sdspImm(11 downto 5) ## i(6 downto 2) ## x2 ## B"011" ## sdspImm(4 downto 0) ## B"0100111" } // C.FSDSP
+ is(22){ ret := swspImm(11 downto 5) ## i(6 downto 2) ## x2 ## B"010" ## swspImm(4 downto 0) ## B"0100011" }
+ if(rvf) is(23){ret := swspImm(11 downto 5) ## i(6 downto 2) ## x2 ## B"010" ## swspImm(4 downto 0) ## B"0100111" } // C.FSwSP
+ }
+
+ ret
+ }
+}
+
+
+object StreamForkVex{
+ def apply[T <: Data](input : Stream[T], portCount: Int, flush : Bool/*, flushDiscardInput : Boolean*/) : Vec[Stream[T]] = {
+ val outputs = Vec(cloneOf(input), portCount)
+ val linkEnable = Vec(RegInit(True), portCount)
+
+ input.ready := True
+ for (i <- 0 until portCount) {
+ when(!outputs(i).ready && linkEnable(i)) {
+ input.ready := False
+ }
+ }
+
+ for (i <- 0 until portCount) {
+ outputs(i).valid := input.valid && linkEnable(i)
+ outputs(i).payload := input.payload
+ when(outputs(i).fire) {
+ linkEnable(i) := False
+ }
+ }
+
+ when(input.ready || flush) {
+ linkEnable.foreach(_ := True)
+ }
+ outputs
+ }
+}
+
+
+object StreamVexPimper{
+ implicit class StreamFlushPimper[T <: Data](pimped : Stream[T]){
+ def m2sPipeWithFlush(flush : Bool, discardInput : Boolean = true, collapsBubble : Boolean = true, flushInput : Bool = null): Stream[T] = {
+ val ret = cloneOf(pimped).setCompositeName(pimped, "m2sPipe", true)
+
+ val rValid = RegInit(False)
+ val rData = Reg(pimped.payloadType)
+ if(!discardInput) rValid.clearWhen(flush)
+
+ pimped.ready := (Bool(collapsBubble) && !ret.valid) || ret.ready
+
+ when(pimped.ready) {
+ if(flushInput == null)
+ rValid := pimped.valid
+ else
+ rValid := pimped.valid && !flushInput
+ rData := pimped.payload
+ }
+
+ ret.valid := rValid
+ ret.payload := rData
+
+ if(discardInput) rValid.clearWhen(flush)
+
+ ret
+ }
+
+ def s2mPipe(flush : Bool): Stream[T] = {
+ val ret = cloneOf(pimped)
+
+ val rValid = RegInit(False)
+ val rBits = Reg(pimped.payloadType)
+
+ ret.valid := pimped.valid || rValid
+ pimped.ready := !rValid
+ ret.payload := Mux(rValid, rBits, pimped.payload)
+
+ when(ret.ready) {
+ rValid := False
+ }
+
+ when(pimped.ready && (!ret.ready)) {
+ rValid := pimped.valid
+ rBits := pimped.payload
+ }
+
+ rValid.clearWhen(flush)
+
+ ret
+ }
+ }
+
+}
+
+
+
+//case class FlowFifoLowLatency[T <: Data](dataType: T, depth: Int) extends Component {
+// require(depth >= 1)
+// val io = new Bundle {
+// val push = slave Flow (dataType)
+// val pop = master Stream (dataType)
+// val flush = in Bool()
+// }
+//
+//
+// val mem = Vec(Reg(dataType), depth)
+// val rPtr, wPtr = Counter(depth + 1)
+// when(io.push.valid){
+// mem(wPtr) := io.push.payload
+// wPtr.increment()
+// }
+//
+// when(io.pop.fire){
+// rPtr.increment()
+// }
+// io.pop.valid := rPtr =/= wPtr
+//
+//
+//} \ No newline at end of file
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/MmuPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/MmuPlugin.scala
new file mode 100644
index 0000000..093f59a
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/MmuPlugin.scala
@@ -0,0 +1,313 @@
+package vexriscv.plugin
+
+import vexriscv.{VexRiscv, _}
+import spinal.core._
+import spinal.lib._
+
+import scala.collection.mutable.ArrayBuffer
+
+trait DBusAccessService{
+ def newDBusAccess() : DBusAccess
+}
+
+case class DBusAccessCmd() extends Bundle {
+ val address = UInt(32 bits)
+ val size = UInt(2 bits)
+ val write = Bool
+ val data = Bits(32 bits)
+ val writeMask = Bits(4 bits)
+}
+
+case class DBusAccessRsp() extends Bundle {
+ val data = Bits(32 bits)
+ val error = Bool()
+ val redo = Bool()
+}
+
+case class DBusAccess() extends Bundle {
+ val cmd = Stream(DBusAccessCmd())
+ val rsp = Flow(DBusAccessRsp())
+}
+
+
+object MmuPort{
+ val PRIORITY_DATA = 1
+ val PRIORITY_INSTRUCTION = 0
+}
+case class MmuPort(bus : MemoryTranslatorBus, priority : Int, args : MmuPortConfig, id : Int)
+
+case class MmuPortConfig(portTlbSize : Int, latency : Int = 0, earlyRequireMmuLockup : Boolean = false, earlyCacheHits : Boolean = false)
+
+class MmuPlugin(ioRange : UInt => Bool,
+ virtualRange : UInt => Bool = address => True,
+// allowUserIo : Boolean = false,
+ enableMmuInMachineMode : Boolean = false) extends Plugin[VexRiscv] with MemoryTranslator {
+
+ var dBusAccess : DBusAccess = null
+ val portsInfo = ArrayBuffer[MmuPort]()
+
+ override def newTranslationPort(priority : Int,args : Any): MemoryTranslatorBus = {
+ val config = args.asInstanceOf[MmuPortConfig]
+ val port = MmuPort(MemoryTranslatorBus(MemoryTranslatorBusParameter(wayCount = config.portTlbSize, latency = config.latency)),priority, config, portsInfo.length)
+ portsInfo += port
+ port.bus
+ }
+
+ object IS_SFENCE_VMA2 extends Stageable(Bool)
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(IS_SFENCE_VMA2, False)
+ decoderService.add(SFENCE_VMA, List(IS_SFENCE_VMA2 -> True))
+
+
+ dBusAccess = pipeline.service(classOf[DBusAccessService]).newDBusAccess()
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ import Riscv._
+ val csrService = pipeline.service(classOf[CsrInterface])
+
+ //Sorted by priority
+ val sortedPortsInfo = portsInfo.sortBy(_.priority)
+
+ case class CacheLine() extends Bundle {
+ val valid, exception, superPage = Bool
+ val virtualAddress = Vec(UInt(10 bits), UInt(10 bits))
+ val physicalAddress = Vec(UInt(10 bits), UInt(10 bits))
+ val allowRead, allowWrite, allowExecute, allowUser = Bool
+
+ def init = {
+ valid init (False)
+ this
+ }
+ }
+
+ val csr = pipeline plug new Area{
+ val status = new Area{
+ val sum, mxr, mprv = RegInit(False)
+ }
+ val satp = new Area {
+ val mode = RegInit(False)
+ val asid = Reg(Bits(9 bits))
+ val ppn = Reg(UInt(20 bits))
+ }
+
+ for(offset <- List(CSR.MSTATUS, CSR.SSTATUS)) csrService.rw(offset, 19 -> status.mxr, 18 -> status.sum, 17 -> status.mprv)
+ csrService.rw(CSR.SATP, 31 -> satp.mode, 22 -> satp.asid, 0 -> satp.ppn)
+ }
+
+ val core = pipeline plug new Area {
+ val ports = for (port <- sortedPortsInfo) yield new Area {
+ val handle = port
+ val id = port.id
+ val privilegeService = pipeline.serviceElse(classOf[PrivilegeService], PrivilegeServiceDefault())
+ val cache = Vec(Reg(CacheLine()) init, port.args.portTlbSize)
+ val dirty = RegInit(False).allowUnsetRegToAvoidLatch
+ if(port.args.earlyRequireMmuLockup){
+ dirty clearWhen(!port.bus.cmd.last.isStuck)
+ }
+
+ def toRsp[T <: Data](data : T, from : MemoryTranslatorCmd) : T = from match {
+ case _ if from == port.bus.cmd.last => data
+ case _ => {
+ val next = port.bus.cmd.dropWhile(_ != from)(1)
+ toRsp(RegNextWhen(data, !next.isStuck), next)
+ }
+ }
+ val requireMmuLockupCmd = port.bus.cmd.takeRight(if(port.args.earlyRequireMmuLockup) 2 else 1).head
+
+ val requireMmuLockupCalc = virtualRange(requireMmuLockupCmd.virtualAddress) && !requireMmuLockupCmd.bypassTranslation && csr.satp.mode
+ if(!enableMmuInMachineMode) {
+ requireMmuLockupCalc clearWhen(!csr.status.mprv && privilegeService.isMachine())
+ when(privilegeService.isMachine()) {
+ if (port.priority == MmuPort.PRIORITY_DATA) {
+ requireMmuLockupCalc clearWhen (!csr.status.mprv || pipeline(MPP) === 3)
+ } else {
+ requireMmuLockupCalc := False
+ }
+ }
+ }
+
+ val cacheHitsCmd = port.bus.cmd.takeRight(if(port.args.earlyCacheHits) 2 else 1).head
+ val cacheHitsCalc = B(cache.map(line => line.valid && line.virtualAddress(1) === cacheHitsCmd.virtualAddress(31 downto 22) && (line.superPage || line.virtualAddress(0) === cacheHitsCmd.virtualAddress(21 downto 12))))
+
+
+ val requireMmuLockup = toRsp(requireMmuLockupCalc, requireMmuLockupCmd)
+ val cacheHits = toRsp(cacheHitsCalc, cacheHitsCmd)
+
+ val cacheHit = cacheHits.asBits.orR
+ val cacheLine = MuxOH(cacheHits, cache)
+ val entryToReplace = Counter(port.args.portTlbSize)
+
+
+ when(requireMmuLockup) {
+ port.bus.rsp.physicalAddress := cacheLine.physicalAddress(1) @@ (cacheLine.superPage ? port.bus.cmd.last.virtualAddress(21 downto 12) | cacheLine.physicalAddress(0)) @@ port.bus.cmd.last.virtualAddress(11 downto 0)
+ port.bus.rsp.allowRead := cacheLine.allowRead || csr.status.mxr && cacheLine.allowExecute
+ port.bus.rsp.allowWrite := cacheLine.allowWrite
+ port.bus.rsp.allowExecute := cacheLine.allowExecute
+ port.bus.rsp.exception := !dirty && cacheHit && (cacheLine.exception || cacheLine.allowUser && privilegeService.isSupervisor() && !csr.status.sum || !cacheLine.allowUser && privilegeService.isUser())
+ port.bus.rsp.refilling := dirty || !cacheHit
+ port.bus.rsp.isPaging := True
+ } otherwise {
+ port.bus.rsp.physicalAddress := port.bus.cmd.last.virtualAddress
+ port.bus.rsp.allowRead := True
+ port.bus.rsp.allowWrite := True
+ port.bus.rsp.allowExecute := True
+ port.bus.rsp.exception := False
+ port.bus.rsp.refilling := False
+ port.bus.rsp.isPaging := False
+ }
+ port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress)
+
+ port.bus.rsp.bypassTranslation := !requireMmuLockup
+ for(wayId <- 0 until port.args.portTlbSize){
+ port.bus.rsp.ways(wayId).sel := cacheHits(wayId)
+ port.bus.rsp.ways(wayId).physical := cache(wayId).physicalAddress(1) @@ (cache(wayId).superPage ? port.bus.cmd.last.virtualAddress(21 downto 12) | cache(wayId).physicalAddress(0)) @@ port.bus.cmd.last.virtualAddress(11 downto 0)
+ }
+
+ // Avoid keeping any invalid line in the cache after an exception.
+ // https://github.com/riscv/riscv-linux/blob/8fe28cb58bcb235034b64cbbb7550a8a43fd88be/arch/riscv/include/asm/pgtable.h#L276
+ when(service(classOf[IContextSwitching]).isContextSwitching) {
+ for (line <- cache) {
+ when(line.exception) {
+ line.valid := False
+ }
+ }
+ }
+ }
+
+ val shared = new Area {
+ val State = new SpinalEnum{
+ val IDLE, L1_CMD, L1_RSP, L0_CMD, L0_RSP = newElement()
+ }
+ val state = RegInit(State.IDLE)
+ val vpn = Reg(Vec(UInt(10 bits), UInt(10 bits)))
+ val portSortedOh = Reg(Bits(portsInfo.length bits))
+ case class PTE() extends Bundle {
+ val V, R, W ,X, U, G, A, D = Bool()
+ val RSW = Bits(2 bits)
+ val PPN0 = UInt(10 bits)
+ val PPN1 = UInt(12 bits)
+ }
+
+ val dBusRspStaged = dBusAccess.rsp.stage()
+ val dBusRsp = new Area{
+ val pte = PTE()
+ pte.assignFromBits(dBusRspStaged.data)
+ val exception = !pte.V || (!pte.R && pte.W) || dBusRspStaged.error
+ val leaf = pte.R || pte.X
+ }
+
+ val pteBuffer = RegNextWhen(dBusRsp.pte, dBusRspStaged.valid && !dBusRspStaged.redo)
+
+ dBusAccess.cmd.valid := False
+ dBusAccess.cmd.write := False
+ dBusAccess.cmd.size := 2
+ dBusAccess.cmd.address.assignDontCare()
+ dBusAccess.cmd.data.assignDontCare()
+ dBusAccess.cmd.writeMask.assignDontCare()
+
+ val refills = OHMasking.last(B(ports.map(port => port.handle.bus.cmd.last.isValid && port.requireMmuLockup && !port.dirty && !port.cacheHit)))
+ switch(state){
+ is(State.IDLE){
+ when(refills.orR){
+ portSortedOh := refills
+ state := State.L1_CMD
+ val address = MuxOH(refills, sortedPortsInfo.map(_.bus.cmd.last.virtualAddress))
+ vpn(1) := address(31 downto 22)
+ vpn(0) := address(21 downto 12)
+ }
+// for(port <- portsInfo.sortBy(_.priority)){
+// when(port.bus.cmd.isValid && port.bus.rsp.refilling){
+// vpn(1) := port.bus.cmd.virtualAddress(31 downto 22)
+// vpn(0) := port.bus.cmd.virtualAddress(21 downto 12)
+// portId := port.id
+// state := State.L1_CMD
+// }
+// }
+ }
+ is(State.L1_CMD){
+ dBusAccess.cmd.valid := True
+ dBusAccess.cmd.address := csr.satp.ppn @@ vpn(1) @@ U"00"
+ when(dBusAccess.cmd.ready){
+ state := State.L1_RSP
+ }
+ }
+ is(State.L1_RSP){
+ when(dBusRspStaged.valid){
+ state := State.L0_CMD
+ when(dBusRsp.leaf || dBusRsp.exception){
+ state := State.IDLE
+ }
+ when(dBusRspStaged.redo){
+ state := State.L1_CMD
+ }
+ }
+ }
+ is(State.L0_CMD){
+ dBusAccess.cmd.valid := True
+ dBusAccess.cmd.address := pteBuffer.PPN1(9 downto 0) @@ pteBuffer.PPN0 @@ vpn(0) @@ U"00"
+ when(dBusAccess.cmd.ready){
+ state := State.L0_RSP
+ }
+ }
+ is(State.L0_RSP){
+ when(dBusRspStaged.valid) {
+ state := State.IDLE
+ when(dBusRspStaged.redo){
+ state := State.L0_CMD
+ }
+ }
+ }
+ }
+
+ for((port, id) <- sortedPortsInfo.zipWithIndex) {
+ port.bus.busy := state =/= State.IDLE && portSortedOh(id)
+ }
+
+ when(dBusRspStaged.valid && !dBusRspStaged.redo && (dBusRsp.leaf || dBusRsp.exception)){
+ for((port, id) <- ports.zipWithIndex) {
+ when(portSortedOh(id)) {
+ port.entryToReplace.increment()
+ if(port.handle.args.earlyRequireMmuLockup) {
+ port.dirty := True
+ } //Avoid having non coherent TLB lookup
+ for ((line, lineId) <- port.cache.zipWithIndex) {
+ when(port.entryToReplace === lineId){
+ val superPage = state === State.L1_RSP
+ line.valid := True
+ line.exception := dBusRsp.exception || (superPage && dBusRsp.pte.PPN0 =/= 0)
+ line.virtualAddress := vpn
+ line.physicalAddress := Vec(dBusRsp.pte.PPN0, dBusRsp.pte.PPN1(9 downto 0))
+ line.allowRead := dBusRsp.pte.R
+ line.allowWrite := dBusRsp.pte.W
+ line.allowExecute := dBusRsp.pte.X
+ line.allowUser := dBusRsp.pte.U
+ line.superPage := state === State.L1_RSP
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ val fenceStage = execute
+
+ //Both SFENCE_VMA and SATP reschedule the next instruction in the CsrPlugin itself with one extra cycle to ensure side effect propagation.
+ fenceStage plug new Area{
+ import fenceStage._
+ when(arbitration.isValid && arbitration.isFiring && input(IS_SFENCE_VMA2)){
+ for(port <- core.ports; line <- port.cache) line.valid := False
+ }
+
+ csrService.onWrite(CSR.SATP){
+ for(port <- core.ports; line <- port.cache) line.valid := False
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/Mul16Plugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/Mul16Plugin.scala
new file mode 100644
index 0000000..f2a63c3
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/Mul16Plugin.scala
@@ -0,0 +1,119 @@
+package vexriscv.plugin
+
+import vexriscv._
+import vexriscv.plugin._
+import spinal.core._
+
+/**
+ * A multiplication plugin using only 16-bit multiplications
+ */
+class Mul16Plugin extends Plugin[VexRiscv]{
+
+ object MUL_LL extends Stageable(UInt(32 bits))
+ object MUL_LH extends Stageable(UInt(32 bits))
+ object MUL_HL extends Stageable(UInt(32 bits))
+ object MUL_HH extends Stageable(UInt(32 bits))
+
+ object MUL extends Stageable(Bits(64 bits))
+
+ object IS_MUL extends Stageable(Bool)
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+
+
+ val actions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC2_CTRL -> Src2CtrlEnum.RS,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> False,
+ RS1_USE -> True,
+ RS2_USE -> True,
+ IS_MUL -> True
+ )
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(IS_MUL, False)
+ decoderService.add(List(
+ MULX -> actions
+ ))
+
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ // Prepare signed inputs for the multiplier in the next stage.
+ // This will map them best to an FPGA DSP.
+ execute plug new Area {
+ import execute._
+ val a,b = Bits(32 bit)
+
+ a := input(SRC1)
+ b := input(SRC2)
+
+ val aLow = a(15 downto 0).asUInt
+ val bLow = b(15 downto 0).asUInt
+ val aHigh = a(31 downto 16).asUInt
+ val bHigh = b(31 downto 16).asUInt
+
+ insert(MUL_LL) := aLow * bLow
+ insert(MUL_LH) := aLow * bHigh
+ insert(MUL_HL) := aHigh * bLow
+ insert(MUL_HH) := aHigh * bHigh
+ }
+
+ memory plug new Area {
+ import memory._
+
+ val ll = UInt(32 bits)
+ val lh = UInt(33 bits)
+ val hl = UInt(32 bits)
+ val hh = UInt(32 bits)
+
+ ll := input(MUL_LL)
+ lh := input(MUL_LH).resized
+ hl := input(MUL_HL)
+ hh := input(MUL_HH)
+
+ val hllh = lh + hl
+ insert(MUL) := ((hh ## ll(31 downto 16)).asUInt + hllh) ## ll(15 downto 0)
+ }
+
+ writeBack plug new Area {
+ import writeBack._
+ val aSigned,bSigned = Bool
+ switch(input(INSTRUCTION)(13 downto 12)) {
+ is(B"01") {
+ aSigned := True
+ bSigned := True
+ }
+ is(B"10") {
+ aSigned := True
+ bSigned := False
+ }
+ default {
+ aSigned := False
+ bSigned := False
+ }
+ }
+
+ val a = (aSigned && input(SRC1).msb) ? input(SRC2).asUInt | U(0)
+ val b = (bSigned && input(SRC2).msb) ? input(SRC1).asUInt | U(0)
+
+ when(arbitration.isValid && input(IS_MUL)){
+ switch(input(INSTRUCTION)(13 downto 12)){
+ is(B"00"){
+ output(REGFILE_WRITE_DATA) := input(MUL)(31 downto 0)
+ }
+ is(B"01",B"10",B"11"){
+ output(REGFILE_WRITE_DATA) := (((input(MUL)(63 downto 32)).asUInt + ~a) + (~b + 2)).asBits
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/MulDivIterativePlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/MulDivIterativePlugin.scala
new file mode 100644
index 0000000..fff12ef
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/MulDivIterativePlugin.scala
@@ -0,0 +1,188 @@
+package vexriscv.plugin
+
+import spinal.core._
+import spinal.lib._
+import vexriscv.{VexRiscv, _}
+
+object MulDivIterativePlugin{
+ object IS_MUL extends Stageable(Bool)
+ object IS_DIV extends Stageable(Bool)
+ object IS_REM extends Stageable(Bool)
+ object IS_RS1_SIGNED extends Stageable(Bool)
+ object IS_RS2_SIGNED extends Stageable(Bool)
+ object FAST_DIV_VALID extends Stageable(Bool)
+ object FAST_DIV_VALUE extends Stageable(UInt(4 bits))
+}
+
+class MulDivIterativePlugin(genMul : Boolean = true,
+ genDiv : Boolean = true,
+ mulUnrollFactor : Int = 1,
+ divUnrollFactor : Int = 1,
+ dhrystoneOpt : Boolean = false,
+ customMul : (UInt, UInt, Stage, VexRiscv) => Area = null) extends Plugin[VexRiscv] with VexRiscvRegressionArg {
+ import MulDivIterativePlugin._
+
+ override def getVexRiscvRegressionArgs(): Seq[String] = {
+ var args = List[String]()
+ if(genMul) args :+= "MUL=yes"
+ if(genDiv) args :+= "DIV=yes"
+ args
+ }
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+
+
+ val commonActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC2_CTRL -> Src2CtrlEnum.RS,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> Bool(pipeline.stages.last == pipeline.execute),
+ BYPASSABLE_MEMORY_STAGE -> True,
+ RS1_USE -> True,
+ RS2_USE -> True
+ )
+
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+
+ if(genMul) {
+ val mulActions = commonActions ++ List(IS_MUL -> True)
+ decoderService.addDefault(IS_MUL, False)
+ decoderService.add(List(
+ MUL -> (mulActions ++ List(IS_RS1_SIGNED -> False, IS_RS2_SIGNED -> False)),
+ MULH -> (mulActions ++ List(IS_RS1_SIGNED -> True, IS_RS2_SIGNED -> True)),
+ MULHSU -> (mulActions ++ List(IS_RS1_SIGNED -> True, IS_RS2_SIGNED -> False)),
+ MULHU -> (mulActions ++ List(IS_RS1_SIGNED -> False, IS_RS2_SIGNED -> False))
+ ))
+ }
+
+ if(genDiv) {
+ val divActions = commonActions ++ List(IS_DIV -> True)
+ decoderService.addDefault(IS_DIV, False)
+ decoderService.add(List(
+ DIV -> (divActions ++ List(IS_RS1_SIGNED -> True, IS_RS2_SIGNED -> True)),
+ DIVU -> (divActions ++ List(IS_RS1_SIGNED -> False, IS_RS2_SIGNED -> False)),
+ REM -> (divActions ++ List(IS_RS1_SIGNED -> True, IS_RS2_SIGNED -> True)),
+ REMU -> (divActions ++ List(IS_RS1_SIGNED -> False, IS_RS2_SIGNED -> False))
+ ))
+ }
+
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ if(!genMul && !genDiv) return
+
+ val flushStage = if(memory != null) memory else execute
+ flushStage plug new Area {
+ import flushStage._
+
+ //Shared ressources
+ val rs1 = Reg(UInt(33 bits))
+ val rs2 = Reg(UInt(32 bits))
+ val accumulator = Reg(UInt(65 bits))
+
+ //FrontendOK is only used for CPU configs without memory/writeback stages, were it is required to wait one extra cycle
+ // to let's the frontend process rs1 rs2 registers
+ val frontendOk = if(flushStage != execute) True else RegInit(False) setWhen(arbitration.isValid && !pipeline.service(classOf[HazardService]).hazardOnExecuteRS && ((if(genDiv) input(IS_DIV) else False) || (if(genMul) input(IS_MUL) else False))) clearWhen(arbitration.isMoving)
+
+ val mul = ifGen(genMul) (if(customMul != null) customMul(rs1,rs2,memory,pipeline) else new Area{
+ assert(isPow2(mulUnrollFactor))
+ val counter = Counter(32 / mulUnrollFactor + 1)
+ val done = counter.willOverflowIfInc
+ when(arbitration.isValid && input(IS_MUL)){
+ when(!frontendOk || !done){
+ arbitration.haltItself := True
+ }
+ when(frontendOk && !done){
+ arbitration.haltItself := True
+ counter.increment()
+ rs2 := rs2 |>> mulUnrollFactor
+ val sumElements = ((0 until mulUnrollFactor).map(i => rs2(i) ? (rs1 << i) | U(0)) :+ (accumulator >> 32))
+ val sumResult = sumElements.map(_.asSInt.resize(32 + mulUnrollFactor + 1).asUInt).reduceBalancedTree(_ + _)
+ accumulator := (sumResult @@ accumulator(31 downto 0)) >> mulUnrollFactor
+ }
+ output(REGFILE_WRITE_DATA) := ((input(INSTRUCTION)(13 downto 12) === B"00") ? accumulator(31 downto 0) | accumulator(63 downto 32)).asBits
+ }
+ when(!arbitration.isStuck) {
+ counter.clear()
+ }
+ })
+
+
+ val div = ifGen(genDiv) (new Area{
+ assert(isPow2(divUnrollFactor))
+ def area = this
+ //register allocation
+ def numerator = rs1(31 downto 0)
+ def denominator = rs2
+ def remainder = accumulator(31 downto 0)
+
+ val needRevert = Reg(Bool)
+ val counter = Counter(32 / divUnrollFactor + 2)
+ val done = Reg(Bool) setWhen(counter === counter.end-1) clearWhen(!arbitration.isStuck)
+ val result = Reg(Bits(32 bits))
+ when(arbitration.isValid && input(IS_DIV)){
+ when(!frontendOk || !done){
+ arbitration.haltItself := True
+ }
+ when(frontendOk && !done){
+ counter.increment()
+
+ def stages(inNumerator: UInt, inRemainder: UInt, stage: Int): Unit = stage match {
+ case 0 => {
+ numerator := inNumerator
+ remainder := inRemainder
+ }
+ case _ => new Area {
+ val remainderShifted = (inRemainder ## inNumerator.msb).asUInt
+ val remainderMinusDenominator = remainderShifted - denominator
+ val outRemainder = !remainderMinusDenominator.msb ? remainderMinusDenominator.resize(32 bits) | remainderShifted.resize(32 bits)
+ val outNumerator = (inNumerator ## !remainderMinusDenominator.msb).asUInt.resize(32 bits)
+ stages(outNumerator, outRemainder, stage - 1)
+ }.setCompositeName(area, "stage_" + (divUnrollFactor-stage))
+ }
+
+ stages(numerator, remainder, divUnrollFactor)
+
+ when(counter === 32 / divUnrollFactor){
+ val selectedResult = (input(INSTRUCTION)(13) ? remainder | numerator)
+ result := selectedResult.twoComplement(needRevert).asBits.resized
+ }
+ }
+
+ output(REGFILE_WRITE_DATA) := result
+ }
+ })
+
+ //Execute stage logic to drive memory stage's input regs
+ when(if(flushStage != execute) !arbitration.isStuck else !frontendOk){
+ accumulator := 0
+ def twoComplement(that : Bits, enable: Bool): UInt = (Mux(enable, ~that, that).asUInt + enable.asUInt)
+ val rs2NeedRevert = execute.input(RS2).msb && execute.input(IS_RS2_SIGNED)
+ val rs1NeedRevert = (if(genMul)(execute.input(IS_MUL) && rs2NeedRevert) else False) ||
+ (if(genDiv)(execute.input(IS_DIV) && execute.input(RS1).msb && execute.input(IS_RS1_SIGNED)) else False)
+ val rs1Extended = B((32 downto 32) -> (execute.input(IS_RS1_SIGNED) && execute.input(RS1).msb), (31 downto 0) -> execute.input(RS1))
+
+ rs1 := twoComplement(rs1Extended, rs1NeedRevert).resized
+ rs2 := twoComplement(execute.input(RS2), rs2NeedRevert)
+ if(genDiv) div.needRevert := (rs1NeedRevert ^ (rs2NeedRevert && !execute.input(INSTRUCTION)(13))) && !(execute.input(RS2) === 0 && execute.input(IS_RS2_SIGNED) && !execute.input(INSTRUCTION)(13))
+ if(genDiv) div.counter.clear()
+ }
+
+ if(dhrystoneOpt) {
+ execute.insert(FAST_DIV_VALID) := execute.input(IS_DIV) && execute.input(INSTRUCTION)(13 downto 12) === B"00" && !execute.input(RS1).msb && !execute.input(RS2).msb && execute.input(RS1).asUInt < 16 && execute.input(RS2).asUInt < 16 && execute.input(RS2) =/= 0
+ execute.insert(FAST_DIV_VALUE) := (0 to 15).flatMap(n => (0 to 15).map(d => U(if (d == 0) 0 else n / d, 4 bits))).read(U(execute.input(RS1)(3 downto 0)) @@ U(execute.input(RS2)(3 downto 0))) //(U(execute.input(RS1)(3 downto 0)) / U(execute.input(RS2)(3 downto 0))
+ when(execute.input(FAST_DIV_VALID)) {
+ execute.output(IS_DIV) := False
+ }
+ when(input(FAST_DIV_VALID)) {
+ output(REGFILE_WRITE_DATA) := B(0, 28 bits) ## input(FAST_DIV_VALUE)
+ }
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/MulPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/MulPlugin.scala
new file mode 100644
index 0000000..3e909a0
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/MulPlugin.scala
@@ -0,0 +1,159 @@
+package vexriscv.plugin
+import vexriscv._
+import vexriscv.VexRiscv
+import spinal.core._
+import spinal.lib.KeepAttribute
+
+//Input buffer generaly avoid the FPGA synthesis to duplicate reg inside the DSP cell, which could stress timings quite much.
+class MulPlugin(var inputBuffer : Boolean = false,
+ var outputBuffer : Boolean = false) extends Plugin[VexRiscv] with VexRiscvRegressionArg {
+ object MUL_LL extends Stageable(UInt(32 bits))
+ object MUL_LH extends Stageable(SInt(34 bits))
+ object MUL_HL extends Stageable(SInt(34 bits))
+ object MUL_HH extends Stageable(SInt(34 bits))
+
+ object MUL_LOW extends Stageable(SInt(34+16+2 bits))
+
+ object IS_MUL extends Stageable(Bool)
+
+ override def getVexRiscvRegressionArgs(): Seq[String] = {
+ List("MUL=yes")
+ }
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+
+
+ val actions = List[(Stageable[_ <: BaseType],Any)](
+// SRC1_CTRL -> Src1CtrlEnum.RS,
+// SRC2_CTRL -> Src2CtrlEnum.RS,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> False,
+ RS1_USE -> True,
+ RS2_USE -> True,
+ IS_MUL -> True
+ )
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(IS_MUL, False)
+ decoderService.add(List(
+ MULX -> actions
+ ))
+
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+
+ //Do partial multiplication, four times 16 bits * 16 bits
+ execute plug new Area {
+ import execute._
+ val aSigned,bSigned = Bool
+ val a,b = Bits(32 bit)
+
+// a := input(SRC1)
+// b := input(SRC2)
+
+ val delay = (if(inputBuffer) 1 else 0) + (if(outputBuffer) 1 else 0)
+
+ val delayLogic = (delay != 0) generate new Area{
+ val counter = Reg(UInt(log2Up(delay+1) bits))
+ when(arbitration.isValid && input(IS_MUL) && counter =/= delay){
+ arbitration.haltItself := True
+ }
+
+ counter := counter + 1
+ when(!arbitration.isStuck || arbitration.isStuckByOthers){
+ counter := 0
+ }
+ }
+
+ val withInputBuffer = inputBuffer generate new Area{
+ val rs1 = RegNext(input(RS1))
+ val rs2 = RegNext(input(RS2))
+ a := rs1
+ b := rs2
+ }
+
+ val noInputBuffer = (!inputBuffer) generate new Area{
+ a := input(RS1)
+ b := input(RS2)
+ }
+
+ switch(input(INSTRUCTION)(13 downto 12)) {
+ is(B"01") {
+ aSigned := True
+ bSigned := True
+ }
+ is(B"10") {
+ aSigned := True
+ bSigned := False
+ }
+ default {
+ aSigned := False
+ bSigned := False
+ }
+ }
+
+ val aULow = a(15 downto 0).asUInt
+ val bULow = b(15 downto 0).asUInt
+ val aSLow = (False ## a(15 downto 0)).asSInt
+ val bSLow = (False ## b(15 downto 0)).asSInt
+ val aHigh = (((aSigned && a.msb) ## a(31 downto 16))).asSInt
+ val bHigh = (((bSigned && b.msb) ## b(31 downto 16))).asSInt
+
+ val withOuputBuffer = outputBuffer generate new Area{
+ val mul_ll = RegNext(aULow * bULow)
+ val mul_lh = RegNext(aSLow * bHigh)
+ val mul_hl = RegNext(aHigh * bSLow)
+ val mul_hh = RegNext(aHigh * bHigh)
+
+ insert(MUL_LL) := mul_ll
+ insert(MUL_LH) := mul_lh
+ insert(MUL_HL) := mul_hl
+ insert(MUL_HH) := mul_hh
+ }
+
+ val noOutputBuffer = (!outputBuffer) generate new Area{
+ insert(MUL_LL) := aULow * bULow
+ insert(MUL_LH) := aSLow * bHigh
+ insert(MUL_HL) := aHigh * bSLow
+ insert(MUL_HH) := aHigh * bHigh
+ }
+
+ Component.current.afterElaboration{
+ //Avoid synthesis tools to retime RS1 RS2 from execute stage to decode stage leading to bad timings (ex : Vivado, even if retiming is disabled)
+ KeepAttribute(input(RS1))
+ KeepAttribute(input(RS2))
+ }
+ }
+
+ //First aggregation of partial multiplication
+ memory plug new Area {
+ import memory._
+ insert(MUL_LOW) := S(0, MUL_HL.dataType.getWidth + 16 + 2 bit) + (False ## input(MUL_LL)).asSInt + (input(MUL_LH) << 16) + (input(MUL_HL) << 16)
+ }
+
+ //Final aggregation of partial multiplications, REGFILE_WRITE_DATA overriding
+ writeBack plug new Area {
+ import writeBack._
+ val result = input(MUL_LOW) + (input(MUL_HH) << 32)
+
+
+ when(arbitration.isValid && input(IS_MUL)){
+ switch(input(INSTRUCTION)(13 downto 12)){
+ is(B"00"){
+ output(REGFILE_WRITE_DATA) := input(MUL_LOW)(31 downto 0).asBits
+ }
+ is(B"01",B"10",B"11"){
+ output(REGFILE_WRITE_DATA) := result(63 downto 32).asBits
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/MulSimplePlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/MulSimplePlugin.scala
new file mode 100644
index 0000000..3b407e1
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/MulSimplePlugin.scala
@@ -0,0 +1,92 @@
+package vexriscv.plugin
+import vexriscv._
+import vexriscv.VexRiscv
+import spinal.core._
+
+class MulSimplePlugin extends Plugin[VexRiscv]{
+ object MUL_OPA extends Stageable(SInt(33 bits))
+ object MUL_OPB extends Stageable(SInt(33 bits))
+ object MUL extends Stageable(Bits(64 bits))
+
+ object IS_MUL extends Stageable(Bool)
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+
+
+ val actions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC2_CTRL -> Src2CtrlEnum.RS,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> Bool(pipeline.stages.last == pipeline.execute),
+ BYPASSABLE_MEMORY_STAGE -> Bool(pipeline.stages.last == pipeline.memory),
+ RS1_USE -> True,
+ RS2_USE -> True,
+ IS_MUL -> True
+ )
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(IS_MUL, False)
+ decoderService.add(List(
+ MULX -> actions
+ ))
+
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ // Prepare signed inputs for the multiplier in the next stage.
+ // This will map them best to an FPGA DSP.
+ execute plug new Area {
+ import execute._
+ val aSigned,bSigned = Bool
+ val a,b = Bits(32 bit)
+
+ a := input(SRC1)
+ b := input(SRC2)
+ switch(input(INSTRUCTION)(13 downto 12)) {
+ is(B"01") {
+ aSigned := True
+ bSigned := True
+ }
+ is(B"10") {
+ aSigned := True
+ bSigned := False
+ }
+ default {
+ aSigned := False
+ bSigned := False
+ }
+ }
+
+ insert(MUL_OPA) := ((aSigned ? a.msb | False) ## a).asSInt
+ insert(MUL_OPB) := ((bSigned ? b.msb | False) ## b).asSInt
+ }
+
+ val injectionStage = if(pipeline.memory != null) pipeline.memory else pipeline.execute
+ injectionStage plug new Area {
+ import injectionStage._
+
+ insert(MUL) := (input(MUL_OPA) * input(MUL_OPB))(63 downto 0).asBits
+ }
+
+ val memStage = stages.last
+ memStage plug new Area {
+ import memStage._
+
+ when(arbitration.isValid && input(IS_MUL)){
+ switch(input(INSTRUCTION)(13 downto 12)){
+ is(B"00"){
+ output(REGFILE_WRITE_DATA) := input(MUL)(31 downto 0)
+ }
+ is(B"01",B"10",B"11"){
+ output(REGFILE_WRITE_DATA) := input(MUL)(63 downto 32)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/NoPipeliningPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/NoPipeliningPlugin.scala
new file mode 100644
index 0000000..b4ad22b
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/NoPipeliningPlugin.scala
@@ -0,0 +1,23 @@
+package vexriscv.plugin
+
+import spinal.core._
+import spinal.lib._
+import vexriscv._
+
+
+class NoPipeliningPlugin() extends Plugin[VexRiscv] {
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(HAS_SIDE_EFFECT, False)
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ val writesInPipeline = stages.dropWhile(_ != execute).map(s => s.arbitration.isValid && s.input(REGFILE_WRITE_VALID)) :+ RegNext(stages.last.arbitration.isValid && stages.last.input(REGFILE_WRITE_VALID))
+ decode.arbitration.haltByOther.setWhen(stagesFromExecute.map(_.arbitration.isValid).orR)
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/PcManagerSimplePlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/PcManagerSimplePlugin.scala
new file mode 100644
index 0000000..5b1226a
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/PcManagerSimplePlugin.scala
@@ -0,0 +1,145 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+import spinal.lib._
+
+import scala.collection.mutable.ArrayBuffer
+
+
+
+
+class PcManagerSimplePlugin(resetVector : BigInt,
+ relaxedPcCalculation : Boolean = false,
+ keepPcPlus4 : Boolean = true) extends Plugin[VexRiscv]{
+ override def build(pipeline: VexRiscv): Unit = {println("PcManagerSimplePlugin is now useless")}
+}
+
+
+//class PcManagerSimplePlugin(resetVector : BigInt,
+// relaxedPcCalculation : Boolean = false,
+// keepPcPlus4 : Boolean = true) extends Plugin[VexRiscv] with JumpService{
+// //FetchService interface
+// case class JumpInfo(interface : Flow[UInt], stage: Stage, priority : Int)
+// val jumpInfos = ArrayBuffer[JumpInfo]()
+// override def createJumpInterface(stage: Stage, priority : Int = 0): Flow[UInt] = {
+// val interface = Flow(UInt(32 bits))
+// jumpInfos += JumpInfo(interface,stage, priority)
+// interface
+// }
+// var prefetchExceptionPort : Flow[ExceptionCause] = null
+//
+// override def setup(pipeline: VexRiscv): Unit = {
+// if(!relaxedPcCalculation) pipeline.unremovableStages += pipeline.prefetch
+// }
+//
+//
+// override def build(pipeline: VexRiscv): Unit = {
+// import pipeline.config._
+// import pipeline._
+//
+// if(relaxedPcCalculation)
+// relaxedImpl(pipeline)
+// else
+// cycleEffectiveImpl(pipeline)
+//
+// //Formal verification signals generation
+// prefetch.insert(FORMAL_PC_NEXT) := prefetch.input(PC) + 4
+// jumpInfos.foreach(info => {
+// when(info.interface.valid){
+// info.stage.output(FORMAL_PC_NEXT) := info.interface.payload
+// }
+// })
+// }
+//
+// //reduce combinatorial path, and expose the PC to the pipeline as a register
+// def relaxedImpl(pipeline: VexRiscv): Unit = {
+// import pipeline.config._
+// import pipeline._
+//
+// prefetch plug new Area {
+// import prefetch._
+// //Stage always valid
+// arbitration.isValid := True
+//
+// //PC calculation without Jump
+// val pcReg = Reg(UInt(32 bits)) init(resetVector) addAttribute(Verilator.public)
+// val pcPlus4 = pcReg + 4
+// if(keepPcPlus4) KeepAttribute(pcPlus4)
+// when(arbitration.isFiring){
+// pcReg := pcPlus4
+// }
+//
+// //JumpService hardware implementation
+// val jump = if(jumpInfos.length != 0) new Area {
+// val sortedByStage = jumpInfos.sortWith((a, b) => {
+// (pipeline.indexOf(a.stage) > pipeline.indexOf(b.stage)) ||
+// (pipeline.indexOf(a.stage) == pipeline.indexOf(b.stage) && a.priority > b.priority)
+// })
+// val valids = sortedByStage.map(_.interface.valid)
+// val pcs = sortedByStage.map(_.interface.payload)
+//
+// val pcLoad = Flow(UInt(32 bits))
+// pcLoad.valid := jumpInfos.map(_.interface.valid).orR
+// pcLoad.payload := MuxOH(OHMasking.first(valids.asBits), pcs)
+//
+// //application of the selected jump request
+// when(pcLoad.valid) {
+// pcReg := pcLoad.payload
+// }
+// }
+//
+// insert(PC_CALC_WITHOUT_JUMP) := pcReg
+// insert(PC) := pcReg
+// }
+// }
+//
+// //Jump take effect instantly (save one cycle), but expose the PC to the pipeline as a 'long' combinatorial path
+// def cycleEffectiveImpl(pipeline: VexRiscv): Unit = {
+// import pipeline.config._
+// import pipeline.prefetch
+//
+// prefetch plug new Area {
+// import prefetch._
+// //Stage always valid
+// arbitration.isValid := True
+//
+// //PC calculation without Jump
+// val pcReg = Reg(UInt(32 bits)) init(resetVector) addAttribute(Verilator.public)
+// val inc = RegInit(False)
+// val pcBeforeJumps = pcReg + (inc ## B"00").asUInt
+// insert(PC_CALC_WITHOUT_JUMP) := pcBeforeJumps
+// val pc = UInt(32 bits)
+// pc := input(PC_CALC_WITHOUT_JUMP)
+//
+// val samplePcNext = False
+//
+// //JumpService hardware implementation
+// val jump = if(jumpInfos.length != 0) new Area {
+// val sortedByStage = jumpInfos.sortWith((a, b) => pipeline.indexOf(a.stage) > pipeline.indexOf(b.stage))
+// val valids = sortedByStage.map(_.interface.valid)
+// val pcs = sortedByStage.map(_.interface.payload)
+//
+// val pcLoad = Flow(UInt(32 bits))
+// pcLoad.valid := jumpInfos.map(_.interface.valid).orR
+// pcLoad.payload := MuxOH(OHMasking.first(valids.asBits), pcs)
+//
+// //application of the selected jump request
+// when(pcLoad.valid) {
+// inc := False
+// samplePcNext := True
+// pc := pcLoad.payload
+// }
+// }
+//
+// when(arbitration.isFiring){
+// inc := True
+// samplePcNext := True
+// }
+//
+// when(samplePcNext) { pcReg := pc }
+//
+// insert(PC) := pc
+// }
+// }
+//} \ No newline at end of file
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/Plugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/Plugin.scala
new file mode 100644
index 0000000..96d2bc6
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/Plugin.scala
@@ -0,0 +1,25 @@
+package vexriscv.plugin
+
+import vexriscv.{Pipeline, Stage}
+import spinal.core.{Area, Nameable}
+
+/**
+ * Created by PIC32F_USER on 03/03/2017.
+ */
+trait Plugin[T <: Pipeline] extends Nameable{
+ var pipeline : T = null.asInstanceOf[T]
+ setName(this.getClass.getSimpleName.replace("$",""))
+
+ // Used to setup things with other plugins
+ def setup(pipeline: T) : Unit = {}
+
+ //Used to flush out the required hardware (called after setup)
+ def build(pipeline: T) : Unit
+
+ implicit class implicitsStage(stage: Stage){
+ def plug[T <: Area](area : T) : T = {area.setCompositeName(stage,getName()).reflectNames();area}
+ }
+ implicit class implicitsPipeline(stage: Pipeline){
+ def plug[T <: Area](area : T) = {area.setName(getName()).reflectNames();area}
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/PmpPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/PmpPlugin.scala
new file mode 100644
index 0000000..35951e5
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/PmpPlugin.scala
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2021 Samuel Lindemer <samuel.lindemer@ri.se>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+package vexriscv.plugin
+
+import vexriscv.{VexRiscv, _}
+import vexriscv.plugin.MemoryTranslatorPort.{_}
+import spinal.core._
+import spinal.lib._
+import spinal.lib.fsm._
+
+/* Each 32-bit pmpcfg# register contains four 8-bit configuration sections.
+ * These section numbers contain flags which apply to regions defined by the
+ * corresponding pmpaddr# register.
+ *
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | pmp3cfg | pmp2cfg | pmp1cfg | pmp0cfg | pmpcfg0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | pmp7cfg | pmp6cfg | pmp5cfg | pmp4cfg | pmpcfg2
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 7 6 5 4 3 2 1 0
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | L | 0 | A | X | W | R | pmp#cfg
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ *
+ * L: locks configuration until system reset (including M-mode)
+ * 0: hardwired to zero
+ * A: 0 = OFF (null region / disabled)
+ * 1 = TOR (top of range)
+ * 2 = NA4 (naturally aligned four-byte region)
+ * 3 = NAPOT (naturally aligned power-of-two region, > 7 bytes)
+ * X: execute
+ * W: write
+ * R: read
+ *
+ * TOR: Each 32-bit pmpaddr# register defines the upper bound of the pmp region
+ * right-shifted by two bits. The lower bound of the region is the previous
+ * pmpaddr# register. In the case of pmpaddr0, the lower bound is address 0x0.
+ *
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | address[33:2] | pmpaddr#
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * NAPOT: Each 32-bit pmpaddr# register defines the region address and the size
+ * of the pmp region. The number of concurrent 1s begging at the LSB indicates
+ * the size of the region as a power of two (e.g. 0x...0 = 8-byte, 0x...1 =
+ * 16-byte, 0x...11 = 32-byte, etc.).
+ *
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | address[33:2] |0|1|1|1|1| pmpaddr#
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * NA4: This is essentially an edge case of NAPOT where the entire pmpaddr#
+ * register defines a 4-byte wide region.
+ *
+ * N.B. THIS IMPLEMENTATION ONLY SUPPORTS NAPOT ADDRESSING. REGIONS ARE NOT
+ * ORDERED BY PRIORITY. A PERMISSION IS GRANTED TO AN ACCESS IF ANY MATCHING
+ * PMP REGION HAS THAT PERMISSION ENABLED.
+ */
+
+trait Pmp {
+ def OFF = 0
+ def TOR = 1
+ def NA4 = 2
+ def NAPOT = 3
+
+ def xlen = 32
+ def rBit = 0
+ def wBit = 1
+ def xBit = 2
+ def aBits = 4 downto 3
+ def lBit = 7
+}
+
+class PmpSetter(cutoff : Int) extends Component with Pmp {
+ val io = new Bundle {
+ val addr = in UInt(xlen bits)
+ val base, mask = out UInt(xlen - cutoff bits)
+ }
+
+ val ones = io.addr & ~(io.addr + 1)
+ io.base := io.addr(xlen - 3 downto cutoff - 2) ^ ones(xlen - 3 downto cutoff - 2)
+ io.mask := ~(ones(xlen - 4 downto cutoff - 2) @@ U"1")
+}
+
+case class ProtectedMemoryTranslatorPort(bus : MemoryTranslatorBus)
+
+class PmpPlugin(regions : Int, granularity : Int, ioRange : UInt => Bool) extends Plugin[VexRiscv] with MemoryTranslator with Pmp {
+ assert(regions % 4 == 0 & regions <= 16)
+ assert(granularity >= 8)
+
+ var setter : PmpSetter = null
+ var dPort, iPort : ProtectedMemoryTranslatorPort = null
+ val cutoff = log2Up(granularity) - 1
+
+ override def newTranslationPort(priority : Int, args : Any): MemoryTranslatorBus = {
+ val port = ProtectedMemoryTranslatorPort(MemoryTranslatorBus(new MemoryTranslatorBusParameter(0, 0)))
+ priority match {
+ case PRIORITY_INSTRUCTION => iPort = port
+ case PRIORITY_DATA => dPort = port
+ }
+ port.bus
+ }
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ setter = new PmpSetter(cutoff)
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+ import pipeline._
+ import Riscv._
+
+ val csrService = pipeline.service(classOf[CsrInterface])
+ val privilegeService = pipeline.service(classOf[PrivilegeService])
+
+ val state = pipeline plug new Area {
+ val pmpaddr = Mem(UInt(xlen bits), regions)
+ val pmpcfg = Vector.fill(regions)(Reg(Bits(8 bits)) init (0))
+ val base, mask = Vector.fill(regions)(Reg(UInt(xlen - cutoff bits)))
+ }
+
+ def machineMode : Bool = privilegeService.isMachine()
+
+ execute plug new Area {
+ import execute._
+
+ val fsmPending = RegInit(False) clearWhen(!arbitration.isStuck)
+ val fsmComplete = False
+ val hazardFree = csrService.isHazardFree()
+
+ val csrAddress = input(INSTRUCTION)(csrRange)
+ val pmpNcfg = csrAddress(log2Up(regions) - 1 downto 0).asUInt
+ val pmpcfgN = pmpNcfg(log2Up(regions) - 3 downto 0)
+ val pmpcfgCsr = input(INSTRUCTION)(31 downto 24) === 0x3a
+ val pmpaddrCsr = input(INSTRUCTION)(31 downto 24) === 0x3b
+
+ val pmpNcfg_ = Reg(UInt(log2Up(regions) bits))
+ val pmpcfgN_ = Reg(UInt(log2Up(regions) - 2 bits))
+ val pmpcfgCsr_ = RegInit(False)
+ val pmpaddrCsr_ = RegInit(False)
+ val writeData_ = Reg(Bits(xlen bits))
+
+ csrService.duringAnyRead {
+ when (machineMode) {
+ when (pmpcfgCsr) {
+ csrService.allowCsr()
+ csrService.readData() :=
+ state.pmpcfg(pmpcfgN @@ U(3, 2 bits)) ##
+ state.pmpcfg(pmpcfgN @@ U(2, 2 bits)) ##
+ state.pmpcfg(pmpcfgN @@ U(1, 2 bits)) ##
+ state.pmpcfg(pmpcfgN @@ U(0, 2 bits))
+ }
+ when (pmpaddrCsr) {
+ csrService.allowCsr()
+ csrService.readData() := state.pmpaddr(pmpNcfg).asBits
+ }
+ }
+ }
+
+ csrService.duringAnyWrite {
+ when ((pmpcfgCsr | pmpaddrCsr) & machineMode) {
+ csrService.allowCsr()
+ arbitration.haltItself := !fsmComplete
+ when (!fsmPending && hazardFree) {
+ fsmPending := True
+ writeData_ := csrService.writeData()
+ pmpNcfg_ := pmpNcfg
+ pmpcfgN_ := pmpcfgN
+ pmpcfgCsr_ := pmpcfgCsr
+ pmpaddrCsr_ := pmpaddrCsr
+ }
+ }
+ }
+
+ val fsm = new StateMachine {
+ val fsmEnable = RegInit(False)
+ val fsmCounter = Reg(UInt(log2Up(regions) bits)) init(0)
+
+ val stateIdle : State = new State with EntryPoint {
+ onEntry {
+ fsmPending := False
+ fsmEnable := False
+ fsmComplete := True
+ fsmCounter := 0
+ }
+ whenIsActive {
+ when (fsmPending) {
+ goto(stateWrite)
+ }
+ }
+ }
+
+ val stateWrite : State = new State {
+ whenIsActive {
+ when (pmpcfgCsr_) {
+ val overwrite = writeData_.subdivideIn(8 bits)
+ for (i <- 0 until 4) {
+ when (~state.pmpcfg(pmpcfgN_ @@ U(i, 2 bits))(lBit)) {
+ state.pmpcfg(pmpcfgN_ @@ U(i, 2 bits)).assignFromBits(overwrite(i))
+ }
+ }
+ goto(stateCfg)
+ }
+ when (pmpaddrCsr_) {
+ when (~state.pmpcfg(pmpNcfg_)(lBit)) {
+ state.pmpaddr(pmpNcfg_) := writeData_.asUInt
+ }
+ goto(stateAddr)
+ }
+ }
+ onExit (fsmEnable := True)
+ }
+
+ val stateCfg : State = new State {
+ onEntry (fsmCounter := pmpcfgN_ @@ U(0, 2 bits))
+ whenIsActive {
+ fsmCounter := fsmCounter + 1
+ when (fsmCounter(1 downto 0) === 3) {
+ goto(stateIdle)
+ }
+ }
+ }
+
+ val stateAddr : State = new State {
+ onEntry (fsmCounter := pmpNcfg_)
+ whenIsActive (goto(stateIdle))
+ }
+
+ when (pmpaddrCsr_) {
+ setter.io.addr := writeData_.asUInt
+ } otherwise {
+ setter.io.addr := state.pmpaddr(fsmCounter)
+ }
+
+ when (fsmEnable & ~state.pmpcfg(fsmCounter)(lBit)) {
+ state.base(fsmCounter) := setter.io.base
+ state.mask(fsmCounter) := setter.io.mask
+ }
+ }
+ }
+
+ pipeline plug new Area {
+ def getHits(address : UInt) = {
+ (0 until regions).map(i =>
+ ((address & state.mask(U(i, log2Up(regions) bits))) === state.base(U(i, log2Up(regions) bits))) &
+ (state.pmpcfg(i)(lBit) | ~machineMode) & (state.pmpcfg(i)(aBits) === NAPOT)
+ )
+ }
+
+ def getPermission(hits : IndexedSeq[Bool], bit : Int) = {
+ MuxOH(OHMasking.first(hits), state.pmpcfg.map(_(bit)))
+ }
+
+ val dGuard = new Area {
+ val address = dPort.bus.cmd(0).virtualAddress
+ dPort.bus.rsp.physicalAddress := address
+ dPort.bus.rsp.isIoAccess := ioRange(address)
+ dPort.bus.rsp.isPaging := False
+ dPort.bus.rsp.exception := False
+ dPort.bus.rsp.refilling := False
+ dPort.bus.rsp.allowExecute := False
+ dPort.bus.busy := False
+
+ val hits = getHits(address(31 downto cutoff))
+
+ when(~hits.orR) {
+ dPort.bus.rsp.allowRead := machineMode
+ dPort.bus.rsp.allowWrite := machineMode
+ } otherwise {
+ dPort.bus.rsp.allowRead := getPermission(hits, rBit)
+ dPort.bus.rsp.allowWrite := getPermission(hits, wBit)
+ }
+ }
+
+ val iGuard = new Area {
+ val address = iPort.bus.cmd(0).virtualAddress
+ iPort.bus.rsp.physicalAddress := address
+ iPort.bus.rsp.isIoAccess := ioRange(address)
+ iPort.bus.rsp.isPaging := False
+ iPort.bus.rsp.exception := False
+ iPort.bus.rsp.refilling := False
+ iPort.bus.rsp.allowRead := False
+ iPort.bus.rsp.allowWrite := False
+ iPort.bus.busy := False
+
+ val hits = getHits(address(31 downto cutoff))
+
+ when(~hits.orR) {
+ iPort.bus.rsp.allowExecute := machineMode
+ } otherwise {
+ iPort.bus.rsp.allowExecute := getPermission(hits, xBit)
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/PmpPluginOld.scala b/VexRiscv/src/main/scala/vexriscv/plugin/PmpPluginOld.scala
new file mode 100644
index 0000000..0426902
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/PmpPluginOld.scala
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2020 Samuel Lindemer <samuel.lindemer@ri.se>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+package vexriscv.plugin
+
+import vexriscv.{VexRiscv, _}
+import spinal.core._
+import spinal.lib._
+import scala.collection.mutable.ArrayBuffer
+
+/* Each 32-bit pmpcfg# register contains four 8-bit configuration sections.
+ * These section numbers contain flags which apply to regions defined by the
+ * corresponding pmpaddr# register.
+ *
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | pmp3cfg | pmp2cfg | pmp1cfg | pmp0cfg | pmpcfg0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | pmp7cfg | pmp6cfg | pmp5cfg | pmp4cfg | pmpcfg2
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 7 6 5 4 3 2 1 0
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | L | 0 | A | X | W | R | pmp#cfg
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ *
+ * L: locks configuration until system reset (including M-mode)
+ * 0: hardwired to zero
+ * A: 0 = OFF (null region / disabled)
+ * 1 = TOR (top of range)
+ * 2 = NA4 (naturally aligned four-byte region)
+ * 3 = NAPOT (naturally aligned power-of-two region, > 7 bytes)
+ * X: execute
+ * W: write
+ * R: read
+ *
+ * TOR: Each 32-bit pmpaddr# register defines the upper bound of the pmp region
+ * right-shifted by two bits. The lower bound of the region is the previous
+ * pmpaddr# register. In the case of pmpaddr0, the lower bound is address 0x0.
+ *
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | address[33:2] | pmpaddr#
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * NAPOT: Each 32-bit pmpaddr# register defines the region address and the size
+ * of the pmp region. The number of concurrent 1s begging at the LSB indicates
+ * the size of the region as a power of two (e.g. 0x...0 = 8-byte, 0x...1 =
+ * 16-byte, 0x...11 = 32-byte, etc.).
+ *
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | address[33:2] |0|1|1|1|1| pmpaddr#
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * NA4: This is essentially an edge case of NAPOT where the entire pmpaddr#
+ * register defines a 4-byte wide region.
+ */
+
+case class PmpRegister(previous : PmpRegister) extends Area {
+
+ def OFF = 0
+ def TOR = 1
+ def NA4 = 2
+ def NAPOT = 3
+
+ val state = new Area {
+ val r, w, x = Reg(Bool)
+ val l = RegInit(False)
+ val a = Reg(UInt(2 bits)) init(0)
+ val addr = Reg(UInt(32 bits))
+ }
+
+ // CSR writes connect to these signals rather than the internal state
+ // registers. This makes locking and WARL possible.
+ val csr = new Area {
+ val r, w, x = Bool
+ val l = Bool
+ val a = UInt(2 bits)
+ val addr = UInt(32 bits)
+ }
+
+ // Last valid assignment wins; nothing happens if a user-initiated write did
+ // not occur on this clock cycle.
+ csr.r := state.r
+ csr.w := state.w
+ csr.x := state.x
+ csr.l := state.l
+ csr.a := state.a
+ csr.addr := state.addr
+
+ // Computed PMP region bounds
+ val region = new Area {
+ val valid, locked = Bool
+ val start, end = UInt(32 bits)
+ }
+
+ when(~state.l) {
+ state.r := csr.r
+ state.w := csr.w
+ state.x := csr.x
+ state.l := csr.l
+ state.a := csr.a
+ state.addr := csr.addr
+
+ if (csr.l == True & csr.a == TOR) {
+ previous.state.l := True
+ }
+ }
+
+ val shifted = state.addr |<< 2
+ val mask = state.addr & ~(state.addr + 1)
+ val masked = (state.addr & ~mask) |<< 2
+
+ // PMP changes take effect two clock cycles after the initial CSR write (i.e.,
+ // settings propagate from csr -> state -> region).
+ region.locked := state.l
+ region.valid := True
+
+ switch(csr.a) {
+ is(TOR) {
+ if (previous == null) region.start := 0
+ else region.start := previous.region.end
+ region.end := shifted
+ }
+ is(NA4) {
+ region.start := shifted
+ region.end := shifted + 4
+ }
+ is(NAPOT) {
+ region.start := masked
+ region.end := masked + ((mask + 1) |<< 3)
+ }
+ default {
+ region.start := 0
+ region.end := shifted
+ region.valid := False
+ }
+ }
+}
+
+
+class PmpPluginOld(regions : Int, ioRange : UInt => Bool) extends Plugin[VexRiscv] with MemoryTranslator {
+
+ // Each pmpcfg# CSR configures four regions.
+ assert((regions % 4) == 0)
+
+ val pmps = ArrayBuffer[PmpRegister]()
+ val portsInfo = ArrayBuffer[ProtectedMemoryTranslatorPort]()
+
+ override def newTranslationPort(priority : Int, args : Any): MemoryTranslatorBus = {
+ val port = ProtectedMemoryTranslatorPort(MemoryTranslatorBus(new MemoryTranslatorBusParameter(0, 0)))
+ portsInfo += port
+ port.bus
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+ import pipeline._
+ import Riscv._
+
+ val csrService = pipeline.service(classOf[CsrInterface])
+ val privilegeService = pipeline.service(classOf[PrivilegeService])
+
+ val core = pipeline plug new Area {
+
+ // Instantiate pmpaddr0 ... pmpaddr# CSRs.
+ for (i <- 0 until regions) {
+ if (i == 0) {
+ pmps += PmpRegister(null)
+ } else {
+ pmps += PmpRegister(pmps.last)
+ }
+ csrService.r(0x3b0 + i, pmps(i).state.addr)
+ csrService.w(0x3b0 + i, pmps(i).csr.addr)
+ }
+
+ // Instantiate pmpcfg0 ... pmpcfg# CSRs.
+ for (i <- 0 until (regions / 4)) {
+ csrService.r(0x3a0 + i,
+ 31 -> pmps((i * 4) + 3).state.l, 23 -> pmps((i * 4) + 2).state.l,
+ 15 -> pmps((i * 4) + 1).state.l, 7 -> pmps((i * 4) ).state.l,
+ 27 -> pmps((i * 4) + 3).state.a, 26 -> pmps((i * 4) + 3).state.x,
+ 25 -> pmps((i * 4) + 3).state.w, 24 -> pmps((i * 4) + 3).state.r,
+ 19 -> pmps((i * 4) + 2).state.a, 18 -> pmps((i * 4) + 2).state.x,
+ 17 -> pmps((i * 4) + 2).state.w, 16 -> pmps((i * 4) + 2).state.r,
+ 11 -> pmps((i * 4) + 1).state.a, 10 -> pmps((i * 4) + 1).state.x,
+ 9 -> pmps((i * 4) + 1).state.w, 8 -> pmps((i * 4) + 1).state.r,
+ 3 -> pmps((i * 4) ).state.a, 2 -> pmps((i * 4) ).state.x,
+ 1 -> pmps((i * 4) ).state.w, 0 -> pmps((i * 4) ).state.r
+ )
+ csrService.w(0x3a0 + i,
+ 31 -> pmps((i * 4) + 3).csr.l, 23 -> pmps((i * 4) + 2).csr.l,
+ 15 -> pmps((i * 4) + 1).csr.l, 7 -> pmps((i * 4) ).csr.l,
+ 27 -> pmps((i * 4) + 3).csr.a, 26 -> pmps((i * 4) + 3).csr.x,
+ 25 -> pmps((i * 4) + 3).csr.w, 24 -> pmps((i * 4) + 3).csr.r,
+ 19 -> pmps((i * 4) + 2).csr.a, 18 -> pmps((i * 4) + 2).csr.x,
+ 17 -> pmps((i * 4) + 2).csr.w, 16 -> pmps((i * 4) + 2).csr.r,
+ 11 -> pmps((i * 4) + 1).csr.a, 10 -> pmps((i * 4) + 1).csr.x,
+ 9 -> pmps((i * 4) + 1).csr.w, 8 -> pmps((i * 4) + 1).csr.r,
+ 3 -> pmps((i * 4) ).csr.a, 2 -> pmps((i * 4) ).csr.x,
+ 1 -> pmps((i * 4) ).csr.w, 0 -> pmps((i * 4) ).csr.r
+ )
+ }
+
+ // Connect memory ports to PMP logic.
+ val ports = for ((port, portId) <- portsInfo.zipWithIndex) yield new Area {
+
+ val address = port.bus.cmd(0).virtualAddress
+ port.bus.rsp.physicalAddress := address
+
+ // Only the first matching PMP region applies.
+ val hits = pmps.map(pmp => pmp.region.valid &
+ pmp.region.start <= address &
+ pmp.region.end > address &
+ (pmp.region.locked | ~privilegeService.isMachine()))
+
+ // M-mode has full access by default, others have none.
+ when(CountOne(hits) === 0) {
+ port.bus.rsp.allowRead := privilegeService.isMachine()
+ port.bus.rsp.allowWrite := privilegeService.isMachine()
+ port.bus.rsp.allowExecute := privilegeService.isMachine()
+ } otherwise {
+ port.bus.rsp.allowRead := MuxOH(OHMasking.first(hits), pmps.map(_.state.r))
+ port.bus.rsp.allowWrite := MuxOH(OHMasking.first(hits), pmps.map(_.state.w))
+ port.bus.rsp.allowExecute := MuxOH(OHMasking.first(hits), pmps.map(_.state.x))
+ }
+
+ port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress)
+ port.bus.rsp.isPaging := False
+ port.bus.rsp.exception := False
+ port.bus.rsp.refilling := False
+ port.bus.busy := False
+
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/RegFilePlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/RegFilePlugin.scala
new file mode 100644
index 0000000..94a3f32
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/RegFilePlugin.scala
@@ -0,0 +1,122 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+import spinal.lib._
+
+import scala.collection.mutable
+
+
+trait RegFileReadKind
+object ASYNC extends RegFileReadKind
+object SYNC extends RegFileReadKind
+
+
+class RegFilePlugin(regFileReadyKind : RegFileReadKind,
+ zeroBoot : Boolean = false,
+ x0Init : Boolean = true,
+ writeRfInMemoryStage : Boolean = false,
+ readInExecute : Boolean = false,
+ syncUpdateOnStall : Boolean = true,
+ rv32e : Boolean = false,
+ withShadow : Boolean = false //shadow registers aren't transition hazard free
+ ) extends Plugin[VexRiscv] with RegFileService{
+ import Riscv._
+
+ override def readStage(): Stage = if(readInExecute) pipeline.execute else pipeline.decode
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(RS1_USE,False)
+ decoderService.addDefault(RS2_USE,False)
+ decoderService.addDefault(REGFILE_WRITE_VALID,False)
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ val readStage = if(readInExecute) execute else decode
+ val writeStage = if(writeRfInMemoryStage) memory else stages.last
+
+ val numRegisters = if(rv32e) 16 else 32
+ def clipRange(that : Range) = if(rv32e) that.tail else that
+
+ val global = pipeline plug new Area{
+ val regFileSize = if(withShadow) numRegisters * 2 else numRegisters
+ val regFile = Mem(Bits(32 bits),regFileSize) addAttribute(Verilator.public)
+ if(zeroBoot) regFile.init(List.fill(regFileSize)(B(0, 32 bits)))
+
+ val shadow = ifGen(withShadow)(new Area{
+ val write, read, clear = RegInit(False)
+
+ read clearWhen(clear && !readStage.arbitration.isStuck)
+ write clearWhen(clear && !writeStage.arbitration.isStuck)
+
+ val csrService = pipeline.service(classOf[CsrInterface])
+ csrService.w(0x7C0,2 -> clear, 1 -> read, 0 -> write)
+ })
+ }
+
+ //Disable rd0 write in decoding stage
+ when(decode.input(INSTRUCTION)(rdRange) === 0) {
+ decode.input(REGFILE_WRITE_VALID) := False
+ }
+ if(rv32e) when(decode.input(INSTRUCTION)(rdRange.head)) {
+ decode.input(REGFILE_WRITE_VALID) := False
+ }
+
+ //Read register file
+ readStage plug new Area{
+ import readStage._
+
+ //read register file
+ val srcInstruction = regFileReadyKind match{
+ case `ASYNC` => input(INSTRUCTION)
+ case `SYNC` if !readInExecute => input(INSTRUCTION_ANTICIPATED)
+ case `SYNC` if readInExecute => if(syncUpdateOnStall) Mux(execute.arbitration.isStuck, execute.input(INSTRUCTION), decode.input(INSTRUCTION)) else decode.input(INSTRUCTION)
+ }
+
+ def shadowPrefix(that : Bits) = if(withShadow) global.shadow.read ## that else that
+ val regFileReadAddress1 = U(shadowPrefix(srcInstruction(clipRange(Riscv.rs1Range))))
+ val regFileReadAddress2 = U(shadowPrefix(srcInstruction(clipRange(Riscv.rs2Range))))
+
+ val (rs1Data,rs2Data) = regFileReadyKind match{
+ case `ASYNC` => (global.regFile.readAsync(regFileReadAddress1),global.regFile.readAsync(regFileReadAddress2))
+ case `SYNC` =>
+ val enable = if(!syncUpdateOnStall) !readStage.arbitration.isStuck else null
+ (global.regFile.readSync(regFileReadAddress1, enable),global.regFile.readSync(regFileReadAddress2, enable))
+ }
+
+ insert(RS1) := rs1Data
+ insert(RS2) := rs2Data
+ }
+
+ //Write register file
+ writeStage plug new Area {
+ import writeStage._
+
+ def shadowPrefix(that : Bits) = if(withShadow) global.shadow.write ## that else that
+ val regFileWrite = global.regFile.writePort.addAttribute(Verilator.public).setName("lastStageRegFileWrite")
+ regFileWrite.valid := output(REGFILE_WRITE_VALID) && arbitration.isFiring
+ regFileWrite.address := U(shadowPrefix(output(INSTRUCTION)(clipRange(rdRange))))
+ regFileWrite.data := output(REGFILE_WRITE_DATA)
+
+ //Ensure no boot glitches modify X0
+ if(!x0Init && zeroBoot) when(regFileWrite.address === 0){
+ regFileWrite.valid := False
+ }
+
+ //CPU will initialise constant register zero in the first cycle
+ if(x0Init) {
+ val boot = RegNext(False) init (True)
+ regFileWrite.valid setWhen (boot)
+ when(boot) {
+ regFileWrite.address := 0
+ regFileWrite.data := 0
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/ShiftPlugins.scala b/VexRiscv/src/main/scala/vexriscv/plugin/ShiftPlugins.scala
new file mode 100644
index 0000000..a4ae716
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/ShiftPlugins.scala
@@ -0,0 +1,193 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+import spinal.lib.Reverse
+
+
+
+class FullBarrelShifterPlugin(earlyInjection : Boolean = false) extends Plugin[VexRiscv]{
+ object ShiftCtrlEnum extends SpinalEnum(binarySequential){
+ val DISABLE, SLL, SRL, SRA = newElement()
+ }
+
+ object SHIFT_CTRL extends Stageable(ShiftCtrlEnum())
+ object SHIFT_RIGHT extends Stageable(Bits(32 bits))
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+
+
+
+ val immediateActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC2_CTRL -> Src2CtrlEnum.IMI,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> Bool(earlyInjection),
+ BYPASSABLE_MEMORY_STAGE -> True,
+ RS1_USE -> True
+ )
+
+ val nonImmediateActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC2_CTRL -> Src2CtrlEnum.RS,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> Bool(earlyInjection),
+ BYPASSABLE_MEMORY_STAGE -> True,
+ RS1_USE -> True,
+ RS2_USE -> True
+ )
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(SHIFT_CTRL, ShiftCtrlEnum.DISABLE)
+ decoderService.add(List(
+ SLL -> (nonImmediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SLL)),
+ SRL -> (nonImmediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SRL)),
+ SRA -> (nonImmediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SRA))
+ ))
+
+ decoderService.add(List(
+ SLLI -> (immediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SLL)),
+ SRLI -> (immediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SRL)),
+ SRAI -> (immediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SRA))
+ ))
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+
+ execute plug new Area{
+ import execute._
+ val amplitude = input(SRC2)(4 downto 0).asUInt
+ val reversed = Mux(input(SHIFT_CTRL) === ShiftCtrlEnum.SLL, Reverse(input(SRC1)), input(SRC1))
+ insert(SHIFT_RIGHT) := (Cat(input(SHIFT_CTRL) === ShiftCtrlEnum.SRA & reversed.msb, reversed).asSInt >> amplitude)(31 downto 0).asBits
+ }
+
+ val injectionStage = if(earlyInjection) execute else memory
+ injectionStage plug new Area{
+ import injectionStage._
+ when(arbitration.isValid){
+ switch(input(SHIFT_CTRL)) {
+ is(ShiftCtrlEnum.SLL) {
+ output(REGFILE_WRITE_DATA) := Reverse(input(SHIFT_RIGHT))
+ }
+ is(ShiftCtrlEnum.SRL, ShiftCtrlEnum.SRA) {
+ output(REGFILE_WRITE_DATA) := input(SHIFT_RIGHT)
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+class LightShifterPlugin extends Plugin[VexRiscv]{
+ object ShiftCtrlEnum extends SpinalEnum(binarySequential){
+ val DISABLE, SLL, SRL, SRA = newElement()
+ }
+
+ object SHIFT_CTRL extends Stageable(ShiftCtrlEnum())
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import Riscv._
+ import pipeline.config._
+ import IntAluPlugin._
+
+ val immediateActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC2_CTRL -> Src2CtrlEnum.IMI,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> True,
+ BYPASSABLE_MEMORY_STAGE -> True,
+ RS1_USE -> True,
+
+ //Get SRC1 through the MMU to the RF write path
+ ALU_CTRL -> AluCtrlEnum.ADD_SUB,
+ SRC_USE_SUB_LESS -> False,
+ SRC_ADD_ZERO -> True
+ )
+
+ val nonImmediateActions = List[(Stageable[_ <: BaseType],Any)](
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ SRC2_CTRL -> Src2CtrlEnum.RS,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> True,
+ BYPASSABLE_MEMORY_STAGE -> True,
+ RS1_USE -> True,
+ RS2_USE -> True,
+
+ //Get SRC1 through the MMU to the RF write path
+ ALU_CTRL -> AluCtrlEnum.ADD_SUB,
+ SRC_USE_SUB_LESS -> False,
+ SRC_ADD_ZERO -> True
+ )
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(SHIFT_CTRL, ShiftCtrlEnum.DISABLE)
+ decoderService.add(List(
+ SLL -> (nonImmediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SLL)),
+ SRL -> (nonImmediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SRL)),
+ SRA -> (nonImmediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SRA))
+ ))
+
+ decoderService.add(List(
+ SLLI -> (immediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SLL)),
+ SRLI -> (immediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SRL)),
+ SRAI -> (immediateActions ++ List(SHIFT_CTRL -> ShiftCtrlEnum.SRA))
+ ))
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+
+ execute plug new Area{
+ import execute._
+
+ val isActive = RegInit(False)
+ val isShift = input(SHIFT_CTRL) =/= ShiftCtrlEnum.DISABLE
+ val amplitudeReg = Reg(UInt(5 bits))
+ val amplitude = isActive ? amplitudeReg | input(SRC2)(4 downto 0).asUInt
+ val shiftReg = ifGen(!withMemoryStage) (RegNextWhen(execute.output(REGFILE_WRITE_DATA), !arbitration.isStuckByOthers))
+ val shiftInput = isActive ? (if(withMemoryStage) memory.input(REGFILE_WRITE_DATA) else shiftReg) | input(SRC1)
+ val done = amplitude(4 downto 1) === 0
+
+ if(withMemoryStage) memory.dontSampleStageable(REGFILE_WRITE_DATA, arbitration.isStuckByOthers)
+
+ when(arbitration.isValid && isShift && input(SRC2)(4 downto 0) =/= 0){
+ output(REGFILE_WRITE_DATA) := input(SHIFT_CTRL).mux(
+ ShiftCtrlEnum.SLL -> (shiftInput |<< 1),
+ default -> (((input(SHIFT_CTRL) === ShiftCtrlEnum.SRA && shiftInput.msb) ## shiftInput).asSInt >> 1).asBits //ALU.SRL,ALU.SRA
+ )
+
+ when(!arbitration.isStuckByOthers){
+ isActive := True
+ amplitudeReg := amplitude - 1
+
+ when(done){
+ isActive := False
+ }
+ }
+
+ when(!done){
+ arbitration.haltItself := True
+ }
+ }
+ when(arbitration.removeIt){
+ isActive := False
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/SingleInstructionLimiterPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/SingleInstructionLimiterPlugin.scala
new file mode 100644
index 0000000..c6c9706
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/SingleInstructionLimiterPlugin.scala
@@ -0,0 +1,17 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+import spinal.lib._
+
+
+class SingleInstructionLimiterPlugin() extends Plugin[VexRiscv] {
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ val fetcher = pipeline.service(classOf[IBusFetcher])
+ when(fetcher.incoming() || List(decode,execute,memory,writeBack).map(_.arbitration.isValid).orR) {
+ fetcher.haltIt()
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/SrcPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/SrcPlugin.scala
new file mode 100644
index 0000000..d67e7cc
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/SrcPlugin.scala
@@ -0,0 +1,83 @@
+package vexriscv.plugin
+
+import vexriscv._
+import spinal.core._
+import spinal.lib.KeepAttribute
+
+
+class SrcPlugin(separatedAddSub : Boolean = false, executeInsertion : Boolean = false, decodeAddSub : Boolean = false) extends Plugin[VexRiscv]{
+ object SRC2_FORCE_ZERO extends Stageable(Bool)
+
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(SRC_ADD_ZERO, False) //TODO avoid this default to simplify decoding ?
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ decode.insert(SRC2_FORCE_ZERO) := decode.input(SRC_ADD_ZERO) && !decode.input(SRC_USE_SUB_LESS)
+
+ val insertionStage = if(executeInsertion) execute else decode
+ insertionStage plug new Area{
+ import insertionStage._
+
+ val imm = Riscv.IMM(input(INSTRUCTION))
+ insert(SRC1) := input(SRC1_CTRL).mux(
+ Src1CtrlEnum.RS -> output(RS1),
+ Src1CtrlEnum.PC_INCREMENT -> (if(pipeline.config.withRvc) Mux(input(IS_RVC), B(2), B(4)) else B(4)).resized,
+ Src1CtrlEnum.IMU -> imm.u.resized,
+ Src1CtrlEnum.URS1 -> input(INSTRUCTION)(Riscv.rs1Range).resized
+ )
+ insert(SRC2) := input(SRC2_CTRL).mux(
+ Src2CtrlEnum.RS -> output(RS2),
+ Src2CtrlEnum.IMI -> imm.i_sext.resized,
+ Src2CtrlEnum.IMS -> imm.s_sext.resized,
+ Src2CtrlEnum.PC -> output(PC).asBits
+ )
+ }
+
+ val addSubStage = if(decodeAddSub) decode else execute
+ if(separatedAddSub) {
+ addSubStage plug new Area {
+ import addSubStage._
+
+ // ADD, SUB
+ val add = (U(input(SRC1)) + U(input(SRC2))).asBits.addAttribute("keep")
+ val sub = (U(input(SRC1)) - U(input(SRC2))).asBits.addAttribute("keep")
+ when(input(SRC_ADD_ZERO)){ add := input(SRC1) }
+
+ // SLT, SLTU
+ val less = Mux(input(SRC1).msb === input(SRC2).msb, sub.msb,
+ Mux(input(SRC_LESS_UNSIGNED), input(SRC2).msb, input(SRC1).msb))
+
+ insert(SRC_ADD_SUB) := input(SRC_USE_SUB_LESS) ? sub | add
+ insert(SRC_ADD) := add
+ insert(SRC_SUB) := sub
+ insert(SRC_LESS) := less
+ }
+ }else{
+ addSubStage plug new Area {
+ import addSubStage._
+
+ // ADD, SUB
+ val addSub = (input(SRC1).asSInt + Mux(input(SRC_USE_SUB_LESS), ~input(SRC2), input(SRC2)).asSInt + Mux(input(SRC_USE_SUB_LESS), S(1, 32 bits), S(0, 32 bits))).asBits
+ when(input(SRC2_FORCE_ZERO)){ addSub := input(SRC1) }
+
+
+ // SLT, SLTU
+ val less = Mux(input(SRC1).msb === input(SRC2).msb, addSub.msb,
+ Mux(input(SRC_LESS_UNSIGNED), input(SRC2).msb, input(SRC1).msb))
+
+ insert(SRC_ADD_SUB) := addSub
+ insert(SRC_ADD) := addSub
+ insert(SRC_SUB) := addSub
+ insert(SRC_LESS) := less
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/StaticMemoryTranslatorPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/StaticMemoryTranslatorPlugin.scala
new file mode 100644
index 0000000..cafd8de
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/StaticMemoryTranslatorPlugin.scala
@@ -0,0 +1,41 @@
+package vexriscv.plugin
+
+import vexriscv.{VexRiscv, _}
+import spinal.core._
+import spinal.lib._
+
+import scala.collection.mutable.ArrayBuffer
+case class StaticMemoryTranslatorPort(bus : MemoryTranslatorBus, priority : Int)
+
+class StaticMemoryTranslatorPlugin(ioRange : UInt => Bool) extends Plugin[VexRiscv] with MemoryTranslator {
+ val portsInfo = ArrayBuffer[StaticMemoryTranslatorPort]()
+
+ override def newTranslationPort(priority : Int,args : Any): MemoryTranslatorBus = {
+ val port = StaticMemoryTranslatorPort(MemoryTranslatorBus(MemoryTranslatorBusParameter(wayCount = 0)),priority)
+ portsInfo += port
+ port.bus
+ }
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ import Riscv._
+
+ val core = pipeline plug new Area {
+ val ports = for ((port, portId) <- portsInfo.zipWithIndex) yield new Area {
+ port.bus.rsp.physicalAddress := port.bus.cmd.last.virtualAddress
+ port.bus.rsp.allowRead := True
+ port.bus.rsp.allowWrite := True
+ port.bus.rsp.allowExecute := True
+ port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress)
+ port.bus.rsp.isPaging := False
+ port.bus.rsp.exception := False
+ port.bus.rsp.refilling := False
+ port.bus.busy := False
+ }
+ }
+ }
+}
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/VfuPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/VfuPlugin.scala
new file mode 100644
index 0000000..a2c0930
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/VfuPlugin.scala
@@ -0,0 +1,136 @@
+package vexriscv.plugin
+
+import vexriscv.{DecoderService, ExceptionCause, ExceptionService, Stage, Stageable, VexRiscv}
+import spinal.core._
+import spinal.lib._
+import spinal.lib.bus.bmb.WeakConnector
+import spinal.lib.bus.misc.{AddressMapping, DefaultMapping}
+import vexriscv.Riscv.IMM
+
+
+object VfuPlugin{
+ val ROUND_MODE_WIDTH = 3
+
+}
+
+
+case class VfuParameter() //Empty for now
+
+case class VfuCmd( p : VfuParameter ) extends Bundle{
+ val instruction = Bits(32 bits)
+ val inputs = Vec(Bits(32 bits), 2)
+ val rounding = Bits(VfuPlugin.ROUND_MODE_WIDTH bits)
+}
+
+case class VfuRsp(p : VfuParameter) extends Bundle{
+ val output = Bits(32 bits)
+}
+
+case class VfuBus(p : VfuParameter) extends Bundle with IMasterSlave{
+ val cmd = Stream(VfuCmd(p))
+ val rsp = Stream(VfuRsp(p))
+
+ def <<(m : VfuBus) : Unit = {
+ val s = this
+ s.cmd << m.cmd
+ m.rsp << s.rsp
+ }
+
+ override def asMaster(): Unit = {
+ master(cmd)
+ slave(rsp)
+ }
+}
+
+
+
+class VfuPlugin(val stageCount : Int,
+ val allowZeroLatency : Boolean,
+ val parameter : VfuParameter) extends Plugin[VexRiscv]{
+ def p = parameter
+
+ var bus : VfuBus = null
+
+ lazy val forkStage = pipeline.execute
+ lazy val joinStage = pipeline.stages(Math.min(pipeline.stages.length - 1, pipeline.indexOf(forkStage) + stageCount))
+
+
+ object VFU_ENABLE extends Stageable(Bool())
+ object VFU_IN_FLIGHT extends Stageable(Bool())
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ bus = master(VfuBus(p))
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(VFU_ENABLE, False)
+
+ decoderService.add(
+ key = M"-------------------------0001011",
+ values = List(
+ VFU_ENABLE -> True,
+ REGFILE_WRITE_VALID -> True, //If you want to write something back into the integer register file
+ BYPASSABLE_EXECUTE_STAGE -> Bool(stageCount == 0),
+ BYPASSABLE_MEMORY_STAGE -> Bool(stageCount <= 1),
+ RS1_USE -> True,
+ RS2_USE -> True
+ )
+ )
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ val csr = pipeline plug new Area{
+ val factory = pipeline.service(classOf[CsrInterface])
+ val rounding = Reg(Bits(VfuPlugin.ROUND_MODE_WIDTH bits))
+
+ factory.rw(csrAddress = 0xBC0, bitOffset = 0, that = rounding)
+ }
+
+
+ forkStage plug new Area{
+ import forkStage._
+ val hazard = stages.dropWhile(_ != forkStage).tail.map(s => s.arbitration.isValid && s.input(HAS_SIDE_EFFECT)).orR
+ val scheduleWish = arbitration.isValid && input(VFU_ENABLE)
+ val schedule = scheduleWish && !hazard
+ arbitration.haltItself setWhen(scheduleWish && hazard)
+
+ val hold = RegInit(False) setWhen(schedule) clearWhen(bus.cmd.ready)
+ val fired = RegInit(False) setWhen(bus.cmd.fire) clearWhen(!arbitration.isStuck)
+ insert(VFU_IN_FLIGHT) := schedule || hold || fired
+
+ bus.cmd.valid := (schedule || hold) && !fired
+ arbitration.haltItself setWhen(bus.cmd.valid && !bus.cmd.ready)
+
+ bus.cmd.instruction := input(INSTRUCTION)
+ bus.cmd.inputs(0) := input(RS1)
+ bus.cmd.inputs(1) := input(RS2)
+ bus.cmd.rounding := csr.rounding
+ }
+
+ joinStage plug new Area{
+ import joinStage._
+
+ val rsp = if(forkStage != joinStage && allowZeroLatency) {
+ bus.rsp.s2mPipe()
+ } else {
+ bus.rsp.combStage()
+ }
+
+ rsp.ready := False
+ when(input(VFU_IN_FLIGHT) && input(REGFILE_WRITE_VALID)){
+ arbitration.haltItself setWhen(!bus.rsp.valid)
+ rsp.ready := !arbitration.isStuckByOthers
+ output(REGFILE_WRITE_DATA) := bus.rsp.output
+ }
+ }
+
+ pipeline.stages.drop(1).foreach(s => s.output(VFU_IN_FLIGHT) clearWhen(s.arbitration.isStuck))
+ addPrePopTask(() => stages.dropWhile(_ != memory).reverse.dropWhile(_ != joinStage).foreach(s => s.input(VFU_IN_FLIGHT).init(False)))
+ }
+}
+
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/YamlPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/YamlPlugin.scala
new file mode 100644
index 0000000..ca53e42
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/YamlPlugin.scala
@@ -0,0 +1,32 @@
+package vexriscv.plugin
+
+import java.util
+
+import vexriscv.{ReportService, VexRiscv}
+import org.yaml.snakeyaml.{DumperOptions, Yaml}
+
+
+/**
+ * Created by spinalvm on 09.06.17.
+ */
+class YamlPlugin(path : String) extends Plugin[VexRiscv] with ReportService{
+
+ val content = new util.HashMap[String, Object]()
+
+ def add(that : (String,Object)) : Unit = content.put(that._1,that._2)
+
+ override def setup(pipeline: VexRiscv): Unit = {
+
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ val options = new DumperOptions()
+ options.setWidth(50)
+ options.setIndent(4)
+ options.setCanonical(true)
+ options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK)
+
+ val yaml = new Yaml()
+ yaml.dump(content, new java.io.FileWriter(path))
+ }
+}