|
|
nono 1.7.0
//
// nono
// Copyright (C) 2023 nono project
// Licensed under nono-license.txt
//
//
// HD647180 I/O デバイス
//
#include "mpu64180.h"
#include "event.h"
#include "hd647180.h"
#include "scheduler.h"
//
// タイマー (Programmable Reload Timer)
//
// タイマーを開始する時 つまり TDE{0,1} の立ち上がりで呼ぶ。
// 該当チャンネルの running をセットしてから呼ぶこと。
void
MPU64180Device::EnableTimer()
{
// すでに他方が開始しているなら、もう何もしなくていい
if (timer[0].running && timer[1].running) {
return;
} else if (timer[0].running == false && timer[1].running == false) {
PANIC("must be called when running");
}
// TDE を立ててから1回目の TDR の減算が始まるまでの時間は
// 0 < t < 20φ と書いてあるので (HD647180.pdf p.112, Figure 13-3)、
// タイマーの tick はタイマー開始時点からカウントし始める方式ではなく、
// フリーランしている tick が来た時に処理をする方式のようだ。
// これの開始点はおそらくリセット時なので、
// 停止していたのを開始する時だけ次の tick までの時間を求める。
uint64 now = scheduler->GetVirtTime() - timer_epoch;
uint64 tick = now / (clock_tsec * 20);
uint64 next = (tick + 1) * (clock_tsec * 20);
timer_event->time = next - now;
scheduler->StartEvent(timer_event);
}
// タイマー系の割り込み線の状態を変更する。
// HD647180.pdf p.112, Figure 13-5。
// TIEn, TIFn, IEF1 の変更でコールすること。
void
MPU64180Device::ChangeTimerInterrupt()
{
for (int ch = 0; ch < timer.size(); ch++) {
auto& t = timer[ch];
if (t.tif != 0 && t.intr_enable && GetIEF1()) {
AssertIntmap(HD647180::IntmapTimer(ch));
} else {
NegateIntmap(HD647180::IntmapTimer(ch));
}
}
}
// タイマーイベント
void
MPU64180Device::TimerCallback(Event *ev)
{
// アクティブがなければ停止
if (active_timer.empty()) {
return;
}
// 少なくとも片方は有効
for (auto tp : active_timer) {
auto& t = *tp;
if (__predict_false(t.count == 0)) {
// 0 になった次の tick でリロード
t.count = t.reload;
} else {
t.count--;
// 0 になったら割り込みを上げる
if (t.count == 0) {
t.tif = 0x03;
ChangeTimerInterrupt();
}
}
}
// タイマーへの入力は原クロックの20倍なので、
// 厳密には 162.7604… * 20 = 3.255… [nsec] だが、
// ここでは clock_tsec * 20 = 3.260 [nsec] とする。
// この代入は本当は1回目だけでいいのだが。
timer_event->time = clock_tsec * 20;
scheduler->RestartEvent(ev);
}
// アクティブタイマーリストを(再)構成する。
// 数が少ないので毎回作り直す。
// running を変更するたびに呼ぶこと。
void
MPU64180Device::MakeActiveTimer()
{
active_timer.clear();
for (int ch = 0; ch < timer.size(); ch++) {
auto& t = timer[ch];
if (t.running) {
active_timer.push_back(&t);
}
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.