Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cadence_UART: Add TX interrupts and TX FIFO #117

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Emulator/Main/Emulator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@
<Compile Include="Utilities\Collections\MultiTreeNode.cs" />
<Compile Include="Utilities\Collections\MultiTree.cs" />
<Compile Include="Utilities\Collections\AutoResizingList.cs" />
<Compile Include="Utilities\Collections\FixedSizeQueue.cs" />
<Compile Include="Core\Structure\NullRegistrationPoint.cs" />
<Compile Include="Storage\LBABackend.cs" />
<Compile Include="Storage\SerializableStreamView.cs" />
Expand Down
27 changes: 27 additions & 0 deletions src/Emulator/Main/Peripherals/UART/UARTBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public abstract class UARTBase : NullRegistrationPointPeripheralContainer<IUART>
protected UARTBase(IMachine machine) : base(machine)
{
queue = new Queue<byte>();
overflowQueue = new Queue<byte>();
innerLock = new object();
}

Expand Down Expand Up @@ -60,6 +61,27 @@ public override void Unregister(IUART uart)
[field: Transient]
public event Action<byte> CharReceived;

public void QueueOverflowByte(byte b)
{
overflowQueue.Enqueue(b);
}

public bool TryDequeueOverflowByte(ref byte b)
{
// Queue<T>.TryDequeue() is unavailable, so mimic it with a try...catch
bool result = false;
try
{
b = overflowQueue.Dequeue();
result = true;
}
catch (InvalidOperationException)
{
// nothing to do on exception
}
return result;
}

protected abstract void CharWritten();
protected abstract void QueueEmptied();

Expand Down Expand Up @@ -109,6 +131,11 @@ protected int Count
protected readonly object innerLock;
private readonly Queue<byte> queue;

// A queue for optionally storing incoming bytes that are received more
// quickly than they can be processed by an executing machine, typically
// used in tandem with a UARTHub.
protected Queue<byte> overflowQueue;

public abstract Bits StopBits { get; }

public abstract Parity ParityBit { get; }
Expand Down
33 changes: 32 additions & 1 deletion src/Emulator/Main/Peripherals/UART/UARTHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ public virtual void DetachFrom(I uart)

public bool IsPaused => !started;

struct WriteCharContext {
public I i;
public byte b;
};

private void HandleCharReceived(byte obj, I sender)
{
if(!started)
Expand All @@ -95,11 +100,37 @@ private void HandleCharReceived(byte obj, I sender)
{
foreach(var item in uarts.Where(x => shouldLoopback || x.Key != sender).Select(x => x.Key))
{
item.GetMachine().HandleTimeDomainEvent(item.WriteChar, obj, TimeDomainsManager.Instance.VirtualTimeStamp);
WriteCharContext context = new WriteCharContext() { i = item, b = obj };
item.GetMachine().HandleTimeDomainEvent(WriteCharCallback, context, TimeDomainsManager.Instance.VirtualTimeStamp);
}
}
}

//
// For any UART device that is correctly modeling its RX buffer state
// and is derived from UARTHub, this callback will queue any byte that
// would trigger a RX overflow, giving the device a mechanism to dequeue
// and process the byte later when its buffer state becomes "Ready"
// again.
//
// For all other devices, the byte is sent immediately.
//
private void WriteCharCallback(WriteCharContext context) {
bool isUARTBase = (context.i as UARTBase) != null;
bool hasBufferState = (context.i as IUARTWithBufferState) != null;
bool bufferIsFull = hasBufferState
? ((context.i as IUARTWithBufferState).BufferState == BufferState.Full)
: false;
if (bufferIsFull && isUARTBase)
{
(context.i as UARTBase).QueueOverflowByte(context.b);
}
else
{
context.i.WriteChar(context.b);
}
}

[PostDeserialization]
private void ReattachUARTsAfterDeserialization()
{
Expand Down
122 changes: 122 additions & 0 deletions src/Emulator/Main/Utilities/Collections/FixedSizeQueue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//
// Copyright (c) 2010-2024 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System;
using System.Collections.Generic;

namespace Antmicro.Renode.Utilities.Collections
{
public class SizeException : Exception { }

public class FixedSizeQueue<T> {
public FixedSizeQueue(int size, bool thresholdCrossingEmptying = true,
Action<FixedSizeQueue<T>> hasBecomeEmpty = null,
Action<FixedSizeQueue<T>> hasBecomeFull = null,
Action<FixedSizeQueue<T>> hasCrossedThreshold = null)
{
if(size <= 0) {
throw new SizeException();
}

this.size = size;
this.threshold = size / 2;
this.hasBecomeEmpty = hasBecomeEmpty;
this.hasBecomeFull = hasBecomeFull;
this.hasCrossedThreshold = hasCrossedThreshold;
this.thresholdCrossingEmptying = thresholdCrossingEmptying;

inner = new Queue<T>();
lastOrDefault = default(T);
}

public void Enqueue(T t) {
// throw exception if queue is already full
if(IsFull) {
throw new InvalidOperationException();
}

var origCount = inner.Count;

inner.Enqueue(t);
lastOrDefault = t;

// invoke action if threshold is crossed and not emptying
if((origCount < threshold) && (inner.Count >= threshold) && !thresholdCrossingEmptying) {
hasCrossedThreshold?.Invoke(this);
}

// invoke action if queue is going from non-full to full
if(IsFull) {
hasBecomeFull?.Invoke(this);
}
}

public T Dequeue() {
// throw exception if queue is empty
if(IsEmpty) {
throw new InvalidOperationException();
}

var origCount = inner.Count;

// throws InvalidOperationException on empty queue
T t = inner.Dequeue();

// invoke action if queue is going from non-empty -> empty
if(IsEmpty) {
lastOrDefault = default(T);
hasBecomeEmpty?.Invoke(this);
}

// invoke action if threshold is crossed and emptying
if((origCount >= threshold) && (inner.Count < threshold) && thresholdCrossingEmptying) {
hasCrossedThreshold?.Invoke(this);
}

return t;
}

// if queue is non-empty, returns a copy of the last element (without dequeueing)
// if queue is empty, returns a default T
public T LastOrDefault() {
return lastOrDefault;
}

public void setThreshold(int threshold) {
this.threshold = threshold;
}

public bool IsEmpty {
get { return inner.Count == 0; }
}

public bool IsFull {
get { return inner.Count >= size; }
}

public bool ThresholdCrossed {
get {
if(thresholdCrossingEmptying) {
return inner.Count < threshold;
} else {
return inner.Count >= threshold;
}
}
}

private event Action<FixedSizeQueue<T>> hasBecomeEmpty;
private event Action<FixedSizeQueue<T>> hasBecomeFull;
private event Action<FixedSizeQueue<T>> hasCrossedThreshold;

private readonly int size;
private int threshold;
private readonly bool thresholdCrossingEmptying;

private Queue<T> inner;
private T lastOrDefault;
}

}
Loading