442 lines
15 KiB
C#
442 lines
15 KiB
C#
|
|
//============================================================================
|
|||
|
|
// 作者:liuwenbo
|
|||
|
|
// 日期:2026-03-24-2026-03-25
|
|||
|
|
//============================================================================
|
|||
|
|
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Collections.ObjectModel;
|
|||
|
|
using Antmicro.Renode.Core;
|
|||
|
|
using Antmicro.Renode.Core.Structure.Registers;
|
|||
|
|
using Antmicro.Renode.Logging;
|
|||
|
|
using Antmicro.Renode.Peripherals.Bus;
|
|||
|
|
using Antmicro.Renode.Time;
|
|||
|
|
using Antmicro.Renode.Peripherals;
|
|||
|
|
using Antmicro.Renode.Peripherals.Timers;
|
|||
|
|
using Antmicro.Renode.Peripherals.IRQControllers;
|
|||
|
|
|
|||
|
|
namespace Antmicro.Renode.Peripherals.Timers
|
|||
|
|
{
|
|||
|
|
public class LC3233_TaskTimer : BasicDoubleWordPeripheral, IKnownSize
|
|||
|
|
{
|
|||
|
|
public LC3233_TaskTimer(IMachine machine) : base(machine)
|
|||
|
|
{
|
|||
|
|
this.machine = machine;
|
|||
|
|
|
|||
|
|
DefineRegisters();
|
|||
|
|
Reset();
|
|||
|
|
|
|||
|
|
this.Log(LogLevel.Info, "LC3233 定时器已初始化 @ 0x80000000");
|
|||
|
|
this.Log(LogLevel.Info, " 周期: 250ms (4Hz), 用于VxWorks任务调度");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ScheduleNextTick()
|
|||
|
|
{
|
|||
|
|
if(timerScheduled || (timerCtrl & TIMER_CTRL_T0_EN) == 0)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
timerScheduled = true;
|
|||
|
|
var generation = timerGeneration;
|
|||
|
|
machine.ScheduleAction(TimeInterval.FromMilliseconds(250), _ =>
|
|||
|
|
{
|
|||
|
|
timerScheduled = false;
|
|||
|
|
|
|||
|
|
if(generation != timerGeneration || (timerCtrl & TIMER_CTRL_T0_EN) == 0)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
OnTimerExpired();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnTimerExpired()
|
|||
|
|
{
|
|||
|
|
timerExpiredCount++;
|
|||
|
|
|
|||
|
|
//isPending = true;
|
|||
|
|
|
|||
|
|
this.Log(LogLevel.Info, "========== 定时器溢出 #{0} ==========", timerExpiredCount);
|
|||
|
|
this.Log(LogLevel.Info, "准备触发中断线4...");
|
|||
|
|
|
|||
|
|
IRQ.Set(true);
|
|||
|
|
|
|||
|
|
this.Log(LogLevel.Info, "已调用 IRQ.Set(true),信号应发送到 lc3233IntCtrl@4");
|
|||
|
|
|
|||
|
|
machine.ScheduleAction(TimeInterval.FromMicroseconds(1),
|
|||
|
|
_ => {
|
|||
|
|
IRQ.Set(false);
|
|||
|
|
this.Log(LogLevel.Debug, "中断脉冲结束");
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
|
|||
|
|
if ((timerCtrl & TIMER_CTRL_T0_LOEN) != 0)
|
|||
|
|
{
|
|||
|
|
timer0Cntr = timer0Reld;
|
|||
|
|
ScheduleNextTick();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DefineRegisters()
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
Registers.TimerCtrl.Define(this)
|
|||
|
|
.WithValueField(0, 32, name: "timer_ctrl",
|
|||
|
|
writeCallback: (_, val) =>
|
|||
|
|
{
|
|||
|
|
timerCtrl = (uint)val;
|
|||
|
|
|
|||
|
|
// Bit 0: Timer0 Enable
|
|||
|
|
bool t0Enable = (val & TIMER_CTRL_T0_EN) != 0;
|
|||
|
|
if(!t0Enable)
|
|||
|
|
{
|
|||
|
|
timerGeneration++;
|
|||
|
|
timerScheduled = false;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
timerGeneration++;
|
|||
|
|
timerScheduled = false;
|
|||
|
|
ScheduleNextTick();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.Log(LogLevel.Info, "TIMER_CTRL 写入: 0x{0:X8}", val);
|
|||
|
|
this.Log(LogLevel.Info, " Timer0 Enable: {0}", t0Enable);
|
|||
|
|
this.Log(LogLevel.Info, " Auto Reload: {0}", (val & TIMER_CTRL_T0_LOEN) != 0);
|
|||
|
|
},
|
|||
|
|
valueProviderCallback: _ => timerCtrl);
|
|||
|
|
|
|||
|
|
Registers.Timer0Cntr.Define(this)
|
|||
|
|
.WithValueField(0, 32, name: "timer0_cntr",
|
|||
|
|
writeCallback: (_, val) =>
|
|||
|
|
{
|
|||
|
|
timer0Cntr = (uint)val;
|
|||
|
|
this.Log(LogLevel.Debug, "TIMER0_CNTR 写入: 0x{0:X8}", val);
|
|||
|
|
},
|
|||
|
|
valueProviderCallback: _ => timer0Cntr);
|
|||
|
|
|
|||
|
|
Registers.Timer0Reld.Define(this)
|
|||
|
|
.WithValueField(0, 32, name: "timer0_reld",
|
|||
|
|
writeCallback: (_, val) =>
|
|||
|
|
{
|
|||
|
|
timer0Reld = (uint)val;
|
|||
|
|
this.Log(LogLevel.Debug, "TIMER0_RELD 写入: 0x{0:X8}", val);
|
|||
|
|
},
|
|||
|
|
valueProviderCallback: _ => timer0Reld);
|
|||
|
|
|
|||
|
|
Registers.Timer0DivFreq.Define(this)
|
|||
|
|
.WithValueField(0, 32, name: "timer0_divfreq",
|
|||
|
|
writeCallback: (_, val) =>
|
|||
|
|
{
|
|||
|
|
timer0DivFreq = (uint)val;
|
|||
|
|
this.Log(LogLevel.Debug, "TIMER0_DIVFREQ 写入: 0x{0:X8}", val);
|
|||
|
|
},
|
|||
|
|
valueProviderCallback: _ => timer0DivFreq);
|
|||
|
|
|
|||
|
|
Registers.Timer0DfReld.Define(this)
|
|||
|
|
.WithValueField(0, 32, name: "timer0_df_reld",
|
|||
|
|
writeCallback: (_, val) =>
|
|||
|
|
{
|
|||
|
|
timer0DfReld = (uint)val;
|
|||
|
|
this.Log(LogLevel.Debug, "TIMER0_DF_RELD 写入: 0x{0:X8}", val);
|
|||
|
|
},
|
|||
|
|
valueProviderCallback: _ => timer0DfReld);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public override void Reset()
|
|||
|
|
{
|
|||
|
|
base.Reset();
|
|||
|
|
timerCtrl = TIMER_CTRL_T0_EN | TIMER_CTRL_T0_LOEN;
|
|||
|
|
timer0Cntr = 0;
|
|||
|
|
timer0Reld = 0;
|
|||
|
|
timer0DivFreq = 0;
|
|||
|
|
timer0DfReld = 0;
|
|||
|
|
isPending = false;
|
|||
|
|
timerExpiredCount = 0;
|
|||
|
|
timerGeneration++;
|
|||
|
|
timerScheduled = false;
|
|||
|
|
ScheduleNextTick();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public long Size => 0x100;
|
|||
|
|
|
|||
|
|
|
|||
|
|
public GPIO IRQ { get; } = new GPIO();
|
|||
|
|
|
|||
|
|
|
|||
|
|
private enum Registers
|
|||
|
|
{
|
|||
|
|
TimerCtrl = 0x00,
|
|||
|
|
Timer0Cntr = 0x04,
|
|||
|
|
Timer0Reld = 0x08,
|
|||
|
|
WdgCntr = 0x0C,
|
|||
|
|
Timer1Cntr = 0x10,
|
|||
|
|
Timer1Reld = 0x14,
|
|||
|
|
Timer0DivFreq = 0x18,
|
|||
|
|
Timer0DfReld = 0x1C,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private const uint TIMER_CTRL_T0_EN = 0x00000001;
|
|||
|
|
private const uint TIMER_CTRL_T0_LOEN = 0x00000002;
|
|||
|
|
private const uint TIMER_CTRL_T0_RSTA = 0x00000004;
|
|||
|
|
|
|||
|
|
|
|||
|
|
private readonly IMachine machine;
|
|||
|
|
private uint timerCtrl;
|
|||
|
|
private uint timer0Cntr;
|
|||
|
|
private uint timer0Reld;
|
|||
|
|
private uint timer0DivFreq;
|
|||
|
|
private uint timer0DfReld;
|
|||
|
|
private bool isPending;
|
|||
|
|
private bool timerScheduled;
|
|||
|
|
private ulong timerGeneration;
|
|||
|
|
private ulong timerExpiredCount;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
namespace Antmicro.Renode.Peripherals.IRQControllers
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
public class LC3233_InterruptController : BasicDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput, IGPIOReceiver
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
public LC3233_InterruptController(IMachine machine) : base(machine)
|
|||
|
|
{
|
|||
|
|
this.machine = machine;
|
|||
|
|
|
|||
|
|
|
|||
|
|
connections = new Dictionary<int, IGPIO>();
|
|||
|
|
for (int i = 0; i <= 15; i++)
|
|||
|
|
{
|
|||
|
|
connections[i] = new GPIO();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Connections = new ReadOnlyDictionary<int, IGPIO>(connections);
|
|||
|
|
DefineRegisters();
|
|||
|
|
Reset();
|
|||
|
|
|
|||
|
|
this.Log(LogLevel.Info, "LC3233 中断控制器已初始化 @ 0x80020000");
|
|||
|
|
this.Log(LogLevel.Info, " 管理15个中断源 (1-15)");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 定义寄存器映射
|
|||
|
|
/// </summary>
|
|||
|
|
private void DefineRegisters()
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
// Registers.IntMaskPriority.Define(this)
|
|||
|
|
// .WithValueField(0, 32, name: "int_mask_priority",
|
|||
|
|
// writeCallback: (_, val) =>
|
|||
|
|
// {
|
|||
|
|
// intMaskPriority = (uint)val;
|
|||
|
|
// this.Log(LogLevel.Info, "INT_MASK_PRIORITY 写入: 0x{0:X8}", val);
|
|||
|
|
// this.Log(LogLevel.Info, " 临时兼容: IRQ4 强制视为使能");
|
|||
|
|
|
|||
|
|
|
|||
|
|
// for (int i = 1; i <= 15; i++)
|
|||
|
|
// {
|
|||
|
|
// if ((val & (uint)(1 << i)) != 0)
|
|||
|
|
// {
|
|||
|
|
// this.Log(LogLevel.Debug, " 中断 {0} 已使能", i);
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// UpdateInterrupts();
|
|||
|
|
// },
|
|||
|
|
// valueProviderCallback: _ => intMaskPriority);
|
|||
|
|
|
|||
|
|
|
|||
|
|
// Registers.IntFTP.Define(this)
|
|||
|
|
// .WithValueField(0, 32, name: "int_f_t_p",
|
|||
|
|
// writeCallback: (_, val) =>
|
|||
|
|
// {
|
|||
|
|
|
|||
|
|
// intFTP |= (uint)val;
|
|||
|
|
// this.Log(LogLevel.Debug, "INT_F_T_P 写入: 0x{0:X8}, 当前值: 0x{1:X8}", val, intFTP);
|
|||
|
|
// UpdateInterrupts();
|
|||
|
|
// },
|
|||
|
|
// valueProviderCallback: _ => intFTP);
|
|||
|
|
|
|||
|
|
|
|||
|
|
// Registers.IntForce.Define(this)
|
|||
|
|
// .WithValueField(0, 32, FieldMode.Write, name: "int_force",
|
|||
|
|
// writeCallback: (_, val) =>
|
|||
|
|
// {
|
|||
|
|
// this.Log(LogLevel.Info, "INT_FORCE 写入: 0x{0:X8}", val);
|
|||
|
|
|
|||
|
|
|
|||
|
|
// intFTP |= (uint)val;
|
|||
|
|
|
|||
|
|
// UpdateInterrupts();
|
|||
|
|
// });
|
|||
|
|
|
|||
|
|
|
|||
|
|
// Registers.IntClr.Define(this)
|
|||
|
|
// .WithValueField(0, 32, FieldMode.Write, name: "int_clr",
|
|||
|
|
// writeCallback: (_, val) =>
|
|||
|
|
// {
|
|||
|
|
// this.Log(LogLevel.Debug, "INT_CLR 写入: 0x{0:X8}", val);
|
|||
|
|
|
|||
|
|
|
|||
|
|
// intFTP &= ~(uint)val;
|
|||
|
|
|
|||
|
|
// UpdateInterrupts();
|
|||
|
|
// });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
public void OnGPIO(int irq, bool value)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
if (irq < 0 || irq > 15)
|
|||
|
|
{
|
|||
|
|
this.Log(LogLevel.Error, "无效的中断号: {0}", irq);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(value)
|
|||
|
|
{
|
|||
|
|
this.Log(LogLevel.Info, "*** 中断控制器接收到信号 IRQ{0}: {1} ***", irq, value ? "拉高" : "拉低");
|
|||
|
|
|
|||
|
|
// 自动使能中断
|
|||
|
|
intMaskPriority |= (uint)(1 << irq);
|
|||
|
|
this.Log(LogLevel.Info, "自动使能中断 {0}: INT_MASK_PRIORITY = 0x{1:X8}",
|
|||
|
|
irq, intMaskPriority);
|
|||
|
|
|
|||
|
|
//设置挂起寄存器
|
|||
|
|
intFTP |= (uint)(1 << irq);
|
|||
|
|
this.Log(LogLevel.Info, "设置挂起位: INT_F_T_P = 0x{0:X8}", intFTP);
|
|||
|
|
|
|||
|
|
UpdateInterrupts();
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//this.Log(LogLevel.Info, "调用 UpdateInterrupts() 检查是否转发到CPU...");
|
|||
|
|
//UpdateInterrupts();
|
|||
|
|
|
|||
|
|
// if (irq < 0 || irq > 15)
|
|||
|
|
// {
|
|||
|
|
// this.Log(LogLevel.Error, "无效的中断号: {0}", irq);
|
|||
|
|
// return;
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// this.Log(LogLevel.Info, "*** 中断控制器接收到信号 IRQ{0}: {1} ***", irq, value ? "拉高" : "拉低");
|
|||
|
|
|
|||
|
|
// if (value)
|
|||
|
|
// {
|
|||
|
|
|
|||
|
|
// intFTP |= (uint)(1 << irq);
|
|||
|
|
// this.Log(LogLevel.Info, "设置挂起位: INT_F_T_P = 0x{0:X8}", intFTP);
|
|||
|
|
// }
|
|||
|
|
// else
|
|||
|
|
// {
|
|||
|
|
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// this.Log(LogLevel.Info, "调用 UpdateInterrupts() 检查是否转发到CPU...");
|
|||
|
|
// UpdateInterrupts();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void UpdateInterrupts()
|
|||
|
|
{
|
|||
|
|
this.Log(LogLevel.Info, "调用 UpdateInterrupts() 检查是否转发到CPU...");
|
|||
|
|
this.Log(LogLevel.Debug, "UpdateInterrupts: MASK=0x{0:X8}, PENDING=0x{1:X8}",
|
|||
|
|
intMaskPriority, intFTP);
|
|||
|
|
|
|||
|
|
|
|||
|
|
for (int i = 15; i > 0; i--)
|
|||
|
|
{
|
|||
|
|
//bool masked = ((intMaskPriority & (uint)(1 << i)) != 0) || (forceEnableIRQ4 && i == 4);
|
|||
|
|
bool masked = ((intMaskPriority & (uint)(1 << i)) != 0);
|
|||
|
|
bool pending = (intFTP & (uint)(1 << i)) != 0;
|
|||
|
|
|
|||
|
|
if (i == 5)
|
|||
|
|
{
|
|||
|
|
this.Log(LogLevel.Info, "IRQ{0} 状态检查: masked={1}, pending={2}", i, masked, pending);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (i == 4)
|
|||
|
|
{
|
|||
|
|
this.Log(LogLevel.Info, "IRQ{0} 状态检查: masked={1}, pending={2}", i, masked, pending);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (masked && pending)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
if (Connections.TryGetValue(i, out var gpio))
|
|||
|
|
{
|
|||
|
|
gpio.Set(true);
|
|||
|
|
this.Log(LogLevel.Info, "===> 转发中断 {0} 到MIC@{0} (CPU中断线{0})", i);
|
|||
|
|
|
|||
|
|
|
|||
|
|
var localI = i;
|
|||
|
|
machine.ScheduleAction(TimeInterval.FromMicroseconds(1),
|
|||
|
|
_ => {
|
|||
|
|
if (Connections.TryGetValue(localI, out var g))
|
|||
|
|
{
|
|||
|
|
g.Set(false);
|
|||
|
|
machine.ScheduleAction(TimeInterval.FromMicroseconds(10),
|
|||
|
|
_ => {
|
|||
|
|
|
|||
|
|
intFTP &= ~(uint)(1 << localI);
|
|||
|
|
this.Log(LogLevel.Info, "*** 10us 安全期已过,自动清除中断 {0} 的挂起位 ***", localI);
|
|||
|
|
UpdateInterrupts();
|
|||
|
|
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
this.Log(LogLevel.Warning, "中断 {0} 无法转发: 连接未找到!", i);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
public override void Reset()
|
|||
|
|
{
|
|||
|
|
base.Reset();
|
|||
|
|
intMaskPriority = 0;
|
|||
|
|
intFTP = 0;
|
|||
|
|
intClr = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public long Size => 0x10;
|
|||
|
|
|
|||
|
|
public IReadOnlyDictionary<int, IGPIO> Connections { get; }
|
|||
|
|
|
|||
|
|
|
|||
|
|
private readonly IMachine machine;
|
|||
|
|
private readonly Dictionary<int, IGPIO> connections;
|
|||
|
|
private readonly Dictionary<IGPIO, int> gpioToIrqMap; // 字段声明
|
|||
|
|
private readonly bool forceEnableIRQ4 = true;
|
|||
|
|
private uint intMaskPriority;
|
|||
|
|
private uint intFTP;
|
|||
|
|
private uint intClr;
|
|||
|
|
|
|||
|
|
// 寄存器定义
|
|||
|
|
private enum Registers
|
|||
|
|
{
|
|||
|
|
IntMaskPriority = 0x00,
|
|||
|
|
IntFTP = 0x04,
|
|||
|
|
IntForce = 0x08,
|
|||
|
|
IntClr = 0x0C,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|