def elaborate(self, platform): m = Module() ack_fanin = 0 err_fanin = 0 rty_fanin = 0 stall_fanin = 0 with m.Switch(self.bus.adr): for sub_map, (sub_pat, sub_ratio) in self._map.window_patterns(): sub_bus = self._subs[sub_map] m.d.comb += [ sub_bus.adr.eq(self.bus.adr << log2_int(sub_ratio)), sub_bus.dat_w.eq(self.bus.dat_w), sub_bus.sel.eq( Cat(Repl(sel, sub_ratio) for sel in self.bus.sel)), sub_bus.we.eq(self.bus.we), sub_bus.stb.eq(self.bus.stb), ] if hasattr(sub_bus, "lock"): m.d.comb += sub_bus.lock.eq(getattr(self.bus, "lock", 0)) if hasattr(sub_bus, "cti"): m.d.comb += sub_bus.cti.eq( getattr(self.bus, "cti", CycleType.CLASSIC)) if hasattr(sub_bus, "bte"): m.d.comb += sub_bus.bte.eq( getattr(self.bus, "bte", BurstTypeExt.LINEAR)) granularity_bits = log2_int(self.bus.data_width // self.bus.granularity) with m.Case(sub_pat[:-granularity_bits if granularity_bits > 0 else None]): m.d.comb += [ sub_bus.cyc.eq(self.bus.cyc), self.bus.dat_r.eq(sub_bus.dat_r), ] ack_fanin |= sub_bus.ack if hasattr(sub_bus, "err"): err_fanin |= sub_bus.err if hasattr(sub_bus, "rty"): rty_fanin |= sub_bus.rty if hasattr(sub_bus, "stall"): stall_fanin |= sub_bus.stall m.d.comb += self.bus.ack.eq(ack_fanin) if hasattr(self.bus, "err"): m.d.comb += self.bus.err.eq(err_fanin) if hasattr(self.bus, "rty"): m.d.comb += self.bus.rty.eq(rty_fanin) if hasattr(self.bus, "stall"): m.d.comb += self.bus.stall.eq(stall_fanin) return m
def __init__(self, csr_bus, *, data_width=None, name=None): if not isinstance(csr_bus, CSRInterface): raise ValueError( "CSR bus must be an instance of CSRInterface, not {!r}".format( csr_bus)) if csr_bus.data_width not in (8, 16, 32, 64): raise ValueError( "CSR bus data width must be one of 8, 16, 32, 64, not {!r}". format(csr_bus.data_width)) if data_width is None: data_width = csr_bus.data_width self.csr_bus = csr_bus self.wb_bus = WishboneInterface(addr_width=max( 0, csr_bus.addr_width - log2_int(data_width // csr_bus.data_width)), data_width=data_width, granularity=csr_bus.data_width, name="wb") wb_map = MemoryMap(addr_width=csr_bus.addr_width, data_width=csr_bus.data_width, name=name) # Since granularity of the Wishbone interface matches the data width of the CSR bus, # no width conversion is performed, even if the Wishbone data width is greater. wb_map.add_window(self.csr_bus.memory_map) self.wb_bus.memory_map = wb_map
def bus(self): if self._bus is None: self._map.freeze() granularity_bits = log2_int(self.data_width // self.granularity) self._bus = Interface(addr_width=self._map.addr_width - granularity_bits, data_width=self.data_width, granularity=self.granularity, features=self.features) self._bus.memory_map = self._map return self._bus
def addr_between(base, limit): assert base in range(0, 2**30 + 1) assert limit in range(0, 2**30 + 1) assert limit >= base assert base % (limit - base) == 0 addr_width = log2_int(limit - base, need_pow2=True) const_bits = 30 - addr_width if const_bits > 0: const_pat = "{:0{}b}".format(base >> addr_width, const_bits) else: const_pat = "" return "{}{}".format(const_pat, "-" * addr_width)
def window(self, *, addr_width, data_width, granularity=None, features=frozenset(), alignment=0, addr=None, sparse=None): """Request a window to a subordinate bus. See :meth:`amaranth_soc.wishbone.Decoder.add` for details. Return value ------------ An instance of :class:`amaranth_soc.wishbone.Interface`. """ window = wishbone.Interface(addr_width=addr_width, data_width=data_width, granularity=granularity, features=features) granularity_bits = log2_int(data_width // window.granularity) window.memory_map = MemoryMap(addr_width=addr_width + granularity_bits, data_width=window.granularity, alignment=alignment) self._windows.append((window, addr, sparse)) return window
def memory_map(self, memory_map): if not isinstance(memory_map, MemoryMap): raise TypeError( "Memory map must be an instance of MemoryMap, not {!r}".format( memory_map)) if memory_map.data_width != self.granularity: raise ValueError( "Memory map has data width {}, which is not the same as bus " "interface granularity {}".format(memory_map.data_width, self.granularity)) granularity_bits = log2_int(self.data_width // self.granularity) if memory_map.addr_width != max(1, self.addr_width + granularity_bits): raise ValueError( "Memory map has address width {}, which is not the same as bus " "interface address width {} ({} address bits + {} granularity bits)" .format(memory_map.addr_width, self.addr_width + granularity_bits, self.addr_width, granularity_bits)) memory_map.freeze() self._map = memory_map
def elaborate(self, platform): csr_bus = self.csr_bus wb_bus = self.wb_bus m = Module() cycle = Signal(range(len(wb_bus.sel) + 1)) m.d.comb += csr_bus.addr.eq( Cat(cycle[:log2_int(len(wb_bus.sel))], wb_bus.adr)) with m.If(wb_bus.cyc & wb_bus.stb): with m.Switch(cycle): def segment(index): return slice(index * wb_bus.granularity, (index + 1) * wb_bus.granularity) for index, sel_index in enumerate(wb_bus.sel): with m.Case(index): if index > 0: # CSR reads are registered, and we need to re-register them. m.d.sync += wb_bus.dat_r[segment(index - 1)].eq( csr_bus.r_data) m.d.comb += csr_bus.r_stb.eq(sel_index & ~wb_bus.we) m.d.comb += csr_bus.w_data.eq( wb_bus.dat_w[segment(index)]) m.d.comb += csr_bus.w_stb.eq(sel_index & wb_bus.we) m.d.sync += cycle.eq(index + 1) with m.Default(): m.d.sync += wb_bus.dat_r[segment(index)].eq(csr_bus.r_data) m.d.sync += wb_bus.ack.eq(1) with m.If(wb_bus.ack): m.d.sync += cycle.eq(0) m.d.sync += wb_bus.ack.eq(0) return m
def __init__(self, *, addr_width, data_width, granularity=None, features=frozenset(), alignment=0, name=None): if granularity is None: granularity = data_width _check_interface(addr_width, data_width, granularity, features) self.data_width = data_width self.granularity = granularity self.features = set(features) self.alignment = alignment granularity_bits = log2_int(data_width // granularity) self._map = MemoryMap(addr_width=max(1, addr_width + granularity_bits), data_width=granularity, alignment=alignment, name=name) self._subs = dict() self._bus = None
def elaborate(self, platform): m = Module() m.submodules.dcache = self._dcache x_dcache_select = Signal() # Test whether the target address is inside the L1 cache region. We use a bit mask in order # to avoid carry chains from arithmetic comparisons. To this end, the base and limit # addresses of the cached region must satisfy the following constraints: # 1) the region size (i.e. ``limit - base``) must be a power of 2 # 2) ``base`` must be a multiple of the region size def addr_between(base, limit): assert base in range(0, 2**30 + 1) assert limit in range(0, 2**30 + 1) assert limit >= base assert base % (limit - base) == 0 addr_width = log2_int(limit - base, need_pow2=True) const_bits = 30 - addr_width if const_bits > 0: const_pat = "{:0{}b}".format(base >> addr_width, const_bits) else: const_pat = "" return "{}{}".format(const_pat, "-" * addr_width) dcache_pattern = addr_between(self._dcache.base >> 2, self._dcache.limit >> 2) with m.If(self.x_addr[2:].matches(dcache_pattern)): m.d.comb += x_dcache_select.eq(1) m_dcache_select = Signal() m_addr = Signal.like(self.x_addr) with m.If(~self.x_stall): m.d.sync += [ m_dcache_select.eq(x_dcache_select), m_addr.eq(self.x_addr), ] m.d.comb += [ self._dcache.s1_addr.eq(self.x_addr[2:]), self._dcache.s1_stall.eq(self.x_stall), self._dcache.s1_valid.eq(self.x_valid), self._dcache.s2_addr.eq(m_addr[2:]), self._dcache.s2_re.eq(self.m_load & m_dcache_select), self._dcache.s2_evict.eq(self.m_store & m_dcache_select), self._dcache.s2_flush.eq(self.m_flush), self._dcache.s2_valid.eq(self.m_valid), ] m.submodules.wrbuf = self._wrbuf m.d.comb += [ self._wrbuf.w.en.eq(self.x_store & self.x_valid & x_dcache_select & ~self.x_stall), self._wrbuf.w.op.addr.eq(self.x_addr[2:]), self._wrbuf.w.op.mask.eq(self.x_mask), self._wrbuf.w.op.data.eq(self.x_store_data), ] dbus_arbiter = m.submodules.dbus_arbiter = WishboneArbiter() m.d.comb += dbus_arbiter.bus.connect(self.dbus) wrbuf_port = dbus_arbiter.port(priority=0) m.d.comb += [ wrbuf_port.cyc.eq(self._wrbuf.r.rdy), wrbuf_port.we.eq(Const(1)), ] with m.If(wrbuf_port.stb): with m.If(wrbuf_port.ack | wrbuf_port.err): m.d.sync += wrbuf_port.stb.eq(0) m.d.comb += self._wrbuf.r.en.eq(1) with m.Elif(self._wrbuf.r.rdy): m.d.sync += [ wrbuf_port.stb.eq(1), wrbuf_port.adr.eq(self._wrbuf.r.op.addr), wrbuf_port.sel.eq(self._wrbuf.r.op.mask), wrbuf_port.dat_w.eq(self._wrbuf.r.op.data), ] dcache_port = dbus_arbiter.port(priority=1) m.d.comb += [ dcache_port.cyc.eq(self._dcache.bus_re), dcache_port.stb.eq(self._dcache.bus_re), dcache_port.adr.eq(self._dcache.bus_addr), dcache_port.sel.eq(0b1111), dcache_port.cti.eq( Mux(self._dcache.bus_last, Cycle.END, Cycle.INCREMENT)), dcache_port.bte.eq(Const(log2_int(self._dcache.nwords) - 1)), self._dcache.bus_valid.eq(dcache_port.ack), self._dcache.bus_error.eq(dcache_port.err), self._dcache.bus_rdata.eq(dcache_port.dat_r) ] bare_port = dbus_arbiter.port(priority=2) bare_rdata = Signal.like(bare_port.dat_r) with m.If(bare_port.cyc): with m.If(bare_port.ack | bare_port.err | ~self.m_valid): m.d.sync += [ bare_port.cyc.eq(0), bare_port.stb.eq(0), bare_rdata.eq(bare_port.dat_r) ] with m.Elif((self.x_load | self.x_store) & ~x_dcache_select & self.x_valid & ~self.x_stall): m.d.sync += [ bare_port.cyc.eq(1), bare_port.stb.eq(1), bare_port.adr.eq(self.x_addr[2:]), bare_port.sel.eq(self.x_mask), bare_port.we.eq(self.x_store), bare_port.dat_w.eq(self.x_store_data) ] with m.If(self.dbus.cyc & self.dbus.err): m.d.sync += [ self.m_load_error.eq(~self.dbus.we), self.m_store_error.eq(self.dbus.we), self.m_badaddr.eq(self.dbus.adr) ] with m.Elif(~self.m_stall): m.d.sync += [self.m_load_error.eq(0), self.m_store_error.eq(0)] with m.If(self.x_fence_i): m.d.comb += self.x_busy.eq(self._wrbuf.r.rdy) with m.Elif(x_dcache_select): m.d.comb += self.x_busy.eq(self.x_store & ~self._wrbuf.w.rdy) with m.Else(): m.d.comb += self.x_busy.eq(bare_port.cyc) with m.If(self.m_flush): m.d.comb += self.m_busy.eq(m_dcache_select & ~self._dcache.s2_flush_ack) with m.Elif(self.m_load_error | self.m_store_error): m.d.comb += self.m_busy.eq(0) with m.Elif(m_dcache_select): m.d.comb += self.m_busy.eq(self.m_load & self._dcache.s2_miss) with m.Else(): m.d.comb += self.m_busy.eq(bare_port.cyc) with m.If(self.m_load_error): m.d.comb += self.m_load_data.eq(0) with m.Elif(m_dcache_select): m.d.comb += self.m_load_data.eq(self._dcache.s2_rdata) with m.Else(): m.d.comb += self.m_load_data.eq(bare_rdata) return m
def elaborate(self, platform): m = Module() m.submodules.icache = self._icache a_icache_select = Signal() # Test whether the target address is inside the L1 cache region. We use a bit mask in order # to avoid carry chains from arithmetic comparisons. To this end, the base and limit # addresses of the cached region must satisfy the following constraints: # 1) the region size (i.e. ``limit - base``) must be a power of 2 # 2) ``base`` must be a multiple of the region size def addr_between(base, limit): assert base in range(0, 2**30 + 1) assert limit in range(0, 2**30 + 1) assert limit >= base assert base % (limit - base) == 0 addr_width = log2_int(limit - base, need_pow2=True) const_bits = 30 - addr_width if const_bits > 0: const_pat = "{:0{}b}".format(base >> addr_width, const_bits) else: const_pat = "" return "{}{}".format(const_pat, "-" * addr_width) icache_pattern = addr_between(self._icache.base >> 2, self._icache.limit >> 2) with m.If(self.a_pc[2:].matches(icache_pattern)): m.d.comb += a_icache_select.eq(1) f_icache_select = Signal() f_flush = Signal() with m.If(~self.a_stall): m.d.sync += [ f_icache_select.eq(a_icache_select), f_flush.eq(self.a_flush), ] m.d.comb += [ self._icache.s1_addr.eq(self.a_pc[2:]), self._icache.s1_stall.eq(self.a_stall), self._icache.s1_valid.eq(self.a_valid), self._icache.s2_addr.eq(self.f_pc[2:]), self._icache.s2_re.eq(f_icache_select), self._icache.s2_evict.eq(Const(0)), self._icache.s2_flush.eq(f_flush), self._icache.s2_valid.eq(self.f_valid), ] ibus_arbiter = m.submodules.ibus_arbiter = WishboneArbiter() m.d.comb += ibus_arbiter.bus.connect(self.ibus) icache_port = ibus_arbiter.port(priority=0) m.d.comb += [ icache_port.cyc.eq(self._icache.bus_re), icache_port.stb.eq(self._icache.bus_re), icache_port.adr.eq(self._icache.bus_addr), icache_port.sel.eq(0b1111), icache_port.cti.eq( Mux(self._icache.bus_last, Cycle.END, Cycle.INCREMENT)), icache_port.bte.eq(Const(log2_int(self._icache.nwords) - 1)), self._icache.bus_valid.eq(icache_port.ack), self._icache.bus_error.eq(icache_port.err), self._icache.bus_rdata.eq(icache_port.dat_r) ] bare_port = ibus_arbiter.port(priority=1) bare_rdata = Signal.like(bare_port.dat_r) with m.If(bare_port.cyc): with m.If(bare_port.ack | bare_port.err | ~self.f_valid): m.d.sync += [ bare_port.cyc.eq(0), bare_port.stb.eq(0), bare_rdata.eq(bare_port.dat_r) ] with m.Elif(~a_icache_select & self.a_valid & ~self.a_stall): m.d.sync += [ bare_port.cyc.eq(1), bare_port.stb.eq(1), bare_port.adr.eq(self.a_pc[2:]) ] m.d.comb += bare_port.sel.eq(0b1111) m.d.comb += self.a_busy.eq(bare_port.cyc) with m.If(self.ibus.cyc & self.ibus.err): m.d.sync += [ self.f_fetch_error.eq(1), self.f_badaddr.eq(self.ibus.adr) ] with m.Elif(~self.f_stall): m.d.sync += self.f_fetch_error.eq(0) with m.If(f_flush): m.d.comb += self.f_busy.eq(f_icache_select & ~self._icache.s2_flush_ack) with m.Elif(self.f_fetch_error): m.d.comb += self.f_busy.eq(0) with m.Elif(f_icache_select): m.d.comb += self.f_busy.eq(self._icache.s2_miss) with m.Else(): m.d.comb += self.f_busy.eq(bare_port.cyc) with m.If(self.f_fetch_error): m.d.comb += self.f_instruction.eq( 0x00000013) # nop (addi x0, x0, 0) with m.Elif(f_icache_select): m.d.comb += self.f_instruction.eq(self._icache.s2_rdata) with m.Else(): m.d.comb += self.f_instruction.eq(bare_rdata) return m
def __init__(self, nways, nlines, nwords, base, limit): if not isinstance(nlines, int): raise TypeError( "nlines must be an integer, not {!r}".format(nlines)) if nlines == 0 or nlines & nlines - 1: raise ValueError( "nlines must be a power of 2, not {}".format(nlines)) if nwords not in {4, 8, 16}: raise ValueError( "nwords must be 4, 8 or 16, not {!r}".format(nwords)) if nways not in {1, 2}: raise ValueError("nways must be 1 or 2, not {!r}".format(nways)) if not isinstance(base, int) or base not in range(0, 2**32): raise ValueError( "base must be an integer between {:#x} and {:#x} inclusive, not {!r}" .format(0, 2**32 - 1, base)) if limit not in range(0, 2**32): raise ValueError( "limit must be an integer between {:#x} and {:#x} inclusive, not {!r}" .format(0, 2**32 - 1, limit)) if base >= limit: raise ValueError( "limit {:#x} must be greater than base {:#x}".format( limit, base)) if (limit - base) & (limit - base) - 1: raise ValueError( "limit - base must be a power of 2, not {:#x}".format(limit - base)) if base % (limit - base): raise ValueError( "base must be a multiple of limit - base, but {:#x} is not a multiple " "of {:#x}".format(base, limit - base)) self.nways = nways self.nlines = nlines self.nwords = nwords self.base = base self.limit = limit offsetbits = log2_int(nwords) linebits = log2_int(nlines) tagbits = bits_for(limit) - linebits - offsetbits - 2 self.s1_addr = Record([("offset", offsetbits), ("line", linebits), ("tag", tagbits)]) self.s1_stall = Signal() self.s1_valid = Signal() self.s2_addr = Record.like(self.s1_addr) self.s2_re = Signal() self.s2_flush = Signal() self.s2_evict = Signal() self.s2_valid = Signal() self.bus_valid = Signal() self.bus_error = Signal() self.bus_rdata = Signal(32) self.s2_miss = Signal() self.s2_flush_ack = Signal() self.s2_rdata = Signal(32) self.bus_re = Signal() self.bus_addr = Record.like(self.s1_addr) self.bus_last = Signal()