// // 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.Exceptions; using Antmicro.Renode.Utilities; namespace Antmicro.Renode.Core.Structure.Registers { public interface IRegisterField { /// /// Gets or sets the field's value. Access to this property does not invoke verification procedures in terms of FieldMode checking. /// Also, it does not invoke callbacks. /// T Value { get; set; } /// /// Gets the field's width in bits. It should be used to verify if the value assigned to is valid, as exceeding /// the field's limits causes an ArgumentException. /// int Width { get; } Action ReadCallback { get; set; } Action WriteCallback { get; set; } Action ChangeCallback { get; set; } Func ValueProviderCallback { get; set; } } public partial class PeripheralRegister { private sealed class ValueRegisterField : RegisterField, IValueRegisterField { public ValueRegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, Action readCallback, Action writeCallback, Action changeCallback, Func valueProviderCallback, string name) : base(parent, position, width, fieldMode, readCallback, writeCallback, changeCallback, valueProviderCallback, name) { } protected override ulong FromBinary(ulong value) { return value; } protected override ulong ToBinary(ulong value) { return value; } } private sealed class EnumRegisterField : RegisterField, IEnumRegisterField where TEnum : struct, IConvertible { public EnumRegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, Action readCallback, Action writeCallback, Action changeCallback, Func valueProviderCallback, string name) : base(parent, position, width, fieldMode, readCallback, writeCallback, changeCallback, valueProviderCallback, name) { } protected override TEnum FromBinary(ulong value) { return EnumConverter.ToEnum(value); } protected override ulong ToBinary(TEnum value) { return EnumConverter.ToUInt64(value); } } private sealed class FlagRegisterField : RegisterField, IFlagRegisterField { public FlagRegisterField(PeripheralRegister parent, int position, FieldMode fieldMode, Action readCallback, Action writeCallback, Action changeCallback, Func valueProviderCallback, string name) : base(parent, position, 1, fieldMode, readCallback, writeCallback, changeCallback, valueProviderCallback, name) { } protected override bool FromBinary(ulong value) { return value != 0; } protected override ulong ToBinary(bool value) { return value ? 1u : 0; } } private abstract class RegisterField : RegisterField, IRegisterField { public T Value { get { return FromBinary(FilterValue(parent.UnderlyingValue)); } set { ulong binary = ToBinary(value); if((binary >> width) > 0 && width < 64) { throw new ConstructionException("Value exceeds the size of the field."); } WriteFiltered(binary); } } public int Width => width; public Action ReadCallback { get; set; } public Action WriteCallback { get; set; } public Action ChangeCallback { get; set; } public Func ValueProviderCallback { get; set; } public override void CallReadHandler(ulong oldValue, ulong newValue) { if(ReadCallback != null) { var oldValueFiltered = FilterValue(oldValue); var newValueFiltered = FilterValue(newValue); ReadCallback(FromBinary(oldValueFiltered), FromBinary(newValueFiltered)); } } public override void CallWriteHandler(ulong oldValue, ulong newValue) { if(WriteCallback != null) { var oldValueFiltered = FilterValue(oldValue); var newValueFiltered = FilterValue(newValue); WriteCallback(FromBinary(oldValueFiltered), FromBinary(newValueFiltered)); } } public override void CallChangeHandler(ulong oldValue, ulong newValue) { if(ChangeCallback != null) { var oldValueFiltered = FilterValue(oldValue); var newValueFiltered = FilterValue(newValue); ChangeCallback(FromBinary(oldValueFiltered), FromBinary(newValueFiltered)); } } public override ulong CallValueProviderHandler(ulong currentValue) { if(ValueProviderCallback != null) { var currentValueFiltered = FilterValue(currentValue); return UnfilterValue(currentValue, ToBinary(ValueProviderCallback(FromBinary(currentValueFiltered)))); } return currentValue; } public override string ToString() { return $"[RegisterType<{typeof(T).Name}> Value={Value} Width={Width}]"; } protected RegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, Action readCallback, Action writeCallback, Action changeCallback, Func valueProviderCallback, string name) : base(parent, position, width, fieldMode, name) { if(!fieldMode.IsReadable() && valueProviderCallback != null) { throw new ConstructionException($"A write-only field cannot provide a value callback."); } ReadCallback = readCallback; WriteCallback = writeCallback; ChangeCallback = changeCallback; ValueProviderCallback = valueProviderCallback; } protected abstract T FromBinary(ulong value); protected abstract ulong ToBinary(T value); } private abstract class RegisterField { public abstract void CallReadHandler(ulong oldValue, ulong newValue); public abstract void CallWriteHandler(ulong oldValue, ulong newValue); public abstract void CallChangeHandler(ulong oldValue, ulong newValue); public abstract ulong CallValueProviderHandler(ulong currentValue); public readonly int position; public readonly int width; public readonly string name; public readonly FieldMode fieldMode; protected RegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, string name) { if(!fieldMode.IsValid()) { throw new ConstructionException("Invalid {0} flags for register field: {1}.".FormatWith(fieldMode.GetType().Name, fieldMode.ToString())); } this.parent = parent; this.position = position; this.fieldMode = fieldMode; this.width = width; this.name = name; } protected ulong FilterValue(ulong value) { return BitHelper.GetValue(value, position, width); } protected ulong UnfilterValue(ulong baseValue, ulong fieldValue) { BitHelper.UpdateWithShifted(ref baseValue, fieldValue, position, width); return baseValue; } protected void WriteFiltered(ulong value) { BitHelper.UpdateWithShifted(ref parent.UnderlyingValue, value, position, width); } protected readonly PeripheralRegister parent; } } }