|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
#pragma once
#include <array>
#include <mutex>
#include "fixedqueue.h"
// ATC 実装種別。
//#define M68030_CUSTOM_ATC
#define ATC_ORDER64
// ATC エントリ数。
#define M68030_ATC_LINES (22)
#if defined(M68030_CUSTOM_ATC)
#else
# if defined(ATC_ORDER64)
# if M68030_ATC_LINES > 24
# error M68030_ATC_LINES must be <= 24 on ATC_ORDER64.
# endif
# if M68030_ATC_LINES < 13
# error M68030_ATC_LINES must be >= 13 on ATC_ORDER64.
# endif
# endif
#endif
class MPU68030Device;
// SRP, CRP
class m68030RP
{
// 6 5 4 3
// 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2
// +-+-----------------------------+---------------------------+---+
// |U| LIMIT | 0 |DT |
// +-+-----------------------------+---------------------------+---+
//
// 3 2 1 0
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +-------------------------------------------------------+-------+
// | TABLE ADDRESS (PA31-PA4) |unused |
// +-------------------------------------------------------+-------+
public:
static const uint32 H_MASK = 0xffff0003;
};
// TT レジスタの各フィールドを束ねた構造体のようなもの。
// MPU68030Device::SetTT(), GetTT() で読み書きする。
class m68030TT
{
// 3 2 1 0
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---------------+---------------+-+-+-+-+-+-+-+-+-+-----+-+-----+
// |LogicalAddrBase|LogicalAddrMask|E| |C|R|M| | FCB | | FCM |
// +---------------+---------------+-+-+-+-+-+-+-+-+-+-----+-+-----+
public:
static const uint32 MASK = 0xffff8777;
static const uint32 LBASE_MASK = 0xff000000;
static const uint32 LMASK_MASK = 0x00ff0000;
static const uint32 E = 0x00008000;
static const uint32 CI = 0x00000400;
static const uint32 RW = 0x00000200;
static const uint32 RWM = 0x00000100;
static const uint32 FCBASE_MASK = 0x00000070;
static const uint32 FCMASK_MASK = 0x00000007;
void Set(uint32 data);
// アクセスがこの TT と一致するか調べる。
bool Match(busaddr addr) const;
bool e {}; // イネーブル
bool ci {}; // キャッシュ禁止
private:
uint64 base {}; // ベース
uint64 mask {}; // マスク
};
// TC レジスタの各フィールドを束ねた構造体のようなもの。
// m68kcpu::SetTC(), GetTC() で読み書きする。
class m68030TC
{
// 3 2 1 0
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +-+---------+-+-+-------+-------+-------+-------+-------+-------+
// |E| 0 |S|F| PS | IS | TIA | TIB | TIC | TID |
// +-+---------+-+-+-------+-------+-------+-------+-------+-------+
// | | +-- FCL (Function Code Lookup Enable)
// | +---- SRE (Supervisor Root Pointer Enable)
// +-- Enable
public:
static const uint32 MASK = 0x83ffffff;
static const uint32 TC_E = 0x80000000;
static const uint32 TC_SRE = 0x02000000;
static const uint32 TC_FCL = 0x01000000;
// レジスタの各フィールド
uint32 e {}; // TC_E の位置
uint32 sre {}; // TC_SRE の位置
uint32 fcl {}; // TC_FCL の位置
uint ps {};
uint is {};
// 68030 のテーブルは最大4階層 (TIA 〜 TID) で、
// TIA から termlv 段までが有効。
union {
uint tix[4] {};
struct {
uint tia;
uint tib;
uint tic;
uint tid;
} __packed;
};
int termlv {}; // 有効な段数 (例えば TIB までなら 2)
int shift[4] {}; // TIA-TID の各部分のシフト量
uint32 mask[4] {}; // TIA-TID の各部分のマスク
uint32 lmask {}; // TIA-TID 全体のマスク
uint32 pgmask {}; // PS 部分のマスク
public:
// 値を代入し、二次変数も用意する。
// 構成例外を起こすなら false を返す。
bool Set(uint32);
// (E に関わらず) 構成が有効なら true を返す。
bool ConfigOK() const { return config_ok; }
private:
bool Check() const;
void MakeMask();
bool config_ok {}; // (E に関わらず) 構成が有効なら true
};
class m68030MMUSR
{
public:
static const uint16 MASK = 0xee43;
static const uint16 B = 0x8000; // バスエラー
static const uint16 L = 0x4000; // リミット違反
static const uint16 S = 0x2000; // スーパバイザ専用
static const uint16 W = 0x0800; // 書き込み保護
static const uint16 I = 0x0400; // 無効
static const uint16 M = 0x0200; // 修正
static const uint16 T = 0x0040; // 透過アクセス
static const uint16 N = 0x0007; // レベル数
};
// ATC の1エントリ
struct m68030ATCLine
{
// tag:
// 3 2 1 0
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---------------+---------------+---------------+-------+-----+-+
// | Logical Address [31:8] | 0 | FC |I|
// +---------------+---------------+---------------+-------+-----+-+
// I: Invalid (%1で無効)。
// FC: busaddr 形式の FC (210 の順) で、_Motorola のみで使用。
// _Custom のほうは FC 別のテーブルなのでここは使わない。
//
// paddr:
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---------------+---------------+---------------+---------------+
// | Physical Address [31:8] | 0 |
// +---------------+---------------+---------------+---------------+
//
// data:
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---------------+---------------+---------------+-------+-+-+-+-+
// | 0 |B|C|P|M|
// +---------------+---------------+---------------+-------+-+-+-+-+
// B: Bus Error
// C: Cache Inhibit
// P: Write Protect
// M: Modified
static const uint32 LADDR_MASK = 0xffffff00;
static const uint32 INVALID = 0x00000001;
static const uint32 PADDR_MASK = 0xffffff00;
static const uint32 BUSERROR = 0x00000008;
static const uint32 CINHIBIT = 0x00000004;
static const uint32 WPROTECT = 0x00000002;
static const uint32 MODIFIED = 0x00000001;
uint32 tag;
uint32 paddr;
uint32 data;
#if defined(M68030_CUSTOM_ATC)
uint32 age;
#endif
uint32 GetTag() const { return tag; }
void Invalidate() { tag |= INVALID; }
bool IsInvalid() const { return tag & INVALID; }
bool IsValid() const { return !IsInvalid(); }
uint32 GetLAddr() const { return tag & LADDR_MASK; }
#if !defined(M68030_CUSTOM_ATC)
uint32 GetFC() const { return (tag >> 1) & 7; }
#endif
uint32 GetPAddr() const { return paddr; }
bool IsBusError() const { return data & BUSERROR; }
bool IsCInhibit() const { return data & CINHIBIT; }
bool IsWProtect() const { return data & WPROTECT; }
bool IsModified() const { return data & MODIFIED; }
};
// ATCTable_* の共通部分だが、ポリモーフィズム (virtual) は使わずに書く。
class m68030ATCTable
{
public:
static constexpr int LINES = M68030_ATC_LINES;
// 論理アドレスマスクを設定する。
void SetMask(uint32 lmask_) {
lmask = lmask_;
}
protected:
// デバッグ表示用
static std::string LineToStrCommon(const m68030ATCLine *, uint fc);
public:
int loglevel {};
protected:
// cpu->mmu_tc.lmask のコピー。
uint32 lmask {};
MPU68030Device *cpu {};
};
#if defined(M68030_CUSTOM_ATC)
//
// カスタム ATC。FC を分離し、TT と ATC を同時に検索する魔改造版。
//
struct m68030ATCStat
{
uint32 total {}; // アドレス変換した回数 (TC と TT 両方)
uint32 tthit {}; // TT にヒットした回数
uint32 miss {}; // ATC ミスした回数
void Clear() {
total = 0;
tthit = 0;
miss = 0;
}
};
class m68030ATCTable_Custom : public m68030ATCTable
{
public:
// ATC ハッシュ
enum : uint8 {
HASH_ATC_BASE = 0, // ATC #0-
HASH_NONE = 0xff, // 対応なし
HASH_TT = 0xfe, // TT
HASH_SUB = 0xfd, // PS<4KB
};
m68030ATCTable_Custom(MPU68030Device *cpu_, uint fc_);
// 統計情報、移動平均等をリセットする。
void ResetStat();
// ハッシュを完全に作り直す。
void Rehash();
// 論理アドレスからタグを作成する。
uint32 MakeTag(const busaddr laddr) const {
return (laddr.Addr() & lmask);
}
// フラッシュ
// Flush() はこのテーブルのすべてのエントリが対象。
// Flush(addr) はこのテーブルのアドレスが一致するエントリが対象。
void Flush();
void Flush(uint32 addr);
m68030ATCLine *Pop();
// PS<4KB の時に ATC を検索。
m68030ATCLine *FindSub(const busaddr laddr);
void Invalidate(m68030ATCLine *a);
void InvalidateSub(const m68030ATCLine *);
void ClearHash();
void MakeHash(const m68030ATCLine *);
void MoveHead(m68030ATCLine *);
// デバッグ表示
std::string LineToStr(const m68030ATCLine *a) const;
// エントリの実体。
std::array<m68030ATCLine, LINES> line {};
// 統計情報。
m68030ATCStat stat {};
// 移動平均用バッファ。(こっちは排他制御が必要)
FixedQueue<m68030ATCStat, 3> hist {};
bool hist_tt_once_hit {};
std::mutex hist_mtx {};
private:
uint32 latest_age {};
// このテーブルの FC
uint fc {};
// PS(ページのビット長) - 12。負数なら PS<4KB
int ps4k {};
public:
// ATC ハッシュ (他のメンバより後ろに置く?)
std::array<uint8, 1024 * 1024> hash {};
};
#else
//
// Motorola ATC
//
struct m68030ATCStat
{
// 先頭からn番目のヒット回数
std::array<uint64, M68030_ATC_LINES> hit {};
// ATC 検索ミス回数
uint64 miss {};
void Clear() {
hit = {};
miss = 0;
}
};
class m68030ATCTable_Motorola : public m68030ATCTable
{
friend class MPU68030Device;
#if defined(ATC_ORDER64)
static constexpr int ORDER0_COUNT = 12;
static constexpr int ORDER1_COUNT = M68030_ATC_LINES - ORDER0_COUNT;
#endif
public:
explicit m68030ATCTable_Motorola(MPU68030Device *cpu_);
// 統計情報、移動平均等をリセットする。
void ResetStat();
// 論理アドレスからタグを作成する。
uint32 MakeTag(const busaddr laddr) const {
return (laddr.Addr() & lmask) | (laddr.GetFC() << 1);
}
// エントリのフラッシュ。(pflush* 命令から呼ばれる)
void Flush();
void Flush(uint32 fc, uint32 mask);
void Flush(uint32 fc, uint32 mask, uint32 addr);
// laddr を検索して見付かれば先頭に移動。
m68030ATCLine *LookupAndMove(const busaddr laddr);
// laddr を検索して見付かったのを返す。(PTEST 用)
const m68030ATCLine *LookupOnly(const busaddr laddr);
// laddr を検索して返す。なければ NULL を返す。(デバッガ用)
const m68030ATCLine *LookupByDebugger(const busaddr laddr) {
// 今は Only と同じ。
return LookupOnly(laddr);
}
// 空きもしくは最も古いエントリを先頭に移動。
m68030ATCLine *Pop();
// pri 番目の idx を返す。
uint GetIdx(uint pri) const
{
#if defined(ATC_ORDER64)
if (__predict_true(pri < ORDER0_COUNT)) {
return (order[0] >> (pri * 5)) & 0x1f;
} else {
return (order[1] >> ((pri - ORDER0_COUNT) * 5)) & 0x1f;
}
#else
return order[pri];
#endif
}
// pri 番目のエントリを返す。
m68030ATCLine *GetLine(uint pri) {
return &line[GetIdx(pri)];
}
// デバッグ表示
static std::string LineToStr(const m68030ATCLine *);
private:
// pri 番目の値を idx にする。
void Set(uint pri, uint idx);
int Lookup(const busaddr);
// pri 番目の要素を先頭に移動。
void MoveHead(uint pri);
// pri 番目の要素を末尾に移動。
void MoveTail(uint pri);
// エントリの実体。
std::array<m68030ATCLine, LINES> line {};
// 順序管理。
#if defined(ATC_ORDER64)
std::array<uint64, 2> order {};
#else
std::array<uint8, LINES> order {};
#endif
// 統計情報。
m68030ATCStat stat {};
};
#endif // M68030_CUSTOM_ATC
class m68030ATC
{
public:
explicit m68030ATC(MPU68030Device *cpu_);
// ログレベルを設定する。Object のとは同じ使い方だけど別物。
void SetLogLevel(int loglevel_);
// TC の設定変更に追従する。
void SetTC(const m68030TC&);
#if defined(M68030_CUSTOM_ATC)
// ハッシュを完全に作り直す。
void Rehash();
#endif
// テーブルを取得する。
#if defined(M68030_CUSTOM_ATC)
m68030ATCTable_Custom *GetTable(uint fc) {
assert((bool)fctables[fc]);
return fctables[fc];
}
#else
m68030ATCTable_Motorola *GetTable() {
return table.get();
}
#endif
// エントリのフラッシュ。(pflush* 命令から呼ばれる)
void FlushAll();
void Flush(uint32 fc, uint32 mask);
void Flush(uint32 fc, uint32 mask, uint32 addr);
// 統計情報、移動平均等をリセットする。
void ResetStat();
// 統計情報、移動平均等を更新する。
void CalcStat();
private:
#if defined(M68030_CUSTOM_ATC)
// FC 別テーブルは 4つ。こっちはループで回せる。
std::array<std::unique_ptr<m68030ATCTable_Custom>, 4> alltables {};
// fc で検索する場合。実際にはこのうち 4つだけが有効。
std::array<m68030ATCTable_Custom *, 8> fctables {};
#else
// テーブルは1つ。
std::unique_ptr<m68030ATCTable_Motorola> table {};
#endif
int loglevel {};
MPU68030Device *cpu {};
};
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.