122 lines
5.1 KiB
Python
122 lines
5.1 KiB
Python
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)
|
|
self.misa = CSR(0x301, misa_layout) # FIXME move elsewhere
|
|
self.mie = CSR(0x304, mie_layout)
|
|
self.mtvec = CSR(0x305, mtvec_layout)
|
|
self.mscratch = CSR(0x340, flat_layout) # FIXME move elsewhere
|
|
self.mepc = CSR(0x341, mepc_layout)
|
|
self.mcause = CSR(0x342, mcause_layout)
|
|
self.mtval = CSR(0x343, flat_layout)
|
|
self.mip = CSR(0x344, mip_layout)
|
|
self.irq_mask = CSR(0x330, flat_layout)
|
|
self.irq_pending = CSR(0x360, flat_layout)
|
|
|
|
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
|