X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fsoc%2Fexperiment%2Fpimem.py;h=7077fbde7b19ee914ddf28c01241542638903b2e;hb=5ef5b14d86fbd3321b67478731f98dc989ab2c1e;hp=3fac8cb649354b110779a250ee71935106065240;hpb=1cc55357cbaac182afb337fc7a9c2080502e963a;p=soc.git diff --git a/src/soc/experiment/pimem.py b/src/soc/experiment/pimem.py index 3fac8cb6..7077fbde 100644 --- a/src/soc/experiment/pimem.py +++ b/src/soc/experiment/pimem.py @@ -12,6 +12,7 @@ Links: * https://bugs.libre-soc.org/show_bug.cgi?id=216 * https://libre-soc.org/3d_gpu/architecture/memory_and_cache/ +* https://bugs.libre-soc.org/show_bug.cgi?id=465 - exception handling """ @@ -23,13 +24,16 @@ from nmigen.utils import log2_int from nmutil.latch import SRLatch, latchregister from nmutil.util import rising_edge -from soc.decoder.power_decoder2 import Data +from openpower.decoder.power_decoder2 import Data from soc.scoreboard.addr_match import LenExpand +from soc.experiment.mem_types import LDSTException # for testing purposes from soc.experiment.testmem import TestMemory #from soc.scoreboard.addr_split import LDSTSplitter - +from nmutil.util import Display +from collections import namedtuple +MSRSpec = namedtuple("MSRSpec", ["dr", "pr", "sf"]) # used in unit tests import unittest @@ -59,13 +63,13 @@ class PortInterface(RecordObject): for the L0 Cache/Buffer to have an additional address latch (because the LDSTCompUnit already has it) - * addr_ok_o (or addr_exc_o) must be waited for. these will + * addr_ok_o (or exception.happened) must be waited for. these will be asserted *only* for one cycle and one cycle only. - * addr_exc_o will be asserted if there is no chance that the + * exception.happened will be asserted if there is no chance that the memory request may be fulfilled. - busy_o is deasserted on the same cycle as addr_exc_o is asserted. + busy_o is deasserted on the same cycle as exception.happened is asserted. * conversely: addr_ok_o must *ONLY* be asserted if there is a HUNDRED PERCENT guarantee that the memory request will be @@ -95,8 +99,8 @@ class PortInterface(RecordObject): RecordObject.__init__(self, name=name) # distinguish op type (ld/st) - self.is_ld_i = Signal(reset_less=True) - self.is_st_i = Signal(reset_less=True) + self.is_ld_i = Signal(reset_less=True) + self.is_st_i = Signal(reset_less=True) # LD/ST data length (TODO: other things may be needed) self.data_len = Signal(4, reset_less=True) @@ -107,25 +111,52 @@ class PortInterface(RecordObject): self.addr = Data(addrwid, "addr_i") # addr/addr-ok # addr is valid (TLB, L1 etc.) self.addr_ok_o = Signal(reset_less=True) - self.addr_exc_o = Signal(reset_less=True) # TODO, "type" of exception + self.exc_o = LDSTException("exc") # LD/ST self.ld = Data(regwid, "ld_data_o") # ok to be set by L0 Cache/Buf self.st = Data(regwid, "st_data_i") # ok to be set by CompUnit + # additional "modes" + self.is_nc = Signal() # no cacheing + + #only priv_mode = not msr_pr is used currently + # TODO: connect signals + self.virt_mode = Signal() # ctrl.msr(MSR_DR); + self.priv_mode = Signal() # not ctrl.msr(MSR_PR); + self.mode_32bit = Signal() # not ctrl.msr(MSR_SF); + + self.is_dcbz_i = Signal(reset_less=True) + + # mmu + self.mmu_done = Signal() # keep for now + + # dcache + self.ldst_error = Signal() + ## Signalling ld/st error - NC cache hit, TLB miss, prot/RC failure + self.cache_paradox = Signal() + def connect_port(self, inport): print("connect_port", self, inport) return [self.is_ld_i.eq(inport.is_ld_i), self.is_st_i.eq(inport.is_st_i), + self.is_nc.eq(inport.is_nc), + self.is_dcbz_i.eq(inport.is_dcbz_i), self.data_len.eq(inport.data_len), self.go_die_i.eq(inport.go_die_i), self.addr.data.eq(inport.addr.data), self.addr.ok.eq(inport.addr.ok), self.st.eq(inport.st), + self.virt_mode.eq(inport.virt_mode), + self.priv_mode.eq(inport.priv_mode), + self.mode_32bit.eq(inport.mode_32bit), inport.ld.eq(self.ld), inport.busy_o.eq(self.busy_o), inport.addr_ok_o.eq(self.addr_ok_o), - inport.addr_exc_o.eq(self.addr_exc_o), + inport.exc_o.eq(self.exc_o), + inport.mmu_done.eq(self.mmu_done), + inport.ldst_error.eq(self.ldst_error), + inport.cache_paradox.eq(self.cache_paradox) ] @@ -152,8 +183,8 @@ class PortInterfaceBase(Elaboratable): def connect_port(self, inport): return self.pi.connect_port(inport) - def set_wr_addr(self, m, addr, mask): pass - def set_rd_addr(self, m, addr, mask): pass + def set_wr_addr(self, m, addr, mask, misalign, msr_pr, is_dcbz): pass + def set_rd_addr(self, m, addr, mask, misalign, msr_pr): pass def set_wr_data(self, m, data, wen): pass def get_rd_data(self, m): pass @@ -191,6 +222,7 @@ class PortInterfaceBase(Elaboratable): pi = self.pi comb += lds.eq(pi.is_ld_i) # ld-req signals comb += sts.eq(pi.is_st_i) # st-req signals + pr = ~pi.priv_mode # detect busy "edge" busy_delay = Signal() @@ -198,6 +230,13 @@ class PortInterfaceBase(Elaboratable): sync += busy_delay.eq(pi.busy_o) comb += busy_edge.eq(pi.busy_o & ~busy_delay) + # misalignment detection: bits at end of lenexpand are set. + # when using the L0CacheBuffer "data expander" which splits requests + # into *two* PortInterfaces, this acts as a "safety check". + misalign = Signal() + comb += misalign.eq(lenexp.lexp_o[8:].bool()) + + # activate mode: only on "edge" comb += ld_active.s.eq(rising_edge(m, lds)) # activate LD mode comb += st_active.s.eq(rising_edge(m, sts)) # activate ST mode @@ -205,6 +244,8 @@ class PortInterfaceBase(Elaboratable): # LD/ST requested activates "busy" (only if not already busy) with m.If(self.pi.is_ld_i | self.pi.is_st_i): comb += busy_l.s.eq(~busy_delay) + with m.If(self.pi.exc_o.happened): + sync += Display("fast exception") # if now in "LD" mode: wait for addr_ok, then send the address out # to memory, acknowledge address, and send out LD data @@ -214,7 +255,7 @@ class PortInterfaceBase(Elaboratable): comb += lenexp.len_i.eq(pi.data_len) comb += lenexp.addr_i.eq(lsbaddr) with m.If(pi.addr.ok & adrok_l.qn): - self.set_rd_addr(m, pi.addr.data, lenexp.lexp_o) + self.set_rd_addr(m, pi.addr.data, lenexp.lexp_o, misalign, pr) comb += pi.addr_ok_o.eq(1) # acknowledge addr ok sync += adrok_l.s.eq(1) # and pull "ack" latch @@ -226,8 +267,9 @@ class PortInterfaceBase(Elaboratable): comb += lenexp.len_i.eq(pi.data_len) comb += lenexp.addr_i.eq(lsbaddr) with m.If(pi.addr.ok): - self.set_wr_addr(m, pi.addr.data, lenexp.lexp_o) - with m.If(adrok_l.qn): + self.set_wr_addr(m, pi.addr.data, lenexp.lexp_o, misalign, pr, + pi.is_dcbz_i) + with m.If(adrok_l.qn & self.pi.exc_o.happened==0): comb += pi.addr_ok_o.eq(1) # acknowledge addr ok sync += adrok_l.s.eq(1) # and pull "ack" latch @@ -247,6 +289,7 @@ class PortInterfaceBase(Elaboratable): comb += reset_l.s.eq(ldok) # reset mode after 1 cycle # for ST mode, when addr has been "ok'd", wait for incoming "ST ok" + sync += st_done.s.eq(0) # store done trigger with m.If(st_active.q & pi.st.ok): # shift data up before storing. lenexp *bit* version of mask is # passed straight through as byte-level "write-enable" lines. @@ -255,7 +298,7 @@ class PortInterfaceBase(Elaboratable): # TODO: replace with link to LoadStoreUnitInterface.x_store_data # and also handle the ready/stall/busy protocol stok = self.set_wr_data(m, stdata, lenexp.lexp_o) - sync += st_done.s.eq(1) # store done trigger + sync += st_done.s.eq(~self.pi.exc_o.happened) # store done trigger with m.If(st_done.q): comb += reset_l.s.eq(stok) # reset mode after 1 cycle @@ -267,15 +310,16 @@ class PortInterfaceBase(Elaboratable): # after waiting one cycle (reset_l is "sync" mode), reset the port with m.If(reset_l.q): - comb += ld_active.r.eq(1) # leave the ST active for 1 cycle + comb += ld_active.r.eq(1) # leave the LD active for 1 cycle comb += st_active.r.eq(1) # leave the ST active for 1 cycle comb += reset_l.r.eq(1) # clear reset comb += adrok_l.r.eq(1) # address reset comb += st_done.r.eq(1) # store done reset - # monitor for an exception or the completion of LD. - with m.If(self.pi.addr_exc_o): + # monitor for an exception, clear busy immediately + with m.If(self.pi.exc_o.happened): comb += busy_l.r.eq(1) + comb += reset_l.s.eq(1) # also reset whole unit # however ST needs one cycle before busy is reset #with m.If(self.pi.st.ok | self.pi.ld.ok): @@ -287,7 +331,14 @@ class PortInterfaceBase(Elaboratable): comb += busy_l.r.eq(1) # busy latch outputs to interface - comb += pi.busy_o.eq(busy_l.q) + if hasattr(self, "external_busy"): + # when there is an extra (external) busy, include that here. + # this is used e.g. in LoadStore1 when an instruction fault + # is being processed (instr_fault) and stops Load/Store requests + # from being made until it's done + comb += pi.busy_o.eq(busy_l.q | self.external_busy(m)) + else: + comb += pi.busy_o.eq(busy_l.q) return m @@ -313,11 +364,11 @@ class TestMemoryPortInterface(PortInterfaceBase): # hard-code memory addressing width to 6 bits self.mem = TestMemory(regwid, 5, granularity=regwid//8, init=False) - def set_wr_addr(self, m, addr, mask): + def set_wr_addr(self, m, addr, mask, misalign, msr_pr, is_dcbz): lsbaddr, msbaddr = self.splitaddr(addr) m.d.comb += self.mem.wrport.addr.eq(msbaddr) - def set_rd_addr(self, m, addr, mask): + def set_rd_addr(self, m, addr, mask, misalign, msr_pr): lsbaddr, msbaddr = self.splitaddr(addr) m.d.comb += self.mem.rdport.addr.eq(msbaddr)