--- /dev/null
+Copyright (C) 2018-2019 LambdaConcept
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+Other authors retain ownership of their contributions. If a submission can
+reasonably be considered independently copyrightable, it's yours and we
+encourage you to claim it with appropriate copyright notices. This submission
+then falls under the "otherwise noted" category. All submissions are strongly
+encouraged to use the two-clause BSD license reproduced above.
--- /dev/null
+from nmigen import *
+from nmigen.asserts import *
+from nmigen.lib.coding import Encoder
+from nmigen.utils import log2_int
+
+
+__all__ = ["L1Cache"]
+
+
+class L1Cache(Elaboratable):
+ def __init__(self, nways, nlines, nwords, base, limit):
+ if not nlines or nlines & nlines-1:
+ raise ValueError("nlines must be a power of 2, not {!r}".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))
+
+ self.nways = nways
+ self.nlines = nlines
+ self.nwords = nwords
+ self.base = base
+ self.limit = limit
+
+ offsetbits = log2_int(nwords)
+ linebits = log2_int(nlines)
+ tagbits = log2_int(limit-base) - log2_int(nlines) - log2_int(nwords) - 2
+
+ self.s1_addr = Record([("offset", offsetbits), ("line", linebits), ("tag", tagbits)])
+ self.s1_flush = Signal()
+ self.s1_stall = Signal()
+ self.s1_valid = Signal()
+ self.s2_addr = Record.like(self.s1_addr)
+ self.s2_re = 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_rdata = Signal(32)
+ self.bus_re = Signal()
+ self.bus_addr = Record.like(self.s1_addr)
+ self.bus_last = Signal()
+
+ def elaborate(self, platform):
+ m = Module()
+
+ ways = Array(Record([("data", self.nwords * 32),
+ ("tag", self.s2_addr.tag.shape()),
+ ("valid", 1),
+ ("bus_re", 1)])
+ for _ in range(self.nways))
+
+ if self.nways == 1:
+ way_lru = Const(0)
+ elif self.nways == 2:
+ way_lru = Signal()
+ with m.If(self.bus_re & self.bus_valid & self.bus_last & ~self.bus_error):
+ m.d.sync += way_lru.eq(~way_lru)
+
+ m.d.comb += ways[way_lru].bus_re.eq(self.bus_re)
+
+ way_hit = m.submodules.way_hit = Encoder(self.nways)
+ for j, way in enumerate(ways):
+ m.d.comb += way_hit.i[j].eq((way.tag == self.s2_addr.tag) & way.valid)
+
+ m.d.comb += [
+ self.s2_miss.eq(way_hit.n),
+ self.s2_rdata.eq(ways[way_hit.o].data.word_select(self.s2_addr.offset, 32))
+ ]
+
+ with m.FSM() as fsm:
+ last_offset = Signal.like(self.s2_addr.offset)
+
+ with m.State("CHECK"):
+ with m.If(self.s2_re & self.s2_miss & self.s2_valid):
+ m.d.sync += [
+ self.bus_addr.eq(self.s2_addr),
+ self.bus_re.eq(1),
+ last_offset.eq(self.s2_addr.offset - 1)
+ ]
+ m.next = "REFILL"
+
+ with m.State("REFILL"):
+ m.d.comb += self.bus_last.eq(self.bus_addr.offset == last_offset)
+ with m.If(self.bus_valid):
+ m.d.sync += self.bus_addr.offset.eq(self.bus_addr.offset + 1)
+ with m.If(self.bus_valid & self.bus_last | self.bus_error):
+ m.d.sync += self.bus_re.eq(0)
+ with m.If(~self.bus_re & ~self.s1_stall):
+ m.next = "CHECK"
+
+ if platform == "formal":
+ with m.If(Initial()):
+ m.d.comb += Assume(fsm.ongoing("CHECK"))
+
+ for way in ways:
+ valid_lines = Signal(self.nlines)
+
+ with m.If(self.s1_flush & self.s1_valid):
+ m.d.sync += valid_lines.eq(0)
+ with m.Elif(way.bus_re & self.bus_error):
+ m.d.sync += valid_lines.bit_select(self.bus_addr.line, 1).eq(0)
+ with m.Elif(way.bus_re & self.bus_valid & self.bus_last):
+ m.d.sync += valid_lines.bit_select(self.bus_addr.line, 1).eq(1)
+ with m.Elif(self.s2_evict & self.s2_valid & (way.tag == self.s2_addr.tag)):
+ m.d.sync += valid_lines.bit_select(self.s2_addr.line, 1).eq(0)
+
+ tag_mem = Memory(width=len(way.tag), depth=self.nlines)
+ tag_rp = tag_mem.read_port()
+ tag_wp = tag_mem.write_port()
+ m.submodules += tag_rp, tag_wp
+
+ data_mem = Memory(width=len(way.data), depth=self.nlines)
+ data_rp = data_mem.read_port()
+ data_wp = data_mem.write_port(granularity=32)
+ m.submodules += data_rp, data_wp
+
+ m.d.comb += [
+ tag_rp.addr.eq(Mux(self.s1_stall, self.s2_addr.line, self.s1_addr.line)),
+ data_rp.addr.eq(Mux(self.s1_stall, self.s2_addr.line, self.s1_addr.line)),
+
+ tag_wp.addr.eq(self.bus_addr.line),
+ tag_wp.en.eq(way.bus_re & self.bus_valid & self.bus_last),
+ tag_wp.data.eq(self.bus_addr.tag),
+
+ data_wp.addr.eq(self.bus_addr.line),
+ data_wp.en.bit_select(self.bus_addr.offset, 1).eq(way.bus_re & self.bus_valid),
+ data_wp.data.eq(self.bus_rdata << self.bus_addr.offset*32),
+
+ way.valid.eq(valid_lines.bit_select(self.s2_addr.line, 1)),
+ way.tag.eq(tag_rp.data),
+ way.data.eq(data_rp.data)
+ ]
+
+ if platform == "formal":
+ with m.If(Initial()):
+ m.d.comb += Assume(~valid_lines.bool())
+
+ return m
--- /dev/null
+import argparse
+import warnings
+from nmigen import cli
+
+from minerva.core import Minerva
+
+
+def main():
+ parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+ parser.add_argument("--reset-addr",
+ type=lambda s: int(s, 16), default="0x00000000",
+ help="reset vector address")
+
+ parser.add_argument("--with-icache",
+ default=False, action="store_true",
+ help="enable the instruction cache")
+ parser.add_argument("--with-dcache",
+ default=False, action="store_true",
+ help="enable the data cache")
+ parser.add_argument("--with-muldiv",
+ default=False, action="store_true",
+ help="enable RV32M support")
+ parser.add_argument("--with-debug",
+ default=False, action="store_true",
+ help="enable the Debug Module")
+ parser.add_argument("--with-trigger",
+ default=False, action="store_true",
+ help="enable the Trigger Module")
+ parser.add_argument("--with-rvfi",
+ default=False, action="store_true",
+ help="enable the riscv-formal interface")
+
+ icache_group = parser.add_argument_group("icache options")
+ icache_group.add_argument("--icache-nways",
+ type=int, choices=[1, 2], default=1,
+ help="number of ways")
+ icache_group.add_argument("--icache-nlines",
+ type=int, default=128,
+ help="number of lines")
+ icache_group.add_argument("--icache-nwords",
+ type=int, choices=[4, 8, 16], default=4,
+ help="number of words in a line")
+ icache_group.add_argument("--icache-base",
+ type=lambda s: int(s, 16), default="0x00000000",
+ help="base address")
+ icache_group.add_argument("--icache-limit",
+ type=lambda s: int(s, 16), default="0x80000000",
+ help="limit address")
+
+ dcache_group = parser.add_argument_group("dcache options")
+ dcache_group.add_argument("--dcache-nways",
+ type=int, choices=[1, 2], default=1,
+ help="number of ways")
+ dcache_group.add_argument("--dcache-nlines",
+ type=int, default=128,
+ help="number of lines")
+ dcache_group.add_argument("--dcache-nwords",
+ type=int, choices=[4, 8, 16], default=4,
+ help="number of words in a line")
+ dcache_group.add_argument("--dcache-base",
+ type=lambda s: int(s, 16), default="0x00000000",
+ help="base address")
+ dcache_group.add_argument("--dcache-limit",
+ type=lambda s: int(s, 16), default="0x80000000",
+ help="limit address")
+
+ trigger_group = parser.add_argument_group("trigger options")
+ trigger_group.add_argument("--nb-triggers",
+ type=int, default=8,
+ help="number of triggers")
+
+ cli.main_parser(parser)
+
+ args = parser.parse_args()
+
+ if args.with_debug and not args.with_trigger:
+ warnings.warn("Support for hardware breakpoints requires --with-trigger")
+
+ cpu = Minerva(args.reset_addr,
+ args.with_icache, args.icache_nways, args.icache_nlines, args.icache_nwords,
+ args.icache_base, args.icache_limit,
+ args.with_dcache, args.dcache_nways, args.dcache_nlines, args.dcache_nwords,
+ args.dcache_base, args.dcache_limit,
+ args.with_muldiv,
+ args.with_debug,
+ args.with_trigger, args.nb_triggers,
+ args.with_rvfi)
+
+ ports = [
+ cpu.external_interrupt, cpu.timer_interrupt, cpu.software_interrupt,
+ cpu.ibus.ack, cpu.ibus.adr, cpu.ibus.bte, cpu.ibus.cti, cpu.ibus.cyc, cpu.ibus.dat_r,
+ cpu.ibus.dat_w, cpu.ibus.sel, cpu.ibus.stb, cpu.ibus.we, cpu.ibus.err,
+ cpu.dbus.ack, cpu.dbus.adr, cpu.dbus.bte, cpu.dbus.cti, cpu.dbus.cyc, cpu.dbus.dat_r,
+ cpu.dbus.dat_w, cpu.dbus.sel, cpu.dbus.stb, cpu.dbus.we, cpu.dbus.err
+ ]
+
+ if args.with_debug:
+ ports += [cpu.jtag.tck, cpu.jtag.tdi, cpu.jtag.tdo, cpu.jtag.tms]
+
+ if args.with_rvfi:
+ ports += [
+ cpu.rvfi.valid, cpu.rvfi.order, cpu.rvfi.insn, cpu.rvfi.trap, cpu.rvfi.halt,
+ cpu.rvfi.intr, cpu.rvfi.mode, cpu.rvfi.ixl, cpu.rvfi.rs1_addr, cpu.rvfi.rs2_addr,
+ cpu.rvfi.rs1_rdata, cpu.rvfi.rs2_rdata, cpu.rvfi.rd_addr, cpu.rvfi.rd_wdata,
+ cpu.rvfi.pc_rdata, cpu.rvfi.pc_wdata, cpu.rvfi.mem_addr, cpu.rvfi.mem_rmask,
+ cpu.rvfi.mem_wmask, cpu.rvfi.mem_rdata, cpu.rvfi.mem_wdata
+ ]
+
+ cli.main_runner(parser, args, cpu, name="minerva_cpu", ports=ports)
+
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+from functools import reduce
+from operator import or_
+from itertools import tee
+
+from nmigen import *
+from nmigen.lib.coding import PriorityEncoder
+
+from .isa import *
+from .stage import *
+from .csr import *
+
+from .units.adder import *
+from .units.compare import *
+from .units.debug import *
+from .units.decoder import *
+from .units.divider import *
+from .units.exception import *
+from .units.fetch import *
+from .units.rvficon import *
+from .units.loadstore import *
+from .units.logic import *
+from .units.multiplier import *
+from .units.predict import *
+from .units.shifter import *
+from .units.trigger import *
+
+from .units.debug.jtag import jtag_layout
+from .wishbone import wishbone_layout
+
+
+__all__ = ["Minerva"]
+
+
+_af_layout = [
+ ("pc", (33, True)),
+]
+
+
+_fd_layout = [
+ ("pc", 32),
+ ("instruction", 32),
+ ("fetch_error", 1),
+ ("fetch_badaddr", 30)
+]
+
+
+_dx_layout = [
+ ("pc", 32),
+ ("instruction", 32),
+ ("fetch_error", 1),
+ ("fetch_badaddr", 30),
+ ("illegal", 1),
+ ("rd", 5),
+ ("rs1", 5),
+ ("rd_we", 1),
+ ("rs1_re", 1),
+ ("src1", 32),
+ ("src2", 32),
+ ("immediate", 32),
+ ("bypass_x", 1),
+ ("bypass_m", 1),
+ ("funct3", 3),
+ ("load", 1),
+ ("store", 1),
+ ("adder", 1),
+ ("adder_sub", 1),
+ ("logic", 1),
+ ("multiply", 1),
+ ("divide", 1),
+ ("shift", 1),
+ ("direction", 1),
+ ("sext", 1),
+ ("jump", 1),
+ ("compare", 1),
+ ("branch", 1),
+ ("branch_target", 32),
+ ("branch_predict_taken", 1),
+ ("fence_i", 1),
+ ("csr", 1),
+ ("csr_adr", 12),
+ ("csr_we", 1),
+ ("ecall", 1),
+ ("ebreak", 1),
+ ("mret", 1),
+]
+
+
+_xm_layout = [
+ ("pc", 32),
+ ("instruction", 32),
+ ("fetch_error", 1),
+ ("fetch_badaddr", 30),
+ ("illegal", 1),
+ ("loadstore_misaligned", 1),
+ ("ecall", 1),
+ ("ebreak", 1),
+ ("rd", 5),
+ ("rd_we", 1),
+ ("bypass_m", 1),
+ ("funct3", 3),
+ ("result", 32),
+ ("shift", 1),
+ ("load", 1),
+ ("store", 1),
+ ("store_data", 32),
+ ("compare", 1),
+ ("multiply", 1),
+ ("divide", 1),
+ ("condition_met", 1),
+ ("branch_target", 32),
+ ("branch_taken", 1),
+ ("branch_predict_taken", 1),
+ ("csr", 1),
+ ("csr_adr", 12),
+ ("csr_we", 1),
+ ("csr_result", 32),
+ ("mret", 1),
+ ("exception", 1)
+]
+
+
+_mw_layout = [
+ ("pc", 32),
+ ("rd", 5),
+ ("rd_we", 1),
+ ("funct3", 3),
+ ("result", 32),
+ ("load", 1),
+ ("load_data", 32),
+ ("multiply", 1),
+ ("exception", 1)
+]
+
+
+class Minerva(Elaboratable):
+ def __init__(self, reset_address=0x00000000,
+ with_icache=False,
+ icache_nways=1, icache_nlines=256, icache_nwords=8, icache_base=0, icache_limit=2**31,
+ with_dcache=False,
+ dcache_nways=1, dcache_nlines=256, dcache_nwords=8, dcache_base=0, dcache_limit=2**31,
+ with_muldiv=False,
+ with_debug=False,
+ with_trigger=False, nb_triggers=8,
+ with_rvfi=False):
+ self.external_interrupt = Signal(32)
+ self.timer_interrupt = Signal()
+ self.software_interrupt = Signal()
+ self.ibus = Record(wishbone_layout)
+ self.dbus = Record(wishbone_layout)
+
+ if with_debug:
+ self.jtag = Record(jtag_layout)
+
+ if with_rvfi:
+ self.rvfi = Record(rvfi_layout)
+
+ self.reset_address = reset_address
+ self.with_icache = with_icache
+ self.icache_args = icache_nways, icache_nlines, icache_nwords, icache_base, icache_limit
+ self.with_dcache = with_dcache
+ self.dcache_args = dcache_nways, dcache_nlines, dcache_nwords, dcache_base, dcache_limit
+ self.with_muldiv = with_muldiv
+ self.with_debug = with_debug
+ self.with_trigger = with_trigger
+ self.nb_triggers = nb_triggers
+ self.with_rvfi = with_rvfi
+
+ def elaborate(self, platform):
+ cpu = Module()
+
+ # pipeline stages
+
+ a = cpu.submodules.a = Stage(None, _af_layout)
+ f = cpu.submodules.f = Stage(_af_layout, _fd_layout)
+ d = cpu.submodules.d = Stage(_fd_layout, _dx_layout)
+ x = cpu.submodules.x = Stage(_dx_layout, _xm_layout)
+ m = cpu.submodules.m = Stage(_xm_layout, _mw_layout)
+ w = cpu.submodules.w = Stage(_mw_layout, None)
+ stages = a, f, d, x, m, w
+
+ sources, sinks = tee(stages)
+ next(sinks)
+ for s1, s2 in zip(sources, sinks):
+ cpu.d.comb += s1.source.connect(s2.sink)
+
+ a.source.pc.reset = self.reset_address - 4
+ cpu.d.comb += a.valid.eq(Const(1))
+
+ # units
+
+ pc_sel = cpu.submodules.pc_sel = PCSelector()
+ data_sel = cpu.submodules.data_sel = DataSelector()
+ adder = cpu.submodules.adder = Adder()
+ compare = cpu.submodules.compare = CompareUnit()
+ decoder = cpu.submodules.decoder = InstructionDecoder(self.with_muldiv)
+ exception = cpu.submodules.exception = ExceptionUnit()
+ logic = cpu.submodules.logic = LogicUnit()
+ predict = cpu.submodules.predict = BranchPredictor()
+ shifter = cpu.submodules.shifter = Shifter()
+
+ if self.with_icache:
+ fetch = cpu.submodules.fetch = CachedFetchUnit(*self.icache_args)
+ else:
+ fetch = cpu.submodules.fetch = BareFetchUnit()
+
+ if self.with_dcache:
+ loadstore = cpu.submodules.loadstore = CachedLoadStoreUnit(*self.dcache_args)
+ else:
+ loadstore = cpu.submodules.loadstore = BareLoadStoreUnit()
+
+ if self.with_muldiv:
+ multiplier = Multiplier() if not self.with_rvfi else DummyMultiplier()
+ divider = Divider() if not self.with_rvfi else DummyDivider()
+ cpu.submodules.multiplier = multiplier
+ cpu.submodules.divider = divider
+
+ if self.with_debug:
+ debug = cpu.submodules.debug = DebugUnit()
+
+ if self.with_trigger:
+ trigger = cpu.submodules.trigger = TriggerUnit(self.nb_triggers)
+
+ if self.with_rvfi:
+ rvficon = cpu.submodules.rvficon = RVFIController()
+
+ # register files
+
+ gprf = Memory(width=32, depth=32)
+ gprf_rp1 = gprf.read_port()
+ gprf_rp2 = gprf.read_port()
+ gprf_wp = gprf.write_port()
+ cpu.submodules += gprf_rp1, gprf_rp2, gprf_wp
+
+ csrf = cpu.submodules.csrf = CSRFile()
+ csrf_rp = csrf.read_port()
+ csrf_wp = csrf.write_port()
+
+ csrf.add_csrs(exception.iter_csrs())
+ if self.with_debug:
+ csrf.add_csrs(debug.iter_csrs())
+ if self.with_trigger:
+ csrf.add_csrs(trigger.iter_csrs())
+
+ # pipeline logic
+
+ cpu.d.comb += [
+ pc_sel.f_pc.eq(f.sink.pc),
+ pc_sel.d_pc.eq(d.sink.pc),
+ pc_sel.d_branch_predict_taken.eq(predict.d_branch_taken & ~predict.d_fetch_misaligned),
+ pc_sel.d_branch_target.eq(predict.d_branch_target),
+ pc_sel.d_valid.eq(d.valid),
+ pc_sel.x_pc.eq(x.sink.pc),
+ pc_sel.x_fence_i.eq(x.sink.fence_i),
+ pc_sel.x_valid.eq(x.valid),
+ pc_sel.m_branch_predict_taken.eq(m.sink.branch_predict_taken),
+ pc_sel.m_branch_taken.eq(m.sink.branch_taken),
+ pc_sel.m_branch_target.eq(m.sink.branch_target),
+ pc_sel.m_exception.eq(exception.m_raise),
+ pc_sel.m_mret.eq(m.sink.mret),
+ pc_sel.m_valid.eq(m.valid),
+ pc_sel.mtvec_r_base.eq(exception.mtvec.r.base),
+ pc_sel.mepc_r_base.eq(exception.mepc.r.base)
+ ]
+
+ cpu.d.comb += [
+ fetch.a_pc.eq(pc_sel.a_pc),
+ fetch.a_stall.eq(a.stall),
+ fetch.a_valid.eq(a.valid),
+ fetch.f_stall.eq(f.stall),
+ fetch.f_valid.eq(f.valid),
+ fetch.ibus.connect(self.ibus)
+ ]
+
+ m.stall_on(fetch.a_busy & a.valid)
+ m.stall_on(fetch.f_busy & f.valid)
+
+ if self.with_icache:
+ flush_icache = x.sink.fence_i & x.valid & ~x.stall
+ if self.with_debug:
+ flush_icache |= debug.resumereq
+
+ cpu.d.comb += [
+ fetch.a_flush.eq(flush_icache),
+ fetch.f_pc.eq(f.sink.pc)
+ ]
+
+ cpu.d.comb += [
+ decoder.instruction.eq(d.sink.instruction)
+ ]
+
+ if self.with_debug:
+ with cpu.If(debug.halt & debug.halted):
+ cpu.d.comb += gprf_rp1.addr.eq(debug.gprf_addr)
+ with cpu.Elif(~d.stall):
+ cpu.d.comb += gprf_rp1.addr.eq(fetch.f_instruction[15:20])
+ with cpu.Else():
+ cpu.d.comb += gprf_rp1.addr.eq(decoder.rs1)
+
+ cpu.d.comb += debug.gprf_dat_r.eq(gprf_rp1.data)
+ else:
+ with cpu.If(~d.stall):
+ cpu.d.comb += gprf_rp1.addr.eq(fetch.f_instruction[15:20])
+ with cpu.Else():
+ cpu.d.comb += gprf_rp1.addr.eq(decoder.rs1)
+
+ with cpu.If(~d.stall):
+ cpu.d.comb += gprf_rp2.addr.eq(fetch.f_instruction[20:25])
+ with cpu.Else():
+ cpu.d.comb += gprf_rp2.addr.eq(decoder.rs2)
+
+ with cpu.If(~f.stall):
+ cpu.d.sync += csrf_rp.addr.eq(fetch.f_instruction[20:32])
+ cpu.d.comb += csrf_rp.en.eq(decoder.csr & d.valid)
+
+ # CSR set/clear instructions are translated to logic operations.
+ x_csr_set_clear = x.sink.funct3[1]
+ x_csr_clear = x_csr_set_clear & x.sink.funct3[0]
+ x_csr_fmt_i = x.sink.funct3[2]
+ x_csr_src1 = Mux(x_csr_fmt_i, x.sink.rs1, x.sink.src1)
+ x_csr_src1 = Mux(x_csr_clear, ~x_csr_src1, x_csr_src1)
+ x_csr_logic_op = x.sink.funct3 | 0b100
+
+ cpu.d.comb += [
+ logic.op.eq(Mux(x.sink.csr, x_csr_logic_op, x.sink.funct3)),
+ logic.src1.eq(Mux(x.sink.csr, x_csr_src1, x.sink.src1)),
+ logic.src2.eq(x.sink.src2)
+ ]
+
+ cpu.d.comb += [
+ adder.sub.eq(x.sink.adder & x.sink.adder_sub | x.sink.compare | x.sink.branch),
+ adder.src1.eq(x.sink.src1),
+ adder.src2.eq(Mux(x.sink.store, x.sink.immediate, x.sink.src2))
+ ]
+
+ if self.with_muldiv:
+ cpu.d.comb += [
+ multiplier.x_op.eq(x.sink.funct3),
+ multiplier.x_src1.eq(x.sink.src1),
+ multiplier.x_src2.eq(x.sink.src2),
+ multiplier.x_stall.eq(x.stall),
+ multiplier.m_stall.eq(m.stall)
+ ]
+
+ cpu.d.comb += [
+ divider.x_op.eq(x.sink.funct3),
+ divider.x_src1.eq(x.sink.src1),
+ divider.x_src2.eq(x.sink.src2),
+ divider.x_valid.eq(x.sink.valid),
+ divider.x_stall.eq(x.stall)
+ ]
+
+ m.stall_on(divider.m_busy)
+
+ cpu.d.comb += [
+ shifter.x_direction.eq(x.sink.direction),
+ shifter.x_sext.eq(x.sink.sext),
+ shifter.x_shamt.eq(x.sink.src2),
+ shifter.x_src1.eq(x.sink.src1),
+ shifter.x_stall.eq(x.stall)
+ ]
+
+ cpu.d.comb += [
+ # compare.op is shared by compare and branch instructions.
+ compare.op.eq(Mux(x.sink.compare, x.sink.funct3 << 1, x.sink.funct3)),
+ compare.zero.eq(x.sink.src1 == x.sink.src2),
+ compare.negative.eq(adder.result[-1]),
+ compare.overflow.eq(adder.overflow),
+ compare.carry.eq(adder.carry)
+ ]
+
+ cpu.d.comb += [
+ exception.external_interrupt.eq(self.external_interrupt),
+ exception.timer_interrupt.eq(self.timer_interrupt),
+ exception.software_interrupt.eq(self.software_interrupt),
+ exception.m_fetch_misaligned.eq(m.sink.branch_taken & m.sink.branch_target[:2].bool()),
+ exception.m_fetch_error.eq(m.sink.fetch_error),
+ exception.m_fetch_badaddr.eq(m.sink.fetch_badaddr),
+ exception.m_load_misaligned.eq(m.sink.load & m.sink.loadstore_misaligned),
+ exception.m_load_error.eq(loadstore.m_load_error),
+ exception.m_store_misaligned.eq(m.sink.store & m.sink.loadstore_misaligned),
+ exception.m_store_error.eq(loadstore.m_store_error),
+ exception.m_loadstore_badaddr.eq(loadstore.m_badaddr),
+ exception.m_branch_target.eq(m.sink.branch_target),
+ exception.m_illegal.eq(m.sink.illegal),
+ exception.m_ecall.eq(m.sink.ecall),
+ exception.m_pc.eq(m.sink.pc),
+ exception.m_instruction.eq(m.sink.instruction),
+ exception.m_result.eq(m.sink.result),
+ exception.m_mret.eq(m.sink.mret),
+ exception.m_stall.eq(m.sink.stall),
+ exception.m_valid.eq(m.valid)
+ ]
+
+ m_ebreak = m.sink.ebreak
+ if self.with_debug:
+ # If dcsr.ebreakm is set, EBREAK instructions enter Debug Mode.
+ # We do not want to raise an exception in this case because Debug Mode
+ # should be invisible to software execution.
+ m_ebreak &= ~debug.dcsr_ebreakm
+ if self.with_trigger:
+ m_ebreak |= trigger.trap
+ cpu.d.comb += exception.m_ebreak.eq(m_ebreak)
+
+ m.kill_on(m.source.exception & m.source.valid)
+
+ cpu.d.comb += [
+ data_sel.x_offset.eq(adder.result[:2]),
+ data_sel.x_funct3.eq(x.sink.funct3),
+ data_sel.x_store_operand.eq(x.sink.src2),
+ data_sel.w_offset.eq(w.sink.result[:2]),
+ data_sel.w_funct3.eq(w.sink.funct3),
+ data_sel.w_load_data.eq(w.sink.load_data)
+ ]
+
+ cpu.d.comb += [
+ loadstore.x_addr.eq(adder.result),
+ loadstore.x_mask.eq(data_sel.x_mask),
+ loadstore.x_load.eq(x.sink.load),
+ loadstore.x_store.eq(x.sink.store),
+ loadstore.x_store_data.eq(data_sel.x_store_data),
+ loadstore.x_stall.eq(x.stall),
+ loadstore.x_valid.eq(x.valid),
+ loadstore.m_stall.eq(m.stall),
+ loadstore.m_valid.eq(m.valid)
+ ]
+
+ m.stall_on(loadstore.x_busy & x.valid)
+ m.stall_on(loadstore.m_busy & m.valid)
+
+ if self.with_dcache:
+ if self.with_debug:
+ cpu.d.comb += loadstore.x_flush.eq(debug.resumereq)
+
+ cpu.d.comb += [
+ loadstore.x_fence_i.eq(x.sink.fence_i),
+ loadstore.m_addr.eq(m.sink.result),
+ loadstore.m_load.eq(m.sink.load),
+ loadstore.m_store.eq(m.sink.store),
+ ]
+
+ x.stall_on(loadstore.x_busy & x.valid)
+
+ for s in a, f:
+ s.kill_on(x.sink.fence_i & x.valid)
+
+ if self.with_debug:
+ with cpu.If(debug.halt & debug.halted):
+ cpu.d.comb += debug.dbus.connect(self.dbus)
+ with cpu.Else():
+ cpu.d.comb += loadstore.dbus.connect(self.dbus)
+ else:
+ cpu.d.comb += loadstore.dbus.connect(self.dbus)
+
+ # RAW hazard management
+
+ x_raw_rs1 = Signal()
+ m_raw_rs1 = Signal()
+ w_raw_rs1 = Signal()
+ x_raw_rs2 = Signal()
+ m_raw_rs2 = Signal()
+ w_raw_rs2 = Signal()
+
+ x_raw_csr = Signal()
+ m_raw_csr = Signal()
+
+ x_lock = Signal()
+ m_lock = Signal()
+
+ cpu.d.comb += [
+ x_raw_rs1.eq((x.sink.rd != 0) & (x.sink.rd == decoder.rs1) & x.sink.rd_we),
+ m_raw_rs1.eq((m.sink.rd != 0) & (m.sink.rd == decoder.rs1) & m.sink.rd_we),
+ w_raw_rs1.eq((w.sink.rd != 0) & (w.sink.rd == decoder.rs1) & w.sink.rd_we),
+
+ x_raw_rs2.eq((x.sink.rd != 0) & (x.sink.rd == decoder.rs2) & x.sink.rd_we),
+ m_raw_rs2.eq((m.sink.rd != 0) & (m.sink.rd == decoder.rs2) & m.sink.rd_we),
+ w_raw_rs2.eq((w.sink.rd != 0) & (w.sink.rd == decoder.rs2) & w.sink.rd_we),
+
+ x_raw_csr.eq((x.sink.csr_adr == decoder.immediate) & x.sink.csr_we),
+ m_raw_csr.eq((m.sink.csr_adr == decoder.immediate) & m.sink.csr_we),
+
+ x_lock.eq(~x.sink.bypass_x & (decoder.rs1_re & x_raw_rs1 | decoder.rs2_re & x_raw_rs2) | decoder.csr & x_raw_csr),
+ m_lock.eq(~m.sink.bypass_m & (decoder.rs1_re & m_raw_rs1 | decoder.rs2_re & m_raw_rs2) | decoder.csr & m_raw_csr)
+ ]
+
+ if self.with_debug:
+ d.stall_on((x_lock & x.valid | m_lock & m.valid) & d.valid & ~debug.dcsr_step)
+ else:
+ d.stall_on((x_lock & x.valid | m_lock & m.valid) & d.valid)
+
+ # result selection
+
+ x_result = Signal(32)
+ m_result = Signal(32)
+ w_result = Signal(32)
+ x_csr_result = Signal(32)
+
+ with cpu.If(x.sink.jump):
+ cpu.d.comb += x_result.eq(x.sink.pc + 4)
+ with cpu.Elif(x.sink.logic):
+ cpu.d.comb += x_result.eq(logic.result)
+ with cpu.Elif(x.sink.csr):
+ cpu.d.comb += x_result.eq(x.sink.src2)
+ with cpu.Else():
+ cpu.d.comb += x_result.eq(adder.result)
+
+ with cpu.If(m.sink.compare):
+ cpu.d.comb += m_result.eq(m.sink.condition_met)
+ if self.with_muldiv:
+ with cpu.Elif(m.sink.divide):
+ cpu.d.comb += m_result.eq(divider.m_result)
+ with cpu.Elif(m.sink.shift):
+ cpu.d.comb += m_result.eq(shifter.m_result)
+ with cpu.Else():
+ cpu.d.comb += m_result.eq(m.sink.result)
+
+ with cpu.If(w.sink.load):
+ cpu.d.comb += w_result.eq(data_sel.w_load_result)
+ if self.with_muldiv:
+ with cpu.Elif(w.sink.multiply):
+ cpu.d.comb += w_result.eq(multiplier.w_result)
+ with cpu.Else():
+ cpu.d.comb += w_result.eq(w.sink.result)
+
+ with cpu.If(x_csr_set_clear):
+ cpu.d.comb += x_csr_result.eq(logic.result)
+ with cpu.Else():
+ cpu.d.comb += x_csr_result.eq(x_csr_src1)
+
+ cpu.d.comb += [
+ csrf_wp.en.eq(m.sink.csr & m.sink.csr_we & m.valid & ~exception.m_raise & ~m.stall),
+ csrf_wp.addr.eq(m.sink.csr_adr),
+ csrf_wp.data.eq(m.sink.csr_result)
+ ]
+
+ if self.with_debug:
+ with cpu.If(debug.halt & debug.halted):
+ cpu.d.comb += [
+ gprf_wp.addr.eq(debug.gprf_addr),
+ gprf_wp.en.eq(debug.gprf_we),
+ gprf_wp.data.eq(debug.gprf_dat_w)
+ ]
+ with cpu.Else():
+ cpu.d.comb += [
+ gprf_wp.en.eq((w.sink.rd != 0) & w.sink.rd_we & w.valid & ~w.sink.exception),
+ gprf_wp.addr.eq(w.sink.rd),
+ gprf_wp.data.eq(w_result)
+ ]
+ else:
+ cpu.d.comb += [
+ gprf_wp.en.eq((w.sink.rd != 0) & w.sink.rd_we & w.valid),
+ gprf_wp.addr.eq(w.sink.rd),
+ gprf_wp.data.eq(w_result)
+ ]
+
+ # D stage operand selection
+
+ d_src1 = Signal(32)
+ d_src2 = Signal(32)
+
+ with cpu.If(decoder.lui):
+ cpu.d.comb += d_src1.eq(0)
+ with cpu.Elif(decoder.auipc):
+ cpu.d.comb += d_src1.eq(d.sink.pc)
+ with cpu.Elif(decoder.rs1_re & (decoder.rs1 == 0)):
+ cpu.d.comb += d_src1.eq(0)
+ with cpu.Elif(x_raw_rs1 & x.valid):
+ cpu.d.comb += d_src1.eq(x_result)
+ with cpu.Elif(m_raw_rs1 & m.valid):
+ cpu.d.comb += d_src1.eq(m_result)
+ with cpu.Elif(w_raw_rs1 & w.valid):
+ cpu.d.comb += d_src1.eq(w_result)
+ with cpu.Else():
+ cpu.d.comb += d_src1.eq(gprf_rp1.data)
+
+ with cpu.If(decoder.csr):
+ cpu.d.comb += d_src2.eq(csrf_rp.data)
+ with cpu.Elif(~decoder.rs2_re):
+ cpu.d.comb += d_src2.eq(decoder.immediate)
+ with cpu.Elif(decoder.rs2 == 0):
+ cpu.d.comb += d_src2.eq(0)
+ with cpu.Elif(x_raw_rs2 & x.valid):
+ cpu.d.comb += d_src2.eq(x_result)
+ with cpu.Elif(m_raw_rs2 & m.valid):
+ cpu.d.comb += d_src2.eq(m_result)
+ with cpu.Elif(w_raw_rs2 & w.valid):
+ cpu.d.comb += d_src2.eq(w_result)
+ with cpu.Else():
+ cpu.d.comb += d_src2.eq(gprf_rp2.data)
+
+ # branch prediction
+
+ cpu.d.comb += [
+ predict.d_branch.eq(decoder.branch),
+ predict.d_jump.eq(decoder.jump),
+ predict.d_offset.eq(decoder.immediate),
+ predict.d_pc.eq(d.sink.pc),
+ predict.d_rs1_re.eq(decoder.rs1_re)
+ ]
+
+ a.kill_on(predict.d_branch_taken & ~predict.d_fetch_misaligned & d.valid)
+ for s in a, f:
+ s.kill_on(m.sink.branch_predict_taken & ~m.sink.branch_taken & m.valid)
+ for s in a, f, d:
+ s.kill_on(~m.sink.branch_predict_taken & m.sink.branch_taken & m.valid)
+ s.kill_on((exception.m_raise | m.sink.mret) & m.valid)
+
+ # debug unit
+
+ if self.with_debug:
+ cpu.d.comb += [
+ debug.jtag.connect(self.jtag),
+ debug.x_pc.eq(x.sink.pc),
+ debug.x_ebreak.eq(x.sink.ebreak),
+ debug.x_stall.eq(x.stall),
+ debug.m_branch_taken.eq(m.sink.branch_taken),
+ debug.m_branch_target.eq(m.sink.branch_target),
+ debug.m_mret.eq(m.sink.mret),
+ debug.m_exception.eq(exception.m_raise),
+ debug.m_pc.eq(m.sink.pc),
+ debug.m_valid.eq(m.valid),
+ debug.mepc_r_base.eq(exception.mepc.r.base),
+ debug.mtvec_r_base.eq(exception.mtvec.r.base)
+ ]
+
+ if self.with_trigger:
+ cpu.d.comb += debug.trigger_haltreq.eq(trigger.haltreq)
+ else:
+ cpu.d.comb += debug.trigger_haltreq.eq(Const(0))
+
+ csrf_debug_rp = csrf.read_port()
+ csrf_debug_wp = csrf.write_port()
+ cpu.d.comb += [
+ csrf_debug_rp.addr.eq(debug.csrf_addr),
+ csrf_debug_rp.en.eq(debug.csrf_re),
+ debug.csrf_dat_r.eq(csrf_debug_rp.data),
+ csrf_debug_wp.addr.eq(debug.csrf_addr),
+ csrf_debug_wp.en.eq(debug.csrf_we),
+ csrf_debug_wp.data.eq(debug.csrf_dat_w)
+ ]
+
+ x.stall_on(debug.halt)
+ m.stall_on(debug.dcsr_step & m.valid & ~debug.halt)
+ for s in a, f, d, x:
+ s.kill_on(debug.killall)
+
+ halted = x.stall & ~reduce(or_, (s.valid for s in (m, w)))
+ cpu.d.sync += debug.halted.eq(halted)
+
+ with cpu.If(debug.resumereq):
+ with cpu.If(~debug.dbus_busy):
+ cpu.d.comb += debug.resumeack.eq(1)
+ cpu.d.sync += a.source.pc.eq(debug.dpc_value - 4)
+
+ if self.with_trigger:
+ cpu.d.comb += [
+ trigger.x_pc.eq(x.sink.pc),
+ trigger.x_valid.eq(x.valid),
+ ]
+
+ if self.with_rvfi:
+ cpu.d.comb += [
+ rvficon.d_insn.eq(decoder.instruction),
+ rvficon.d_rs1_addr.eq(Mux(decoder.rs1_re, decoder.rs1, 0)),
+ rvficon.d_rs2_addr.eq(Mux(decoder.rs2_re, decoder.rs2, 0)),
+ rvficon.d_rs1_rdata.eq(Mux(decoder.rs1_re, d_src1, 0)),
+ rvficon.d_rs2_rdata.eq(Mux(decoder.rs2_re, d_src2, 0)),
+ rvficon.d_stall.eq(d.stall),
+ rvficon.x_mem_addr.eq(loadstore.x_addr[2:] << 2),
+ rvficon.x_mem_wmask.eq(Mux(loadstore.x_store, loadstore.x_mask, 0)),
+ rvficon.x_mem_rmask.eq(Mux(loadstore.x_load, loadstore.x_mask, 0)),
+ rvficon.x_mem_wdata.eq(loadstore.x_store_data),
+ rvficon.x_stall.eq(x.stall),
+ rvficon.m_mem_rdata.eq(loadstore.m_load_data),
+ rvficon.m_fetch_misaligned.eq(exception.m_fetch_misaligned),
+ rvficon.m_illegal_insn.eq(m.sink.illegal),
+ rvficon.m_load_misaligned.eq(exception.m_load_misaligned),
+ rvficon.m_store_misaligned.eq(exception.m_store_misaligned),
+ rvficon.m_exception.eq(exception.m_raise),
+ rvficon.m_mret.eq(m.sink.mret),
+ rvficon.m_branch_taken.eq(m.sink.branch_taken),
+ rvficon.m_branch_target.eq(m.sink.branch_target),
+ rvficon.m_pc_rdata.eq(m.sink.pc),
+ rvficon.m_stall.eq(m.stall),
+ rvficon.m_valid.eq(m.valid),
+ rvficon.w_rd_addr.eq(Mux(gprf_wp.en, gprf_wp.addr, 0)),
+ rvficon.w_rd_wdata.eq(Mux(gprf_wp.en, gprf_wp.data, 0)),
+ rvficon.mtvec_r_base.eq(exception.mtvec.r.base),
+ rvficon.mepc_r_value.eq(exception.mepc.r),
+ rvficon.rvfi.connect(self.rvfi)
+ ]
+
+ # pipeline registers
+
+ # A/F
+ with cpu.If(~a.stall):
+ cpu.d.sync += a.source.pc.eq(fetch.a_pc)
+
+ # F/D
+ with cpu.If(~f.stall):
+ cpu.d.sync += [
+ f.source.pc.eq(f.sink.pc),
+ f.source.instruction.eq(fetch.f_instruction),
+ f.source.fetch_error.eq(fetch.f_fetch_error),
+ f.source.fetch_badaddr.eq(fetch.f_badaddr)
+ ]
+
+ # D/X
+ with cpu.If(~d.stall):
+ cpu.d.sync += [
+ d.source.pc.eq(d.sink.pc),
+ d.source.instruction.eq(d.sink.instruction),
+ d.source.fetch_error.eq(d.sink.fetch_error),
+ d.source.fetch_badaddr.eq(d.sink.fetch_badaddr),
+ d.source.illegal.eq(decoder.illegal),
+ d.source.rd.eq(decoder.rd),
+ d.source.rs1.eq(decoder.rs1),
+ d.source.rd_we.eq(decoder.rd_we),
+ d.source.rs1_re.eq(decoder.rs1_re),
+ d.source.immediate.eq(decoder.immediate),
+ d.source.bypass_x.eq(decoder.bypass_x),
+ d.source.bypass_m.eq(decoder.bypass_m),
+ d.source.funct3.eq(decoder.funct3),
+ d.source.load.eq(decoder.load),
+ d.source.store.eq(decoder.store),
+ d.source.adder.eq(decoder.adder),
+ d.source.adder_sub.eq(decoder.adder_sub),
+ d.source.compare.eq(decoder.compare),
+ d.source.logic.eq(decoder.logic),
+ d.source.shift.eq(decoder.shift),
+ d.source.direction.eq(decoder.direction),
+ d.source.sext.eq(decoder.sext),
+ d.source.jump.eq(decoder.jump),
+ d.source.branch.eq(decoder.branch),
+ d.source.fence_i.eq(decoder.fence_i),
+ d.source.csr.eq(decoder.csr),
+ d.source.csr_adr.eq(decoder.immediate),
+ d.source.csr_we.eq(decoder.csr_we),
+ d.source.ecall.eq(decoder.ecall),
+ d.source.ebreak.eq(decoder.ebreak),
+ d.source.mret.eq(decoder.mret),
+ d.source.src1.eq(d_src1),
+ d.source.src2.eq(d_src2),
+ d.source.branch_predict_taken.eq(predict.d_branch_taken & ~predict.d_fetch_misaligned),
+ d.source.branch_target.eq(predict.d_branch_target)
+ ]
+ if self.with_muldiv:
+ cpu.d.sync += [
+ d.source.multiply.eq(decoder.multiply),
+ d.source.divide.eq(decoder.divide)
+ ]
+
+ # X/M
+ with cpu.If(~x.stall):
+ cpu.d.sync += [
+ x.source.pc.eq(x.sink.pc),
+ x.source.instruction.eq(x.sink.instruction),
+ x.source.fetch_error.eq(x.sink.fetch_error),
+ x.source.fetch_badaddr.eq(x.sink.fetch_badaddr),
+ x.source.illegal.eq(x.sink.illegal),
+ x.source.loadstore_misaligned.eq(data_sel.x_misaligned),
+ x.source.ecall.eq(x.sink.ecall),
+ x.source.ebreak.eq(x.sink.ebreak),
+ x.source.rd.eq(x.sink.rd),
+ x.source.rd_we.eq(x.sink.rd_we),
+ x.source.bypass_m.eq(x.sink.bypass_m | x.sink.bypass_x),
+ x.source.funct3.eq(x.sink.funct3),
+ x.source.load.eq(x.sink.load),
+ x.source.store.eq(x.sink.store),
+ x.source.store_data.eq(loadstore.x_store_data),
+ x.source.compare.eq(x.sink.compare),
+ x.source.shift.eq(x.sink.shift),
+ x.source.mret.eq(x.sink.mret),
+ x.source.condition_met.eq(compare.condition_met),
+ x.source.branch_taken.eq(x.sink.jump | x.sink.branch & compare.condition_met),
+ x.source.branch_target.eq(Mux(x.sink.jump & x.sink.rs1_re, adder.result[1:] << 1, x.sink.branch_target)),
+ x.source.branch_predict_taken.eq(x.sink.branch_predict_taken),
+ x.source.csr.eq(x.sink.csr),
+ x.source.csr_adr.eq(x.sink.csr_adr),
+ x.source.csr_we.eq(x.sink.csr_we),
+ x.source.csr_result.eq(x_csr_result),
+ x.source.result.eq(x_result)
+ ]
+ if self.with_muldiv:
+ cpu.d.sync += [
+ x.source.multiply.eq(x.sink.multiply),
+ x.source.divide.eq(x.sink.divide)
+ ]
+
+ # M/W
+ with cpu.If(~m.stall):
+ cpu.d.sync += [
+ m.source.pc.eq(m.sink.pc),
+ m.source.rd.eq(m.sink.rd),
+ m.source.load.eq(m.sink.load),
+ m.source.funct3.eq(m.sink.funct3),
+ m.source.load_data.eq(loadstore.m_load_data),
+ m.source.rd_we.eq(m.sink.rd_we),
+ m.source.result.eq(m_result),
+ m.source.exception.eq(exception.m_raise)
+ ]
+ if self.with_muldiv:
+ cpu.d.sync += [
+ m.source.multiply.eq(m.sink.multiply)
+ ]
+
+ return cpu
--- /dev/null
+from enum import Enum
+from collections import OrderedDict
+
+from nmigen import *
+from nmigen.hdl.rec import *
+from nmigen.utils import bits_for
+
+
+__all__ = ["CSRAccess", "CSR", "AutoCSR", "CSRFile"]
+
+
+CSRAccess = Enum("CSRAccess", ("WIRI", "WPRI", "WLRL", "WARL"))
+
+
+class CSR():
+ def __init__(self, addr, description, name):
+ fields = []
+ mask = 0
+ offset = 0
+ for name, shape, access in description:
+ if isinstance(shape, int):
+ shape = shape, False
+ nbits, signed = shape
+ fields.append((name, shape))
+ if access in {CSRAccess.WLRL, CSRAccess.WARL}:
+ mask |= ((1 << nbits) - 1) << offset
+ offset += nbits
+
+ self.addr = addr
+ self.rmask = self.wmask = Const(mask)
+ self.r = Record(fields)
+ self.w = Record(fields)
+ self.re = Signal()
+ self.we = Signal()
+
+
+class AutoCSR():
+ def iter_csrs(self):
+ for v in vars(self).values():
+ if isinstance(v, CSR):
+ yield v
+ elif hasattr(v, "iter_csrs"):
+ yield from v.iter_csrs()
+
+
+class CSRFile(Elaboratable):
+ def __init__(self, width=32, depth=2**12):
+ self.width = width
+ self.depth = depth
+ self._csr_map = OrderedDict()
+ self._read_ports = []
+ self._write_ports = []
+
+ def add_csrs(self, csrs):
+ for csr in csrs:
+ if not isinstance(csr, CSR):
+ raise TypeError("Object {!r} is not a CSR".format(csr))
+ if csr.addr in self._csr_map:
+ raise ValueError("CSR address 0x{:x} has already been allocated"
+ .format(csr.addr))
+ self._csr_map[csr.addr] = csr
+
+ def read_port(self):
+ port = Record([("addr", bits_for(self.depth)), ("en", 1), ("data", self.width)])
+ self._read_ports.append(port)
+ return port
+
+ def write_port(self):
+ port = Record([("addr", bits_for(self.depth)), ("en", 1), ("data", self.width)])
+ self._write_ports.append(port)
+ return port
+
+ def elaborate(self, platform):
+ m = Module()
+
+ for rp in self._read_ports:
+ with m.Switch(rp.addr):
+ for addr, csr in self._csr_map.items():
+ with m.Case(addr):
+ m.d.comb += [
+ csr.re.eq(rp.en),
+ rp.data.eq(csr.r & csr.rmask)
+ ]
+
+ for wp in self._write_ports:
+ with m.Switch(wp.addr):
+ for addr, csr in self._csr_map.items():
+ with m.Case(addr):
+ m.d.comb += [
+ csr.we.eq(wp.en),
+ csr.w.eq(wp.data & csr.wmask)
+ ]
+
+ return m
--- /dev/null
+from .csr import *
+
+__all__ = [
+ "Opcode", "Funct3", "Funct7", "Funct12", "CSRIndex", "Cause",
+ "flat_layout", "misa_layout", "mstatus_layout", "mtvec_layout", "mepc_layout",
+ "mip_layout", "mie_layout", "mcause_layout", "dcsr_layout", "tdata1_layout"
+]
+
+class Opcode:
+ LUI = 0b01101
+ AUIPC = 0b00101
+ JAL = 0b11011
+ JALR = 0b11001
+ BRANCH = 0b11000
+ LOAD = 0b00000
+ STORE = 0b01000
+ OP_IMM_32 = 0b00100
+ OP_32 = 0b01100
+ MISC_MEM = 0b00011
+ SYSTEM = 0b11100
+
+
+class Funct3:
+ BEQ = B = ADD = FENCE = PRIV = MUL = 0b000
+ BNE = H = SLL = FENCEI = CSRRW = MULH = 0b001
+ _ = W = SLT = _ = CSRRS = MULHSU = 0b010
+ _ = _ = SLTU = _ = CSRRC = MULHU = 0b011
+ BLT = BU = XOR = _ = _ = DIV = 0b100
+ BGE = HU = SR = _ = CSRRWI = DIVU = 0b101
+ BLTU = _ = OR = _ = CSRRSI = REM = 0b110
+ BGEU = _ = AND = _ = CSRRCI = REMU = 0b111
+
+
+class Funct7:
+ SRL = ADD = 0b0000000
+ MULDIV = 0b0000001
+ SRA = SUB = 0b0100000
+
+
+class Funct12:
+ ECALL = 0b000000000000
+ EBREAK = 0b000000000001
+ MRET = 0b001100000010
+ WFI = 0b000100000101
+
+
+class CSRIndex:
+ MVENDORID = 0xF11
+ MARCHID = 0xF12
+ MIMPID = 0xF13
+ MHARTID = 0xF14
+ MSTATUS = 0x300
+ MISA = 0x301
+ MEDELEG = 0x302
+ MIDELEG = 0x303
+ MIE = 0x304
+ MTVEC = 0x305
+ MCOUTEREN = 0x306
+ MSCRATCH = 0x340
+ MEPC = 0x341
+ MCAUSE = 0x342
+ MTVAL = 0x343
+ MIP = 0x344
+ # ยตarch specific
+ IRQ_MASK = 0x330
+ IRQ_PENDING = 0x360
+ # trigger module
+ TSELECT = 0x7a0
+ TDATA1 = 0x7a1
+ TDATA2 = 0x7a2
+ TDATA3 = 0x7a3
+ TINFO = 0x7a4
+ MCONTEXT = 0x7a8
+ # debug module
+ DCSR = 0x7b0
+ DPC = 0x7b1
+
+
+class Cause:
+ FETCH_MISALIGNED = 0
+ FETCH_ACCESS_FAULT = 1
+ ILLEGAL_INSTRUCTION = 2
+ BREAKPOINT = 3
+ LOAD_MISALIGNED = 4
+ LOAD_ACCESS_FAULT = 5
+ STORE_MISALIGNED = 6
+ STORE_ACCESS_FAULT = 7
+ ECALL_FROM_U = 8
+ ECALL_FROM_S = 9
+ ECALL_FROM_M = 11
+ FETCH_PAGE_FAULT = 12
+ LOAD_PAGE_FAULT = 13
+ STORE_PAGE_FAULT = 15
+ # interrupts
+ U_SOFTWARE_INTERRUPT = 0
+ S_SOFTWARE_INTERRUPT = 1
+ M_SOFTWARE_INTERRUPT = 3
+ U_TIMER_INTERRUPT = 4
+ S_TIMER_INTERRUPT = 5
+ M_TIMER_INTERRUPT = 7
+ U_EXTERNAL_INTERRUPT = 8
+ S_EXTERNAL_INTERRUPT = 9
+ M_EXTERNAL_INTERRUPT = 11
+
+
+# CSR layouts
+
+flat_layout = [
+ ("value", 32, CSRAccess.WARL)
+]
+
+
+misa_layout = [
+ ("extensions", 26, CSRAccess.WARL),
+ ("wiri0", 4, CSRAccess.WIRI),
+ ("mxl", 2, CSRAccess.WARL)
+]
+
+
+mstatus_layout = [
+ ("uie", 1, CSRAccess.WARL), # User Interrupt Enable
+ ("sie", 1, CSRAccess.WARL), # Supervisor Interrupt Enable
+ ("wpri0", 1, CSRAccess.WPRI),
+ ("mie", 1, CSRAccess.WARL), # Machine Interrupt Enable
+ ("upie", 1, CSRAccess.WARL), # User Previous Interrupt Enable
+ ("spie", 1, CSRAccess.WARL), # Supervisor Previous Interrupt Enable
+ ("wpri1", 1, CSRAccess.WPRI),
+ ("mpie", 1, CSRAccess.WARL), # Machine Previous Interrupt Enable
+ ("spp", 1, CSRAccess.WARL), # Supervisor Previous Privilege
+ ("wpri2", 2, CSRAccess.WPRI),
+ ("mpp", 2, CSRAccess.WARL), # Machine Previous Privilege
+ ("fs", 2, CSRAccess.WARL), # FPU Status
+ ("xs", 2, CSRAccess.WARL), # user-mode eXtensions Status
+ ("mprv", 1, CSRAccess.WARL), # Modify PRiVilege
+ ("sum", 1, CSRAccess.WARL), # Supervisor User Memory access
+ ("mxr", 1, CSRAccess.WARL), # Make eXecutable Readable
+ ("tvm", 1, CSRAccess.WARL), # Trap Virtual Memory
+ ("tw", 1, CSRAccess.WARL), # Timeout Wait
+ ("tsr", 1, CSRAccess.WARL), # Trap SRET
+ ("wpri3", 8, CSRAccess.WPRI),
+ ("sd", 1, CSRAccess.WARL) # State Dirty (set if XS or FS are set to dirty)
+]
+
+
+mtvec_layout = [
+ ("mode", 2, CSRAccess.WARL),
+ ("base", 30, CSRAccess.WARL)
+]
+
+
+mepc_layout = [
+ ("zero", 2, CSRAccess.WIRI), # 16-bit instructions are not supported
+ ("base", 30, CSRAccess.WARL)
+]
+
+
+mip_layout = [
+ ("usip", 1, CSRAccess.WARL),
+ ("ssip", 1, CSRAccess.WARL),
+ ("wiri0", 1, CSRAccess.WIRI),
+ ("msip", 1, CSRAccess.WARL),
+ ("utip", 1, CSRAccess.WARL),
+ ("stip", 1, CSRAccess.WARL),
+ ("wiri1", 1, CSRAccess.WIRI),
+ ("mtip", 1, CSRAccess.WARL),
+ ("ueip", 1, CSRAccess.WARL),
+ ("seip", 1, CSRAccess.WARL),
+ ("wiri2", 1, CSRAccess.WIRI),
+ ("meip", 1, CSRAccess.WARL),
+ ("wiri3", 20, CSRAccess.WIRI)
+]
+
+
+mie_layout = [
+ ("usie", 1, CSRAccess.WARL),
+ ("ssie", 1, CSRAccess.WARL),
+ ("wpri0", 1, CSRAccess.WPRI),
+ ("msie", 1, CSRAccess.WARL),
+ ("utie", 1, CSRAccess.WARL),
+ ("stie", 1, CSRAccess.WARL),
+ ("wpri1", 1, CSRAccess.WPRI),
+ ("mtie", 1, CSRAccess.WARL),
+ ("ueie", 1, CSRAccess.WARL),
+ ("seie", 1, CSRAccess.WARL),
+ ("wpri2", 1, CSRAccess.WPRI),
+ ("meie", 1, CSRAccess.WARL),
+ ("wpri3", 20, CSRAccess.WPRI)
+]
+
+
+mcause_layout = [
+ ("ecode", 31, CSRAccess.WARL),
+ ("interrupt", 1, CSRAccess.WARL)
+]
+
+
+dcsr_layout = [
+ ("prv", 2, CSRAccess.WARL), # Privilege level before Debug Mode was entered
+ ("step", 1, CSRAccess.WARL), # Execute a single instruction and re-enter Debug Mode
+ ("nmip", 1, CSRAccess.WLRL), # A non-maskable interrupt is pending
+ ("mprven", 1, CSRAccess.WARL), # Use mstatus.mprv in Debug Mode
+ ("zero0", 1, CSRAccess.WPRI),
+ ("cause", 3, CSRAccess.WLRL), # Explains why Debug Mode was entered
+ ("stoptime", 1, CSRAccess.WARL), # Stop timer increment during Debug Mode
+ ("stopcount", 1, CSRAccess.WARL), # Stop counter increment during Debug Mode
+ ("stepie", 1, CSRAccess.WARL), # Enable interrupts during single stepping
+ ("ebreaku", 1, CSRAccess.WARL), # EBREAKs in U-mode enter Debug Mode
+ ("ebreaks", 1, CSRAccess.WARL), # EBREAKs in S-mode enter Debug Mode
+ ("zero1", 1, CSRAccess.WPRI),
+ ("ebreakm", 1, CSRAccess.WARL), # EBREAKs in M-mode enter Debug Mode
+ ("zero2", 12, CSRAccess.WPRI),
+ ("xdebugver", 4, CSRAccess.WLRL) # External Debug specification version
+]
+
+
+tdata1_layout = [
+ ("data", 27, CSRAccess.WARL),
+ ("dmode", 1, CSRAccess.WARL),
+ ("type", 4, CSRAccess.WARL)
+]
--- /dev/null
+from functools import reduce
+from operator import or_
+
+from nmigen import *
+from nmigen.hdl.rec import *
+
+
+__all__ = ["Stage"]
+
+
+def _make_m2s(layout):
+ r = []
+ for f in layout:
+ if isinstance(f[1], (int, tuple)):
+ r.append((f[0], f[1], DIR_FANOUT))
+ else:
+ r.append((f[0], _make_m2s(f[1])))
+ return r
+
+
+class _EndpointDescription:
+ def __init__(self, payload_layout):
+ self.payload_layout = payload_layout
+
+ def get_full_layout(self):
+ reserved = {"valid", "stall", "kill"}
+ attributed = set()
+ for f in self.payload_layout:
+ if f[0] in attributed:
+ raise ValueError(f[0] + " already attributed in payload layout")
+ if f[0] in reserved:
+ raise ValueError(f[0] + " cannot be used in endpoint layout")
+ attributed.add(f[0])
+
+ full_layout = [
+ ("valid", 1, DIR_FANOUT),
+ ("stall", 1, DIR_FANIN),
+ ("kill", 1, DIR_FANOUT),
+ ("payload", _make_m2s(self.payload_layout))
+ ]
+ return full_layout
+
+
+class _Endpoint(Record):
+ def __init__(self, layout):
+ self.description = _EndpointDescription(layout)
+ super().__init__(self.description.get_full_layout())
+
+ def __getattr__(self, name):
+ try:
+ return super().__getattr__(name)
+ except AttributeError:
+ return self.fields["payload"][name]
+
+
+class Stage(Elaboratable):
+ def __init__(self, sink_layout, source_layout):
+ self.kill = Signal()
+ self.stall = Signal()
+ self.valid = Signal()
+
+ if sink_layout is None and source_layout is None:
+ raise ValueError
+ if sink_layout is not None:
+ self.sink = _Endpoint(sink_layout)
+ if source_layout is not None:
+ self.source = _Endpoint(source_layout)
+
+ self._kill_sources = []
+ self._stall_sources = []
+
+ def kill_on(self, cond):
+ self._kill_sources.append(cond)
+
+ def stall_on(self, cond):
+ self._stall_sources.append(cond)
+
+ def elaborate(self, platform):
+ m = Module()
+
+ if hasattr(self, "sink"):
+ m.d.comb += [
+ self.valid.eq(self.sink.valid & ~self.sink.kill),
+ self.sink.stall.eq(self.stall)
+ ]
+
+ if hasattr(self, "source"):
+ with m.If(~self.stall):
+ m.d.sync += self.source.valid.eq(self.valid)
+ with m.Elif(~self.source.stall | self.kill):
+ m.d.sync += self.source.valid.eq(0)
+ self.stall_on(self.source.stall)
+ m.d.comb += [
+ self.source.kill.eq(self.kill),
+ self.kill.eq(reduce(or_, self._kill_sources, 0))
+ ]
+
+ m.d.comb += self.stall.eq(reduce(or_, self._stall_sources, 0))
+
+ return m
--- /dev/null
+from nmigen import *
+from nmigen.utils import log2_int
+from nmigen.test.utils import *
+from nmigen.asserts import *
+
+from ..cache import L1Cache
+
+
+class L1CacheSpec(Elaboratable):
+ """
+ A cache read at a given address must either:
+ - miss and start a cache refill;
+ - hit and return the last refilled value for that address.
+ """
+ def __init__(self, cache):
+ self.cache = cache
+
+ def elaborate(self, platform):
+ m = Module()
+ m.submodules.dut = cache = self.cache
+
+ addr = AnyConst(cache.s1_addr.shape())
+ s1_re = AnySeq(1)
+
+ with m.If(~cache.s1_stall):
+ m.d.sync += [
+ cache.s2_addr.eq(cache.s1_addr),
+ cache.s2_re.eq(s1_re),
+ cache.s2_valid.eq(cache.s1_valid)
+ ]
+
+ m.d.comb += Assume(cache.s1_valid.implies(cache.s1_addr == addr))
+ with m.If(cache.s2_re & cache.s2_miss & cache.s2_valid):
+ m.d.comb += Assume(cache.s1_stall)
+
+ spec_s2_rdata = Signal(32)
+ spec_bus_re = Signal()
+ spec_bus_addr = Record.like(cache.bus_addr)
+ spec_bus_last = Signal()
+ last_offset = Signal.like(cache.s2_addr.offset)
+
+ with m.If(~Past(cache.s1_stall)):
+ with m.If(cache.s2_re & cache.s2_miss & cache.s2_valid):
+ m.d.sync += [
+ spec_bus_re.eq(1),
+ spec_bus_addr.eq(cache.s2_addr),
+ last_offset.eq(cache.s2_addr.offset - 1)
+ ]
+
+ with m.If(cache.bus_re):
+ with m.If(cache.bus_valid):
+ # Lines are refilled with an incremental burst that starts at the missed address
+ # and wraps around the offset bits.
+ m.d.sync += spec_bus_addr.offset.eq(spec_bus_addr.offset + 1)
+ with m.If((cache.bus_addr == cache.s2_addr) & ~cache.bus_error):
+ m.d.sync += spec_s2_rdata.eq(cache.bus_rdata)
+ # A burst ends when all words in the line have been refilled, or an error occured.
+ m.d.comb += spec_bus_last.eq(cache.bus_addr.offset == last_offset)
+ with m.If(cache.bus_valid & cache.bus_last | cache.bus_error):
+ m.d.sync += spec_bus_re.eq(0)
+
+ with m.If(cache.s2_re & cache.s2_valid & ~cache.s2_miss):
+ m.d.comb += Assert(cache.s2_rdata == spec_s2_rdata)
+
+ m.d.comb += Assert(cache.bus_re == spec_bus_re)
+ with m.If(cache.bus_re):
+ m.d.comb += [
+ Assert(cache.bus_addr == spec_bus_addr),
+ Assert(cache.bus_last == spec_bus_last)
+ ]
+
+ with m.If(Initial()):
+ m.d.comb += [
+ Assume(cache.s1_stall),
+ Assume(~cache.s2_valid),
+ Assume(~cache.bus_re),
+ Assume(~spec_bus_re)
+ ]
+
+ return m
+
+
+class L1CacheTestCase(FHDLTestCase):
+ def check(self, cache):
+ self.assertFormal(L1CacheSpec(cache), mode="bmc", depth=10)
+
+ def test_direct_mapped(self):
+ self.check(L1Cache(nways=1, nlines=2, nwords=4, base=0, limit=1024))
+
+ def test_2_ways(self):
+ self.check(L1Cache(nways=2, nlines=2, nwords=4, base=0, limit=1024))
--- /dev/null
+from nmigen import *
+from nmigen.back.pysim import *
+from nmigen.test.utils import *
+
+from ..units.divider import *
+from ..isa import Funct3
+
+
+def test_op(funct3, src1, src2, result):
+ def test(self):
+ with Simulator(self.dut) as sim:
+ def process():
+ yield self.dut.x_op.eq(funct3)
+ yield self.dut.x_src1.eq(src1)
+ yield self.dut.x_src2.eq(src2)
+ yield self.dut.x_valid.eq(1)
+ yield self.dut.x_stall.eq(0)
+ yield Tick()
+ yield self.dut.x_valid.eq(0)
+ yield Tick()
+ while (yield self.dut.m_busy):
+ yield Tick()
+ self.assertEqual((yield self.dut.m_result), result)
+ sim.add_clock(1e-6)
+ sim.add_sync_process(process)
+ sim.run()
+ return test
+
+
+class DividerTestCase(FHDLTestCase):
+ def setUp(self):
+ self.dut = Divider()
+
+ # Test cases are taken from the riscv-compliance testbench:
+ # https://github.com/riscv/riscv-compliance/tree/master/riscv-test-suite/rv32im
+
+ # DIV ------------------------------------------------------------------------
+
+ test_div_0 = test_op(Funct3.DIV, 0x00000000, 0x00000000, result=0xffffffff)
+ test_div_1 = test_op(Funct3.DIV, 0x00000000, 0x00000001, result=0x00000000)
+ test_div_2 = test_op(Funct3.DIV, 0x00000000, 0xffffffff, result=0x00000000)
+ test_div_3 = test_op(Funct3.DIV, 0x00000000, 0x7fffffff, result=0x00000000)
+ test_div_4 = test_op(Funct3.DIV, 0x00000000, 0x80000000, result=0x00000000)
+
+ test_div_5 = test_op(Funct3.DIV, 0x00000001, 0x00000000, result=0xffffffff)
+ test_div_6 = test_op(Funct3.DIV, 0x00000001, 0x00000001, result=0x00000001)
+ test_div_7 = test_op(Funct3.DIV, 0x00000001, 0xffffffff, result=0xffffffff)
+ test_div_8 = test_op(Funct3.DIV, 0x00000001, 0x7fffffff, result=0x00000000)
+ test_div_9 = test_op(Funct3.DIV, 0x00000001, 0x80000000, result=0x00000000)
+
+ test_div_10 = test_op(Funct3.DIV, 0xffffffff, 0x00000000, result=0xffffffff)
+ test_div_11 = test_op(Funct3.DIV, 0xffffffff, 0x00000001, result=0xffffffff)
+ test_div_12 = test_op(Funct3.DIV, 0xffffffff, 0xffffffff, result=0x00000001)
+ test_div_13 = test_op(Funct3.DIV, 0xffffffff, 0x7fffffff, result=0x00000000)
+ test_div_14 = test_op(Funct3.DIV, 0xffffffff, 0x80000000, result=0x00000000)
+
+ test_div_15 = test_op(Funct3.DIV, 0x7fffffff, 0x00000000, result=0xffffffff)
+ test_div_16 = test_op(Funct3.DIV, 0x7fffffff, 0x00000001, result=0x7fffffff)
+ test_div_17 = test_op(Funct3.DIV, 0x7fffffff, 0xffffffff, result=0x80000001)
+ test_div_18 = test_op(Funct3.DIV, 0x7fffffff, 0x7fffffff, result=0x00000001)
+ test_div_19 = test_op(Funct3.DIV, 0x7fffffff, 0x80000000, result=0x00000000)
+
+ test_div_20 = test_op(Funct3.DIV, 0x80000000, 0x00000000, result=0xffffffff)
+ test_div_21 = test_op(Funct3.DIV, 0x80000000, 0x00000001, result=0x80000000)
+ test_div_22 = test_op(Funct3.DIV, 0x80000000, 0xffffffff, result=0x80000000)
+ test_div_23 = test_op(Funct3.DIV, 0x80000000, 0x7fffffff, result=0xffffffff)
+ test_div_24 = test_op(Funct3.DIV, 0x80000000, 0x80000000, result=0x00000001)
+
+ # DIVU -----------------------------------------------------------------------
+
+ test_divu_0 = test_op(Funct3.DIVU, 0x00000000, 0x00000000, result=0xffffffff)
+ test_divu_1 = test_op(Funct3.DIVU, 0x00000000, 0x00000001, result=0x00000000)
+ test_divu_2 = test_op(Funct3.DIVU, 0x00000000, 0xffffffff, result=0x00000000)
+ test_divu_3 = test_op(Funct3.DIVU, 0x00000000, 0x7fffffff, result=0x00000000)
+ test_divu_4 = test_op(Funct3.DIVU, 0x00000000, 0x80000000, result=0x00000000)
+
+ test_divu_5 = test_op(Funct3.DIVU, 0x00000001, 0x00000000, result=0xffffffff)
+ test_divu_6 = test_op(Funct3.DIVU, 0x00000001, 0x00000001, result=0x00000001)
+ test_divu_7 = test_op(Funct3.DIVU, 0x00000001, 0xffffffff, result=0x00000000)
+ test_divu_8 = test_op(Funct3.DIVU, 0x00000001, 0x7fffffff, result=0x00000000)
+ test_divu_9 = test_op(Funct3.DIVU, 0x00000001, 0x80000000, result=0x00000000)
+
+ test_divu_10 = test_op(Funct3.DIVU, 0xffffffff, 0x00000000, result=0xffffffff)
+ test_divu_11 = test_op(Funct3.DIVU, 0xffffffff, 0x00000001, result=0xffffffff)
+ test_divu_12 = test_op(Funct3.DIVU, 0xffffffff, 0xffffffff, result=0x00000001)
+ test_divu_13 = test_op(Funct3.DIVU, 0xffffffff, 0x7fffffff, result=0x00000002)
+ test_divu_14 = test_op(Funct3.DIVU, 0xffffffff, 0x80000000, result=0x00000001)
+
+ test_divu_15 = test_op(Funct3.DIVU, 0x7fffffff, 0x00000000, result=0xffffffff)
+ test_divu_16 = test_op(Funct3.DIVU, 0x7fffffff, 0x00000001, result=0x7fffffff)
+ test_divu_17 = test_op(Funct3.DIVU, 0x7fffffff, 0xffffffff, result=0x00000000)
+ test_divu_18 = test_op(Funct3.DIVU, 0x7fffffff, 0x7fffffff, result=0x00000001)
+ test_divu_19 = test_op(Funct3.DIVU, 0x7fffffff, 0x80000000, result=0x00000000)
+
+ test_divu_20 = test_op(Funct3.DIVU, 0x80000000, 0x00000000, result=0xffffffff)
+ test_divu_21 = test_op(Funct3.DIVU, 0x80000000, 0x00000001, result=0x80000000)
+ test_divu_22 = test_op(Funct3.DIVU, 0x80000000, 0xffffffff, result=0x00000000)
+ test_divu_23 = test_op(Funct3.DIVU, 0x80000000, 0x7fffffff, result=0x00000001)
+ test_divu_24 = test_op(Funct3.DIVU, 0x80000000, 0x80000000, result=0x00000001)
+
+ # REM ------------------------------------------------------------------------
+
+ test_rem_0 = test_op(Funct3.REM, 0x00000000, 0x00000000, result=0x00000000)
+ test_rem_1 = test_op(Funct3.REM, 0x00000000, 0x00000001, result=0x00000000)
+ test_rem_2 = test_op(Funct3.REM, 0x00000000, 0xffffffff, result=0x00000000)
+ test_rem_3 = test_op(Funct3.REM, 0x00000000, 0x7fffffff, result=0x00000000)
+ test_rem_4 = test_op(Funct3.REM, 0x00000000, 0x80000000, result=0x00000000)
+
+ test_rem_5 = test_op(Funct3.REM, 0x00000001, 0x00000000, result=0x00000001)
+ test_rem_6 = test_op(Funct3.REM, 0x00000001, 0x00000001, result=0x00000000)
+ test_rem_7 = test_op(Funct3.REM, 0x00000001, 0xffffffff, result=0x00000000)
+ test_rem_8 = test_op(Funct3.REM, 0x00000001, 0x7fffffff, result=0x00000001)
+ test_rem_9 = test_op(Funct3.REM, 0x00000001, 0x80000000, result=0x00000001)
+
+ test_rem_10 = test_op(Funct3.REM, 0xffffffff, 0x00000000, result=0xffffffff)
+ test_rem_11 = test_op(Funct3.REM, 0xffffffff, 0x00000001, result=0x00000000)
+ test_rem_12 = test_op(Funct3.REM, 0xffffffff, 0xffffffff, result=0x00000000)
+ test_rem_13 = test_op(Funct3.REM, 0xffffffff, 0x7fffffff, result=0xffffffff)
+ test_rem_14 = test_op(Funct3.REM, 0xffffffff, 0x80000000, result=0xffffffff)
+
+ test_rem_15 = test_op(Funct3.REM, 0x7fffffff, 0x00000000, result=0x7fffffff)
+ test_rem_16 = test_op(Funct3.REM, 0x7fffffff, 0x00000001, result=0x00000000)
+ test_rem_17 = test_op(Funct3.REM, 0x7fffffff, 0xffffffff, result=0x00000000)
+ test_rem_18 = test_op(Funct3.REM, 0x7fffffff, 0x7fffffff, result=0x00000000)
+ test_rem_19 = test_op(Funct3.REM, 0x7fffffff, 0x80000000, result=0x7fffffff)
+
+ test_rem_20 = test_op(Funct3.REM, 0x80000000, 0x00000000, result=0x80000000)
+ test_rem_21 = test_op(Funct3.REM, 0x80000000, 0x00000001, result=0x00000000)
+ test_rem_22 = test_op(Funct3.REM, 0x80000000, 0xffffffff, result=0x00000000)
+ test_rem_23 = test_op(Funct3.REM, 0x80000000, 0x7fffffff, result=0xffffffff)
+ test_rem_24 = test_op(Funct3.REM, 0x80000000, 0x80000000, result=0x00000000)
+
+ # REMU -----------------------------------------------------------------------
+
+ test_remu_0 = test_op(Funct3.REMU, 0x00000000, 0x00000000, result=0x00000000)
+ test_remu_1 = test_op(Funct3.REMU, 0x00000000, 0x00000001, result=0x00000000)
+ test_remu_2 = test_op(Funct3.REMU, 0x00000000, 0xffffffff, result=0x00000000)
+ test_remu_3 = test_op(Funct3.REMU, 0x00000000, 0x7fffffff, result=0x00000000)
+ test_remu_4 = test_op(Funct3.REMU, 0x00000000, 0x80000000, result=0x00000000)
+
+ test_remu_5 = test_op(Funct3.REMU, 0x00000001, 0x00000000, result=0x00000001)
+ test_remu_6 = test_op(Funct3.REMU, 0x00000001, 0x00000001, result=0x00000000)
+ test_remu_7 = test_op(Funct3.REMU, 0x00000001, 0xffffffff, result=0x00000001)
+ test_remu_8 = test_op(Funct3.REMU, 0x00000001, 0x7fffffff, result=0x00000001)
+ test_remu_9 = test_op(Funct3.REMU, 0x00000001, 0x80000000, result=0x00000001)
+
+ test_remu_10 = test_op(Funct3.REMU, 0xffffffff, 0x00000000, result=0xffffffff)
+ test_remu_11 = test_op(Funct3.REMU, 0xffffffff, 0x00000001, result=0x00000000)
+ test_remu_12 = test_op(Funct3.REMU, 0xffffffff, 0xffffffff, result=0x00000000)
+ test_remu_13 = test_op(Funct3.REMU, 0xffffffff, 0x7fffffff, result=0x00000001)
+ test_remu_14 = test_op(Funct3.REMU, 0xffffffff, 0x80000000, result=0x7fffffff)
+
+ test_remu_15 = test_op(Funct3.REMU, 0x7fffffff, 0x00000000, result=0x7fffffff)
+ test_remu_16 = test_op(Funct3.REMU, 0x7fffffff, 0x00000001, result=0x00000000)
+ test_remu_17 = test_op(Funct3.REMU, 0x7fffffff, 0xffffffff, result=0x7fffffff)
+ test_remu_18 = test_op(Funct3.REMU, 0x7fffffff, 0x7fffffff, result=0x00000000)
+ test_remu_19 = test_op(Funct3.REMU, 0x7fffffff, 0x80000000, result=0x7fffffff)
+
+ test_remu_20 = test_op(Funct3.REMU, 0x80000000, 0x00000000, result=0x80000000)
+ test_remu_21 = test_op(Funct3.REMU, 0x80000000, 0x00000001, result=0x00000000)
+ test_remu_22 = test_op(Funct3.REMU, 0x80000000, 0xffffffff, result=0x80000000)
+ test_remu_23 = test_op(Funct3.REMU, 0x80000000, 0x7fffffff, result=0x00000001)
+ test_remu_24 = test_op(Funct3.REMU, 0x80000000, 0x80000000, result=0x00000000)
--- /dev/null
+from nmigen import *
+from nmigen.back.pysim import *
+from nmigen.test.utils import *
+
+from ..units.multiplier import *
+from ..isa import Funct3
+
+
+def test_op(funct3, src1, src2, result):
+ def test(self):
+ with Simulator(self.dut) as sim:
+ def process():
+ yield self.dut.x_op.eq(funct3)
+ yield self.dut.x_src1.eq(src1)
+ yield self.dut.x_src2.eq(src2)
+ yield self.dut.x_stall.eq(0)
+ yield Tick()
+ yield self.dut.m_stall.eq(0)
+ yield Tick()
+ yield Tick()
+ self.assertEqual((yield self.dut.w_result), result)
+ sim.add_clock(1e-6)
+ sim.add_sync_process(process)
+ sim.run()
+ return test
+
+
+class MultiplierTestCase(FHDLTestCase):
+ def setUp(self):
+ self.dut = Multiplier()
+
+ # Test cases are taken from the riscv-compliance testbench:
+ # https://github.com/riscv/riscv-compliance/tree/master/riscv-test-suite/rv32im
+
+ # MUL ----------------------------------------------------------------------------
+
+ test_mul_0 = test_op(Funct3.MUL, 0x00000000, 0x00000000, result=0x00000000)
+ test_mul_1 = test_op(Funct3.MUL, 0x00000000, 0x00000001, result=0x00000000)
+ test_mul_2 = test_op(Funct3.MUL, 0x00000000, 0xffffffff, result=0x00000000)
+ test_mul_3 = test_op(Funct3.MUL, 0x00000000, 0x7fffffff, result=0x00000000)
+ test_mul_4 = test_op(Funct3.MUL, 0x00000000, 0x80000000, result=0x00000000)
+
+ test_mul_5 = test_op(Funct3.MUL, 0x00000001, 0x00000000, result=0x00000000)
+ test_mul_6 = test_op(Funct3.MUL, 0x00000001, 0x00000001, result=0x00000001)
+ test_mul_7 = test_op(Funct3.MUL, 0x00000001, 0xffffffff, result=0xffffffff)
+ test_mul_8 = test_op(Funct3.MUL, 0x00000001, 0x7fffffff, result=0x7fffffff)
+ test_mul_9 = test_op(Funct3.MUL, 0x00000001, 0x80000000, result=0x80000000)
+
+ test_mul_10 = test_op(Funct3.MUL, 0xffffffff, 0x00000000, result=0x00000000)
+ test_mul_11 = test_op(Funct3.MUL, 0xffffffff, 0x00000001, result=0xffffffff)
+ test_mul_12 = test_op(Funct3.MUL, 0xffffffff, 0xffffffff, result=0x00000001)
+ test_mul_13 = test_op(Funct3.MUL, 0xffffffff, 0x7fffffff, result=0x80000001)
+ test_mul_14 = test_op(Funct3.MUL, 0xffffffff, 0x80000000, result=0x80000000)
+
+ test_mul_15 = test_op(Funct3.MUL, 0x7fffffff, 0x00000000, result=0x00000000)
+ test_mul_16 = test_op(Funct3.MUL, 0x7fffffff, 0x00000001, result=0x7fffffff)
+ test_mul_17 = test_op(Funct3.MUL, 0x7fffffff, 0xffffffff, result=0x80000001)
+ test_mul_18 = test_op(Funct3.MUL, 0x7fffffff, 0x7fffffff, result=0x00000001)
+ test_mul_19 = test_op(Funct3.MUL, 0x7fffffff, 0x80000000, result=0x80000000)
+
+ test_mul_20 = test_op(Funct3.MUL, 0x80000000, 0x00000000, result=0x00000000)
+ test_mul_21 = test_op(Funct3.MUL, 0x80000000, 0x00000001, result=0x80000000)
+ test_mul_22 = test_op(Funct3.MUL, 0x80000000, 0xffffffff, result=0x80000000)
+ test_mul_23 = test_op(Funct3.MUL, 0x80000000, 0x7fffffff, result=0x80000000)
+ test_mul_24 = test_op(Funct3.MUL, 0x80000000, 0x80000000, result=0x00000000)
+
+ # MULH ---------------------------------------------------------------------------
+
+ test_mulh_0 = test_op(Funct3.MULH, 0x00000000, 0x00000000, result=0x00000000)
+ test_mulh_1 = test_op(Funct3.MULH, 0x00000000, 0x00000001, result=0x00000000)
+ test_mulh_2 = test_op(Funct3.MULH, 0x00000000, 0xffffffff, result=0x00000000)
+ test_mulh_3 = test_op(Funct3.MULH, 0x00000000, 0x7fffffff, result=0x00000000)
+ test_mulh_4 = test_op(Funct3.MULH, 0x00000000, 0x80000000, result=0x00000000)
+
+ test_mulh_5 = test_op(Funct3.MULH, 0x00000001, 0x00000000, result=0x00000000)
+ test_mulh_6 = test_op(Funct3.MULH, 0x00000001, 0x00000001, result=0x00000000)
+ test_mulh_7 = test_op(Funct3.MULH, 0x00000001, 0xffffffff, result=0xffffffff)
+ test_mulh_8 = test_op(Funct3.MULH, 0x00000001, 0x7fffffff, result=0x00000000)
+ test_mulh_9 = test_op(Funct3.MULH, 0x00000001, 0x80000000, result=0xffffffff)
+
+ test_mulh_10 = test_op(Funct3.MULH, 0xffffffff, 0x00000000, result=0x00000000)
+ test_mulh_11 = test_op(Funct3.MULH, 0xffffffff, 0x00000001, result=0xffffffff)
+ test_mulh_12 = test_op(Funct3.MULH, 0xffffffff, 0xffffffff, result=0x00000000)
+ test_mulh_13 = test_op(Funct3.MULH, 0xffffffff, 0x7fffffff, result=0xffffffff)
+ test_mulh_14 = test_op(Funct3.MULH, 0xffffffff, 0x80000000, result=0x00000000)
+
+ test_mulh_15 = test_op(Funct3.MULH, 0x7fffffff, 0x00000000, result=0x00000000)
+ test_mulh_16 = test_op(Funct3.MULH, 0x7fffffff, 0x00000001, result=0x00000000)
+ test_mulh_17 = test_op(Funct3.MULH, 0x7fffffff, 0xffffffff, result=0xffffffff)
+ test_mulh_18 = test_op(Funct3.MULH, 0x7fffffff, 0x7fffffff, result=0x3fffffff)
+ test_mulh_19 = test_op(Funct3.MULH, 0x7fffffff, 0x80000000, result=0xc0000000)
+
+ test_mulh_20 = test_op(Funct3.MULH, 0x80000000, 0x00000000, result=0x00000000)
+ test_mulh_21 = test_op(Funct3.MULH, 0x80000000, 0x00000001, result=0xffffffff)
+ test_mulh_22 = test_op(Funct3.MULH, 0x80000000, 0xffffffff, result=0x00000000)
+ test_mulh_23 = test_op(Funct3.MULH, 0x80000000, 0x7fffffff, result=0xc0000000)
+ test_mulh_24 = test_op(Funct3.MULH, 0x80000000, 0x80000000, result=0x40000000)
+
+ # MULHSU -------------------------------------------------------------------------
+
+ test_mulhsu_0 = test_op(Funct3.MULHSU, 0x00000000, 0x00000000, result=0x00000000)
+ test_mulhsu_1 = test_op(Funct3.MULHSU, 0x00000000, 0x00000001, result=0x00000000)
+ test_mulhsu_2 = test_op(Funct3.MULHSU, 0x00000000, 0xffffffff, result=0x00000000)
+ test_mulhsu_3 = test_op(Funct3.MULHSU, 0x00000000, 0x7fffffff, result=0x00000000)
+ test_mulhsu_4 = test_op(Funct3.MULHSU, 0x00000000, 0x80000000, result=0x00000000)
+
+ test_mulhsu_5 = test_op(Funct3.MULHSU, 0x00000001, 0x00000000, result=0x00000000)
+ test_mulhsu_6 = test_op(Funct3.MULHSU, 0x00000001, 0x00000001, result=0x00000000)
+ test_mulhsu_7 = test_op(Funct3.MULHSU, 0x00000001, 0xffffffff, result=0x00000000)
+ test_mulhsu_8 = test_op(Funct3.MULHSU, 0x00000001, 0x7fffffff, result=0x00000000)
+ test_mulhsu_9 = test_op(Funct3.MULHSU, 0x00000001, 0x80000000, result=0x00000000)
+
+ test_mulhsu_10 = test_op(Funct3.MULHSU, 0xffffffff, 0x00000000, result=0x00000000)
+ test_mulhsu_11 = test_op(Funct3.MULHSU, 0xffffffff, 0x00000001, result=0xffffffff)
+ test_mulhsu_12 = test_op(Funct3.MULHSU, 0xffffffff, 0xffffffff, result=0xffffffff)
+ test_mulhsu_13 = test_op(Funct3.MULHSU, 0xffffffff, 0x7fffffff, result=0xffffffff)
+ test_mulhsu_14 = test_op(Funct3.MULHSU, 0xffffffff, 0x80000000, result=0xffffffff)
+
+ test_mulhsu_15 = test_op(Funct3.MULHSU, 0x7fffffff, 0x00000000, result=0x00000000)
+ test_mulhsu_16 = test_op(Funct3.MULHSU, 0x7fffffff, 0x00000001, result=0x00000000)
+ test_mulhsu_17 = test_op(Funct3.MULHSU, 0x7fffffff, 0xffffffff, result=0x7ffffffe)
+ test_mulhsu_18 = test_op(Funct3.MULHSU, 0x7fffffff, 0x7fffffff, result=0x3fffffff)
+ test_mulhsu_19 = test_op(Funct3.MULHSU, 0x7fffffff, 0x80000000, result=0x3fffffff)
+
+ test_mulhsu_20 = test_op(Funct3.MULHSU, 0x80000000, 0x00000000, result=0x00000000)
+ test_mulhsu_21 = test_op(Funct3.MULHSU, 0x80000000, 0x00000001, result=0xffffffff)
+ test_mulhsu_22 = test_op(Funct3.MULHSU, 0x80000000, 0xffffffff, result=0x80000000)
+ test_mulhsu_23 = test_op(Funct3.MULHSU, 0x80000000, 0x7fffffff, result=0xc0000000)
+ test_mulhsu_24 = test_op(Funct3.MULHSU, 0x80000000, 0x80000000, result=0xc0000000)
+
+ # MULHU --------------------------------------------------------------------------
+
+ test_mulhu_0 = test_op(Funct3.MULHU, 0x00000000, 0x00000000, result=0x00000000)
+ test_mulhu_1 = test_op(Funct3.MULHU, 0x00000000, 0x00000001, result=0x00000000)
+ test_mulhu_2 = test_op(Funct3.MULHU, 0x00000000, 0xffffffff, result=0x00000000)
+ test_mulhu_3 = test_op(Funct3.MULHU, 0x00000000, 0x7fffffff, result=0x00000000)
+ test_mulhu_4 = test_op(Funct3.MULHU, 0x00000000, 0x80000000, result=0x00000000)
+
+ test_mulhu_5 = test_op(Funct3.MULHU, 0x00000001, 0x00000000, result=0x00000000)
+ test_mulhu_6 = test_op(Funct3.MULHU, 0x00000001, 0x00000001, result=0x00000000)
+ test_mulhu_7 = test_op(Funct3.MULHU, 0x00000001, 0xffffffff, result=0x00000000)
+ test_mulhu_8 = test_op(Funct3.MULHU, 0x00000001, 0x7fffffff, result=0x00000000)
+ test_mulhu_9 = test_op(Funct3.MULHU, 0x00000001, 0x80000000, result=0x00000000)
+
+ test_mulhu_10 = test_op(Funct3.MULHU, 0xffffffff, 0x00000000, result=0x00000000)
+ test_mulhu_11 = test_op(Funct3.MULHU, 0xffffffff, 0x00000001, result=0x00000000)
+ test_mulhu_12 = test_op(Funct3.MULHU, 0xffffffff, 0xffffffff, result=0xfffffffe)
+ test_mulhu_13 = test_op(Funct3.MULHU, 0xffffffff, 0x7fffffff, result=0x7ffffffe)
+ test_mulhu_14 = test_op(Funct3.MULHU, 0xffffffff, 0x80000000, result=0x7fffffff)
+
+ test_mulhu_15 = test_op(Funct3.MULHU, 0x7fffffff, 0x00000000, result=0x00000000)
+ test_mulhu_16 = test_op(Funct3.MULHU, 0x7fffffff, 0x00000001, result=0x00000000)
+ test_mulhu_17 = test_op(Funct3.MULHU, 0x7fffffff, 0xffffffff, result=0x7ffffffe)
+ test_mulhu_18 = test_op(Funct3.MULHU, 0x7fffffff, 0x7fffffff, result=0x3fffffff)
+ test_mulhu_19 = test_op(Funct3.MULHU, 0x7fffffff, 0x80000000, result=0x3fffffff)
+
+ test_mulhu_20 = test_op(Funct3.MULHU, 0x80000000, 0x00000000, result=0x00000000)
+ test_mulhu_21 = test_op(Funct3.MULHU, 0x80000000, 0x00000001, result=0x00000000)
+ test_mulhu_22 = test_op(Funct3.MULHU, 0x80000000, 0xffffffff, result=0x7fffffff)
+ test_mulhu_23 = test_op(Funct3.MULHU, 0x80000000, 0x7fffffff, result=0x3fffffff)
+ test_mulhu_24 = test_op(Funct3.MULHU, 0x80000000, 0x80000000, result=0x40000000)
--- /dev/null
+from nmigen import *
+
+
+__all__ = ["Adder"]
+
+
+class Adder(Elaboratable):
+ def __init__(self):
+ self.sub = Signal()
+ self.src1 = Signal(32)
+ self.src2 = Signal(32)
+
+ self.result = Signal(32)
+ self.carry = Signal()
+ self.overflow = Signal()
+
+ def elaborate(self, platform):
+ m = Module()
+
+ with m.If(self.sub):
+ m.d.comb += [
+ Cat(self.result, self.carry).eq(self.src1 - self.src2),
+ self.overflow.eq((self.src1[-1] != self.src2[-1]) & (self.result[-1] == self.src2[-1]))
+ ]
+ with m.Else():
+ m.d.comb += [
+ Cat(self.result, self.carry).eq(self.src1 + self.src2),
+ self.overflow.eq(~self.src1[-1] & self.src2[-1] & self.result[-1])
+ ]
+
+ return m
--- /dev/null
+from nmigen import *
+
+from ..isa import Funct3
+
+
+__all__ = ["CompareUnit"]
+
+
+class CompareUnit(Elaboratable):
+ def __init__(self):
+ self.op = Signal(3)
+ self.zero = Signal()
+ self.negative = Signal()
+ self.overflow = Signal()
+ self.carry = Signal()
+
+ self.condition_met = Signal()
+
+ def elaborate(self, platform):
+ m = Module()
+
+ with m.Switch(self.op):
+ with m.Case(Funct3.BEQ):
+ m.d.comb += self.condition_met.eq(self.zero)
+ with m.Case(Funct3.BNE):
+ m.d.comb += self.condition_met.eq(~self.zero)
+ with m.Case(Funct3.BLT):
+ m.d.comb += self.condition_met.eq(~self.zero & (self.negative != self.overflow))
+ with m.Case(Funct3.BGE):
+ m.d.comb += self.condition_met.eq(self.negative == self.overflow)
+ with m.Case(Funct3.BLTU):
+ m.d.comb += self.condition_met.eq(~self.zero & self.carry)
+ with m.Case(Funct3.BGEU):
+ m.d.comb += self.condition_met.eq(~self.carry)
+
+ return m
--- /dev/null
+from .top import *
--- /dev/null
+from nmigen import *
+from nmigen.lib.coding import PriorityEncoder
+
+from ...csr import *
+from ...isa import *
+from ...wishbone import wishbone_layout
+from .dmi import DebugReg, Command, Error, Version, cmd_access_reg_layout
+
+
+__all__ = ["DebugController"]
+
+
+class HaltCause:
+ EBREAK = 1
+ TRIGGER = 0
+ HALTREQ = 3
+ STEP = 4
+ RESET_HALTREQ = 2
+
+
+cause_map = [2, 1, 5, 3, 4]
+
+
+class DebugController(Elaboratable, AutoCSR):
+ def __init__(self, debugrf):
+ self.dcsr = CSR(0x7b0, dcsr_layout, name="dcsr")
+ self.dpc = CSR(0x7b1, flat_layout, name="dpc")
+
+ self.dmstatus = debugrf.reg_port(DebugReg.DMSTATUS)
+ self.dmcontrol = debugrf.reg_port(DebugReg.DMCONTROL)
+ self.hartinfo = debugrf.reg_port(DebugReg.HARTINFO)
+ self.abstractcs = debugrf.reg_port(DebugReg.ABSTRACTCS)
+ self.command = debugrf.reg_port(DebugReg.COMMAND)
+ self.data0 = debugrf.reg_port(DebugReg.DATA0)
+
+ self.trigger_haltreq = Signal()
+
+ self.x_pc = Signal(32)
+ self.x_ebreak = Signal()
+ self.x_stall = Signal()
+
+ self.m_branch_taken = Signal()
+ self.m_branch_target = Signal(32)
+ self.m_mret = Signal()
+ self.m_exception = Signal()
+ self.m_pc = Signal(32)
+ self.m_valid = Signal()
+ self.mepc_r_base = Signal(30)
+ self.mtvec_r_base = Signal(30)
+
+ self.halt = Signal()
+ self.halted = Signal()
+ self.killall = Signal()
+ self.resumereq = Signal()
+ self.resumeack = Signal()
+
+ self.gprf_addr = Signal(5)
+ self.gprf_re = Signal()
+ self.gprf_dat_r = Signal(32)
+ self.gprf_we = Signal()
+ self.gprf_dat_w = Signal(32)
+
+ self.csrf_addr = Signal(12)
+ self.csrf_re = Signal()
+ self.csrf_dat_r = Signal(32)
+ self.csrf_we = Signal()
+ self.csrf_dat_w = Signal(32)
+
+ def elaborate(self, platform):
+ m = Module()
+
+ for csr in self.dcsr, self.dpc:
+ with m.If(csr.we):
+ m.d.sync += csr.r.eq(csr.w)
+
+ with m.If(self.dmcontrol.update):
+ m.d.sync += [
+ self.dmcontrol.w.dmactive.eq(self.dmcontrol.r.dmactive),
+ self.dmcontrol.w.ndmreset.eq(self.dmcontrol.r.ndmreset),
+ self.dmcontrol.w.hartselhi.eq(self.dmcontrol.r.hartselhi),
+ self.dmcontrol.w.hartsello.eq(self.dmcontrol.r.hartsello),
+ self.dmcontrol.w.hasel.eq(self.dmcontrol.r.hasel),
+ self.dmcontrol.w.hartreset.eq(self.dmcontrol.r.hartreset),
+ self.dmcontrol.w.resumereq.eq(self.dmcontrol.r.resumereq)
+ ]
+
+ with m.If(self.abstractcs.update):
+ m.d.sync += self.abstractcs.w.cmderr.eq(self.abstractcs.r.cmderr)
+
+ m.d.comb += [
+ self.dmstatus.w.version.eq(Version.V013),
+ self.dmstatus.w.authenticated.eq(1),
+ self.resumereq.eq(self.dmcontrol.w.resumereq)
+ ]
+
+ m_breakpoint = Signal()
+ with m.If(~self.x_stall):
+ m.d.comb += m_breakpoint.eq(self.x_ebreak & self.dcsr.r.ebreakm)
+
+ halt_pe = m.submodules.halt_pe = PriorityEncoder(5)
+ m.d.comb += [
+ halt_pe.i[HaltCause.EBREAK].eq(m_breakpoint & self.m_valid),
+ halt_pe.i[HaltCause.TRIGGER].eq(self.trigger_haltreq),
+ halt_pe.i[HaltCause.HALTREQ].eq(self.dmcontrol.r.haltreq),
+ halt_pe.i[HaltCause.STEP].eq(self.dcsr.r.step & self.m_valid),
+ ]
+
+ with m.FSM():
+ with m.State("RUN"):
+ m.d.comb += self.dmstatus.w.allrunning.eq(1)
+ with m.If(~halt_pe.n):
+ m.d.sync += [
+ self.halt.eq(1),
+ self.dcsr.r.cause.eq(Array(cause_map)[halt_pe.o]),
+ self.dcsr.r.stepie.eq(1)
+ ]
+ with m.If(halt_pe.o == HaltCause.EBREAK):
+ m.d.sync += self.dpc.r.eq(self.m_pc)
+ with m.Elif(self.m_exception & self.m_valid):
+ m.d.sync += self.dpc.r.eq(self.mtvec_r_base << 30)
+ with m.Elif(self.m_mret & self.m_valid):
+ m.d.sync += self.dpc.r.eq(self.mepc_r_base << 30)
+ with m.Elif(self.m_branch_taken & self.m_valid):
+ m.d.sync += self.dpc.r.eq(self.m_branch_target)
+ with m.Else():
+ m.d.sync += self.dpc.r.eq(self.x_pc)
+ m.next = "HALTING"
+
+ with m.State("HALTING"):
+ with m.If(self.halted):
+ m.d.comb += self.killall.eq(1)
+ m.d.sync += self.dmstatus.w.allhalted.eq(1)
+ m.next = "WAIT"
+
+ with m.State("WAIT"):
+ with m.If(self.dmcontrol.w.resumereq):
+ m.next = "RESUME"
+ with m.Elif(self.command.update):
+ m.d.sync += self.abstractcs.w.busy.eq(1)
+ m.next = "COMMAND:START"
+
+ with m.State("RESUME"):
+ with m.If(self.resumeack):
+ m.d.sync += [
+ self.dmcontrol.w.resumereq.eq(0),
+ self.dmstatus.w.allresumeack.eq(1),
+ self.halt.eq(0),
+ self.dmstatus.w.allhalted.eq(0)
+ ]
+ m.next = "RUN"
+
+ with m.State("COMMAND:START"):
+ with m.Switch(self.command.r.cmdtype):
+ with m.Case(Command.ACCESS_REG):
+ control = Record(cmd_access_reg_layout)
+ m.d.comb += control.eq(self.command.r.control)
+ m.d.comb += self.gprf_addr.eq(control.regno)
+ m.next = "COMMAND:ACCESS-REG"
+ with m.Case():
+ m.d.sync += self.abstractcs.w.cmderr.eq(Error.UNSUPPORTED)
+ m.next = "COMMAND:DONE"
+
+ with m.State("COMMAND:ACCESS-REG"):
+ control = Record(cmd_access_reg_layout)
+ m.d.comb += control.eq(self.command.r.control)
+ with m.If(control.postexec | (control.aarsize != 2) | control.aarpostincrement):
+ # Unsupported parameters.
+ m.d.sync += self.abstractcs.w.cmderr.eq(Error.EXCEPTION)
+ with m.Elif((control.regno >= 0x0000) & (control.regno < 0x1000)):
+ with m.If(control.transfer):
+ m.d.comb += self.csrf_addr.eq(control.regno)
+ with m.If(control.write):
+ m.d.comb += [
+ self.csrf_we.eq(1),
+ self.csrf_dat_w.eq(self.data0.r)
+ ]
+ with m.Else():
+ m.d.comb += self.csrf_re.eq(1)
+ m.d.sync += self.data0.w.eq(self.csrf_dat_r)
+ m.d.sync += self.abstractcs.w.cmderr.eq(Error.NONE)
+ with m.Elif((control.regno >= 0x1000) & (control.regno < 0x1020)):
+ with m.If(control.transfer):
+ m.d.comb += self.gprf_addr.eq(control.regno)
+ with m.If(control.write):
+ m.d.comb += [
+ self.gprf_we.eq(1),
+ self.gprf_dat_w.eq(self.data0.r)
+ ]
+ with m.Else():
+ m.d.sync += self.data0.w.eq(self.gprf_dat_r)
+ m.d.sync += self.abstractcs.w.cmderr.eq(Error.NONE)
+ with m.Else():
+ # Unknown register number.
+ m.d.sync += self.abstractcs.w.cmderr.eq(Error.EXCEPTION)
+ m.next = "COMMAND:DONE"
+
+ with m.State("COMMAND:DONE"):
+ m.d.sync += self.abstractcs.w.busy.eq(0)
+ m.next = "WAIT"
+
+ return m
--- /dev/null
+from enum import Enum
+
+
+__all__ = [
+ "Version", "Command", "Error", "RegMode", "DmiOp", "DebugReg", "dmstatus_layout",
+ "dmcontrol_layout", "abstractcs_layout", "cmd_access_reg_layout", "command_layout",
+ "sbcs_layout", "flat_layout"
+]
+
+
+class Version:
+ NONE = 0
+ V011 = 1
+ V013 = 2
+ OTHER = 15
+
+
+class Command:
+ ACCESS_REG = 0
+ QUICK_ACCESS = 1
+ ACCESS_MEM = 2
+
+
+class Error:
+ NONE = 0
+ BUSY = 1
+ UNSUPPORTED = 2
+ EXCEPTION = 3
+ HALT_RESUME = 4
+
+
+RegMode = Enum("RegMode", ("R", "W", "W1", "RW", "RW1C", "WARL"))
+
+
+class DmiOp:
+ NOP = 0
+ READ = 1
+ WRITE = 2
+
+
+# Debug registers
+
+class DebugReg:
+ DATA0 = 0x04
+ DMCONTROL = 0x10
+ DMSTATUS = 0x11
+ HARTINFO = 0x12
+ ABSTRACTCS = 0x16
+ COMMAND = 0x17
+ PROGBUF0 = 0x20
+ SBCS = 0x38
+ SBADDRESS0 = 0x39
+ SBDATA0 = 0x3c
+
+
+dmstatus_layout = [
+ ("version", 4, RegMode.R, Version.V013),
+ ("confstrptrvalid", 1, RegMode.R, False),
+ ("hasresethaltreq", 1, RegMode.R, False),
+ ("authbusy", 1, RegMode.R, False),
+ ("authenticated", 1, RegMode.R, True),
+ ("anyhalted", 1, RegMode.R, False),
+ ("allhalted", 1, RegMode.R, False),
+ ("anyrunning", 1, RegMode.R, False),
+ ("allrunning", 1, RegMode.R, False),
+ ("anyunavail", 1, RegMode.R, False),
+ ("allunavail", 1, RegMode.R, False),
+ ("anynonexistent", 1, RegMode.R, False),
+ ("allnonexistent", 1, RegMode.R, False),
+ ("anyresumeack", 1, RegMode.R, False),
+ ("allresumeack", 1, RegMode.R, False),
+ ("anyhavereset", 1, RegMode.R, False),
+ ("allhavereset", 1, RegMode.R, False),
+ ("zero0", 2, RegMode.R, 0),
+ ("impebreak", 1, RegMode.R, False),
+ ("zero1", 9, RegMode.R, 0)
+]
+
+
+dmcontrol_layout = [
+ ("dmactive", 1, RegMode.RW, False),
+ ("ndmreset", 1, RegMode.RW, False),
+ ("clrresethaltreq", 1, RegMode.W1, False),
+ ("setresethaltreq", 1, RegMode.W1, False),
+ ("zero0", 2, RegMode.R, 0),
+ ("hartselhi", 10, RegMode.RW, 0),
+ ("hartsello", 10, RegMode.RW, 0),
+ ("hasel", 1, RegMode.RW, False),
+ ("zero1", 1, RegMode.R, 0),
+ ("ackhavereset", 1, RegMode.W1, False),
+ ("hartreset", 1, RegMode.RW, False),
+ ("resumereq", 1, RegMode.W1, False),
+ ("haltreq", 1, RegMode.W, False)
+]
+
+
+abstractcs_layout = [
+ ("datacount", 4, RegMode.R, 1),
+ ("zero0", 4, RegMode.R, 0),
+ ("cmderr", 3, RegMode.RW1C, 0),
+ ("zero1", 1, RegMode.R, 0),
+ ("busy", 1, RegMode.R, False),
+ ("zero2", 11, RegMode.R, 0),
+ ("progbufsize", 5, RegMode.R, 0),
+ ("zero3", 3, RegMode.R, 0)
+]
+
+
+cmd_access_reg_layout = [
+ ("regno", 16),
+ ("write", 1),
+ ("transfer", 1),
+ ("postexec", 1),
+ ("aarpostincrement", 1),
+ ("aarsize", 3),
+ ("zero0", 1),
+]
+
+
+command_layout = [
+ ("control", 24, RegMode.W, 0),
+ ("cmdtype", 8, RegMode.W, Command.ACCESS_REG)
+]
+
+
+sbcs_layout = [
+ ("sbaccess8", 1, RegMode.R, True),
+ ("sbaccess16", 1, RegMode.R, True),
+ ("sbaccess32", 1, RegMode.R, True),
+ ("sbaccess64", 1, RegMode.R, False),
+ ("sbaccess128", 1, RegMode.R, False),
+ ("sbasize", 7, RegMode.R, 32),
+ ("sberror", 3, RegMode.RW1C, 0),
+ ("sbreadondata", 1, RegMode.RW, False),
+ ("sbautoincrement", 1, RegMode.RW, False),
+ ("sbaccess", 3, RegMode.RW, 2),
+ ("sbreadonaddr", 1, RegMode.RW, False),
+ ("sbbusy", 1, RegMode.R, False),
+ ("sbbusyerror", 1, RegMode.RW1C, False),
+ ("zero0", 6, RegMode.R, 0),
+ ("sbversion", 3, RegMode.R, 1)
+]
+
+
+flat_layout = [
+ ("value", 32, RegMode.RW, 0)
+]
--- /dev/null
+from nmigen.hdl.rec import *
+
+
+__all__ = ["jtag_layout", "JTAGReg", "dtmcs_layout", "dmi_layout"]
+
+
+jtag_layout = [
+ ("tck", 1, DIR_FANIN),
+ ("tdi", 1, DIR_FANIN),
+ ("tdo", 1, DIR_FANOUT),
+ ("tms", 1, DIR_FANIN),
+ ("trst", 1, DIR_FANIN) # TODO
+]
+
+
+class JTAGReg:
+ BYPASS = 0x00
+ IDCODE = 0x01
+ DTMCS = 0x10
+ DMI = 0x11
+
+
+# JTAG register layouts
+
+dtmcs_layout = [
+ ("version", 4),
+ ("abits", 6),
+ ("dmistat", 2),
+ ("idle", 3),
+ ("zero0", 1),
+ ("dmireset", 1),
+ ("dmihardreset", 1),
+ ("zero1", 14)
+]
+
+
+dmi_layout = [
+ ("op", 2),
+ ("data", 32),
+ ("addr", 6)
+]
--- /dev/null
+from nmigen import *
+from nmigen.hdl.rec import *
+
+from .dmi import *
+
+
+__all__ = ["DebugRegisterFile"]
+
+
+class DmiOp:
+ NOP = 0
+ READ = 1
+ WRITE = 2
+
+
+class DmiResult:
+ OK = 0
+ FAIL = 2
+ BUSY = 3
+
+
+reg_map = {
+ DebugReg.DMSTATUS: dmstatus_layout,
+ DebugReg.DMCONTROL: dmcontrol_layout,
+ DebugReg.HARTINFO: flat_layout,
+ DebugReg.ABSTRACTCS: abstractcs_layout,
+ DebugReg.COMMAND: command_layout,
+ DebugReg.SBCS: sbcs_layout,
+ DebugReg.SBADDRESS0: flat_layout,
+ DebugReg.SBDATA0: flat_layout,
+ DebugReg.DATA0: flat_layout
+}
+
+
+class DebugRegisterFile(Elaboratable):
+ def __init__(self, dmi):
+ self.dmi = dmi
+ self.ports = dict()
+
+ def reg_port(self, addr):
+ if addr not in reg_map:
+ raise ValueError("Unknown register {:x}.".format(addr))
+ if addr in self.ports:
+ raise ValueError("Register {:x} has already been allocated.".format(addr))
+ layout = [f[:2] for f in reg_map[addr]]
+ port = Record([("r", layout), ("w", layout), ("update", 1), ("capture", 1)])
+ for name, shape, mode, reset in reg_map[addr]:
+ getattr(port.r, name).reset = reset
+ getattr(port.w, name).reset = reset
+ self.ports[addr] = port
+ return port
+
+ def elaborate(self, platform):
+ m = Module()
+
+ def do_read(addr, port):
+ rec = Record(port.w.layout)
+ m.d.sync += self.dmi.r.data.eq(rec)
+ for name, shape, mode, reset in reg_map[addr]:
+ dst = getattr(rec, name)
+ src = getattr(port.w, name)
+ if mode in {RegMode.R, RegMode.RW, RegMode.RW1C}:
+ m.d.comb += dst.eq(src)
+ else:
+ m.d.comb += dst.eq(Const(0))
+ m.d.sync += port.capture.eq(1)
+
+ def do_write(addr, port):
+ rec = Record(port.r.layout)
+ m.d.comb += rec.eq(self.dmi.w.data)
+ for name, shape, mode, reset in reg_map[addr]:
+ dst = getattr(port.r, name)
+ src = getattr(rec, name)
+ if mode in {RegMode.W, RegMode.RW}:
+ m.d.sync += dst.eq(src)
+ elif mode is RegMode.W1:
+ m.d.sync += dst.eq(getattr(port.w, name) | src)
+ elif mode is RegMode.RW1C:
+ m.d.sync += dst.eq(getattr(port.w, name) & ~src)
+
+ m.d.sync += port.update.eq(1)
+
+ with m.If(self.dmi.update):
+ with m.Switch(self.dmi.w.addr):
+ for addr, port in self.ports.items():
+ with m.Case(addr):
+ with m.If(self.dmi.w.op == DmiOp.READ):
+ do_read(addr, port)
+ with m.Elif(self.dmi.w.op == DmiOp.WRITE):
+ do_write(addr, port)
+ m.d.sync += self.dmi.r.op.eq(DmiResult.OK)
+ with m.Case():
+ # Invalid register.
+ m.d.sync += self.dmi.r.op.eq(DmiResult.FAIL)
+
+ for port in self.ports.values():
+ with m.If(port.update):
+ m.d.sync += port.update.eq(0)
+ with m.If(port.capture):
+ m.d.sync += port.capture.eq(0)
+
+ return m
--- /dev/null
+from nmigen import *
+from nmigen.hdl.rec import *
+
+
+from ...csr import *
+from ...isa import *
+from ...wishbone import wishbone_layout
+from .controller import *
+from .dmi import *
+from .jtag import *
+from .regfile import *
+from .wbmaster import *
+
+
+__all__ = ["DebugUnit"]
+
+
+jtag_regs = {
+ JTAGReg.IDCODE: [("value", 32)],
+ JTAGReg.DTMCS: dtmcs_layout,
+ JTAGReg.DMI: dmi_layout
+}
+
+
+class DebugUnit(Elaboratable, AutoCSR):
+ def __init__(self):
+ self.jtag = Record(jtag_layout)
+ self.dbus = Record(wishbone_layout)
+
+ self.trigger_haltreq = Signal()
+
+ self.x_ebreak = Signal()
+ self.x_pc = Signal(32)
+ self.x_stall = Signal()
+
+ self.m_branch_taken = Signal()
+ self.m_branch_target = Signal(32)
+ self.m_mret = Signal()
+ self.m_exception = Signal()
+ self.m_pc = Signal(32)
+ self.m_valid = Signal()
+ self.mepc_r_base = Signal(30)
+ self.mtvec_r_base = Signal(30)
+
+ self.dcsr_step = Signal()
+ self.dcsr_ebreakm = Signal()
+ self.dpc_value = Signal(32)
+
+ self.halt = Signal()
+ self.halted = Signal()
+ self.killall = Signal()
+ self.resumereq = Signal()
+ self.resumeack = Signal()
+
+ self.dbus_busy = Signal()
+
+ self.csrf_addr = Signal(12)
+ self.csrf_re = Signal()
+ self.csrf_dat_r = Signal(32)
+ self.csrf_we = Signal()
+ self.csrf_dat_w = Signal(32)
+
+ self.gprf_addr = Signal(5)
+ self.gprf_re = Signal()
+ self.gprf_dat_r = Signal(32)
+ self.gprf_we = Signal()
+ self.gprf_dat_w = Signal(32)
+
+ def elaborate(self, platform):
+ m = Module()
+
+ from jtagtap import JTAGTap
+ tap = m.submodules.tap = JTAGTap(jtag_regs)
+ regfile = m.submodules.regfile = DebugRegisterFile(tap.regs[JTAGReg.DMI])
+ controller = m.submodules.controller = DebugController(regfile)
+ wbmaster = m.submodules.wbmaster = DebugWishboneMaster(regfile)
+
+ m.d.comb += [
+ tap.port.connect(self.jtag),
+ tap.regs[JTAGReg.IDCODE].r.eq(0x10e31913), # Usurpate a Spike core for now.
+ tap.regs[JTAGReg.DTMCS].r.eq(0x61) # (abits=6, version=1) TODO
+ ]
+
+ m.d.comb += [
+ controller.trigger_haltreq.eq(self.trigger_haltreq),
+
+ controller.x_ebreak.eq(self.x_ebreak),
+ controller.x_pc.eq(self.x_pc),
+ controller.x_stall.eq(self.x_stall),
+
+ controller.m_branch_taken.eq(self.m_branch_taken),
+ controller.m_branch_target.eq(self.m_branch_target),
+ controller.m_pc.eq(self.m_pc),
+ controller.m_valid.eq(self.m_valid),
+
+ self.halt.eq(controller.halt),
+ controller.halted.eq(self.halted),
+ self.killall.eq(controller.killall),
+ self.resumereq.eq(controller.resumereq),
+ controller.resumeack.eq(self.resumeack),
+
+ self.dcsr_step.eq(controller.dcsr.r.step),
+ self.dcsr_ebreakm.eq(controller.dcsr.r.ebreakm),
+ self.dpc_value.eq(controller.dpc.r.value),
+
+ self.csrf_addr.eq(controller.csrf_addr),
+ self.csrf_re.eq(controller.csrf_re),
+ controller.csrf_dat_r.eq(self.csrf_dat_r),
+ self.csrf_we.eq(controller.csrf_we),
+ self.csrf_dat_w.eq(controller.csrf_dat_w),
+
+ self.gprf_addr.eq(controller.gprf_addr),
+ self.gprf_re.eq(controller.gprf_re),
+ controller.gprf_dat_r.eq(self.gprf_dat_r),
+ self.gprf_we.eq(controller.gprf_we),
+ self.gprf_dat_w.eq(controller.gprf_dat_w),
+ ]
+
+ m.d.comb += [
+ wbmaster.bus.connect(self.dbus),
+ self.dbus_busy.eq(wbmaster.dbus_busy)
+ ]
+
+ return m
--- /dev/null
+from functools import reduce
+from operator import or_
+
+from nmigen import *
+from nmigen.hdl.rec import *
+
+from ...wishbone import wishbone_layout
+from .dmi import *
+
+
+__all__ = ["BusError", "AccessSize", "DebugWishboneMaster"]
+
+
+class BusError:
+ NONE = 0
+ TIMEOUT = 1
+ BAD_ADDRESS = 2
+ MISALIGNED = 3
+ BAD_SIZE = 4
+ OTHER = 7
+
+
+class AccessSize:
+ BYTE = 0
+ HALF = 1
+ WORD = 2
+
+
+class DebugWishboneMaster(Elaboratable):
+ def __init__(self, debugrf):
+ self.bus = Record(wishbone_layout)
+
+ self.dbus_busy = Signal()
+
+ self.sbcs = debugrf.reg_port(DebugReg.SBCS)
+ self.sbaddress0 = debugrf.reg_port(DebugReg.SBADDRESS0)
+ self.sbdata0 = debugrf.reg_port(DebugReg.SBDATA0)
+
+ def elaborate(self, platform):
+ m = Module()
+
+ addr = self.sbaddress0.w.value
+ size = self.sbcs.r.sbaccess
+
+ width = Signal(6)
+ m.d.comb += width.eq((1<<size)*8)
+
+ sbbusyerror = self.sbcs.w.sbbusyerror
+ sberror = self.sbcs.w.sberror
+ m.d.comb += self.dbus_busy.eq(self.sbcs.w.sbbusy)
+
+ m.d.comb += [
+ self.sbcs.w.sbaccess8.eq(1),
+ self.sbcs.w.sbaccess16.eq(1),
+ self.sbcs.w.sbaccess32.eq(1),
+ self.sbcs.w.sbasize.eq(32),
+ self.sbcs.w.sbversion.eq(1)
+ ]
+
+ with m.If(self.sbcs.update):
+ m.d.sync += [
+ self.sbcs.w.sbbusyerror.eq(self.sbcs.r.sbbusyerror),
+ self.sbcs.w.sberror.eq(self.sbcs.r.sberror)
+ ]
+
+ we = Signal()
+ re = Signal()
+
+ with m.If(self.sbdata0.update):
+ with m.If(self.sbcs.w.sbbusy):
+ m.d.sync += self.sbcs.w.sbbusyerror.eq(1)
+ with m.Else():
+ m.d.sync += we.eq(~sberror.bool())
+
+ with m.If(self.sbdata0.capture):
+ with m.If(self.sbcs.w.sbbusy):
+ m.d.sync += self.sbcs.w.sbbusyerror.eq(1)
+ with m.Else():
+ m.d.sync += re.eq(self.sbcs.r.sbreadondata & ~sberror.bool())
+
+ with m.If(self.sbaddress0.update):
+ with m.If(self.sbcs.w.sbbusy):
+ m.d.sync += self.sbcs.w.sbbusyerror.eq(1)
+ with m.Else():
+ m.d.sync += [
+ re.eq(self.sbcs.r.sbreadonaddr & ~sberror.bool()),
+ self.sbaddress0.w.value.eq(self.sbaddress0.r.value)
+ ]
+
+ with m.FSM():
+ with m.State("IDLE"):
+ with m.If(we | re):
+ m.d.sync += we.eq(0), re.eq(0)
+ with m.If(size > AccessSize.WORD):
+ m.d.sync += sberror.eq(BusError.BAD_SIZE)
+ with m.Elif((addr & (1<<size)-1) != 0):
+ m.d.sync += sberror.eq(BusError.MISALIGNED)
+ with m.Else():
+ m.d.sync += [
+ self.bus.cyc.eq(1),
+ self.bus.stb.eq(1),
+ self.bus.adr.eq(addr[2:]),
+ self.bus.we.eq(we),
+ self.bus.sel.eq((1<<(1<<size))-1 << addr[:2]),
+ self.bus.dat_w.eq((self.sbdata0.r & (1<<width)-1) << addr[:2]*8)
+ ]
+ m.next = "BUSY"
+
+ with m.State("BUSY"):
+ m.d.comb += self.sbcs.w.sbbusy.eq(1)
+ with m.If(self.bus.ack | self.bus.err):
+ m.d.sync += [
+ self.bus.cyc.eq(0),
+ self.bus.stb.eq(0),
+ self.bus.we.eq(0),
+ ]
+ with m.If(self.bus.err):
+ m.d.sync += sberror.eq(BusError.OTHER)
+ with m.Else():
+ with m.If(~self.bus.we):
+ m.d.sync += self.sbdata0.w.eq((self.bus.dat_r >> addr[:2]*8) & (1<<width)-1)
+ with m.If(self.sbcs.r.sbautoincrement):
+ m.d.sync += addr.eq(addr + (1<<size))
+ m.next = "IDLE"
+
+ return m
--- /dev/null
+from functools import reduce
+from itertools import starmap
+from operator import or_
+
+from nmigen import *
+
+from ..isa import Opcode, Funct3, Funct7, Funct12
+
+
+__all__ = ["InstructionDecoder"]
+
+
+class Type:
+ R = 0
+ I = 1
+ S = 2
+ B = 3
+ U = 4
+ J = 5
+
+
+class InstructionDecoder(Elaboratable):
+ def __init__(self, with_muldiv):
+ self.with_muldiv = with_muldiv
+
+ self.instruction = Signal(32)
+
+ self.rd = Signal(5)
+ self.rd_we = Signal()
+ self.rs1 = Signal(5)
+ self.rs1_re = Signal()
+ self.rs2 = Signal(5)
+ self.rs2_re = Signal()
+ self.immediate = Signal((32, True))
+ self.bypass_x = Signal()
+ self.bypass_m = Signal()
+ self.load = Signal()
+ self.store = Signal()
+ self.fence_i = Signal()
+ self.adder = Signal()
+ self.adder_sub = Signal()
+ self.logic = Signal()
+ self.multiply = Signal()
+ self.divide = Signal()
+ self.shift = Signal()
+ self.direction = Signal()
+ self.sext = Signal()
+ self.lui = Signal()
+ self.auipc = Signal()
+ self.jump = Signal()
+ self.branch = Signal()
+ self.compare = Signal()
+ self.csr = Signal()
+ self.csr_we = Signal()
+ self.privileged = Signal()
+ self.ecall = Signal()
+ self.ebreak = Signal()
+ self.mret = Signal()
+ self.funct3 = Signal(3)
+ self.illegal = Signal()
+
+ def elaborate(self, platform):
+ m = Module()
+
+ opcode = Signal(5)
+ funct3 = Signal(3)
+ funct7 = Signal(7)
+ funct12 = Signal(12)
+
+ iimm12 = Signal((12, True))
+ simm12 = Signal((12, True))
+ bimm12 = Signal((13, True))
+ uimm20 = Signal(20)
+ jimm20 = Signal((21, True))
+
+ insn = self.instruction
+ fmt = Signal(range(Type.J + 1))
+
+ m.d.comb += [
+ opcode.eq(insn[2:7]),
+ funct3.eq(insn[12:15]),
+ funct7.eq(insn[25:32]),
+ funct12.eq(insn[20:32]),
+
+ iimm12.eq(insn[20:32]),
+ simm12.eq(Cat(insn[7:12], insn[25:32])),
+ bimm12.eq(Cat(0, insn[8:12], insn[25:31], insn[7], insn[31])),
+ uimm20.eq(insn[12:32]),
+ jimm20.eq(Cat(0, insn[21:31], insn[20], insn[12:20], insn[31])),
+ ]
+
+ with m.Switch(opcode):
+ with m.Case(Opcode.LUI):
+ m.d.comb += fmt.eq(Type.U)
+ with m.Case(Opcode.AUIPC):
+ m.d.comb += fmt.eq(Type.U)
+ with m.Case(Opcode.JAL):
+ m.d.comb += fmt.eq(Type.J)
+ with m.Case(Opcode.JALR):
+ m.d.comb += fmt.eq(Type.I)
+ with m.Case(Opcode.BRANCH):
+ m.d.comb += fmt.eq(Type.B)
+ with m.Case(Opcode.LOAD):
+ m.d.comb += fmt.eq(Type.I)
+ with m.Case(Opcode.STORE):
+ m.d.comb += fmt.eq(Type.S)
+ with m.Case(Opcode.OP_IMM_32):
+ m.d.comb += fmt.eq(Type.I)
+ with m.Case(Opcode.OP_32):
+ m.d.comb += fmt.eq(Type.R)
+ with m.Case(Opcode.MISC_MEM):
+ m.d.comb += fmt.eq(Type.I)
+ with m.Case(Opcode.SYSTEM):
+ m.d.comb += fmt.eq(Type.I)
+
+ with m.Switch(fmt):
+ with m.Case(Type.I):
+ m.d.comb += self.immediate.eq(iimm12)
+ with m.Case(Type.S):
+ m.d.comb += self.immediate.eq(simm12)
+ with m.Case(Type.B):
+ m.d.comb += self.immediate.eq(bimm12)
+ with m.Case(Type.U):
+ m.d.comb += self.immediate.eq(uimm20 << 12)
+ with m.Case(Type.J):
+ m.d.comb += self.immediate.eq(jimm20)
+
+ m.d.comb += [
+ self.rd.eq(insn[7:12]),
+ self.rs1.eq(insn[15:20]),
+ self.rs2.eq(insn[20:25]),
+
+ self.rd_we.eq(reduce(or_, (fmt == T for T in (Type.R, Type.I, Type.U, Type.J)))),
+ self.rs1_re.eq(reduce(or_, (fmt == T for T in (Type.R, Type.I, Type.S, Type.B)))),
+ self.rs2_re.eq(reduce(or_, (fmt == T for T in (Type.R, Type.S, Type.B)))),
+
+ self.funct3.eq(funct3)
+ ]
+
+ def matcher(encodings):
+ return reduce(or_, starmap(
+ lambda opc, f3=None, f7=None, f12=None:
+ (opcode == opc if opc is not None else 1) \
+ & (funct3 == f3 if f3 is not None else 1) \
+ & (funct7 == f7 if f7 is not None else 1) \
+ & (funct12 == f12 if f12 is not None else 1),
+ encodings))
+
+ m.d.comb += [
+ self.compare.eq(matcher([
+ (Opcode.OP_IMM_32, Funct3.SLT, None), # slti
+ (Opcode.OP_IMM_32, Funct3.SLTU, None), # sltiu
+ (Opcode.OP_32, Funct3.SLT, 0), # slt
+ (Opcode.OP_32, Funct3.SLTU, 0) # sltu
+ ])),
+ self.branch.eq(matcher([
+ (Opcode.BRANCH, Funct3.BEQ, None), # beq
+ (Opcode.BRANCH, Funct3.BNE, None), # bne
+ (Opcode.BRANCH, Funct3.BLT, None), # blt
+ (Opcode.BRANCH, Funct3.BGE, None), # bge
+ (Opcode.BRANCH, Funct3.BLTU, None), # bltu
+ (Opcode.BRANCH, Funct3.BGEU, None) # bgeu
+ ])),
+
+ self.adder.eq(matcher([
+ (Opcode.OP_IMM_32, Funct3.ADD, None), # addi
+ (Opcode.OP_32, Funct3.ADD, Funct7.ADD), # add
+ (Opcode.OP_32, Funct3.ADD, Funct7.SUB) # sub
+ ])),
+ self.adder_sub.eq(self.rs2_re & (funct7 == Funct7.SUB)),
+
+ self.logic.eq(matcher([
+ (Opcode.OP_IMM_32, Funct3.XOR, None), # xori
+ (Opcode.OP_IMM_32, Funct3.OR, None), # ori
+ (Opcode.OP_IMM_32, Funct3.AND, None), # andi
+ (Opcode.OP_32, Funct3.XOR, 0), # xor
+ (Opcode.OP_32, Funct3.OR, 0), # or
+ (Opcode.OP_32, Funct3.AND, 0) # and
+ ])),
+ ]
+
+ if self.with_muldiv:
+ m.d.comb += [
+ self.multiply.eq(matcher([
+ (Opcode.OP_32, Funct3.MUL, Funct7.MULDIV), # mul
+ (Opcode.OP_32, Funct3.MULH, Funct7.MULDIV), # mulh
+ (Opcode.OP_32, Funct3.MULHSU, Funct7.MULDIV), # mulhsu
+ (Opcode.OP_32, Funct3.MULHU, Funct7.MULDIV), # mulhu
+ ])),
+
+ self.divide.eq(matcher([
+ (Opcode.OP_32, Funct3.DIV, Funct7.MULDIV), # div
+ (Opcode.OP_32, Funct3.DIVU, Funct7.MULDIV), # divu
+ (Opcode.OP_32, Funct3.REM, Funct7.MULDIV), # rem
+ (Opcode.OP_32, Funct3.REMU, Funct7.MULDIV) # remu
+ ])),
+ ]
+
+ m.d.comb += [
+ self.shift.eq(matcher([
+ (Opcode.OP_IMM_32, Funct3.SLL, 0), # slli
+ (Opcode.OP_IMM_32, Funct3.SR, Funct7.SRL), # srli
+ (Opcode.OP_IMM_32, Funct3.SR, Funct7.SRA), # srai
+ (Opcode.OP_32, Funct3.SLL, 0), # sll
+ (Opcode.OP_32, Funct3.SR, Funct7.SRL), # srl
+ (Opcode.OP_32, Funct3.SR, Funct7.SRA) # sra
+ ])),
+ self.direction.eq(funct3 == Funct3.SR),
+ self.sext.eq(funct7 == Funct7.SRA),
+
+ self.lui.eq(opcode == Opcode.LUI),
+ self.auipc.eq(opcode == Opcode.AUIPC),
+
+ self.jump.eq(matcher([
+ (Opcode.JAL, None), # jal
+ (Opcode.JALR, 0) # jalr
+ ])),
+
+ self.load.eq(matcher([
+ (Opcode.LOAD, Funct3.B), # lb
+ (Opcode.LOAD, Funct3.BU), # lbu
+ (Opcode.LOAD, Funct3.H), # lh
+ (Opcode.LOAD, Funct3.HU), # lhu
+ (Opcode.LOAD, Funct3.W) # lw
+ ])),
+ self.store.eq(matcher([
+ (Opcode.STORE, Funct3.B), # sb
+ (Opcode.STORE, Funct3.H), # sh
+ (Opcode.STORE, Funct3.W) # sw
+ ])),
+
+ self.fence_i.eq(matcher([
+ (Opcode.MISC_MEM, Funct3.FENCEI) # fence.i
+ ])),
+
+ self.csr.eq(matcher([
+ (Opcode.SYSTEM, Funct3.CSRRW), # csrrw
+ (Opcode.SYSTEM, Funct3.CSRRS), # csrrs
+ (Opcode.SYSTEM, Funct3.CSRRC), # csrrc
+ (Opcode.SYSTEM, Funct3.CSRRWI), # csrrwi
+ (Opcode.SYSTEM, Funct3.CSRRSI), # csrrsi
+ (Opcode.SYSTEM, Funct3.CSRRCI) # csrrci
+ ])),
+ self.csr_we.eq(~funct3[1] | (self.rs1 != 0)),
+
+ self.privileged.eq((opcode == Opcode.SYSTEM) & (funct3 == Funct3.PRIV)),
+ self.ecall.eq(self.privileged & (funct12 == Funct12.ECALL)),
+ self.ebreak.eq(self.privileged & (funct12 == Funct12.EBREAK)),
+ self.mret.eq(self.privileged & (funct12 == Funct12.MRET)),
+
+ self.bypass_x.eq(self.adder | self.logic | self.lui | self.auipc | self.csr),
+ self.bypass_m.eq(self.compare | self.divide | self.shift),
+
+ self.illegal.eq((self.instruction[:2] != 0b11) | ~reduce(or_, (
+ self.compare, self.branch, self.adder, self.logic, self.multiply, self.divide, self.shift,
+ self.lui, self.auipc, self.jump, self.load, self.store,
+ self.csr, self.ecall, self.ebreak, self.mret
+ )))
+ ]
+
+ return m
--- /dev/null
+from nmigen import *
+
+from ..isa import Funct3
+
+
+__all__ = ["DividerInterface", "Divider", "DummyDivider"]
+
+
+class DividerInterface:
+ def __init__(self):
+ self.x_op = Signal(3)
+ self.x_src1 = Signal(32)
+ self.x_src2 = Signal(32)
+ self.x_valid = Signal()
+ self.x_stall = Signal()
+
+ self.m_result = Signal(32)
+ self.m_busy = Signal()
+
+
+class Divider(DividerInterface, Elaboratable):
+ def elaborate(self, platform):
+ m = Module()
+
+ x_enable = Signal()
+ x_modulus = Signal()
+ x_signed = Signal()
+
+ with m.Switch(self.x_op):
+ with m.Case(Funct3.DIV):
+ m.d.comb += x_enable.eq(1), x_signed.eq(1)
+ with m.Case(Funct3.DIVU):
+ m.d.comb += x_enable.eq(1)
+ with m.Case(Funct3.REM):
+ m.d.comb += x_enable.eq(1), x_modulus.eq(1), x_signed.eq(1)
+ with m.Case(Funct3.REMU):
+ m.d.comb += x_enable.eq(1), x_modulus.eq(1)
+
+ x_negative = Signal()
+ with m.If(x_modulus):
+ m.d.comb += x_negative.eq(x_signed & self.x_src1[31])
+ with m.Else():
+ m.d.comb += x_negative.eq(x_signed & (self.x_src1[31] ^ self.x_src2[31]))
+
+ x_dividend = Signal(32)
+ x_divisor = Signal(32)
+ m.d.comb += [
+ x_dividend.eq(Mux(x_signed & self.x_src1[31], -self.x_src1, self.x_src1)),
+ x_divisor.eq(Mux(x_signed & self.x_src2[31], -self.x_src2, self.x_src2))
+ ]
+
+ m_modulus = Signal()
+ m_negative = Signal()
+
+ timer = Signal(range(33), reset=32)
+ quotient = Signal(32)
+ divisor = Signal(32)
+ remainder = Signal(32)
+ difference = Signal(33)
+
+ with m.FSM() as fsm:
+ with m.State("IDLE"):
+ with m.If(x_enable & self.x_valid & ~self.x_stall):
+ m.d.sync += [
+ m_modulus.eq(x_modulus),
+ m_negative.eq(x_negative)
+ ]
+ with m.If(x_divisor == 0):
+ # Division by zero
+ m.d.sync += [
+ quotient.eq(-1),
+ remainder.eq(self.x_src1)
+ ]
+ with m.Elif(x_signed & (self.x_src1 == -2**31) & (self.x_src2 == -1)):
+ # Signed overflow
+ m.d.sync += [
+ quotient.eq(self.x_src1),
+ remainder.eq(0)
+ ]
+ with m.Elif(x_dividend == 0):
+ m.d.sync += [
+ quotient.eq(0),
+ remainder.eq(0)
+ ]
+ with m.Else():
+ m.d.sync += [
+ quotient.eq(x_dividend),
+ remainder.eq(0),
+ divisor.eq(x_divisor),
+ timer.eq(timer.reset)
+ ]
+ m.next = "DIVIDE"
+
+ with m.State("DIVIDE"):
+ m.d.comb += self.m_busy.eq(1)
+ with m.If(timer != 0):
+ m.d.sync += timer.eq(timer - 1)
+ m.d.comb += difference.eq(Cat(quotient[31], remainder) - divisor)
+ with m.If(difference[32]):
+ m.d.sync += [
+ remainder.eq(Cat(quotient[31], remainder)),
+ quotient.eq(Cat(0, quotient))
+ ]
+ with m.Else():
+ m.d.sync += [
+ remainder.eq(difference),
+ quotient.eq(Cat(1, quotient))
+ ]
+ with m.Else():
+ m.d.sync += [
+ quotient.eq(Mux(m_negative, -quotient, quotient)),
+ remainder.eq(Mux(m_negative, -remainder, remainder))
+ ]
+ m.next = "IDLE"
+
+ m.d.comb += self.m_result.eq(Mux(m_modulus, remainder, quotient))
+
+ return m
+
+
+class DummyDivider(DividerInterface, Elaboratable):
+ def elaborate(self, platform):
+ m = Module()
+
+ x_result = Signal.like(self.m_result)
+
+ with m.Switch(self.x_op):
+ # As per the RVFI specification (ยง "Alternative Arithmetic Operations").
+ # https://github.com/SymbioticEDA/riscv-formal/blob/master/docs/rvfi.md
+ with m.Case(Funct3.DIV):
+ m.d.comb += x_result.eq((self.x_src1 - self.x_src2) ^ C(0x7f8529ec))
+ with m.Case(Funct3.DIVU):
+ m.d.comb += x_result.eq((self.x_src1 - self.x_src2) ^ C(0x10e8fd70))
+ with m.Case(Funct3.REM):
+ m.d.comb += x_result.eq((self.x_src1 - self.x_src2) ^ C(0x8da68fa5))
+ with m.Case(Funct3.REMU):
+ m.d.comb += x_result.eq((self.x_src1 - self.x_src2) ^ C(0x3138d0e1))
+
+ with m.If(~self.x_stall):
+ m.d.sync += self.m_result.eq(x_result)
+
+ m.d.comb += self.m_busy.eq(C(0))
+
+ return m
--- /dev/null
+from nmigen import *
+from nmigen.lib.coding import PriorityEncoder
+
+from ..csr import *
+from ..isa import *
+
+
+__all__ = ["ExceptionUnit"]
+
+
+class ExceptionUnit(Elaboratable, AutoCSR):
+ def __init__(self):
+ self.mstatus = CSR(0x300, mstatus_layout, name="mstatus")
+ self.misa = CSR(0x301, misa_layout, name="misa") # FIXME move elsewhere
+ self.mie = CSR(0x304, mie_layout, name="mie")
+ self.mtvec = CSR(0x305, mtvec_layout, name="mtvec")
+ self.mscratch = CSR(0x340, flat_layout, name="mscratch") # FIXME move elsewhere
+ self.mepc = CSR(0x341, mepc_layout, name="mepc")
+ self.mcause = CSR(0x342, mcause_layout, name="mcause")
+ self.mtval = CSR(0x343, flat_layout, name="mtval")
+ self.mip = CSR(0x344, mip_layout, name="mip")
+ self.irq_mask = CSR(0x330, flat_layout, name="irq_mask")
+ self.irq_pending = CSR(0x360, flat_layout, name="irq_pending")
+
+ self.external_interrupt = Signal(32)
+ self.timer_interrupt = Signal()
+ self.software_interrupt = Signal()
+
+ self.m_fetch_misaligned = Signal()
+ self.m_fetch_error = Signal()
+ self.m_fetch_badaddr = Signal(30)
+ self.m_load_misaligned = Signal()
+ self.m_load_error = Signal()
+ self.m_store_misaligned = Signal()
+ self.m_store_error = Signal()
+ self.m_loadstore_badaddr = Signal(30)
+ self.m_branch_target = Signal(32)
+ self.m_illegal = Signal()
+ self.m_ebreak = Signal()
+ self.m_ecall = Signal()
+ self.m_pc = Signal(32)
+ self.m_instruction = Signal(32)
+ self.m_result = Signal(32)
+ self.m_mret = Signal()
+ self.m_stall = Signal()
+ self.m_valid = Signal()
+
+ self.m_raise = Signal()
+
+ def elaborate(self, platform):
+ m = Module()
+
+ for csr in self.iter_csrs():
+ with m.If(csr.we):
+ m.d.sync += csr.r.eq(csr.w)
+
+ trap_pe = m.submodules.trap_pe = PriorityEncoder(16)
+ m.d.comb += [
+ trap_pe.i[Cause.FETCH_MISALIGNED ].eq(self.m_fetch_misaligned),
+ trap_pe.i[Cause.FETCH_ACCESS_FAULT ].eq(self.m_fetch_error),
+ trap_pe.i[Cause.ILLEGAL_INSTRUCTION].eq(self.m_illegal),
+ trap_pe.i[Cause.BREAKPOINT ].eq(self.m_ebreak),
+ trap_pe.i[Cause.LOAD_MISALIGNED ].eq(self.m_load_misaligned),
+ trap_pe.i[Cause.LOAD_ACCESS_FAULT ].eq(self.m_load_error),
+ trap_pe.i[Cause.STORE_MISALIGNED ].eq(self.m_store_misaligned),
+ trap_pe.i[Cause.STORE_ACCESS_FAULT ].eq(self.m_store_error),
+ trap_pe.i[Cause.ECALL_FROM_M ].eq(self.m_ecall)
+ ]
+
+ m.d.sync += [
+ self.irq_pending.r.eq(self.external_interrupt & self.irq_mask.r),
+ self.mip.r.msip.eq(self.software_interrupt),
+ self.mip.r.mtip.eq(self.timer_interrupt),
+ self.mip.r.meip.eq(self.irq_pending.r.bool())
+ ]
+
+ interrupt_pe = m.submodules.interrupt_pe = PriorityEncoder(16)
+ m.d.comb += [
+ interrupt_pe.i[Cause.M_SOFTWARE_INTERRUPT].eq(self.mip.r.msip & self.mie.r.msie),
+ interrupt_pe.i[Cause.M_TIMER_INTERRUPT ].eq(self.mip.r.mtip & self.mie.r.mtie),
+ interrupt_pe.i[Cause.M_EXTERNAL_INTERRUPT].eq(self.mip.r.meip & self.mie.r.meie)
+ ]
+
+ m.d.comb += self.m_raise.eq(~trap_pe.n | ~interrupt_pe.n & self.mstatus.r.mie)
+
+ with m.If(self.m_valid & ~self.m_stall):
+ with m.If(self.m_raise):
+ m.d.sync += [
+ self.mstatus.r.mpie.eq(self.mstatus.r.mie),
+ self.mstatus.r.mie.eq(0),
+ self.mepc.r.base.eq(self.m_pc[2:])
+ ]
+ with m.If(~trap_pe.n):
+ m.d.sync += [
+ self.mcause.r.ecode.eq(trap_pe.o),
+ self.mcause.r.interrupt.eq(0)
+ ]
+ with m.Switch(trap_pe.o):
+ with m.Case(Cause.FETCH_MISALIGNED):
+ m.d.sync += self.mtval.r.eq(self.m_branch_target)
+ with m.Case(Cause.FETCH_ACCESS_FAULT):
+ m.d.sync += self.mtval.r.eq(self.m_fetch_badaddr << 2)
+ with m.Case(Cause.ILLEGAL_INSTRUCTION):
+ m.d.sync += self.mtval.r.eq(self.m_instruction)
+ with m.Case(Cause.BREAKPOINT):
+ m.d.sync += self.mtval.r.eq(self.m_pc)
+ with m.Case(Cause.LOAD_MISALIGNED, Cause.STORE_MISALIGNED):
+ m.d.sync += self.mtval.r.eq(self.m_result)
+ with m.Case(Cause.LOAD_ACCESS_FAULT, Cause.STORE_ACCESS_FAULT):
+ m.d.sync += self.mtval.r.eq(self.m_loadstore_badaddr << 2)
+ with m.Case():
+ m.d.sync += self.mtval.r.eq(0)
+ with m.Else():
+ m.d.sync += [
+ self.mcause.r.ecode.eq(interrupt_pe.o),
+ self.mcause.r.interrupt.eq(1)
+ ]
+ with m.Elif(self.m_mret):
+ m.d.sync += self.mstatus.r.mie.eq(self.mstatus.r.mpie)
+
+ return m
--- /dev/null
+from nmigen import *
+from nmigen.utils import log2_int
+
+from ..cache import *
+from ..wishbone import *
+
+
+__all__ = ["PCSelector", "FetchUnitInterface", "BareFetchUnit", "CachedFetchUnit"]
+
+
+class PCSelector(Elaboratable):
+ def __init__(self):
+ self.f_pc = Signal(32)
+ self.d_pc = Signal(32)
+ self.d_branch_predict_taken = Signal()
+ self.d_branch_target = Signal(32)
+ self.d_valid = Signal()
+ self.x_pc = Signal(32)
+ self.x_fence_i = Signal()
+ self.x_valid = Signal()
+ self.m_branch_predict_taken = Signal()
+ self.m_branch_taken = Signal()
+ self.m_branch_target = Signal(32)
+ self.m_exception = Signal()
+ self.m_mret = Signal()
+ self.m_valid = Signal()
+ self.mtvec_r_base = Signal(30)
+ self.mepc_r_base = Signal(30)
+
+ self.a_pc = Signal(32)
+
+ def elaborate(self, platform):
+ m = Module()
+
+ with m.If(self.m_exception & self.m_valid):
+ m.d.comb += self.a_pc.eq(self.mtvec_r_base << 2)
+ with m.Elif(self.m_mret & self.m_valid):
+ m.d.comb += self.a_pc.eq(self.mepc_r_base << 2)
+ with m.Elif(self.m_branch_predict_taken & ~self.m_branch_taken & self.m_valid):
+ m.d.comb += self.a_pc.eq(self.x_pc)
+ with m.Elif(~self.m_branch_predict_taken & self.m_branch_taken & self.m_valid):
+ m.d.comb += self.a_pc.eq(self.m_branch_target),
+ with m.Elif(self.x_fence_i & self.x_valid):
+ m.d.comb += self.a_pc.eq(self.d_pc)
+ with m.Elif(self.d_branch_predict_taken & self.d_valid):
+ m.d.comb += self.a_pc.eq(self.d_branch_target),
+ with m.Else():
+ m.d.comb += self.a_pc.eq(self.f_pc + 4)
+
+ return m
+
+
+class FetchUnitInterface:
+ def __init__(self):
+ self.ibus = Record(wishbone_layout)
+
+ self.a_pc = Signal(32)
+ self.a_stall = Signal()
+ self.a_valid = Signal()
+ self.f_stall = Signal()
+ self.f_valid = Signal()
+
+ self.a_busy = Signal()
+ self.f_busy = Signal()
+ self.f_instruction = Signal(32)
+ self.f_fetch_error = Signal()
+ self.f_badaddr = Signal(30)
+
+
+class BareFetchUnit(FetchUnitInterface, Elaboratable):
+ def elaborate(self, platform):
+ m = Module()
+
+ ibus_rdata = Signal.like(self.ibus.dat_r)
+ with m.If(self.ibus.cyc):
+ with m.If(self.ibus.ack | self.ibus.err | ~self.f_valid):
+ m.d.sync += [
+ self.ibus.cyc.eq(0),
+ self.ibus.stb.eq(0),
+ ibus_rdata.eq(self.ibus.dat_r)
+ ]
+ with m.Elif(self.a_valid & ~self.a_stall):
+ m.d.sync += [
+ self.ibus.adr.eq(self.a_pc[2:]),
+ self.ibus.cyc.eq(1),
+ self.ibus.stb.eq(1)
+ ]
+
+ 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)
+
+ m.d.comb += self.a_busy.eq(self.ibus.cyc)
+
+ with m.If(self.f_fetch_error):
+ m.d.comb += [
+ self.f_busy.eq(0),
+ self.f_instruction.eq(0x00000013) # nop (addi x0, x0, 0)
+ ]
+ with m.Else():
+ m.d.comb += [
+ self.f_busy.eq(self.ibus.cyc),
+ self.f_instruction.eq(ibus_rdata)
+ ]
+
+ return m
+
+
+class CachedFetchUnit(FetchUnitInterface, Elaboratable):
+ def __init__(self, *icache_args):
+ super().__init__()
+
+ self.icache_args = icache_args
+
+ self.a_flush = Signal()
+ self.f_pc = Signal(32)
+
+ def elaborate(self, platform):
+ m = Module()
+
+ icache = m.submodules.icache = L1Cache(*self.icache_args)
+
+ a_icache_select = Signal()
+ f_icache_select = Signal()
+
+ m.d.comb += a_icache_select.eq((self.a_pc >= icache.base) & (self.a_pc < icache.limit))
+ with m.If(~self.a_stall):
+ m.d.sync += f_icache_select.eq(a_icache_select)
+
+ m.d.comb += [
+ icache.s1_addr.eq(self.a_pc[2:]),
+ icache.s1_flush.eq(self.a_flush),
+ icache.s1_stall.eq(self.a_stall),
+ icache.s1_valid.eq(self.a_valid & a_icache_select),
+ icache.s2_addr.eq(self.f_pc[2:]),
+ icache.s2_re.eq(Const(1)),
+ icache.s2_evict.eq(Const(0)),
+ icache.s2_valid.eq(self.f_valid & f_icache_select)
+ ]
+
+ 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(icache.bus_re),
+ icache_port.stb.eq(icache.bus_re),
+ icache_port.adr.eq(icache.bus_addr),
+ icache_port.cti.eq(Mux(icache.bus_last, Cycle.END, Cycle.INCREMENT)),
+ icache_port.bte.eq(Const(log2_int(icache.nwords) - 1)),
+ icache.bus_valid.eq(icache_port.ack),
+ icache.bus_error.eq(icache_port.err),
+ 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:])
+ ]
+
+ 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(a_icache_select):
+ m.d.comb += self.a_busy.eq(0)
+ with m.Else():
+ m.d.comb += self.a_busy.eq(bare_port.cyc)
+
+ with m.If(self.f_fetch_error):
+ m.d.comb += [
+ self.f_busy.eq(0),
+ self.f_instruction.eq(0x00000013) # nop (addi x0, x0, 0)
+ ]
+ with m.Elif(f_icache_select):
+ m.d.comb += [
+ self.f_busy.eq(icache.s2_re & icache.s2_miss),
+ self.f_instruction.eq(icache.s2_rdata)
+ ]
+ with m.Else():
+ m.d.comb += [
+ self.f_busy.eq(bare_port.cyc),
+ self.f_instruction.eq(bare_rdata)
+ ]
+
+ return m
--- /dev/null
+from nmigen import *
+from nmigen.utils import log2_int
+from nmigen.lib.fifo import SyncFIFO
+
+from ..cache import *
+from ..isa import Funct3
+from ..wishbone import *
+
+
+__all__ = ["DataSelector", "LoadStoreUnitInterface", "BareLoadStoreUnit", "CachedLoadStoreUnit"]
+
+
+class DataSelector(Elaboratable):
+ def __init__(self):
+ self.x_offset = Signal(2)
+ self.x_funct3 = Signal(3)
+ self.x_store_operand = Signal(32)
+ self.w_offset = Signal(2)
+ self.w_funct3 = Signal(3)
+ self.w_load_data = Signal(32)
+
+ self.x_misaligned = Signal()
+ self.x_mask = Signal(4)
+ self.x_store_data = Signal(32)
+ self.w_load_result = Signal((32, True))
+
+ def elaborate(self, platform):
+ m = Module()
+
+ with m.Switch(self.x_funct3):
+ with m.Case(Funct3.H, Funct3.HU):
+ m.d.comb += self.x_misaligned.eq(self.x_offset[0])
+ with m.Case(Funct3.W):
+ m.d.comb += self.x_misaligned.eq(self.x_offset.bool())
+
+ with m.Switch(self.x_funct3):
+ with m.Case(Funct3.B, Funct3.BU):
+ m.d.comb += self.x_mask.eq(0b1 << self.x_offset)
+ with m.Case(Funct3.H, Funct3.HU):
+ m.d.comb += self.x_mask.eq(0b11 << self.x_offset)
+ with m.Case(Funct3.W):
+ m.d.comb += self.x_mask.eq(0b1111)
+
+ with m.Switch(self.x_funct3):
+ with m.Case(Funct3.B):
+ m.d.comb += self.x_store_data.eq(self.x_store_operand[:8] << self.x_offset*8)
+ with m.Case(Funct3.H):
+ m.d.comb += self.x_store_data.eq(self.x_store_operand[:16] << self.x_offset[1]*16)
+ with m.Case(Funct3.W):
+ m.d.comb += self.x_store_data.eq(self.x_store_operand)
+
+ w_byte = Signal((8, True))
+ w_half = Signal((16, True))
+
+ m.d.comb += [
+ w_byte.eq(self.w_load_data.word_select(self.w_offset, 8)),
+ w_half.eq(self.w_load_data.word_select(self.w_offset[1], 16))
+ ]
+
+ with m.Switch(self.w_funct3):
+ with m.Case(Funct3.B):
+ m.d.comb += self.w_load_result.eq(w_byte)
+ with m.Case(Funct3.BU):
+ m.d.comb += self.w_load_result.eq(Cat(w_byte, 0))
+ with m.Case(Funct3.H):
+ m.d.comb += self.w_load_result.eq(w_half)
+ with m.Case(Funct3.HU):
+ m.d.comb += self.w_load_result.eq(Cat(w_half, 0))
+ with m.Case(Funct3.W):
+ m.d.comb += self.w_load_result.eq(self.w_load_data)
+
+ return m
+
+
+class LoadStoreUnitInterface:
+ def __init__(self):
+ self.dbus = Record(wishbone_layout)
+
+ self.x_addr = Signal(32)
+ self.x_mask = Signal(4)
+ self.x_load = Signal()
+ self.x_store = Signal()
+ self.x_store_data = Signal(32)
+ self.x_stall = Signal()
+ self.x_valid = Signal()
+ self.m_stall = Signal()
+ self.m_valid = Signal()
+
+ self.x_busy = Signal()
+ self.m_busy = Signal()
+ self.m_load_data = Signal(32)
+ self.m_load_error = Signal()
+ self.m_store_error = Signal()
+ self.m_badaddr = Signal(30)
+
+
+class BareLoadStoreUnit(LoadStoreUnitInterface, Elaboratable):
+ def elaborate(self, platform):
+ m = Module()
+
+ with m.If(self.dbus.cyc):
+ with m.If(self.dbus.ack | self.dbus.err | ~self.m_valid):
+ m.d.sync += [
+ self.dbus.cyc.eq(0),
+ self.dbus.stb.eq(0),
+ self.m_load_data.eq(self.dbus.dat_r)
+ ]
+ with m.Elif((self.x_load | self.x_store) & self.x_valid & ~self.x_stall):
+ m.d.sync += [
+ self.dbus.cyc.eq(1),
+ self.dbus.stb.eq(1),
+ self.dbus.adr.eq(self.x_addr[2:]),
+ self.dbus.sel.eq(self.x_mask),
+ self.dbus.we.eq(self.x_store),
+ self.dbus.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)
+ ]
+
+ m.d.comb += self.x_busy.eq(self.dbus.cyc)
+
+ with m.If(self.m_load_error | self.m_store_error):
+ m.d.comb += self.m_busy.eq(0)
+ with m.Else():
+ m.d.comb += self.m_busy.eq(self.dbus.cyc)
+
+ return m
+
+
+class CachedLoadStoreUnit(LoadStoreUnitInterface, Elaboratable):
+ def __init__(self, *dcache_args):
+ super().__init__()
+
+ self.dcache_args = dcache_args
+
+ self.x_fence_i = Signal()
+ self.x_flush = Signal()
+ self.m_addr = Signal(32)
+ self.m_load = Signal()
+ self.m_store = Signal()
+
+ def elaborate(self, platform):
+ m = Module()
+
+ dcache = m.submodules.dcache = L1Cache(*self.dcache_args)
+
+ x_dcache_select = Signal()
+ m_dcache_select = Signal()
+
+ m.d.comb += x_dcache_select.eq((self.x_addr >= dcache.base) & (self.x_addr < dcache.limit))
+ with m.If(~self.x_stall):
+ m.d.sync += m_dcache_select.eq(x_dcache_select)
+
+ m.d.comb += [
+ dcache.s1_addr.eq(self.x_addr[2:]),
+ dcache.s1_flush.eq(self.x_flush),
+ dcache.s1_stall.eq(self.x_stall),
+ dcache.s1_valid.eq(self.x_valid & x_dcache_select),
+ dcache.s2_addr.eq(self.m_addr[2:]),
+ dcache.s2_re.eq(self.m_load),
+ dcache.s2_evict.eq(self.m_store),
+ dcache.s2_valid.eq(self.m_valid & m_dcache_select)
+ ]
+
+ wrbuf_w_data = Record([("addr", 30), ("mask", 4), ("data", 32)])
+ wrbuf_r_data = Record.like(wrbuf_w_data)
+ wrbuf = m.submodules.wrbuf = SyncFIFO(width=len(wrbuf_w_data), depth=dcache.nwords)
+ m.d.comb += [
+ wrbuf.w_data.eq(wrbuf_w_data),
+ wrbuf_w_data.addr.eq(self.x_addr[2:]),
+ wrbuf_w_data.mask.eq(self.x_mask),
+ wrbuf_w_data.data.eq(self.x_store_data),
+ wrbuf.w_en.eq(self.x_store & self.x_valid & x_dcache_select & ~self.x_stall),
+ wrbuf_r_data.eq(wrbuf.r_data),
+ ]
+
+ dbus_arbiter = m.submodules.dbus_arbiter = WishboneArbiter()
+ m.d.comb += dbus_arbiter.bus.connect(self.dbus)
+
+ wrbuf_port = dbus_arbiter.port(priority=0)
+ with m.If(wrbuf_port.cyc):
+ with m.If(wrbuf_port.ack | wrbuf_port.err):
+ m.d.sync += [
+ wrbuf_port.cyc.eq(0),
+ wrbuf_port.stb.eq(0)
+ ]
+ m.d.comb += wrbuf.r_en.eq(1)
+ with m.Elif(wrbuf.r_rdy):
+ m.d.sync += [
+ wrbuf_port.cyc.eq(1),
+ wrbuf_port.stb.eq(1),
+ wrbuf_port.adr.eq(wrbuf_r_data.addr),
+ wrbuf_port.sel.eq(wrbuf_r_data.mask),
+ wrbuf_port.dat_w.eq(wrbuf_r_data.data)
+ ]
+ m.d.comb += wrbuf_port.we.eq(Const(1))
+
+ dcache_port = dbus_arbiter.port(priority=1)
+ m.d.comb += [
+ dcache_port.cyc.eq(dcache.bus_re),
+ dcache_port.stb.eq(dcache.bus_re),
+ dcache_port.adr.eq(dcache.bus_addr),
+ dcache_port.cti.eq(Mux(dcache.bus_last, Cycle.END, Cycle.INCREMENT)),
+ dcache_port.bte.eq(Const(log2_int(dcache.nwords) - 1)),
+ dcache.bus_valid.eq(dcache_port.ack),
+ dcache.bus_error.eq(dcache_port.err),
+ 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(wrbuf.r_rdy)
+ with m.Elif(x_dcache_select):
+ m.d.comb += self.x_busy.eq(self.x_store & ~wrbuf.w_rdy)
+ with m.Else():
+ m.d.comb += self.x_busy.eq(bare_port.cyc)
+
+ with m.If(self.m_load_error | self.m_store_error):
+ m.d.comb += [
+ self.m_busy.eq(0),
+ self.m_load_data.eq(0)
+ ]
+ with m.Elif(m_dcache_select):
+ m.d.comb += [
+ self.m_busy.eq(dcache.s2_re & dcache.s2_miss),
+ self.m_load_data.eq(dcache.s2_rdata)
+ ]
+ with m.Else():
+ m.d.comb += [
+ self.m_busy.eq(bare_port.cyc),
+ self.m_load_data.eq(bare_rdata)
+ ]
+
+ return m
--- /dev/null
+from nmigen import *
+
+from ..isa import Funct3
+
+
+__all__ = ["LogicUnit"]
+
+
+class LogicUnit(Elaboratable):
+ def __init__(self):
+ self.op = Signal(3)
+ self.src1 = Signal(32)
+ self.src2 = Signal(32)
+
+ self.result = Signal(32)
+
+ def elaborate(self, platform):
+ m = Module()
+
+ with m.Switch(self.op):
+ with m.Case(Funct3.XOR):
+ m.d.comb += self.result.eq(self.src1 ^ self.src2)
+ with m.Case(Funct3.OR):
+ m.d.comb += self.result.eq(self.src1 | self.src2)
+ with m.Case(Funct3.AND):
+ m.d.comb += self.result.eq(self.src1 & self.src2)
+
+ return m
--- /dev/null
+from nmigen import *
+
+from ..isa import Funct3
+
+
+__all__ = ["MultiplierInterface", "Multiplier", "DummyMultiplier"]
+
+
+class MultiplierInterface:
+ def __init__(self):
+ self.x_op = Signal(3)
+ self.x_src1 = Signal(32)
+ self.x_src2 = Signal(32)
+ self.x_stall = Signal()
+ self.m_stall = Signal()
+
+ self.w_result = Signal(32)
+
+
+class Multiplier(MultiplierInterface, Elaboratable):
+ def elaborate(self, platform):
+ m = Module()
+
+ x_low = Signal()
+ x_src1_signed = Signal()
+ x_src2_signed = Signal()
+
+ m.d.comb += [
+ x_low.eq(self.x_op == Funct3.MUL),
+ x_src1_signed.eq((self.x_op == Funct3.MULH) | (self.x_op == Funct3.MULHSU)),
+ x_src2_signed.eq(self.x_op == Funct3.MULH)
+ ]
+
+ x_src1 = Signal(signed(33))
+ x_src2 = Signal(signed(33))
+
+ m.d.comb += [
+ x_src1.eq(Cat(self.x_src1, x_src1_signed & self.x_src1[31])),
+ x_src2.eq(Cat(self.x_src2, x_src2_signed & self.x_src2[31]))
+ ]
+
+ m_low = Signal()
+ m_prod = Signal(signed(66))
+
+ with m.If(~self.x_stall):
+ m.d.sync += [
+ m_low.eq(x_low),
+ m_prod.eq(x_src1 * x_src2)
+ ]
+
+ with m.If(~self.m_stall):
+ m.d.sync += self.w_result.eq(Mux(m_low, m_prod[:32], m_prod[32:]))
+
+ return m
+
+
+class DummyMultiplier(MultiplierInterface, Elaboratable):
+ def elaborate(self, platform):
+ m = Module()
+
+ x_result = Signal.like(self.w_result)
+ m_result = Signal.like(self.w_result)
+
+ with m.Switch(self.x_op):
+ # As per the RVFI specification (ยง "Alternative Arithmetic Operations").
+ # https://github.com/SymbioticEDA/riscv-formal/blob/master/docs/rvfi.md
+ with m.Case(Funct3.MUL):
+ m.d.comb += x_result.eq((self.x_src1 + self.x_src2) ^ C(0x5876063e))
+ with m.Case(Funct3.MULH):
+ m.d.comb += x_result.eq((self.x_src1 + self.x_src2) ^ C(0xf6583fb7))
+ with m.Case(Funct3.MULHSU):
+ m.d.comb += x_result.eq((self.x_src1 - self.x_src2) ^ C(0xecfbe137))
+ with m.Case(Funct3.MULHU):
+ m.d.comb += x_result.eq((self.x_src1 + self.x_src2) ^ C(0x949ce5e8))
+
+ with m.If(~self.x_stall):
+ m.d.sync += m_result.eq(x_result)
+ with m.If(~self.m_stall):
+ m.d.sync += self.w_result.eq(m_result)
+
+ return m
--- /dev/null
+from nmigen import *
+
+
+__all__ = ["BranchPredictor"]
+
+
+class BranchPredictor(Elaboratable):
+ def __init__(self):
+ self.d_branch = Signal()
+ self.d_jump = Signal()
+ self.d_offset = Signal((32, True))
+ self.d_pc = Signal(32)
+ self.d_rs1_re = Signal()
+
+ self.d_branch_taken = Signal()
+ self.d_branch_target = Signal(32)
+ self.d_fetch_misaligned = Signal()
+
+ def elaborate(self, platform):
+ m = Module()
+
+ with m.If(self.d_branch):
+ # Backward conditional branches are predicted as taken.
+ # Forward conditional branches are predicted as not taken.
+ m.d.comb += self.d_branch_taken.eq(self.d_offset[-1])
+ with m.Else():
+ # Direct jumps are predicted as taken.
+ # Other branch types (ie. indirect jumps, exceptions) are not predicted.
+ m.d.comb += self.d_branch_taken.eq(self.d_jump & ~self.d_rs1_re)
+
+ m.d.comb += [
+ self.d_branch_target.eq(self.d_pc + self.d_offset),
+ self.d_fetch_misaligned.eq(self.d_branch_target[:2].bool())
+ ]
+
+ return m
--- /dev/null
+from functools import reduce
+from operator import or_
+
+from nmigen import *
+from nmigen.hdl.rec import *
+
+from ..isa import *
+from ..wishbone import *
+
+
+__all__ = ["rvfi_layout", "RVFIController"]
+
+# RISC-V Formal Interface
+# https://github.com/SymbioticEDA/riscv-formal/blob/master/docs/rvfi.md
+
+rvfi_layout = [
+ ("valid", 1, DIR_FANOUT),
+ ("order", 64, DIR_FANOUT),
+ ("insn", 32, DIR_FANOUT),
+ ("trap", 1, DIR_FANOUT),
+ ("halt", 1, DIR_FANOUT),
+ ("intr", 1, DIR_FANOUT),
+ ("mode", 2, DIR_FANOUT),
+ ("ixl", 2, DIR_FANOUT),
+
+ ("rs1_addr", 5, DIR_FANOUT),
+ ("rs2_addr", 5, DIR_FANOUT),
+ ("rs1_rdata", 32, DIR_FANOUT),
+ ("rs2_rdata", 32, DIR_FANOUT),
+ ("rd_addr", 5, DIR_FANOUT),
+ ("rd_wdata", 32, DIR_FANOUT),
+
+ ("pc_rdata", 32, DIR_FANOUT),
+ ("pc_wdata", 32, DIR_FANOUT),
+
+ ("mem_addr", 32, DIR_FANOUT),
+ ("mem_rmask", 4, DIR_FANOUT),
+ ("mem_wmask", 4, DIR_FANOUT),
+ ("mem_rdata", 32, DIR_FANOUT),
+ ("mem_wdata", 32, DIR_FANOUT)
+]
+
+
+class RVFIController(Elaboratable):
+ def __init__(self):
+ self.rvfi = Record(rvfi_layout)
+
+ self.d_insn = Signal.like(self.rvfi.insn)
+ self.d_rs1_addr = Signal.like(self.rvfi.rs1_addr)
+ self.d_rs2_addr = Signal.like(self.rvfi.rs2_addr)
+ self.d_rs1_rdata = Signal.like(self.rvfi.rs1_rdata)
+ self.d_rs2_rdata = Signal.like(self.rvfi.rs2_rdata)
+ self.d_stall = Signal()
+ self.x_mem_addr = Signal.like(self.rvfi.mem_addr)
+ self.x_mem_wmask = Signal.like(self.rvfi.mem_wmask)
+ self.x_mem_rmask = Signal.like(self.rvfi.mem_rmask)
+ self.x_mem_wdata = Signal.like(self.rvfi.mem_wdata)
+ self.x_stall = Signal()
+ self.m_mem_rdata = Signal.like(self.rvfi.mem_rdata)
+ self.m_fetch_misaligned = Signal()
+ self.m_illegal_insn = Signal()
+ self.m_load_misaligned = Signal()
+ self.m_store_misaligned = Signal()
+ self.m_exception = Signal()
+ self.m_mret = Signal()
+ self.m_branch_taken = Signal()
+ self.m_branch_target = Signal(32)
+ self.m_pc_rdata = Signal.like(self.rvfi.pc_rdata)
+ self.m_stall = Signal()
+ self.m_valid = Signal()
+ self.w_rd_addr = Signal.like(self.rvfi.rd_addr)
+ self.w_rd_wdata = Signal.like(self.rvfi.rd_wdata)
+
+ self.mtvec_r_base = Signal(30)
+ self.mepc_r_value = Signal(32)
+
+ def elaborate(self, platform):
+ m = Module()
+
+ # Instruction Metadata
+
+ with m.If(~self.m_stall):
+ m.d.sync += self.rvfi.valid.eq(self.m_valid)
+ with m.Elif(self.rvfi.valid):
+ m.d.sync += self.rvfi.valid.eq(0)
+
+ with m.If(self.rvfi.valid):
+ m.d.sync += self.rvfi.order.eq(self.rvfi.order + 1)
+
+ x_insn = Signal.like(self.rvfi.insn)
+ m_insn = Signal.like(self.rvfi.insn)
+
+ with m.If(~self.d_stall):
+ m.d.sync += x_insn.eq(self.d_insn)
+ with m.If(~self.x_stall):
+ m.d.sync += m_insn.eq(x_insn)
+ with m.If(~self.m_stall):
+ m.d.sync += self.rvfi.insn.eq(m_insn)
+
+ with m.If(~self.m_stall):
+ m.d.sync += [
+ self.rvfi.trap.eq(reduce(or_, (
+ self.m_fetch_misaligned,
+ self.m_illegal_insn,
+ self.m_load_misaligned,
+ self.m_store_misaligned
+ ))),
+ self.rvfi.intr.eq(self.m_pc_rdata == self.mtvec_r_base << 2)
+ ]
+
+ m.d.comb += [
+ self.rvfi.mode.eq(Const(3)), # M-mode
+ self.rvfi.ixl.eq(Const(1)) # XLEN=32
+ ]
+
+ # Integer Register Read/Write
+
+ x_rs1_addr = Signal.like(self.rvfi.rs1_addr)
+ x_rs2_addr = Signal.like(self.rvfi.rs2_addr)
+ x_rs1_rdata = Signal.like(self.rvfi.rs1_rdata)
+ x_rs2_rdata = Signal.like(self.rvfi.rs2_rdata)
+
+ m_rs1_addr = Signal.like(self.rvfi.rs1_addr)
+ m_rs2_addr = Signal.like(self.rvfi.rs2_addr)
+ m_rs1_rdata = Signal.like(self.rvfi.rs1_rdata)
+ m_rs2_rdata = Signal.like(self.rvfi.rs2_rdata)
+
+ with m.If(~self.d_stall):
+ m.d.sync += [
+ x_rs1_addr.eq(self.d_rs1_addr),
+ x_rs2_addr.eq(self.d_rs2_addr),
+ x_rs1_rdata.eq(self.d_rs1_rdata),
+ x_rs2_rdata.eq(self.d_rs2_rdata)
+ ]
+ with m.If(~self.x_stall):
+ m.d.sync += [
+ m_rs1_addr.eq(x_rs1_addr),
+ m_rs2_addr.eq(x_rs2_addr),
+ m_rs1_rdata.eq(x_rs1_rdata),
+ m_rs2_rdata.eq(x_rs2_rdata)
+ ]
+ with m.If(~self.m_stall):
+ m.d.sync += [
+ self.rvfi.rs1_addr.eq(m_rs1_addr),
+ self.rvfi.rs2_addr.eq(m_rs2_addr),
+ self.rvfi.rs1_rdata.eq(m_rs1_rdata),
+ self.rvfi.rs2_rdata.eq(m_rs2_rdata)
+ ]
+
+ m.d.comb += [
+ self.rvfi.rd_addr.eq(self.w_rd_addr),
+ self.rvfi.rd_wdata.eq(self.w_rd_wdata)
+ ]
+
+ # Program Counter
+
+ m_pc_wdata = Signal.like(self.rvfi.pc_wdata)
+
+ with m.If(self.m_exception):
+ m.d.comb += m_pc_wdata.eq(self.mtvec_r_base << 2)
+ with m.Elif(self.m_mret):
+ m.d.comb += m_pc_wdata.eq(self.mepc_r_value)
+ with m.Elif(self.m_branch_taken):
+ m.d.comb += m_pc_wdata.eq(self.m_branch_target)
+ with m.Else():
+ m.d.comb += m_pc_wdata.eq(self.m_pc_rdata + 4)
+
+ with m.If(~self.m_stall):
+ m.d.sync += [
+ self.rvfi.pc_rdata.eq(self.m_pc_rdata),
+ self.rvfi.pc_wdata.eq(m_pc_wdata)
+ ]
+
+ # Memory Access
+
+ m_mem_addr = Signal.like(self.rvfi.mem_addr)
+ m_mem_wmask = Signal.like(self.rvfi.mem_wmask)
+ m_mem_rmask = Signal.like(self.rvfi.mem_rmask)
+ m_mem_wdata = Signal.like(self.rvfi.mem_wdata)
+
+ with m.If(~self.x_stall):
+ m.d.sync += [
+ m_mem_addr.eq(self.x_mem_addr),
+ m_mem_wmask.eq(self.x_mem_wmask),
+ m_mem_rmask.eq(self.x_mem_rmask),
+ m_mem_wdata.eq(self.x_mem_wdata)
+ ]
+ with m.If(~self.m_stall):
+ m.d.sync += [
+ self.rvfi.mem_addr.eq(m_mem_addr),
+ self.rvfi.mem_wmask.eq(m_mem_wmask),
+ self.rvfi.mem_rmask.eq(m_mem_rmask),
+ self.rvfi.mem_wdata.eq(m_mem_wdata),
+ self.rvfi.mem_rdata.eq(self.m_mem_rdata)
+ ]
+
+ return m
--- /dev/null
+from nmigen import *
+
+
+__all__ = ["Shifter"]
+
+
+class Shifter(Elaboratable):
+ def __init__(self):
+ self.x_direction = Signal()
+ self.x_sext = Signal()
+ self.x_shamt = Signal(5)
+ self.x_src1 = Signal(32)
+ self.x_stall = Signal()
+
+ self.m_result = Signal(32)
+
+ def elaborate(self, platform):
+ m = Module()
+
+ x_operand = Signal(32)
+ x_filler = Signal()
+ m_direction = Signal()
+ m_result = Signal(32)
+
+ m.d.comb += [
+ # left shifts are equivalent to right shifts with reversed bits
+ x_operand.eq(Mux(self.x_direction, self.x_src1, self.x_src1[::-1])),
+ x_filler.eq(Mux(self.x_direction & self.x_sext, self.x_src1[-1], 0))
+ ]
+
+ with m.If(~self.x_stall):
+ m.d.sync += [
+ m_direction.eq(self.x_direction),
+ m_result.eq(Cat(x_operand, Repl(x_filler, 32)) >> self.x_shamt)
+ ]
+
+ m.d.comb += self.m_result.eq(Mux(m_direction, m_result, m_result[::-1]))
+
+ return m
--- /dev/null
+from functools import reduce
+from operator import or_
+
+from nmigen import *
+from nmigen.hdl.rec import *
+
+from ..csr import *
+from ..isa import *
+
+
+__all__ = ["TriggerUnit"]
+
+
+class Type:
+ NOP = 0
+ LEGACY = 1
+ MATCH = 2
+ INSN_COUNT = 3
+ INTERRUPT = 4
+ EXCEPTION = 5
+
+
+mcontrol_layout = [
+ ("load", 1),
+ ("store", 1),
+ ("execute", 1),
+ ("u", 1),
+ ("s", 1),
+ ("zero0", 1),
+ ("m", 1),
+ ("match", 4),
+ ("chain", 1),
+ ("action", 4),
+ ("size", 2),
+ ("timing", 1),
+ ("select", 1),
+ ("hit", 1),
+ ("maskmax", 6)
+]
+
+
+class TriggerUnit(Elaboratable, AutoCSR):
+ def __init__(self, nb_triggers):
+ self.nb_triggers = nb_triggers
+
+ self.tselect = CSR(0x7a0, flat_layout, name="tselect")
+ self.tdata1 = CSR(0x7a1, tdata1_layout, name="tdata1")
+ self.tdata2 = CSR(0x7a2, flat_layout, name="tdata2")
+
+ self.x_pc = Signal(32)
+ self.x_valid = Signal()
+
+ self.haltreq = Signal()
+ self.trap = Signal()
+
+ def elaborate(self, platform):
+ m = Module()
+
+ triggers = [Record.like(self.tdata1.r) for _ in range(self.nb_triggers)]
+ for t in triggers:
+ # We only support address/data match triggers.
+ m.d.comb += t.type.eq(Type.MATCH)
+
+ def do_trigger_update(trigger):
+ m.d.sync += trigger.dmode.eq(self.tdata1.w.dmode)
+ mcontrol = Record([("i", mcontrol_layout), ("o", mcontrol_layout)])
+ m.d.comb += [
+ mcontrol.i.eq(self.tdata1.w.data),
+ mcontrol.o.execute.eq(mcontrol.i.execute),
+ mcontrol.o.m.eq(mcontrol.i.m),
+ mcontrol.o.action.eq(mcontrol.i.action),
+ ]
+ m.d.sync += trigger.data.eq(mcontrol.o)
+
+ with m.Switch(self.tselect.r.value):
+ for i, t in enumerate(triggers):
+ with m.Case(i):
+ m.d.comb += self.tdata1.r.eq(t)
+ with m.If(self.tdata1.we):
+ do_trigger_update(t)
+
+ with m.If(self.tselect.we):
+ with m.If(self.tselect.w.value < self.nb_triggers):
+ m.d.sync += self.tselect.r.value.eq(self.tselect.w.value)
+
+ with m.If(self.tdata2.we):
+ m.d.sync += self.tdata2.r.eq(self.tdata2.w)
+
+ hit = Signal()
+ halt = Signal()
+
+ with m.Switch(self.tdata1.r.type):
+ with m.Case(Type.MATCH):
+ mcontrol = Record(mcontrol_layout)
+ m.d.comb += mcontrol.eq(self.tdata1.r.data)
+ match = Signal()
+ with m.If(mcontrol.execute):
+ m.d.comb += match.eq(self.tdata2.r == self.x_pc & self.x_valid)
+ m.d.comb += [
+ hit.eq(match & mcontrol.m),
+ halt.eq(mcontrol.action)
+ ]
+
+ with m.If(hit):
+ with m.If(halt):
+ m.d.comb += self.haltreq.eq(self.tdata1.r.dmode)
+ with m.Else():
+ m.d.comb += self.trap.eq(1)
+
+ return m
--- /dev/null
+from nmigen import *
+from nmigen.hdl.rec import *
+from nmigen.lib.coding import *
+
+
+__all__ = ["Cycle", "wishbone_layout", "WishboneArbiter"]
+
+
+class Cycle:
+ CLASSIC = 0
+ CONSTANT = 1
+ INCREMENT = 2
+ END = 7
+
+
+wishbone_layout = [
+ ("adr", 30, DIR_FANOUT),
+ ("dat_w", 32, DIR_FANOUT),
+ ("dat_r", 32, DIR_FANIN),
+ ("sel", 4, DIR_FANOUT),
+ ("cyc", 1, DIR_FANOUT),
+ ("stb", 1, DIR_FANOUT),
+ ("ack", 1, DIR_FANIN),
+ ("we", 1, DIR_FANOUT),
+ ("cti", 3, DIR_FANOUT),
+ ("bte", 2, DIR_FANOUT),
+ ("err", 1, DIR_FANIN)
+]
+
+
+class WishboneArbiter(Elaboratable):
+ def __init__(self):
+ self.bus = Record(wishbone_layout)
+ self._port_map = dict()
+
+ def port(self, priority):
+ if not isinstance(priority, int) or priority < 0:
+ raise TypeError("Priority must be a non-negative integer, not '{!r}'"
+ .format(priority))
+ if priority in self._port_map:
+ raise ValueError("Conflicting priority: '{!r}'".format(priority))
+ port = self._port_map[priority] = Record.like(self.bus)
+ return port
+
+ def elaborate(self, platform):
+ m = Module()
+
+ ports = [port for priority, port in sorted(self._port_map.items())]
+
+ for port in ports:
+ m.d.comb += port.dat_r.eq(self.bus.dat_r)
+
+ bus_pe = m.submodules.bus_pe = PriorityEncoder(len(ports))
+ with m.If(~self.bus.cyc):
+ for j, port in enumerate(ports):
+ m.d.sync += bus_pe.i[j].eq(port.cyc)
+
+ source = Array(ports)[bus_pe.o]
+ m.d.comb += [
+ self.bus.adr.eq(source.adr),
+ self.bus.dat_w.eq(source.dat_w),
+ self.bus.sel.eq(source.sel),
+ self.bus.cyc.eq(source.cyc),
+ self.bus.stb.eq(source.stb),
+ self.bus.we.eq(source.we),
+ self.bus.cti.eq(source.cti),
+ self.bus.bte.eq(source.bte),
+ source.ack.eq(self.bus.ack),
+ source.err.eq(self.bus.err)
+ ]
+
+ return m