wishbone: add Cache (from WB2LASMI)
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 17 Jun 2015 13:31:49 +0000 (15:31 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 17 Jun 2015 13:31:49 +0000 (15:31 +0200)
migen/bus/wishbone.py

index fedadfe8949beb3c0e33935c2fd4898c74c2364e..7e6224a74b39aef51bd71ee288773c46b45f6a86 100644 (file)
@@ -1,8 +1,10 @@
 from migen.fhdl.std import *
 from migen.genlib import roundrobin
 from migen.genlib.record import *
-from migen.genlib.misc import optree, chooser, FlipFlop, Counter
+from migen.genlib.misc import split, displacer, optree, chooser
+from migen.genlib.misc import FlipFlop, Counter
 from migen.genlib.fsm import FSM, NextState
+from migen.bank.description import *
 from migen.bus.transactions import *
 
 _layout = [
@@ -407,6 +409,162 @@ class Converter(Module):
             Record.connect(master, slave)
 
 
+class Cache(Module, AutoCSR):
+    """Cache
+
+    This module is a write-back wishbone cache that can be used as a L2 cache.
+    Cachesize (in 32-bit words) is the size of the data store and must be a power of 2
+    """
+    def __init__(self, cachesize, master, slave):
+        self._size = CSRStatus(8, reset=log2_int(cachesize))
+        self.master = master
+        self.slave = slave
+
+        ###
+
+        dw_from = flen(master.dat_r)
+        dw_to = flen(slave.dat_r)
+        if dw_to > dw_from and (dw_to % dw_from) != 0:
+            raise ValueError("Slave data width must be a multiple of {dw}".format(dw=dw_from))
+        if dw_to < dw_from and (dw_from % dw_to) != 0:
+            raise ValueError("Master data width must be a multiple of {dw}".format(dw=dw_to))
+
+        # Split address:
+        # TAG | LINE NUMBER | LINE OFFSET
+        offsetbits = log2_int(max(dw_to//dw_from, 1))
+        addressbits = flen(slave.adr) + offsetbits
+        linebits = log2_int(cachesize) - offsetbits
+        tagbits = addressbits - linebits
+        wordbits = log2_int(max(dw_from//dw_to, 1))
+        adr_offset, adr_line, adr_tag = split(master.adr, offsetbits, linebits, tagbits)
+        word = Signal(wordbits) if wordbits else None
+
+        # Data memory
+        data_mem = Memory(dw_to*2**wordbits, 2**linebits)
+        data_port = data_mem.get_port(write_capable=True, we_granularity=8)
+        self.specials += data_mem, data_port
+
+        write_from_slave = Signal()
+        if adr_offset is None:
+            adr_offset_r = None
+        else:
+            adr_offset_r = Signal(offsetbits)
+            self.sync += adr_offset_r.eq(adr_offset)
+
+        self.comb += [
+            data_port.adr.eq(adr_line),
+            If(write_from_slave,
+                displacer(slave.dat_r, word, data_port.dat_w),
+                displacer(Replicate(1, dw_to//8), word, data_port.we)
+            ).Else(
+                data_port.dat_w.eq(Replicate(master.dat_w, max(dw_to//dw_from, 1))),
+                If(master.cyc & master.stb & master.we & master.ack,
+                    displacer(master.sel, adr_offset, data_port.we, 2**offsetbits, reverse=True)
+                )
+            ),
+            chooser(data_port.dat_r, word, slave.dat_w),
+            slave.sel.eq(2**(dw_to//8)-1),
+            chooser(data_port.dat_r, adr_offset_r, master.dat_r, reverse=True)
+        ]
+
+
+        # Tag memory
+        tag_layout = [("tag", tagbits), ("dirty", 1)]
+        tag_mem = Memory(layout_len(tag_layout), 2**linebits)
+        tag_port = tag_mem.get_port(write_capable=True)
+        self.specials += tag_mem, tag_port
+        tag_do = Record(tag_layout)
+        tag_di = Record(tag_layout)
+        self.comb += [
+            tag_do.raw_bits().eq(tag_port.dat_r),
+            tag_port.dat_w.eq(tag_di.raw_bits())
+        ]
+
+        self.comb += [
+            tag_port.adr.eq(adr_line),
+            tag_di.tag.eq(adr_tag)
+        ]
+        if word is not None:
+            self.comb += slave.adr.eq(Cat(word, adr_line, tag_do.tag))
+        else:
+            self.comb += slave.adr.eq(Cat(adr_line, tag_do.tag))
+
+        # slave word computation, word_clr and word_inc will be simplified
+        # at synthesis when wordbits=0
+        word_clr = Signal()
+        word_inc = Signal()
+        if word is not None:
+            self.sync += \
+                If(word_clr,
+                    word.eq(0),
+                ).Elif(word_inc,
+                    word.eq(word+1)
+                )
+
+        def word_is_last(word):
+            if word is not None:
+                return word == 2**wordbits-1
+            else:
+                return 1
+
+        # Control FSM
+        self.submodules.fsm = fsm = FSM(reset_state="IDLE")
+        fsm.act("IDLE",
+            If(master.cyc & master.stb,
+                NextState("TEST_HIT")
+            )
+        )
+        fsm.act("TEST_HIT",
+            word_clr.eq(1),
+            If(tag_do.tag == adr_tag,
+                master.ack.eq(1),
+                If(master.we,
+                    tag_di.dirty.eq(1),
+                    tag_port.we.eq(1)
+                ),
+                NextState("IDLE")
+            ).Else(
+                If(tag_do.dirty,
+                    NextState("EVICT")
+                ).Else(
+                    NextState("REFILL_WRTAG")
+                )
+            )
+        )
+
+        fsm.act("EVICT",
+            slave.stb.eq(1),
+            slave.cyc.eq(1),
+            slave.we.eq(1),
+            If(slave.ack,
+                word_inc.eq(1),
+                 If(word_is_last(word),
+                    NextState("REFILL_WRTAG")
+                )
+            )
+        )
+        fsm.act("REFILL_WRTAG",
+            # Write the tag first to set the slave address
+            tag_port.we.eq(1),
+            word_clr.eq(1),
+            NextState("REFILL")
+        )
+        fsm.act("REFILL",
+            slave.stb.eq(1),
+            slave.cyc.eq(1),
+            slave.we.eq(0),
+            If(slave.ack,
+                write_from_slave.eq(1),
+                word_inc.eq(1),
+                If(word_is_last(word),
+                    NextState("TEST_HIT"),
+                ).Else(
+                    NextState("REFILL")
+                )
+            )
+        )
+
+
 class Tap(Module):
     def __init__(self, bus, handler=print):
         self.bus = bus