678 lines
23 KiB
C#
678 lines
23 KiB
C#
|
|
//
|
||
|
|
// 16550 UART 外设实现
|
||
|
|
// 基于 16550 UART 规格,包含完整的 FIFO、中断和调制解调器控制功能
|
||
|
|
// author:liuwenbo
|
||
|
|
//
|
||
|
|
|
||
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using Antmicro.Renode.Core;
|
||
|
|
using Antmicro.Renode.Logging;
|
||
|
|
using Antmicro.Renode.Peripherals.Bus;
|
||
|
|
using Antmicro.Renode.Peripherals.UART;
|
||
|
|
using Antmicro.Renode.Utilities;
|
||
|
|
|
||
|
|
namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// 16550 UART 控制器
|
||
|
|
/// 兼容 16550 UART 规格,支持 FIFO、中断和调制解调器控制
|
||
|
|
/// </summary>
|
||
|
|
public class UART16550 : UARTBase, IDoubleWordPeripheral, IBytePeripheral, IKnownSize
|
||
|
|
{
|
||
|
|
public UART16550(IMachine machine, uint clockFrequency = 1843200)
|
||
|
|
: base(machine)
|
||
|
|
{
|
||
|
|
this.clockFrequency = clockFrequency;
|
||
|
|
|
||
|
|
// 创建 FIFO
|
||
|
|
rxFifo = new Queue<byte>();
|
||
|
|
txFifo = new Queue<byte>();
|
||
|
|
|
||
|
|
// 创建中断线
|
||
|
|
IRQ = new GPIO();
|
||
|
|
|
||
|
|
// 初始化寄存器
|
||
|
|
DefineRegisters();
|
||
|
|
Reset();
|
||
|
|
|
||
|
|
this.Log(LogLevel.Info, "16550 UART initialized, clock: {0} Hz", clockFrequency);
|
||
|
|
}
|
||
|
|
|
||
|
|
public override void Reset()
|
||
|
|
{
|
||
|
|
rxFifo.Clear();
|
||
|
|
txFifo.Clear();
|
||
|
|
|
||
|
|
// 寄存器复位值
|
||
|
|
ier = 0x00;
|
||
|
|
lcr = 0x00;
|
||
|
|
mcr = 0x00;
|
||
|
|
lsr = (byte)(LSR_TEMT | LSR_THRE); // 发送器初始为空
|
||
|
|
msr = 0x00;
|
||
|
|
scr = 0x00;
|
||
|
|
dll = 0x00;
|
||
|
|
dlh = 0x00;
|
||
|
|
fcr = 0x00;
|
||
|
|
|
||
|
|
fifoEnabled = false;
|
||
|
|
fifoTriggerLevel = 1;
|
||
|
|
|
||
|
|
IRQ.Set(false);
|
||
|
|
UpdateInterrupts();
|
||
|
|
|
||
|
|
this.Log(LogLevel.Debug, "16550 UART reset");
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DefineRegisters()
|
||
|
|
{
|
||
|
|
// 寄存器访问通过 ReadDoubleWord/WriteDoubleWord 实现
|
||
|
|
// 因为 16550 是字节外设,但 Renode 通常使用 32 位访问
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// IBusPeripheral 接口实现
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
public uint ReadDoubleWord(long offset)
|
||
|
|
{
|
||
|
|
return ReadByte(offset);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void WriteDoubleWord(long offset, uint value)
|
||
|
|
{
|
||
|
|
WriteByte(offset, (byte)value);
|
||
|
|
}
|
||
|
|
|
||
|
|
public byte ReadByte(long offset)
|
||
|
|
{
|
||
|
|
byte value = 0;
|
||
|
|
|
||
|
|
switch (offset)
|
||
|
|
{
|
||
|
|
case (long)Registers.RBR_THR_DLL:
|
||
|
|
if ((lcr & LCR_DLAB) != 0)
|
||
|
|
{
|
||
|
|
// DLAB=1: 读取 DLL
|
||
|
|
value = dll;
|
||
|
|
this.Log(LogLevel.Noisy, "Read DLL: 0x{0:X2}", value);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// DLAB=0: 读取 RBR
|
||
|
|
value = ReadRBR();
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.IER_DLH:
|
||
|
|
if ((lcr & LCR_DLAB) != 0)
|
||
|
|
{
|
||
|
|
// DLAB=1: 读取 DLH
|
||
|
|
value = dlh;
|
||
|
|
this.Log(LogLevel.Noisy, "Read DLH: 0x{0:X2}", value);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// DLAB=0: 读取 IER
|
||
|
|
value = (byte)(ier & 0x0F);
|
||
|
|
this.Log(LogLevel.Noisy, "Read IER: 0x{0:X2}", value);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.IIR_FCR:
|
||
|
|
// IIR - 中断识别寄存器
|
||
|
|
value = ReadIIR();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.LCR:
|
||
|
|
value = lcr;
|
||
|
|
this.Log(LogLevel.Noisy, "Read LCR: 0x{0:X2}", value);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.MCR:
|
||
|
|
value = mcr;
|
||
|
|
this.Log(LogLevel.Noisy, "Read MCR: 0x{0:X2}", value);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.LSR:
|
||
|
|
value = lsr;
|
||
|
|
this.Log(LogLevel.Noisy, "Read LSR: 0x{0:X2}", value);
|
||
|
|
// 读取 LSR 清除错误位
|
||
|
|
lsr = (byte)(lsr & ~(LSR_BI | LSR_FE | LSR_PE));
|
||
|
|
UpdateInterrupts();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.MSR:
|
||
|
|
value = msr;
|
||
|
|
this.Log(LogLevel.Noisy, "Read MSR: 0x{0:X2}", value);
|
||
|
|
// 读取 MSR 清除变化位
|
||
|
|
msr = (byte)(msr & 0xF0);
|
||
|
|
UpdateInterrupts();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.SCR:
|
||
|
|
value = scr;
|
||
|
|
this.Log(LogLevel.Noisy, "Read SCR: 0x{0:X2}", value);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
this.Log(LogLevel.Warning, "Read from unknown offset: 0x{0:X}", offset);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void WriteByte(long offset, byte value)
|
||
|
|
{
|
||
|
|
switch (offset)
|
||
|
|
{
|
||
|
|
case (long)Registers.RBR_THR_DLL:
|
||
|
|
if ((lcr & LCR_DLAB) != 0)
|
||
|
|
{
|
||
|
|
// DLAB=1: 写入 DLL
|
||
|
|
dll = value;
|
||
|
|
UpdateBaudRate();
|
||
|
|
this.Log(LogLevel.Debug, "Write DLL: 0x{0:X2}", value);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// DLAB=0: 写入 THR
|
||
|
|
WriteTHR(value);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.IER_DLH:
|
||
|
|
if ((lcr & LCR_DLAB) != 0)
|
||
|
|
{
|
||
|
|
// DLAB=1: 写入 DLH
|
||
|
|
dlh = value;
|
||
|
|
UpdateBaudRate();
|
||
|
|
this.Log(LogLevel.Debug, "Write DLH: 0x{0:X2}", value);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// DLAB=0: 写入 IER
|
||
|
|
ier = (byte)(value & 0x0F);
|
||
|
|
this.Log(LogLevel.Debug, "Write IER: 0x{0:X2}", ier);
|
||
|
|
UpdateInterrupts();
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.IIR_FCR:
|
||
|
|
// FCR - FIFO 控制寄存器
|
||
|
|
WriteFCR(value);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.LCR:
|
||
|
|
lcr = value;
|
||
|
|
this.Log(LogLevel.Debug, "Write LCR: 0x{0:X2}", value);
|
||
|
|
UpdateLineParameters();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.MCR:
|
||
|
|
mcr = (byte)(value & 0x1F);
|
||
|
|
this.Log(LogLevel.Debug, "Write MCR: 0x{0:X2}", mcr);
|
||
|
|
UpdateModemControl();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.LSR:
|
||
|
|
// LSR 是只读寄存器
|
||
|
|
this.Log(LogLevel.Warning, "Attempted write to read-only LSR");
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.MSR:
|
||
|
|
// MSR 是只读寄存器
|
||
|
|
this.Log(LogLevel.Warning, "Attempted write to read-only MSR");
|
||
|
|
break;
|
||
|
|
|
||
|
|
case (long)Registers.SCR:
|
||
|
|
scr = value;
|
||
|
|
this.Log(LogLevel.Noisy, "Write SCR: 0x{0:X2}", value);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
this.Log(LogLevel.Warning, "Write to unknown offset: 0x{0:X} = 0x{1:X2}", offset, value);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// FIFO 和数据传输
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
private byte ReadRBR()
|
||
|
|
{
|
||
|
|
byte value = 0;
|
||
|
|
|
||
|
|
if (rxFifo.Count > 0)
|
||
|
|
{
|
||
|
|
value = rxFifo.Dequeue();
|
||
|
|
this.Log(LogLevel.Debug, "Read RBR: 0x{0:X2} ('{1}')", value,
|
||
|
|
(value >= 32 && value < 127) ? (char)value : '.');
|
||
|
|
|
||
|
|
// 更新状态
|
||
|
|
if (rxFifo.Count == 0)
|
||
|
|
{
|
||
|
|
lsr = (byte)(lsr & ~LSR_DR); // 清除数据就绪
|
||
|
|
}
|
||
|
|
|
||
|
|
// 检查溢出
|
||
|
|
if (rxFifo.Count == 0)
|
||
|
|
{
|
||
|
|
lsr = (byte)(lsr & ~LSR_OE); // 清除溢出错误
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
this.Log(LogLevel.Warning, "Read from empty RBR");
|
||
|
|
}
|
||
|
|
|
||
|
|
UpdateInterrupts();
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void WriteTHR(byte value)
|
||
|
|
{
|
||
|
|
this.Log(LogLevel.Debug, "Write THR: 0x{0:X2} ('{1}')", value,
|
||
|
|
(value >= 32 && value < 127) ? (char)value : '.');
|
||
|
|
|
||
|
|
if (fifoEnabled)
|
||
|
|
{
|
||
|
|
if (txFifo.Count < FIFO_SIZE)
|
||
|
|
{
|
||
|
|
txFifo.Enqueue(value);
|
||
|
|
lsr = (byte)(lsr & ~LSR_THRE); // THR 非空
|
||
|
|
lsr = (byte)(lsr & ~LSR_TEMT); // 发送器非空
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
this.Log(LogLevel.Warning, "TX FIFO overflow");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// 非 FIFO 模式,直接发送
|
||
|
|
txFifo.Clear();
|
||
|
|
txFifo.Enqueue(value);
|
||
|
|
lsr = (byte)(lsr & ~LSR_THRE);
|
||
|
|
lsr = (byte)(lsr & ~LSR_TEMT);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 实际发送数据
|
||
|
|
TransmitData();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void WriteFCR(byte value)
|
||
|
|
{
|
||
|
|
fcr = value;
|
||
|
|
this.Log(LogLevel.Debug, "Write FCR: 0x{0:X2}", value);
|
||
|
|
|
||
|
|
// FIFO 使能
|
||
|
|
bool newFifoEnabled = (value & FCR_FIFO_EN) != 0;
|
||
|
|
if (newFifoEnabled != fifoEnabled)
|
||
|
|
{
|
||
|
|
fifoEnabled = newFifoEnabled;
|
||
|
|
this.Log(LogLevel.Info, "FIFO {0}", fifoEnabled ? "enabled" : "disabled");
|
||
|
|
|
||
|
|
if (!fifoEnabled)
|
||
|
|
{
|
||
|
|
// 禁用 FIFO 时清空
|
||
|
|
rxFifo.Clear();
|
||
|
|
txFifo.Clear();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 清除 RX FIFO
|
||
|
|
if ((value & FCR_RCVR_RESET) != 0)
|
||
|
|
{
|
||
|
|
rxFifo.Clear();
|
||
|
|
lsr = (byte)(lsr & ~LSR_DR);
|
||
|
|
this.Log(LogLevel.Debug, "RX FIFO cleared");
|
||
|
|
}
|
||
|
|
|
||
|
|
// 清除 TX FIFO
|
||
|
|
if ((value & FCR_XMIT_RESET) != 0)
|
||
|
|
{
|
||
|
|
txFifo.Clear();
|
||
|
|
lsr = (byte)(lsr | (LSR_THRE | LSR_TEMT));
|
||
|
|
this.Log(LogLevel.Debug, "TX FIFO cleared");
|
||
|
|
}
|
||
|
|
|
||
|
|
// 设置触发级别
|
||
|
|
byte trigger = (byte)((value >> 6) & 0x03);
|
||
|
|
switch (trigger)
|
||
|
|
{
|
||
|
|
case 0: fifoTriggerLevel = 1; break;
|
||
|
|
case 1: fifoTriggerLevel = 4; break;
|
||
|
|
case 2: fifoTriggerLevel = 8; break;
|
||
|
|
case 3: fifoTriggerLevel = 14; break;
|
||
|
|
}
|
||
|
|
this.Log(LogLevel.Debug, "FIFO trigger level: {0} bytes", fifoTriggerLevel);
|
||
|
|
|
||
|
|
UpdateInterrupts();
|
||
|
|
}
|
||
|
|
|
||
|
|
private byte ReadIIR()
|
||
|
|
{
|
||
|
|
byte iir = IIR_NO_INT; // 默认无中断
|
||
|
|
|
||
|
|
// 检查中断使能
|
||
|
|
if ((mcr & MCR_OUT2) == 0)
|
||
|
|
{
|
||
|
|
// OUT2 未置位,禁用中断
|
||
|
|
this.Log(LogLevel.Noisy, "Read IIR: 0x{0:X2} (interrupts disabled)", iir);
|
||
|
|
return iir;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 按优先级检查中断
|
||
|
|
// 1. 接收线路状态中断 (最高优先级)
|
||
|
|
if ((ier & IER_ELSI) != 0 && (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) != 0)
|
||
|
|
{
|
||
|
|
iir = IIR_RLS; // 0x06
|
||
|
|
}
|
||
|
|
// 2. 接收数据可用中断
|
||
|
|
else if ((ier & IER_ERBFI) != 0 && (lsr & LSR_DR) != 0 && rxFifo.Count >= fifoTriggerLevel)
|
||
|
|
{
|
||
|
|
iir = IIR_RDA; // 0x04
|
||
|
|
}
|
||
|
|
// 3. 字符超时中断
|
||
|
|
else if ((ier & IER_ERBFI) != 0 && (lsr & LSR_DR) != 0 && rxFifo.Count > 0)
|
||
|
|
{
|
||
|
|
iir = IIR_CTI; // 0x0C
|
||
|
|
}
|
||
|
|
// 4. THR 空中断
|
||
|
|
else if ((ier & IER_ETBEI) != 0 && (lsr & LSR_THRE) != 0)
|
||
|
|
{
|
||
|
|
iir = IIR_THRE; // 0x02
|
||
|
|
}
|
||
|
|
// 5. 调制解调器状态中断 (最低优先级)
|
||
|
|
else if ((ier & IER_EDSSI) != 0 && (msr & 0x0F) != 0)
|
||
|
|
{
|
||
|
|
iir = IIR_MS; // 0x00
|
||
|
|
}
|
||
|
|
|
||
|
|
// FIFO 使能状态
|
||
|
|
if (fifoEnabled)
|
||
|
|
{
|
||
|
|
iir |= IIR_FIFO_EN;
|
||
|
|
}
|
||
|
|
|
||
|
|
this.Log(LogLevel.Noisy, "Read IIR: 0x{0:X2}", iir);
|
||
|
|
|
||
|
|
// 读取 IIR 清除 THRE 中断
|
||
|
|
if ((iir & 0x0F) == IIR_THRE)
|
||
|
|
{
|
||
|
|
UpdateInterrupts();
|
||
|
|
}
|
||
|
|
|
||
|
|
return iir;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void TransmitData()
|
||
|
|
{
|
||
|
|
// 从 TX FIFO 发送数据
|
||
|
|
while (txFifo.Count > 0)
|
||
|
|
{
|
||
|
|
byte data = txFifo.Dequeue();
|
||
|
|
|
||
|
|
// 调用基类的 TransmitCharacter 发送数据
|
||
|
|
TransmitCharacter(data);
|
||
|
|
|
||
|
|
this.Log(LogLevel.Debug, "Transmitted: 0x{0:X2} ('{1}')", data,
|
||
|
|
(data >= 32 && data < 127) ? (char)data : '.');
|
||
|
|
}
|
||
|
|
|
||
|
|
// 更新状态
|
||
|
|
lsr = (byte)(lsr | LSR_THRE); // THR 空
|
||
|
|
lsr = (byte)(lsr | LSR_TEMT); // 发送器空
|
||
|
|
|
||
|
|
UpdateInterrupts();
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// UARTBase 接口实现
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
public override void WriteChar(byte value)
|
||
|
|
{
|
||
|
|
// 从外部接收数据 (例如从终端或网络)
|
||
|
|
if (fifoEnabled)
|
||
|
|
{
|
||
|
|
if (rxFifo.Count < FIFO_SIZE)
|
||
|
|
{
|
||
|
|
rxFifo.Enqueue(value);
|
||
|
|
lsr = (byte)(lsr | LSR_DR); // 数据就绪
|
||
|
|
|
||
|
|
this.Log(LogLevel.Debug, "Received: 0x{0:X2} ('{1}'), FIFO count: {2}",
|
||
|
|
value, (value >= 32 && value < 127) ? (char)value : '.', rxFifo.Count);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
lsr = (byte)(lsr | LSR_OE); // 溢出错误
|
||
|
|
this.Log(LogLevel.Warning, "RX FIFO overflow");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// 非 FIFO 模式
|
||
|
|
if (rxFifo.Count > 0)
|
||
|
|
{
|
||
|
|
lsr = (byte)(lsr | LSR_OE); // 溢出错误
|
||
|
|
}
|
||
|
|
rxFifo.Clear();
|
||
|
|
rxFifo.Enqueue(value);
|
||
|
|
lsr = (byte)(lsr | LSR_DR);
|
||
|
|
}
|
||
|
|
|
||
|
|
UpdateInterrupts();
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void CharWritten()
|
||
|
|
{
|
||
|
|
// UARTBase 要求实现此方法
|
||
|
|
// 在字符写入后调用,这里不需要额外操作
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void QueueEmptied()
|
||
|
|
{
|
||
|
|
// UARTBase 要求实现此方法
|
||
|
|
// 当队列为空时调用,这里不需要额外操作
|
||
|
|
}
|
||
|
|
|
||
|
|
public override Bits StopBits
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return ((lcr & LCR_STB) != 0) ? Bits.Two : Bits.One;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public override Parity ParityBit
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
if ((lcr & LCR_PEN) == 0)
|
||
|
|
return Parity.None;
|
||
|
|
|
||
|
|
if ((lcr & LCR_EPS) != 0)
|
||
|
|
return Parity.Even;
|
||
|
|
else
|
||
|
|
return Parity.Odd;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public override uint BaudRate
|
||
|
|
{
|
||
|
|
get { return currentBaudRate; }
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// 辅助方法
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
private void UpdateBaudRate()
|
||
|
|
{
|
||
|
|
ushort divisor = (ushort)((dlh << 8) | dll);
|
||
|
|
|
||
|
|
if (divisor == 0)
|
||
|
|
{
|
||
|
|
currentBaudRate = 0;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
currentBaudRate = clockFrequency / (16u * divisor);
|
||
|
|
this.Log(LogLevel.Info, "Baud rate set to {0} (divisor: {1})", currentBaudRate, divisor);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void UpdateLineParameters()
|
||
|
|
{
|
||
|
|
// 字长
|
||
|
|
byte wordLength = (byte)((lcr & LCR_WLS) + 5);
|
||
|
|
|
||
|
|
// 停止位
|
||
|
|
string stopBits = ((lcr & LCR_STB) != 0) ? "2" : "1";
|
||
|
|
|
||
|
|
// 奇偶校验
|
||
|
|
string parity;
|
||
|
|
if ((lcr & LCR_PEN) == 0)
|
||
|
|
parity = "N";
|
||
|
|
else if ((lcr & LCR_EPS) != 0)
|
||
|
|
parity = "E";
|
||
|
|
else
|
||
|
|
parity = "O";
|
||
|
|
|
||
|
|
this.Log(LogLevel.Info, "Line format: {0}{1}{2}", wordLength, parity, stopBits);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void UpdateModemControl()
|
||
|
|
{
|
||
|
|
this.Log(LogLevel.Debug, "Modem control: DTR={0}, RTS={1}, OUT1={2}, OUT2={3}, LOOP={4}",
|
||
|
|
(mcr & MCR_DTR) != 0, (mcr & MCR_RTS) != 0, (mcr & MCR_OUT1) != 0,
|
||
|
|
(mcr & MCR_OUT2) != 0, (mcr & MCR_LOOP) != 0);
|
||
|
|
|
||
|
|
UpdateInterrupts();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void UpdateInterrupts()
|
||
|
|
{
|
||
|
|
bool interrupt = false;
|
||
|
|
|
||
|
|
// OUT2 必须置位才能产生中断
|
||
|
|
if ((mcr & MCR_OUT2) != 0)
|
||
|
|
{
|
||
|
|
// 检查各种中断条件
|
||
|
|
if ((ier & IER_ELSI) != 0 && (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) != 0)
|
||
|
|
interrupt = true;
|
||
|
|
else if ((ier & IER_ERBFI) != 0 && (lsr & LSR_DR) != 0 && rxFifo.Count >= fifoTriggerLevel)
|
||
|
|
interrupt = true;
|
||
|
|
else if ((ier & IER_ETBEI) != 0 && (lsr & LSR_THRE) != 0)
|
||
|
|
interrupt = true;
|
||
|
|
else if ((ier & IER_EDSSI) != 0 && (msr & 0x0F) != 0)
|
||
|
|
interrupt = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
IRQ.Set(interrupt);
|
||
|
|
|
||
|
|
if (interrupt)
|
||
|
|
{
|
||
|
|
this.Log(LogLevel.Debug, "Interrupt asserted");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public long Size => 0x08;
|
||
|
|
|
||
|
|
public GPIO IRQ { get; }
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// 寄存器定义
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
private enum Registers : long
|
||
|
|
{
|
||
|
|
RBR_THR_DLL = 0x00, // RBR/THR (DLAB=0), DLL (DLAB=1)
|
||
|
|
IER_DLH = 0x01, // IER (DLAB=0), DLH (DLAB=1)
|
||
|
|
IIR_FCR = 0x02, // IIR (R), FCR (W)
|
||
|
|
LCR = 0x03, // Line Control Register
|
||
|
|
MCR = 0x04, // Modem Control Register
|
||
|
|
LSR = 0x05, // Line Status Register
|
||
|
|
MSR = 0x06, // Modem Status Register
|
||
|
|
SCR = 0x07, // Scratch Register
|
||
|
|
}
|
||
|
|
|
||
|
|
// LCR 位定义
|
||
|
|
private const byte LCR_WLS = 0x03; // 字长选择
|
||
|
|
private const byte LCR_STB = 0x04; // 停止位
|
||
|
|
private const byte LCR_PEN = 0x08; // 奇偶校验使能
|
||
|
|
private const byte LCR_EPS = 0x10; // 偶校验选择
|
||
|
|
private const byte LCR_SPAR = 0x20; // 强制奇偶校验
|
||
|
|
private const byte LCR_SBC = 0x40; // 设置中断
|
||
|
|
private const byte LCR_DLAB = 0x80; // 除数锁存访问
|
||
|
|
|
||
|
|
// LSR 位定义
|
||
|
|
private const byte LSR_DR = 0x01; // 数据就绪
|
||
|
|
private const byte LSR_OE = 0x02; // 溢出错误
|
||
|
|
private const byte LSR_PE = 0x04; // 奇偶校验错误
|
||
|
|
private const byte LSR_FE = 0x08; // 帧错误
|
||
|
|
private const byte LSR_BI = 0x10; // 中断指示
|
||
|
|
private const byte LSR_THRE = 0x20; // THR 空
|
||
|
|
private const byte LSR_TEMT = 0x40; // 发送器空
|
||
|
|
private const byte LSR_FIFO_ERR = 0x80; // FIFO 错误
|
||
|
|
|
||
|
|
// IER 位定义
|
||
|
|
private const byte IER_ERBFI = 0x01; // 使能接收数据可用中断
|
||
|
|
private const byte IER_ETBEI = 0x02; // 使能 THR 空中断
|
||
|
|
private const byte IER_ELSI = 0x04; // 使能接收线路状态中断
|
||
|
|
private const byte IER_EDSSI = 0x08; // 使能调制解调器状态中断
|
||
|
|
|
||
|
|
// IIR 值定义
|
||
|
|
private const byte IIR_NO_INT = 0x01; // 无中断挂起
|
||
|
|
private const byte IIR_MS = 0x00; // 调制解调器状态
|
||
|
|
private const byte IIR_THRE = 0x02; // THR 空
|
||
|
|
private const byte IIR_RDA = 0x04; // 接收数据可用
|
||
|
|
private const byte IIR_RLS = 0x06; // 接收线路状态
|
||
|
|
private const byte IIR_CTI = 0x0C; // 字符超时
|
||
|
|
private const byte IIR_FIFO_EN = 0xC0; // FIFO 使能标志
|
||
|
|
|
||
|
|
// FCR 位定义
|
||
|
|
private const byte FCR_FIFO_EN = 0x01; // FIFO 使能
|
||
|
|
private const byte FCR_RCVR_RESET = 0x02; // 清除 RX FIFO
|
||
|
|
private const byte FCR_XMIT_RESET = 0x04; // 清除 TX FIFO
|
||
|
|
|
||
|
|
// MCR 位定义
|
||
|
|
private const byte MCR_DTR = 0x01; // DTR
|
||
|
|
private const byte MCR_RTS = 0x02; // RTS
|
||
|
|
private const byte MCR_OUT1 = 0x04; // OUT1
|
||
|
|
private const byte MCR_OUT2 = 0x08; // OUT2
|
||
|
|
private const byte MCR_LOOP = 0x10; // 环回模式
|
||
|
|
|
||
|
|
// 常量
|
||
|
|
private const int FIFO_SIZE = 16;
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// 私有字段
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
private readonly uint clockFrequency;
|
||
|
|
private uint currentBaudRate;
|
||
|
|
|
||
|
|
// 寄存器
|
||
|
|
private byte ier; // 中断使能寄存器
|
||
|
|
private byte lcr; // 线路控制寄存器
|
||
|
|
private byte mcr; // 调制解调器控制寄存器
|
||
|
|
private byte lsr; // 线路状态寄存器
|
||
|
|
private byte msr; // 调制解调器状态寄存器
|
||
|
|
private byte scr; // 暂存寄存器
|
||
|
|
private byte dll; // 除数锁存低字节
|
||
|
|
private byte dlh; // 除数锁存高字节
|
||
|
|
private byte fcr; // FIFO 控制寄存器
|
||
|
|
|
||
|
|
// FIFO
|
||
|
|
private readonly Queue<byte> rxFifo;
|
||
|
|
private readonly Queue<byte> txFifo;
|
||
|
|
private bool fifoEnabled;
|
||
|
|
private int fifoTriggerLevel;
|
||
|
|
}
|
||
|
|
}
|