//
// Copyright (c) 2010-2024 Antmicro
// Copyright (c) 2011-2015 Realtime Embedded
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System;
using Antmicro.Renode.Peripherals;
using System.Collections.Generic;
using Antmicro.Renode.Utilities;
using System.Linq;
using Antmicro.Renode.Logging;
namespace Antmicro.Renode.Core.Structure.Registers
{
///
/// 64 bit .
///
public sealed class QuadWordRegister : PeripheralRegister, IPeripheralRegister
{
///
/// Creates a register with one field, serving a purpose of read and write register.
///
/// A new register.
/// Reset value.
/// Ignored parameter, for convenience. Treat it as a comment.
public static QuadWordRegister CreateRWRegister(ulong resetValue = 0, string name = null, bool softResettable = true)
{
//null because parent is used for logging purposes only - this will never happen in this case.
var register = new QuadWordRegister(null, resetValue, softResettable);
register.DefineValueField(0, register.RegisterWidth);
return register;
}
public QuadWordRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true) : base(parent, resetValue, softResettable, QuadWordWidth)
{
}
///
/// Retrieves the current value of readable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
///
public ulong Read()
{
return ReadInner();
}
///
/// Writes the given value to writeable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
///
public void Write(long offset, ulong value)
{
WriteInner(offset, value);
}
///
/// Defines the read callback that is called once on each read, regardles of the number of defined register fields.
/// Note that it will also be called for unreadable registers.
///
/// Method to be called whenever this register is read. The first parameter is the value of this register before read,
/// the second parameter is the value after read.
public void DefineReadCallback(Action readCallback)
{
readCallbacks.Add(readCallback);
}
///
/// Defines the write callback that is called once on each write, regardles of the number of defined register fields.
/// Note that it will also be called for unwrittable registers.
///
/// Method to be called whenever this register is written to. The first parameter is the value of this register before write,
/// the second parameter is the value written (without any modification).
public void DefineWriteCallback(Action writeCallback)
{
writeCallbacks.Add(writeCallback);
}
///
/// Defines the change callback that is called once on each change, regardles of the number of defined register fields.
/// Note that it will also be called for unwrittable registers.
///
/// Method to be called whenever this register's value is changed, either due to read or write. The first parameter is the value of this register before change,
/// the second parameter is the value after change.
public void DefineChangeCallback(Action changeCallback)
{
changeCallbacks.Add(changeCallback);
}
///
/// Gets or sets the underlying value without any modification or reaction.
///
public ulong Value
{
get
{
return UnderlyingValue;
}
set
{
UnderlyingValue = value;
}
}
public const int QuadWordWidth = 64;
protected override void CallChangeHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(changeCallbacks, oldValue, newValue);
}
protected override void CallReadHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(readCallbacks, oldValue, newValue);
}
protected override void CallWriteHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(writeCallbacks, oldValue, newValue);
}
private List> readCallbacks = new List>();
private List> writeCallbacks = new List>();
private List> changeCallbacks = new List>();
}
///
/// 32 bit .
///
public sealed class DoubleWordRegister : PeripheralRegister, IPeripheralRegister
{
///
/// Creates a register with one field, serving a purpose of read and write register.
///
/// A new register.
/// Reset value.
/// Ignored parameter, for convenience. Treat it as a comment.
public static DoubleWordRegister CreateRWRegister(uint resetValue = 0, string name = null, bool softResettable = true)
{
//null because parent is used for logging purposes only - this will never happen in this case.
var register = new DoubleWordRegister(null, resetValue, softResettable);
register.DefineValueField(0, register.RegisterWidth);
return register;
}
public DoubleWordRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true) : base(parent, resetValue, softResettable, DoubleWordWidth)
{
}
///
/// Retrieves the current value of readable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
///
public uint Read()
{
return (uint)ReadInner();
}
///
/// Writes the given value to writeable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
///
public void Write(long offset, uint value)
{
WriteInner(offset, value);
}
///
/// Defines the read callback that is called once on each read, regardles of the number of defined register fields.
/// Note that it will also be called for unreadable registers.
///
/// Method to be called whenever this register is read. The first parameter is the value of this register before read,
/// the second parameter is the value after read.
public void DefineReadCallback(Action readCallback)
{
readCallbacks.Add(readCallback);
}
///
/// Defines the write callback that is called once on each write, regardles of the number of defined register fields.
/// Note that it will also be called for unwrittable registers.
///
/// Method to be called whenever this register is written to. The first parameter is the value of this register before write,
/// the second parameter is the value written (without any modification).
public void DefineWriteCallback(Action writeCallback)
{
writeCallbacks.Add(writeCallback);
}
///
/// Defines the change callback that is called once on each change, regardles of the number of defined register fields.
/// Note that it will also be called for unwrittable registers.
///
/// Method to be called whenever this register's value is changed, either due to read or write. The first parameter is the value of this register before change,
/// the second parameter is the value after change.
public void DefineChangeCallback(Action changeCallback)
{
changeCallbacks.Add(changeCallback);
}
///
/// Gets or sets the underlying value without any modification or reaction.
///
public uint Value
{
get
{
return (uint)UnderlyingValue;
}
set
{
UnderlyingValue = value;
}
}
public const int DoubleWordWidth = 32;
protected override void CallChangeHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(changeCallbacks, (uint)oldValue, (uint)newValue);
}
protected override void CallReadHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(readCallbacks, (uint)oldValue, (uint)newValue);
}
protected override void CallWriteHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(writeCallbacks, (uint)oldValue, (uint)newValue);
}
private List> readCallbacks = new List>();
private List> writeCallbacks = new List>();
private List> changeCallbacks = new List>();
}
///
/// 16 bit .
///
public sealed class WordRegister : PeripheralRegister, IPeripheralRegister
{
///
/// Creates a register with one field, serving a purpose of read and write register.
///
/// A new register.
/// Reset value.
/// Ignored parameter, for convenience. Treat it as a comment.
public static WordRegister CreateRWRegister(uint resetValue = 0, string name = null, bool softResettable = true)
{
//null because parent is used for logging purposes only - this will never happen in this case.
var register = new WordRegister(null, resetValue, softResettable);
register.DefineValueField(0, register.RegisterWidth);
return register;
}
public WordRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true) : base(parent, resetValue, softResettable, WordWidth)
{
}
///
/// Retrieves the current value of readable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
///
public ushort Read()
{
return (ushort)ReadInner();
}
///
/// Writes the given value to writeable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
///
public void Write(long offset, ushort value)
{
WriteInner(offset, value);
}
///
/// Defines the read callback that is called once on each read, regardles of the number of defined register fields.
/// Note that it will also be called for unreadable registers.
///
/// Method to be called whenever this register is read. The first parameter is the value of this register before read,
/// the second parameter is the value after read.
public void DefineReadCallback(Action readCallback)
{
readCallbacks.Add(readCallback);
}
///
/// Defines the write callback that is called once on each write, regardles of the number of defined register fields.
/// Note that it will also be called for unwrittable registers.
///
/// Method to be called whenever this register is written to. The first parameter is the value of this register before write,
/// the second parameter is the value written (without any modification).
public void DefineWriteCallback(Action writeCallback)
{
writeCallbacks.Add(writeCallback);
}
///
/// Defines the change callback that is called once on each change, regardles of the number of defined register fields.
/// Note that it will also be called for unwrittable registers.
///
/// Method to be called whenever this register's value is changed, either due to read or write. The first parameter is the value of this register before change,
/// the second parameter is the value after change.
public void DefineChangeCallback(Action changeCallback)
{
changeCallbacks.Add(changeCallback);
}
///
/// Gets or sets the underlying value without any modification or reaction.
///
public ushort Value
{
get
{
return (ushort)UnderlyingValue;
}
set
{
UnderlyingValue = value;
}
}
public const int WordWidth = 16;
protected override void CallChangeHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(changeCallbacks, (ushort)oldValue, (ushort)newValue);
}
protected override void CallReadHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(readCallbacks, (ushort)oldValue, (ushort)newValue);
}
protected override void CallWriteHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(writeCallbacks, (ushort)oldValue, (ushort)newValue);
}
private List> readCallbacks = new List>();
private List> writeCallbacks = new List>();
private List> changeCallbacks = new List>();
}
///
/// 8 bit .
///
public sealed class ByteRegister : PeripheralRegister, IPeripheralRegister
{
///
/// Creates a register with one field, serving a purpose of read and write register.
///
/// A new register.
/// Reset value.
/// Ignored parameter, for convenience. Treat it as a comment.
public static ByteRegister CreateRWRegister(uint resetValue = 0, string name = null, bool softResettable = true)
{
//null because parent is used for logging purposes only - this will never happen in this case.
var register = new ByteRegister(null, resetValue, softResettable);
register.DefineValueField(0, register.RegisterWidth);
return register;
}
public ByteRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true) : base(parent, resetValue, softResettable, ByteWidth)
{
}
///
/// Retrieves the current value of readable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
///
public byte Read()
{
return (byte)ReadInner();
}
///
/// Writes the given value to writeable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
///
public void Write(long offset, byte value)
{
WriteInner(offset, value);
}
///
/// Defines the read callback that is called once on each read, regardles of the number of defined register fields.
/// Note that it will also be called for unreadable registers.
///
/// Method to be called whenever this register is read. The first parameter is the value of this register before read,
/// the second parameter is the value after read.
public void DefineReadCallback(Action readCallback)
{
readCallbacks.Add(readCallback);
}
///
/// Defines the write callback that is called once on each write, regardles of the number of defined register fields.
/// Note that it will also be called for unwrittable registers.
///
/// Method to be called whenever this register is written to. The first parameter is the value of this register before write,
/// the second parameter is the value written (without any modification).
public void DefineWriteCallback(Action writeCallback)
{
writeCallbacks.Add(writeCallback);
}
///
/// Defines the change callback that is called once on each change, regardles of the number of defined register fields.
/// Note that it will also be called for unwrittable registers.
///
/// Method to be called whenever this register's value is changed, either due to read or write. The first parameter is the value of this register before change,
/// the second parameter is the value after change.
public void DefineChangeCallback(Action changeCallback)
{
changeCallbacks.Add(changeCallback);
}
///
/// Gets or sets the underlying value without any modification or reaction.
///
public byte Value
{
get
{
return (byte)UnderlyingValue;
}
set
{
UnderlyingValue = value;
}
}
public const int ByteWidth = 8;
protected override void CallChangeHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(changeCallbacks, (byte)oldValue, (byte)newValue);
}
protected override void CallReadHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(readCallbacks, (byte)oldValue, (byte)newValue);
}
protected override void CallWriteHandlers(ulong oldValue, ulong newValue)
{
CallHandlers(writeCallbacks, (byte)oldValue, (byte)newValue);
}
private List> readCallbacks = new List>();
private List> writeCallbacks = new List>();
private List> changeCallbacks = new List>();
}
public interface IPeripheralRegister
{
T Read();
void Write(long offset, T value);
void Reset();
}
///
/// Represents a register of a given width, containing defined fields.
/// Fields may not exceed this register's width, nor may they overlap each other.
/// Fields that are not handled (e.g. left for future implementation or unimportant) have to be tagged.
/// Otherwise, they will not be logged.
///
public abstract partial class PeripheralRegister
{
///
/// Restores this register's value to its reset value, defined on per-field basis.
///
public void Reset()
{
BitHelper.UpdateWithMasked(ref UnderlyingValue, resetValue, resettableMask);
}
///
/// Wrapper for method, tagging bits as "RESERVED".
///
/// Offset in the register.
/// Width of field.
/// Value allowed to be written.<\param>
public void Reserved(int position, int width, ulong? allowedValue = null)
{
Tag("RESERVED", position, width, allowedValue);
}
///
/// Mark an unhandled field, so it is logged with its name.
///
/// Name of the unhandled field.
/// Offset in the register.
/// Width of field.
/// Value allowed to be written.<\param>
public void Tag(string name, int position, int width, ulong? allowedValue = null)
{
ThrowIfRangeIllegal(position, width, name);
if(allowedValue != null)
{
ThrowIfAllowedValueDoesNotFitInWidth(width, allowedValue.Value, name);
}
tags.Add(new Tag
{
Name = name,
Position = position,
Width = width,
AllowedValue = allowedValue
});
}
///
/// Defines the flag field. Its width is always 1 and is interpreted as boolean value.
///
/// Offset in the register.
/// Access modifiers of this field.
/// Method to be called whenever the containing register is read. The first parameter is the value of this field before read,
/// the second parameter is the value after read. Note that it will also be called for unreadable fields.
/// Method to be called whenever the containing register is written to. The first parameter is the value of this field before write,
/// the second parameter is the value written (without any modification). Note that it will also be called for unwrittable fields.
/// Method to be called whenever this field's value is changed, either due to read or write. The first parameter is the value of this field before change,
/// the second parameter is the value after change. Note that it will also be called for unwrittable fields.
/// Method to be called whenever this field is read. The value passed is the current field's value, that will be overwritten by
/// the value returned from it. This returned value is eventually passed as the first parameter of .
/// Indicates whether the field should be cleared by soft reset.
/// Ignored parameter, for convenience. Treat it as a comment.
public IFlagRegisterField DefineFlagField(int position, FieldMode mode = FieldMode.Read | FieldMode.Write, Action readCallback = null,
Action writeCallback = null, Action changeCallback = null, Func valueProviderCallback = null, bool softResettable = true,
string name = null)
{
ThrowIfRangeIllegal(position, 1, name);
var field = new FlagRegisterField(this, position, mode, readCallback, writeCallback, changeCallback, valueProviderCallback, name);
registerFields.Add(field);
if(!softResettable)
{
MarkNonResettable(position, 1);
}
RecalculateFieldMask();
return field;
}
///
/// Defines the value field. Its value is interpreted as a regular number.
///
/// Offset in the register.
/// Maximum width of the value, in terms of binary representation.
/// Access modifiers of this field.
/// Method to be called whenever the containing register is read. The first parameter is the value of this field before read,
/// the second parameter is the value after read. Note that it will also be called for unreadable fields.
/// Method to be called whenever the containing register is written to. The first parameter is the value of this field before write,
/// the second parameter is the value written (without any modification). Note that it will also be called for unwrittable fields.
/// Method to be called whenever this field's value is changed, either due to read or write. The first parameter is the value of this field before change,
/// the second parameter is the value after change. Note that it will also be called for unwrittable fields.
/// Method to be called whenever this field is read. The value passed is the current field's value, that will be overwritten by
/// the value returned from it. This returned value is eventually passed as the first parameter of .
/// Indicates whether the field should be cleared by soft reset.
/// Ignored parameter, for convenience. Treat it as a comment.
public IValueRegisterField DefineValueField(int position, int width, FieldMode mode = FieldMode.Read | FieldMode.Write, Action readCallback = null,
Action writeCallback = null, Action changeCallback = null, Func valueProviderCallback = null, bool softResettable = true,
string name = null)
{
ThrowIfRangeIllegal(position, width, name);
ThrowIfZeroWidth(position, width, name);
var field = new ValueRegisterField(this, position, width, mode, readCallback, writeCallback, changeCallback, valueProviderCallback, name);
registerFields.Add(field);
if(!softResettable)
{
MarkNonResettable(position, width);
}
RecalculateFieldMask();
return field;
}
///
/// Defines the enum field. Its value is interpreted as an enumeration
///
/// Offset in the register.
/// Maximum width of the value, in terms of binary representation.
/// Access modifiers of this field.
/// Method to be called whenever the containing register is read. The first parameter is the value of this field before read,
/// the second parameter is the value after read. Note that it will also be called for unreadable fields.
/// Method to be called whenever the containing register is written to. The first parameter is the value of this field before write,
/// the second parameter is the value written (without any modification). Note that it will also be called for unwrittable fields.
/// Method to be called whenever this field's value is changed, either due to read or write. The first parameter is the value of this field before change,
/// the second parameter is the value after change. Note that it will also be called for unwrittable fields.
/// Method to be called whenever this field is read. The value passed is the current field's value, that will be overwritten by
/// the value returned from it. This returned value is eventually passed as the first parameter of .
/// Indicates whether the field should be cleared by soft reset.
/// Ignored parameter, for convenience. Treat it as a comment.
public IEnumRegisterField DefineEnumField(int position, int width, FieldMode mode = FieldMode.Read | FieldMode.Write, Action readCallback = null,
Action writeCallback = null, Action changeCallback = null, Func valueProviderCallback = null, bool softResettable = true,
string name = null)
where TEnum : struct, IConvertible
{
ThrowIfRangeIllegal(position, width, name);
ThrowIfZeroWidth(position, width, name);
var field = new EnumRegisterField(this, position, width, mode, readCallback, writeCallback, changeCallback, valueProviderCallback, name);
registerFields.Add(field);
if(!softResettable)
{
MarkNonResettable(position, width);
}
RecalculateFieldMask();
return field;
}
public int RegisterWidth { get; }
protected PeripheralRegister(IPeripheral parent, ulong resetValue, bool softResettable, int width)
{
this.parent = parent;
RegisterWidth = width;
this.resetValue = resetValue;
// We want to reset the register before setting the resettableMask. If we don't do that then
// the register will not be initialized to the resetValue, instead it will hold the default value of 0
Reset();
if(!softResettable)
{
resettableMask = 0;
}
}
protected ulong ReadInner()
{
foreach(var registerField in registerFields)
{
UnderlyingValue = registerField.CallValueProviderHandler(UnderlyingValue);
}
var baseValue = UnderlyingValue;
var valueToRead = UnderlyingValue;
var changedFields = new List();
foreach(var registerField in registerFields)
{
if(!registerField.fieldMode.IsReadable())
{
BitHelper.ClearBits(ref valueToRead, registerField.position, registerField.width);
}
if(registerField.fieldMode.IsFlagSet(FieldMode.ReadToClear)
&& BitHelper.AreAnyBitsSet(UnderlyingValue, registerField.position, registerField.width))
{
BitHelper.ClearBits(ref UnderlyingValue, registerField.position, registerField.width);
changedFields.Add(registerField);
}
if(registerField.fieldMode.IsFlagSet(FieldMode.ReadToSet)
&& !BitHelper.AreAllBitsSet(UnderlyingValue, registerField.position, registerField.width))
{
BitHelper.SetBits(ref UnderlyingValue, registerField.position, registerField.width);
changedFields.Add(registerField);
}
}
foreach(var registerField in registerFields)
{
registerField.CallReadHandler(baseValue, UnderlyingValue);
}
foreach(var changedRegister in changedFields.Distinct())
{
changedRegister.CallChangeHandler(baseValue, UnderlyingValue);
}
CallReadHandlers(baseValue, UnderlyingValue);
if(changedFields.Any())
{
CallChangeHandlers(baseValue, UnderlyingValue);
}
return valueToRead;
}
protected void WriteInner(long offset, ulong value)
{
var baseValue = UnderlyingValue;
var difference = UnderlyingValue ^ value;
var changedRegisters = new List();
foreach(var registerField in registerFields)
{
//switch is OK, because write modes are exclusive.
switch(registerField.fieldMode.WriteBits())
{
case FieldMode.Write:
if(BitHelper.AreAnyBitsSet(difference, registerField.position, registerField.width))
{
BitHelper.UpdateWith(ref UnderlyingValue, value, registerField.position, registerField.width);
changedRegisters.Add(registerField);
}
break;
case FieldMode.Set:
var setRegisters = value & (~UnderlyingValue);
if(BitHelper.AreAnyBitsSet(setRegisters, registerField.position, registerField.width))
{
BitHelper.OrWith(ref UnderlyingValue, setRegisters, registerField.position, registerField.width);
changedRegisters.Add(registerField);
}
break;
case FieldMode.Toggle:
if(BitHelper.AreAnyBitsSet(value, registerField.position, registerField.width))
{
BitHelper.XorWith(ref UnderlyingValue, value, registerField.position, registerField.width);
changedRegisters.Add(registerField);
}
break;
case FieldMode.WriteOneToClear:
if(BitHelper.AreAnyBitsSet((~difference & value), registerField.position, registerField.width))
{
BitHelper.AndWithNot(ref UnderlyingValue, value, registerField.position, registerField.width);
changedRegisters.Add(registerField);
}
break;
case FieldMode.WriteZeroToClear:
if(BitHelper.AreAnyBitsSet((difference & UnderlyingValue), registerField.position, registerField.width))
{
BitHelper.AndWithNot(ref UnderlyingValue, ~value, registerField.position, registerField.width);
changedRegisters.Add(registerField);
}
break;
case FieldMode.WriteZeroToSet:
var negSetRegisters = ~value & (~UnderlyingValue);
if(BitHelper.AreAnyBitsSet(negSetRegisters, registerField.position, registerField.width))
{
BitHelper.OrWith(ref UnderlyingValue, negSetRegisters, registerField.position, registerField.width);
changedRegisters.Add(registerField);
}
break;
case FieldMode.WriteZeroToToggle:
if(BitHelper.AreAnyBitsSet(~value, registerField.position, registerField.width))
{
BitHelper.XorWith(ref UnderlyingValue, ~value, registerField.position, registerField.width);
changedRegisters.Add(registerField);
}
break;
case FieldMode.WriteToClear:
if(BitHelper.AreAnyBitsSet(UnderlyingValue, registerField.position, registerField.width))
{
BitHelper.ClearBits(ref UnderlyingValue, registerField.position, registerField.width);
changedRegisters.Add(registerField);
}
break;
}
}
foreach(var registerField in registerFields)
{
registerField.CallWriteHandler(baseValue, value);
}
foreach(var changedRegister in changedRegisters.Distinct())
{
changedRegister.CallChangeHandler(baseValue, UnderlyingValue);
}
CallWriteHandlers(baseValue, value);
if(changedRegisters.Any())
{
CallChangeHandlers(baseValue, UnderlyingValue);
}
var unhandledWrites = difference & ~definedFieldsMask;
if(unhandledWrites != 0)
{
parent.Log(LogLevel.Warning, TagLogger(offset, unhandledWrites, value));
}
if(InvalidTagValues(offset, value, out var invalidValueLog))
{
parent.Log(LogLevel.Error, invalidValueLog);
}
}
protected void CallHandlers(List> handlers, T oldValue, T newValue)
{
foreach(var handler in handlers)
{
handler(oldValue, newValue);
}
}
protected abstract void CallWriteHandlers(ulong oldValue, ulong newValue);
protected abstract void CallReadHandlers(ulong oldValue, ulong newValue);
protected abstract void CallChangeHandlers(ulong oldValue, ulong newValue);
protected ulong UnderlyingValue;
///
/// Returns information about tag writes. Extracted as a method to allow future lazy evaluation.
///
/// The offset of the affected register.
/// Unhandled bits mask.
/// The whole value written to the register.
private string TagLogger(long offset, ulong unhandledMask, ulong originalValue)
{
var tagsAffected = tags.Where(x => BitHelper.AreAnyBitsSet(unhandledMask, x.Position, x.Width))
.Select(x => new { x.Name, Value = BitHelper.GetValue(originalValue, x.Position, x.Width) });
return "Unhandled write to offset 0x{2:X}. Unhandled bits: [{1}] when writing value 0x{3:X}.{0}"
.FormatWith(tagsAffected.Any() ? " Tags: {0}.".FormatWith(
tagsAffected.Select(x => "{0} (0x{1:X})".FormatWith(x.Name, x.Value)).Stringify(", ")) : String.Empty,
BitHelper.GetSetBitsPretty(unhandledMask),
offset,
originalValue);
}
private bool InvalidTagValues(long offset, ulong originalValue, out string log)
{
ulong allowedValuesMask = 0;
ulong allowedValues = 0;
foreach(var tag in tags.Where(x => x.AllowedValue != null))
{
allowedValuesMask |= BitHelper.CalculateQuadWordMask(tag.Width, tag.Position);
allowedValues |= tag.AllowedValue.Value << tag.Position;
}
var invalidBits = (allowedValues ^ originalValue) & allowedValuesMask;
if(invalidBits == 0)
{
log = "";
return false;
}
var writtenValue = "0b" + Convert.ToString((long)originalValue, 2).PadLeft(RegisterWidth, '0');
var desiredValues = "0b";
for(int i = RegisterWidth - 1; i >= 0; i--)
{
if(((allowedValuesMask >> i) & 1u) == 1)
{
desiredValues += ((allowedValues >> i) & 1) == 0 ? "0" : "1";
}
else
{
desiredValues += "x";
}
}
log = $"Invalid value written to offset 0x{offset:X} reserved bits. Allowed values = {desiredValues}, Value written = {writtenValue}";
return true;
}
private void ThrowIfRangeIllegal(int position, int width, string name)
{
if(width < 0)
{
throw new ArgumentException("Field {0} has to have a size larger than or equal to 0.".FormatWith(name ?? "at {0} of {1} bits".FormatWith(position, width)));
}
if(position + width > RegisterWidth)
{
throw new ArgumentException("Field {0} does not fit in the register size.".FormatWith(name ?? "at {0} of {1} bits".FormatWith(position, width)));
}
foreach(var field in registerFields.Select(x => new { x.position, x.width }).Concat(tags.Select(x => new { position = x.Position, width = x.Width })))
{
var minEnd = Math.Min(position + width, field.position + field.width);
var maxStart = Math.Max(position, field.position);
if(minEnd > maxStart)
{
throw new ArgumentException("Field {0} intersects with another range.".FormatWith(name ?? "at {0} of {1} bits".FormatWith(position, width)));
}
}
}
private void ThrowIfZeroWidth(int position, int width, string name)
{
if(width == 0)
{
throw new ArgumentException("Field {0} has to have a size not equal to 0.".FormatWith(name ?? "at {0} of {1} bits".FormatWith(position, width)));
}
}
private void ThrowIfAllowedValueDoesNotFitInWidth(int width, ulong allowedValue, string name)
{
if((allowedValue >> width) != 0)
{
throw new ArgumentException($"Fields {name} allowedValue does not fit in its width");
}
}
private void RecalculateFieldMask()
{
var mask = 0UL;
foreach(var field in registerFields)
{
mask |= BitHelper.CalculateQuadWordMask(field.width, field.position);
}
definedFieldsMask = mask;
}
private void MarkNonResettable(int position, int width)
{
BitHelper.ClearBits(ref resettableMask, position, width);
}
private List registerFields = new List();
private List tags = new List();
private IPeripheral parent;
private ulong definedFieldsMask;
private ulong resettableMask = ulong.MaxValue;
private readonly ulong resetValue;
}
}