274 lines
11 KiB
C#
274 lines
11 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using Antmicro.Renode.Core;
|
||
using Antmicro.Renode.Peripherals.Bus;
|
||
using Antmicro.Renode.Logging;
|
||
|
||
namespace Antmicro.Renode.Peripherals.Timers
|
||
{
|
||
public class Custom_RTC_R17V1 : IDoubleWordPeripheral, IKnownSize
|
||
{
|
||
// 寄存器偏移地址定义
|
||
private const uint CMD_ADDR_OFFSET = 0x00000000; // 命令寄存器
|
||
private const uint STATUS_ADDR_OFFSET = 0x00000004; // 状态寄存器
|
||
private const uint US_LOW_ADDR_OFFSET = 0x00000008; // 微秒低位
|
||
private const uint US_HIGH_ADDR_OFFSET = 0x0000000C; // 微秒高位
|
||
private const uint SEC_LOW_ADDR_OFFSET = 0x00000010; // 秒低位
|
||
private const uint SEC_HIGH_ADDR_OFFSET = 0x00000014; // 秒高位
|
||
|
||
// 寄存器存储
|
||
private readonly Dictionary<long, uint> registerMap;
|
||
private readonly object lockObject = new object();
|
||
private readonly uint baseAddress;
|
||
|
||
// 时间相关变量
|
||
private DateTime startTime;
|
||
private ulong baseOffsetUs;
|
||
private ulong baseOffsetSec;
|
||
private bool isTimeLocked;
|
||
private uint lockedUsLow;
|
||
private uint lockedUsHigh;
|
||
private uint lockedSecLow;
|
||
private uint lockedSecHigh;
|
||
|
||
public Custom_RTC_R17V1(IMachine machine)
|
||
{
|
||
baseAddress = 0x20800D80;
|
||
registerMap = new Dictionary<long, uint>();
|
||
|
||
// 设置启动时间对齐到整秒
|
||
var now = DateTime.Now;
|
||
startTime = new DateTime(now.Year, now.Month, now.Day,
|
||
now.Hour, now.Minute, now.Second,
|
||
DateTimeKind.Local);
|
||
|
||
InitializeRegisters();
|
||
LogInitializationInfo();
|
||
}
|
||
|
||
private void InitializeRegisters()
|
||
{
|
||
lock (lockObject)
|
||
{
|
||
// 初始化所有寄存器为0
|
||
registerMap[CMD_ADDR_OFFSET] = 0x00000000;
|
||
registerMap[STATUS_ADDR_OFFSET] = 0x00000000;
|
||
registerMap[US_LOW_ADDR_OFFSET] = 0x00000000;
|
||
registerMap[US_HIGH_ADDR_OFFSET] = 0x00000000;
|
||
registerMap[SEC_LOW_ADDR_OFFSET] = 0x00000000;
|
||
registerMap[SEC_HIGH_ADDR_OFFSET] = 0x00000000;
|
||
|
||
baseOffsetUs = 0;
|
||
baseOffsetSec = 0;
|
||
isTimeLocked = false;
|
||
lockedUsLow = 0;
|
||
lockedUsHigh = 0;
|
||
lockedSecLow = 0;
|
||
lockedSecHigh = 0;
|
||
}
|
||
}
|
||
|
||
private void LogInitializationInfo()
|
||
{
|
||
this.Log(LogLevel.Info, "==========================================");
|
||
this.Log(LogLevel.Info, "RTC实时时钟初始化完成");
|
||
this.Log(LogLevel.Info, "基地址: 0x{0:X8}", baseAddress);
|
||
this.Log(LogLevel.Info, "6个寄存器,32位有效");
|
||
this.Log(LogLevel.Info, "寄存器定义: CMD(写0xBB锁定时间), US_LOW+US_HIGH(微秒20位), SEC_LOW+SEC_HIGH(秒32位)");
|
||
this.Log(LogLevel.Info, "启动时间已对齐到整秒: {0}", startTime.ToString("HH:mm:ss.fff"));
|
||
this.Log(LogLevel.Info, "");
|
||
this.Log(LogLevel.Info, "寄存器地址映射:");
|
||
this.Log(LogLevel.Info, " CMD寄存器 : 地址0x{0:X8} (偏移0x00), 写入0xBB锁定时间", baseAddress + CMD_ADDR_OFFSET);
|
||
this.Log(LogLevel.Info, " STATUS寄存器 : 地址0x{0:X8} (偏移0x04), 只读", baseAddress + STATUS_ADDR_OFFSET);
|
||
this.Log(LogLevel.Info, " US_LOW寄存器 : 地址0x{0:X8} (偏移0x08), 微秒低16位", baseAddress + US_LOW_ADDR_OFFSET);
|
||
this.Log(LogLevel.Info, " US_HIGH寄存器: 地址0x{0:X8} (偏移0x0C), 微秒高4位", baseAddress + US_HIGH_ADDR_OFFSET);
|
||
this.Log(LogLevel.Info, " SEC_LOW寄存器: 地址0x{0:X8} (偏移0x10), 秒低16位", baseAddress + SEC_LOW_ADDR_OFFSET);
|
||
this.Log(LogLevel.Info, " SEC_HIGH寄存器: 地址0x{0:X8} (偏移0x14), 秒高16位(读取后自动解锁)", baseAddress + SEC_HIGH_ADDR_OFFSET);
|
||
this.Log(LogLevel.Info, "==========================================");
|
||
}
|
||
|
||
// 获取当前微秒值
|
||
private uint GetCurrentMicrosecondsLow()
|
||
{
|
||
var elapsed = DateTime.Now - startTime;
|
||
if (elapsed.TotalSeconds < 0)
|
||
{
|
||
this.Log(LogLevel.Error, "检测到系统时间回退!重置startTime");
|
||
startTime = DateTime.Now;
|
||
elapsed = TimeSpan.Zero;
|
||
}
|
||
|
||
double totalSeconds = elapsed.TotalSeconds + (double)baseOffsetSec;
|
||
double fractionalSeconds = totalSeconds - Math.Floor(totalSeconds);
|
||
ulong microseconds = (ulong)(fractionalSeconds * 1_000_000.0);
|
||
|
||
if (microseconds >= 1_000_000)
|
||
{
|
||
microseconds = 999_999;
|
||
}
|
||
|
||
return (uint)(microseconds & 0xFFFF);
|
||
}
|
||
|
||
private uint GetCurrentMicrosecondsHigh()
|
||
{
|
||
var elapsed = DateTime.Now - startTime;
|
||
if (elapsed.TotalSeconds < 0)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
double totalSeconds = elapsed.TotalSeconds + (double)baseOffsetSec;
|
||
double fractionalSeconds = totalSeconds - Math.Floor(totalSeconds);
|
||
ulong microseconds = (ulong)(fractionalSeconds * 1_000_000.0);
|
||
|
||
if (microseconds >= 1_000_000)
|
||
{
|
||
microseconds = 999_999;
|
||
}
|
||
|
||
return (uint)((microseconds >> 16) & 0xF);
|
||
}
|
||
|
||
// 获取当前秒值
|
||
private uint GetCurrentSecondsLow()
|
||
{
|
||
var elapsed = DateTime.Now - startTime;
|
||
if (elapsed.TotalSeconds < 0)
|
||
{
|
||
return (uint)(baseOffsetSec & 0xFFFF);
|
||
}
|
||
ulong seconds = baseOffsetSec + (ulong)elapsed.TotalSeconds;
|
||
return (uint)(seconds & 0xFFFF);
|
||
}
|
||
|
||
private uint GetCurrentSecondsHigh()
|
||
{
|
||
var elapsed = DateTime.Now - startTime;
|
||
if (elapsed.TotalSeconds < 0)
|
||
{
|
||
return (uint)((baseOffsetSec >> 16) & 0xFFFF);
|
||
}
|
||
ulong seconds = baseOffsetSec + (ulong)elapsed.TotalSeconds;
|
||
return (uint)((seconds >> 16) & 0xFFFF);
|
||
}
|
||
|
||
// 锁定时间
|
||
private void LockTime()
|
||
{
|
||
lockedUsLow = GetCurrentMicrosecondsLow();
|
||
lockedUsHigh = GetCurrentMicrosecondsHigh();
|
||
lockedSecLow = GetCurrentSecondsLow();
|
||
lockedSecHigh = GetCurrentSecondsHigh();
|
||
isTimeLocked = true;
|
||
|
||
this.Log(LogLevel.Debug, "时间已锁定: 微秒=0x{0:X4}{1:X4}, 秒=0x{2:X4}{3:X4}",
|
||
lockedUsHigh, lockedUsLow, lockedSecHigh, lockedSecLow);
|
||
}
|
||
|
||
public uint ReadDoubleWord(long offset)
|
||
{
|
||
lock (lockObject)
|
||
{
|
||
uint fullAddress = baseAddress + (uint)offset;
|
||
|
||
// 处理各个寄存器的读取
|
||
if (offset == US_LOW_ADDR_OFFSET)
|
||
{
|
||
uint value = isTimeLocked ? lockedUsLow : GetCurrentMicrosecondsLow();
|
||
this.Log(LogLevel.Info, "US_LOW读取: 地址0x{0:X8} (偏移0x08), 值0x{1:X4}",
|
||
fullAddress, value);
|
||
return value;
|
||
}
|
||
else if (offset == US_HIGH_ADDR_OFFSET)
|
||
{
|
||
uint value = isTimeLocked ? lockedUsHigh : GetCurrentMicrosecondsHigh();
|
||
this.Log(LogLevel.Info, "US_HIGH读取: 地址0x{0:X8} (偏移0x0C), 值0x{1:X4}",
|
||
fullAddress, value);
|
||
return value;
|
||
}
|
||
else if (offset == SEC_LOW_ADDR_OFFSET)
|
||
{
|
||
uint value = isTimeLocked ? lockedSecLow : GetCurrentSecondsLow();
|
||
this.Log(LogLevel.Info, "SEC_LOW读取: 地址0x{0:X8} (偏移0x10), 值0x{1:X4}",
|
||
fullAddress, value);
|
||
return value;
|
||
}
|
||
else if (offset == SEC_HIGH_ADDR_OFFSET)
|
||
{
|
||
uint value = isTimeLocked ? lockedSecHigh : GetCurrentSecondsHigh();
|
||
this.Log(LogLevel.Info, "SEC_HIGH读取: 地址0x{0:X8} (偏移0x14), 值0x{1:X4}",
|
||
fullAddress, value);
|
||
|
||
// 读取SEC_HIGH后自动解锁
|
||
if (isTimeLocked)
|
||
{
|
||
isTimeLocked = false;
|
||
this.Log(LogLevel.Debug, "时间自动解锁(读取SEC_HIGH后)");
|
||
}
|
||
|
||
return value;
|
||
}
|
||
else if (offset == STATUS_ADDR_OFFSET)
|
||
{
|
||
uint value = 0; // 状态寄存器始终返回0
|
||
this.Log(LogLevel.Info, "STATUS读取: 地址0x{0:X8} (偏移0x04), 值0x{1:X4}",
|
||
fullAddress, value);
|
||
return value;
|
||
}
|
||
|
||
this.Log(LogLevel.Warning, "尝试读取未定义的寄存器偏移: 0x{0:X8} (地址0x{1:X8})",
|
||
offset, fullAddress);
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
public void WriteDoubleWord(long offset, uint value)
|
||
{
|
||
lock (lockObject)
|
||
{
|
||
uint fullAddress = baseAddress + (uint)offset;
|
||
|
||
// 只允许写入CMD寄存器
|
||
if (offset == CMD_ADDR_OFFSET)
|
||
{
|
||
this.Log(LogLevel.Info, "CMD写入: 地址0x{0:X8} (偏移0x00), 值0x{1:X8}",
|
||
fullAddress, value);
|
||
|
||
if (value == 0xBB)
|
||
{
|
||
LockTime();
|
||
this.Log(LogLevel.Info, "时间锁定命令已执行");
|
||
}
|
||
else
|
||
{
|
||
this.Log(LogLevel.Warning, "未知的CMD命令: 0x{0:X8}", value);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
this.Log(LogLevel.Warning, "尝试写入未定义的寄存器偏移: 0x{0:X8} (地址0x{1:X8}), 值0x{2:X8}",
|
||
offset, fullAddress, value);
|
||
}
|
||
}
|
||
}
|
||
|
||
public void Reset()
|
||
{
|
||
lock (lockObject)
|
||
{
|
||
registerMap.Clear();
|
||
InitializeRegisters();
|
||
|
||
// 重置启动时间
|
||
var now = DateTime.Now;
|
||
startTime = new DateTime(now.Year, now.Month, now.Day,
|
||
now.Hour, now.Minute, now.Second,
|
||
DateTimeKind.Local);
|
||
}
|
||
this.Log(LogLevel.Info, "RTC (基地址0x{0:X8}) 已复位,时间重置", baseAddress);
|
||
}
|
||
|
||
public long Size => 0x80; // 4KB地址空间
|
||
}
|
||
} |