arch-riscv: adding support for local interrupts (#813)

Besides the standard RISC-V interrupts software, timer, and external
interrupt, the RISC-V specification also offers the possibility to
implement local interrupts. With this patch, we contribute an extension
of RiscvInterrupts that enables connecting interrupt sources to the
local interrupt controller. We assigned the local interrupts to
machine-level and gave them the highest priority. If two local
interrupts are pending, there exception code will be the tie-breaker
(higher ID > lower ID). 32 Bit systems only recognize the local
interrupts 16 to 31, 64 Bit systems 16 to 63.

Change-Id: Iff8d34e740b925dce351c0c6f54f4bd37a647e0c

---------

Co-authored-by: Robert Hauser <robert.hauser@uni-rostock.de>
This commit is contained in:
Robert Hauser
2024-02-06 18:38:50 +01:00
committed by GitHub
parent de0342128c
commit f289f9e8b5
6 changed files with 350 additions and 128 deletions

View File

@@ -2,6 +2,7 @@
# Copyright (c) 2014 Sven Karlsson
# Copyright (c) 2016 RISC-V Foundation
# Copyright (c) 2016 The University of Virginia
# Copyright (c) 2024 University of Rostock
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -27,10 +28,35 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from m5.citations import add_citation
from m5.objects.BaseInterrupts import BaseInterrupts
from m5.objects.IntPin import VectorIntSinkPin
from m5.params import VectorParam
class RiscvInterrupts(BaseInterrupts):
type = "RiscvInterrupts"
cxx_class = "gem5::RiscvISA::Interrupts"
cxx_header = "arch/riscv/interrupts.hh"
local_interrupt_pins = VectorIntSinkPin("Pins for local interrupts")
local_interrupt_ids = VectorParam.Unsigned(
[], "list of local interrupt ids"
)
add_citation(
RiscvInterrupts,
r"""@inproceedings{Hauser:2024:LocalRiscvInterrupts,
author = {Robert Hauser and
Lukas Steffen and
Florian Grützmacher and
Christian Haubelt},
title = {Analyzing Local RISC-V Interrupt Latencies with Virtual Prototyping},
booktitle = {Workshop Methoden und Beschreibungssprachen zur Modellierung und Verifikation von Schaltungen und Systemen (MBMV24)},
pages = {1-7},
year = {2024},
month = {2}
}
""",
)

View File

@@ -4,6 +4,7 @@
# Copyright (c) 2014 Sven Karlsson
# Copyright (c) 2020 Barkhausen Institut
# Copyright (c) 2021 Huawei International
# Copyright (c) 2024 University of Rostock
# All rights reserved
#
# The license below extends only to copyright in the software and shall
@@ -48,6 +49,7 @@ if env['CONF']['USE_RISCV_ISA']:
Source('decoder.cc', tags='riscv isa')
Source('faults.cc', tags='riscv isa')
Source('interrupts.cc', tags='riscv isa')
Source('isa.cc', tags='riscv isa')
Source('process.cc', tags='riscv isa')
Source('pagetable.cc', tags='riscv isa')

View File

@@ -2,6 +2,7 @@
* Copyright (c) 2016 RISC-V Foundation
* Copyright (c) 2016 The University of Virginia
* Copyright (c) 2018 TU Dresden
* Copyright (c) 2024 University of Rostock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -93,6 +94,54 @@ enum ExceptionCode : uint64_t
INT_EXT_USER = 8,
INT_EXT_SUPER = 9,
INT_EXT_MACHINE = 11,
INT_LOCAL_0 = 16,
INT_LOCAL_1 = 17,
INT_LOCAL_2 = 18,
INT_LOCAL_3 = 19,
INT_LOCAL_4 = 20,
INT_LOCAL_5 = 21,
INT_LOCAL_6 = 22,
INT_LOCAL_7 = 23,
INT_LOCAL_8 = 24,
INT_LOCAL_9 = 25,
INT_LOCAL_10 = 26,
INT_LOCAL_11 = 27,
INT_LOCAL_12 = 28,
INT_LOCAL_13 = 29,
INT_LOCAL_14 = 30,
INT_LOCAL_15 = 31,
INT_LOCAL_16 = 32,
INT_LOCAL_17 = 33,
INT_LOCAL_18 = 34,
INT_LOCAL_19 = 35,
INT_LOCAL_20 = 36,
INT_LOCAL_21 = 37,
INT_LOCAL_22 = 38,
INT_LOCAL_23 = 39,
INT_LOCAL_24 = 40,
INT_LOCAL_25 = 41,
INT_LOCAL_26 = 42,
INT_LOCAL_27 = 43,
INT_LOCAL_28 = 44,
INT_LOCAL_29 = 45,
INT_LOCAL_30 = 46,
INT_LOCAL_31 = 47,
INT_LOCAL_32 = 48,
INT_LOCAL_33 = 49,
INT_LOCAL_34 = 50,
INT_LOCAL_35 = 51,
INT_LOCAL_36 = 52,
INT_LOCAL_37 = 53,
INT_LOCAL_38 = 54,
INT_LOCAL_39 = 55,
INT_LOCAL_40 = 56,
INT_LOCAL_41 = 57,
INT_LOCAL_42 = 58,
INT_LOCAL_43 = 59,
INT_LOCAL_44 = 60,
INT_LOCAL_45 = 61,
INT_LOCAL_46 = 62,
INT_LOCAL_47 = 63,
NumInterruptTypes,
// INT_NMI does not exist in the spec, it's a modeling artifact for NMI. We
// intentionally set it to be NumInterruptTypes so it can never conflict

View File

@@ -0,0 +1,248 @@
/*
* Copyright (c) 2011 Google
* Copyright (c) 2024 University of Rostock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* 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;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* 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.
*/
#include "arch/riscv/interrupts.hh"
namespace gem5
{
namespace RiscvISA
{
Interrupts::Interrupts(const Params &p) : BaseInterrupts(p),
ip(0),
ie(0)
{
for (uint8_t i = 0;
i < p.port_local_interrupt_pins_connection_count;
++i) {
uint8_t interruptID = p.local_interrupt_ids[i];
assert(interruptID >= 0);
assert(interruptID <= 47);
std::string pinName =
csprintf("%s.local_interrupt_pins[%d]", p.name, i);
IntSinkPin<Interrupts>* pin =
new IntSinkPin<Interrupts>(pinName,i, this, interruptID);
localInterruptPins.push_back(pin);
}
}
std::bitset<NumInterruptTypes>
Interrupts::globalMask() const
{
INTERRUPT mask = 0;
STATUS status = tc->readMiscReg(MISCREG_STATUS);
MISA misa = tc->readMiscRegNoEffect(MISCREG_ISA);
INTERRUPT mideleg = 0;
if (misa.rvs || misa.rvn) {
mideleg = tc->readMiscReg(MISCREG_MIDELEG);
}
INTERRUPT sideleg = 0;
if (misa.rvs && misa.rvn) {
sideleg = tc->readMiscReg(MISCREG_SIDELEG);
}
PrivilegeMode prv = (PrivilegeMode)tc->readMiscReg(MISCREG_PRV);
switch (prv) {
case PRV_U:
// status.uie is always 0 if misa.rvn is disabled
if (misa.rvs) {
mask.local = ~sideleg.local;
if (status.uie)
mask.local = mask.local | sideleg.local;
mask.mei = (!sideleg.mei) | (sideleg.mei & status.uie);
mask.mti = (!sideleg.mti) | (sideleg.mti & status.uie);
mask.msi = (!sideleg.msi) | (sideleg.msi & status.uie);
mask.sei = (!sideleg.sei) | (sideleg.sei & status.uie);
mask.sti = (!sideleg.sti) | (sideleg.sti & status.uie);
mask.ssi = (!sideleg.ssi) | (sideleg.ssi & status.uie);
} else {
// According to the RISC-V privilege spec v1.10, if the
// S privilege mode is not implemented and user-trap
// support, setting mideleg/medeleg bits will delegate the
// trap to U-mode trap handler
mask.local = ~mideleg.local;
if (status.uie)
mask.local = mask.local | mideleg.local;
mask.mei = (!mideleg.mei) | (mideleg.mei & status.uie);
mask.mti = (!mideleg.mti) | (mideleg.mti & status.uie);
mask.msi = (!mideleg.msi) | (mideleg.msi & status.uie);
mask.sei = mask.sti = mask.ssi = 0;
}
if (status.uie)
mask.uei = mask.uti = mask.usi = 1;
break;
case PRV_S:
mask.local = ~mideleg.local;
mask.mei = (!mideleg.mei) | (mideleg.mei & status.sie);
mask.mti = (!mideleg.mti) | (mideleg.mti & status.sie);
mask.msi = (!mideleg.msi) | (mideleg.msi & status.sie);
if (status.sie) {
mask.sei = mask.sti = mask.ssi = 1;
mask.local = mask.local | mideleg.local;
}
mask.uei = mask.uti = mask.usi = 0;
break;
case PRV_M:
if (status.mie) {
mask.local = gem5::mask(48);
mask.mei = mask.mti = mask.msi = 1;
}
mask.sei = mask.sti = mask.ssi = 0;
mask.uei = mask.uti = mask.usi = 0;
break;
default:
panic("Unknown privilege mode %d.", prv);
break;
}
return std::bitset<NumInterruptTypes>(mask);
}
Fault
Interrupts::getInterrupt()
{
assert(checkInterrupts());
if (checkNonMaskableInterrupt())
return std::make_shared<NonMaskableInterruptFault>();
std::bitset<NumInterruptTypes> mask = globalMask();
if (((ISA*) tc->getIsaPtr())->rvType() == RV64) {
const std::vector<int> interrupt_order {
INT_LOCAL_47, INT_LOCAL_46, INT_LOCAL_45, INT_LOCAL_44,
INT_LOCAL_43, INT_LOCAL_42, INT_LOCAL_41, INT_LOCAL_40,
INT_LOCAL_39, INT_LOCAL_38, INT_LOCAL_37, INT_LOCAL_36,
INT_LOCAL_35, INT_LOCAL_34, INT_LOCAL_33, INT_LOCAL_32,
INT_LOCAL_31, INT_LOCAL_30, INT_LOCAL_29, INT_LOCAL_28,
INT_LOCAL_27, INT_LOCAL_26, INT_LOCAL_25, INT_LOCAL_24,
INT_LOCAL_23, INT_LOCAL_22, INT_LOCAL_21, INT_LOCAL_20,
INT_LOCAL_19, INT_LOCAL_18, INT_LOCAL_17, INT_LOCAL_16,
INT_LOCAL_15, INT_LOCAL_14, INT_LOCAL_13, INT_LOCAL_12,
INT_LOCAL_11, INT_LOCAL_10, INT_LOCAL_9, INT_LOCAL_8,
INT_LOCAL_7, INT_LOCAL_6, INT_LOCAL_5, INT_LOCAL_4,
INT_LOCAL_3, INT_LOCAL_2, INT_LOCAL_1, INT_LOCAL_0,
INT_EXT_MACHINE, INT_SOFTWARE_MACHINE, INT_TIMER_MACHINE,
INT_EXT_SUPER, INT_SOFTWARE_SUPER, INT_TIMER_SUPER,
INT_EXT_USER, INT_SOFTWARE_USER, INT_TIMER_USER
};
for (const int &id : interrupt_order) {
if (checkInterrupt(id) && mask[id]) {
return std::make_shared<InterruptFault>(id);
}
}
} else if (((ISA*) tc->getIsaPtr())->rvType() == RV32) {
const std::vector<int> interrupt_order {
INT_LOCAL_15, INT_LOCAL_14, INT_LOCAL_13, INT_LOCAL_12,
INT_LOCAL_11, INT_LOCAL_10, INT_LOCAL_9, INT_LOCAL_8,
INT_LOCAL_7, INT_LOCAL_6, INT_LOCAL_5, INT_LOCAL_4,
INT_LOCAL_3, INT_LOCAL_2, INT_LOCAL_1, INT_LOCAL_0,
INT_EXT_MACHINE, INT_SOFTWARE_MACHINE, INT_TIMER_MACHINE,
INT_EXT_SUPER, INT_SOFTWARE_SUPER, INT_TIMER_SUPER,
INT_EXT_USER, INT_SOFTWARE_USER, INT_TIMER_USER
};
for (const int &id : interrupt_order) {
if (checkInterrupt(id) && mask[id]) {
return std::make_shared<InterruptFault>(id);
}
}
}
return NoFault;
}
void
Interrupts::post(int int_num, int index)
{
DPRINTF(Interrupt, "Interrupt %d:%d posted\n", int_num, index);
if (int_num != INT_NMI) {
ip[int_num] = true;
} else {
postNMI();
}
}
void
Interrupts::clear(int int_num, int index)
{
DPRINTF(Interrupt, "Interrupt %d:%d cleared\n", int_num, index);
if (int_num != INT_NMI) {
ip[int_num] = false;
} else {
clearNMI();
}
}
void
Interrupts::clearAll()
{
DPRINTF(Interrupt, "All interrupts cleared\n");
ip = 0;
clearNMI();
}
void
Interrupts::raiseInterruptPin(uint32_t num)
{
tc->getCpuPtr()->postInterrupt(tc->threadId(), num + 16, 0);
}
void
Interrupts::serialize(CheckpointOut &cp) const
{
unsigned long ip_ulong = ip.to_ulong();
unsigned long ie_ulong = ie.to_ulong();
SERIALIZE_SCALAR(ip_ulong);
SERIALIZE_SCALAR(ie_ulong);
}
void
Interrupts::unserialize(CheckpointIn &cp)
{
unsigned long ip_ulong;
unsigned long ie_ulong;
UNSERIALIZE_SCALAR(ip_ulong);
ip = ip_ulong;
UNSERIALIZE_SCALAR(ie_ulong);
ie = ie_ulong;
}
Port &
Interrupts::getPort(const std::string &if_name, PortID idx)
{
if (if_name == "local_interrupt_pins" && idx < localInterruptPins.size()) {
return *localInterruptPins[idx];
} else {
return BaseInterrupts::getPort(if_name, idx);
}
}
} // namespace RiscvISA
} // namespace gem5

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2011 Google
* Copyright (c) 2024 University of Rostock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -36,8 +37,10 @@
#include "arch/riscv/faults.hh"
#include "arch/riscv/regs/misc.hh"
#include "base/logging.hh"
#include "cpu/base.hh"
#include "cpu/thread_context.hh"
#include "debug/Interrupt.hh"
#include "dev/intpin.hh"
#include "params/RiscvInterrupts.hh"
#include "sim/sim_object.hh"
@@ -59,71 +62,13 @@ class Interrupts : public BaseInterrupts
std::bitset<NumInterruptTypes> ip;
std::bitset<NumInterruptTypes> ie;
std::vector<gem5::IntSinkPin<Interrupts>*> localInterruptPins;
public:
using Params = RiscvInterruptsParams;
Interrupts(const Params &p) : BaseInterrupts(p), ip(0), ie(0) {}
Interrupts(const Params &p);
std::bitset<NumInterruptTypes>
globalMask() const
{
INTERRUPT mask = 0;
STATUS status = tc->readMiscReg(MISCREG_STATUS);
MISA misa = tc->readMiscRegNoEffect(MISCREG_ISA);
INTERRUPT mideleg = 0;
if (misa.rvs || misa.rvn) {
mideleg = tc->readMiscReg(MISCREG_MIDELEG);
}
INTERRUPT sideleg = 0;
if (misa.rvs && misa.rvn) {
sideleg = tc->readMiscReg(MISCREG_SIDELEG);
}
PrivilegeMode prv = (PrivilegeMode)tc->readMiscReg(MISCREG_PRV);
switch (prv) {
case PRV_U:
// status.uie is always 0 if misa.rvn is disabled
if (misa.rvs) {
mask.mei = (!sideleg.mei) | (sideleg.mei & status.uie);
mask.mti = (!sideleg.mti) | (sideleg.mti & status.uie);
mask.msi = (!sideleg.msi) | (sideleg.msi & status.uie);
mask.sei = (!sideleg.sei) | (sideleg.sei & status.uie);
mask.sti = (!sideleg.sti) | (sideleg.sti & status.uie);
mask.ssi = (!sideleg.ssi) | (sideleg.ssi & status.uie);
} else {
// According to the RISC-V privilege spec v1.10, if the
// S privilege mode is not implemented and user-trap
// support, setting mideleg/medeleg bits will delegate the
// trap to U-mode trap handler
mask.mei = (!mideleg.mei) | (mideleg.mei & status.uie);
mask.mti = (!mideleg.mti) | (mideleg.mti & status.uie);
mask.msi = (!mideleg.msi) | (mideleg.msi & status.uie);
mask.sei = mask.sti = mask.ssi = 0;
}
if (status.uie)
mask.uei = mask.uti = mask.usi = 1;
break;
case PRV_S:
// status.sie is always 0 if misa.rvn is disabled
mask.mei = (!mideleg.mei) | (mideleg.mei & status.sie);
mask.mti = (!mideleg.mti) | (mideleg.mti & status.sie);
mask.msi = (!mideleg.msi) | (mideleg.msi & status.sie);
if (status.sie)
mask.sei = mask.sti = mask.ssi = 1;
mask.uei = mask.uti = mask.usi = 0;
break;
case PRV_M:
if (status.mie)
mask.mei = mask.mti = mask.msi = 1;
mask.sei = mask.sti = mask.ssi = 0;
mask.uei = mask.uti = mask.usi = 0;
break;
default:
panic("Unknown privilege mode %d.", prv);
break;
}
return std::bitset<NumInterruptTypes>(mask);
}
std::bitset<NumInterruptTypes> globalMask() const;
bool
checkNonMaskableInterrupt() const
@@ -137,83 +82,32 @@ class Interrupts : public BaseInterrupts
return checkNonMaskableInterrupt() || (ip & ie & globalMask()).any();
}
Fault
getInterrupt() override
{
assert(checkInterrupts());
if (checkNonMaskableInterrupt())
return std::make_shared<NonMaskableInterruptFault>();
std::bitset<NumInterruptTypes> mask = globalMask();
const std::vector<int> interrupt_order {
INT_EXT_MACHINE, INT_SOFTWARE_MACHINE, INT_TIMER_MACHINE,
INT_EXT_SUPER, INT_SOFTWARE_SUPER, INT_TIMER_SUPER,
INT_EXT_USER, INT_SOFTWARE_USER, INT_TIMER_USER
};
for (const int &id : interrupt_order)
if (checkInterrupt(id) && mask[id])
return std::make_shared<InterruptFault>(id);
return NoFault;
}
Fault getInterrupt() override;
void updateIntrInfo() override {}
void
post(int int_num, int index) override
{
DPRINTF(Interrupt, "Interrupt %d:%d posted\n", int_num, index);
if (int_num != INT_NMI) {
ip[int_num] = true;
} else {
postNMI();
}
}
void post(int int_num, int index) override;
void
clear(int int_num, int index) override
{
DPRINTF(Interrupt, "Interrupt %d:%d cleared\n", int_num, index);
if (int_num != INT_NMI) {
ip[int_num] = false;
} else {
clearNMI();
}
}
void clear(int int_num, int index) override;
void postNMI() { tc->setMiscReg(MISCREG_NMIP, 1); }
void clearNMI() { tc->setMiscReg(MISCREG_NMIP, 0); }
void
clearAll() override
{
DPRINTF(Interrupt, "All interrupts cleared\n");
ip = 0;
clearNMI();
}
void clearAll() override;
uint64_t readIP() const { return (uint64_t)ip.to_ulong(); }
uint64_t readIE() const { return (uint64_t)ie.to_ulong(); }
void setIP(const uint64_t& val) { ip = val; }
void setIE(const uint64_t& val) { ie = val; }
void
serialize(CheckpointOut &cp) const override
{
unsigned long ip_ulong = ip.to_ulong();
unsigned long ie_ulong = ie.to_ulong();
SERIALIZE_SCALAR(ip_ulong);
SERIALIZE_SCALAR(ie_ulong);
}
void serialize(CheckpointOut &cp) const override;
void
unserialize(CheckpointIn &cp) override
{
unsigned long ip_ulong;
unsigned long ie_ulong;
UNSERIALIZE_SCALAR(ip_ulong);
ip = ip_ulong;
UNSERIALIZE_SCALAR(ie_ulong);
ie = ie_ulong;
}
void unserialize(CheckpointIn &cp) override;
Port &getPort(const std::string &if_name, PortID idx) override;
void raiseInterruptPin(uint32_t num);
void lowerInterruptPin(uint32_t num) {};
};
} // namespace RiscvISA

View File

@@ -17,6 +17,7 @@
*
* Copyright (c) 2016 RISC-V Foundation
* Copyright (c) 2016 The University of Virginia
* Copyright (c) 2024 University of Rostock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -1233,6 +1234,7 @@ EndBitUnion(MISA)
* this bit union.
*/
BitUnion64(INTERRUPT)
Bitfield<63,16> local;
Bitfield<11> mei;
Bitfield<9> sei;
Bitfield<8> uei;
@@ -1438,6 +1440,7 @@ USTATUS_MASKS[enums::Num_RiscvType][enums::Num_PrivilegeModeSet] = {
},
};
const RegVal LOCAL_MASK = mask(63,16);
const RegVal MEI_MASK = 1ULL << 11;
const RegVal SEI_MASK = 1ULL << 9;
const RegVal UEI_MASK = 1ULL << 8;
@@ -1448,13 +1451,13 @@ const RegVal MSI_MASK = 1ULL << 3;
const RegVal SSI_MASK = 1ULL << 1;
const RegVal USI_MASK = 1ULL << 0;
const RegVal MI_MASK[enums::Num_PrivilegeModeSet] = {
[enums::M] = MEI_MASK| MTI_MASK | MSI_MASK,
[enums::MU] = MEI_MASK| MTI_MASK | MSI_MASK,
[enums::MNU] = MEI_MASK | UEI_MASK | MTI_MASK | UTI_MASK |
[enums::M] = LOCAL_MASK | MEI_MASK| MTI_MASK | MSI_MASK,
[enums::MU] = LOCAL_MASK | MEI_MASK| MTI_MASK | MSI_MASK,
[enums::MNU] = LOCAL_MASK | MEI_MASK | UEI_MASK | MTI_MASK | UTI_MASK |
MSI_MASK | USI_MASK,
[enums::MSU] = MEI_MASK | SEI_MASK | MTI_MASK | STI_MASK |
[enums::MSU] = LOCAL_MASK | MEI_MASK | SEI_MASK | MTI_MASK | STI_MASK |
MSI_MASK | SSI_MASK,
[enums::MNSU] = MEI_MASK | SEI_MASK | UEI_MASK |
[enums::MNSU] = LOCAL_MASK | MEI_MASK | SEI_MASK | UEI_MASK |
MTI_MASK | STI_MASK | UTI_MASK |
MSI_MASK | SSI_MASK | USI_MASK,
};