mirror of
https://github.com/lowRISC/ibex.git
synced 2025-06-27 17:00:41 -04:00
253 lines
7.2 KiB
C++
253 lines
7.2 KiB
C++
// Copyright lowRISC contributors.
|
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#include "base_register.h"
|
|
|
|
#include <cassert>
|
|
#include <iostream>
|
|
|
|
BaseRegister::BaseRegister(
|
|
uint32_t addr, std::vector<std::unique_ptr<BaseRegister>> *map_pointer)
|
|
: register_value_(0), register_address_(addr), map_pointer_(map_pointer) {}
|
|
|
|
uint32_t BaseRegister::RegisterWrite(uint32_t newval) {
|
|
uint32_t lock_mask = GetLockMask();
|
|
uint32_t read_value = register_value_;
|
|
register_value_ &= lock_mask;
|
|
register_value_ |= (newval & ~lock_mask);
|
|
return read_value;
|
|
}
|
|
|
|
uint32_t BaseRegister::RegisterSet(uint32_t newval) {
|
|
uint32_t lock_mask = GetLockMask();
|
|
uint32_t read_value = register_value_;
|
|
register_value_ |= (newval & ~lock_mask);
|
|
return read_value;
|
|
}
|
|
|
|
uint32_t BaseRegister::RegisterClear(uint32_t newval) {
|
|
uint32_t lock_mask = GetLockMask();
|
|
uint32_t read_value = register_value_;
|
|
register_value_ &= (~newval | lock_mask);
|
|
return read_value;
|
|
}
|
|
|
|
bool BaseRegister::MatchAddr(uint32_t addr, uint32_t addr_mask) {
|
|
return ((addr & addr_mask) == (register_address_ & addr_mask));
|
|
}
|
|
|
|
bool BaseRegister::ProcessTransaction(bool *match, RegisterTransaction *trans) {
|
|
uint32_t read_val;
|
|
if (!MatchAddr(trans->csr_addr)) {
|
|
return false;
|
|
}
|
|
*match = true;
|
|
switch (trans->csr_op) {
|
|
case kCSRRead:
|
|
read_val = RegisterRead();
|
|
break;
|
|
case kCSRWrite:
|
|
read_val = RegisterWrite(trans->csr_wdata);
|
|
break;
|
|
case kCSRSet:
|
|
read_val = RegisterSet(trans->csr_wdata);
|
|
break;
|
|
case kCSRClear:
|
|
read_val = RegisterClear(trans->csr_wdata);
|
|
break;
|
|
}
|
|
if (trans->csr_addr == kCSRMCycle || trans->csr_addr == kCSRMCycleH) {
|
|
// MCycle(H) can increment or even overflow without TB interaction
|
|
if (trans->csr_rdata < read_val) {
|
|
std::cout << "MCycle(H) overflow detected" << std::endl;
|
|
}
|
|
// else if (read_val != trans->csr_rdata) {
|
|
// std::cout << "MCycle(H) incrementing as expected" << std::endl;
|
|
//}
|
|
// Don't panic about MCycle(H) incrementing, this is expected behavior as
|
|
// the clock is always running. Silently ignore mismatches for MCycle(H).
|
|
} else if (read_val != trans->csr_rdata) {
|
|
std::cout << "Error, transaction:" << std::endl;
|
|
trans->Print();
|
|
std::cout << "Expected rdata: " << std::hex << read_val << std::dec
|
|
<< std::endl;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void BaseRegister::RegisterReset() { register_value_ = 0; }
|
|
|
|
uint32_t BaseRegister::RegisterRead() { return register_value_; }
|
|
|
|
uint32_t BaseRegister::GetLockMask() { return 0; }
|
|
|
|
BaseRegister *BaseRegister::GetRegisterFromMap(uint32_t addr) {
|
|
for (auto ® : *map_pointer_) {
|
|
if (reg->MatchAddr(addr)) {
|
|
return reg.get();
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
MSeccfgRegister::MSeccfgRegister(
|
|
uint32_t addr, std::vector<std::unique_ptr<BaseRegister>> *map_pointer)
|
|
: BaseRegister(addr, map_pointer) {}
|
|
|
|
bool MSeccfgRegister::AnyPmpCfgsLocked() {
|
|
for (auto ® : *map_pointer_) {
|
|
// Iterate through PMPCfgX CSRs, returning true is any has a lock bit set
|
|
if (reg->MatchAddr(kCSRPMPCfg0, 0xfffffffc)) {
|
|
if ((reg->RegisterRead() & 0x80808080) != 0) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint32_t MSeccfgRegister::GetLockMask() {
|
|
uint32_t lock_mask = 0xFFFFFFF8;
|
|
|
|
// When RLB == 0 and any PMPCfgX has a lock bit set RLB must remain 0
|
|
if (AnyPmpCfgsLocked() && ((register_value_ & kMSeccfgRlb) == 0)) {
|
|
lock_mask |= kMSeccfgRlb;
|
|
}
|
|
|
|
// Once set MMWP cannot be unset
|
|
if (register_value_ & kMSeccfgMmwp) {
|
|
lock_mask |= kMSeccfgMmwp;
|
|
}
|
|
|
|
// Once set MML cannot be unset
|
|
if (register_value_ & kMSeccfgMml) {
|
|
lock_mask |= kMSeccfgMml;
|
|
}
|
|
|
|
return lock_mask;
|
|
}
|
|
|
|
uint32_t PmpCfgRegister::GetLockMask() {
|
|
BaseRegister *mseccfg = GetRegisterFromMap(kCSRMSeccfg);
|
|
assert(mseccfg);
|
|
|
|
if (mseccfg->RegisterRead() & kMSeccfgRlb) {
|
|
return 0;
|
|
}
|
|
|
|
uint32_t lock_mask = 0;
|
|
if (register_value_ & 0x80)
|
|
lock_mask |= 0xFF;
|
|
if (register_value_ & 0x8000)
|
|
lock_mask |= 0xFF00;
|
|
if (register_value_ & 0x800000)
|
|
lock_mask |= 0xFF0000;
|
|
if (register_value_ & 0x80000000)
|
|
lock_mask |= 0xFF000000;
|
|
return lock_mask;
|
|
}
|
|
|
|
uint32_t PmpCfgRegister::RegisterWrite(uint32_t newval) {
|
|
uint32_t lock_mask = GetLockMask();
|
|
uint32_t read_value = register_value_;
|
|
|
|
register_value_ &= lock_mask;
|
|
register_value_ |= (newval & ~lock_mask);
|
|
register_value_ = HandleReservedVals(register_value_);
|
|
|
|
return read_value;
|
|
}
|
|
|
|
uint32_t PmpCfgRegister::RegisterSet(uint32_t newval) {
|
|
uint32_t lock_mask = GetLockMask();
|
|
uint32_t read_value = register_value_;
|
|
|
|
register_value_ |= (newval & ~lock_mask);
|
|
register_value_ = HandleReservedVals(register_value_);
|
|
|
|
return read_value;
|
|
}
|
|
|
|
uint32_t PmpCfgRegister::RegisterClear(uint32_t newval) {
|
|
uint32_t lock_mask = GetLockMask();
|
|
uint32_t read_value = register_value_;
|
|
|
|
register_value_ &= (~newval | lock_mask);
|
|
register_value_ = HandleReservedVals(register_value_);
|
|
|
|
return read_value;
|
|
}
|
|
|
|
uint32_t PmpCfgRegister::HandleReservedVals(uint32_t cfg_val) {
|
|
BaseRegister *mseccfg = GetRegisterFromMap(kCSRMSeccfg);
|
|
assert(mseccfg);
|
|
|
|
cfg_val &= raz_mask_;
|
|
|
|
if (mseccfg->RegisterRead() & kMSeccfgMml) {
|
|
// No reserved L/R/W/X values when MML Set
|
|
return cfg_val;
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
// Reserved check, W = 1, R = 0
|
|
if (((cfg_val >> (8 * i)) & 0x3) == 0x2) {
|
|
cfg_val &= ~(0x3 << (8 * i));
|
|
}
|
|
}
|
|
|
|
return cfg_val;
|
|
}
|
|
|
|
uint32_t PmpAddrRegister::GetLockMask() {
|
|
// Calculate which region this is
|
|
uint32_t pmp_region = (register_address_ & 0xF);
|
|
// Form the address of the corresponding CFG register
|
|
uint32_t pmp_cfg_addr = 0x3A0 + (pmp_region / 4);
|
|
// Form the address of the CFG registerfor the next region
|
|
// For region 15, this will point to a non-existant register, which is fine
|
|
uint32_t pmp_cfg_plus1_addr = 0x3A0 + ((pmp_region + 1) / 4);
|
|
uint32_t cfg_value = 0;
|
|
uint32_t cfg_plus1_value = 0;
|
|
// Find and read the two CFG registers
|
|
for (auto it = map_pointer_->begin(); it != map_pointer_->end(); ++it) {
|
|
if ((*it)->MatchAddr(pmp_cfg_addr)) {
|
|
cfg_value = (*it)->RegisterRead();
|
|
}
|
|
if ((*it)->MatchAddr(pmp_cfg_plus1_addr)) {
|
|
cfg_plus1_value = (*it)->RegisterRead();
|
|
}
|
|
}
|
|
// Shift to the relevant bits in the CFG registers
|
|
cfg_value >>= ((pmp_region & 0x3) * 8);
|
|
cfg_plus1_value >>= (((pmp_region + 1) & 0x3) * 8);
|
|
// Locked if the lock bit is set, or the next region is TOR
|
|
if ((cfg_value & 0x80) || ((cfg_plus1_value & 0x18) == 0x8)) {
|
|
return 0xFFFFFFFF;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
uint32_t NonImpRegister::RegisterRead() { return 0; }
|
|
|
|
uint32_t NonImpRegister::RegisterWrite(uint32_t newval) { return 0; }
|
|
|
|
uint32_t NonImpRegister::RegisterSet(uint32_t newval) { return 0; }
|
|
|
|
uint32_t NonImpRegister::RegisterClear(uint32_t newval) { return 0; }
|
|
|
|
WARLRegister::WARLRegister(
|
|
uint32_t addr, std::vector<std::unique_ptr<BaseRegister>> *map_pointer,
|
|
uint32_t mask, uint32_t resval)
|
|
: BaseRegister{addr, map_pointer},
|
|
register_mask_(mask),
|
|
register_value_reset_(resval) {}
|
|
|
|
void WARLRegister::RegisterReset() { register_value_ = register_value_reset_; }
|
|
|
|
uint32_t WARLRegister::GetLockMask() { return register_mask_; }
|