From 61b935abb13cb965b2a42eaa49e1b616ca1fed4b Mon Sep 17 00:00:00 2001 From: Shareef Jalloq Date: Mon, 5 Aug 2024 10:57:11 +0100 Subject: [PATCH 1/3] STM32H7: add PWR peripheral This commit adds the STM32H7_PWR peripheral. --- src/Emulator/Peripherals/Peripherals.csproj | 1 + .../Peripherals/Miscellaneous/STM32H7_PWR.cs | 318 ++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32H7_PWR.cs diff --git a/src/Emulator/Peripherals/Peripherals.csproj b/src/Emulator/Peripherals/Peripherals.csproj index 3e64d4a8e..693973e43 100644 --- a/src/Emulator/Peripherals/Peripherals.csproj +++ b/src/Emulator/Peripherals/Peripherals.csproj @@ -588,6 +588,7 @@ + diff --git a/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32H7_PWR.cs b/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32H7_PWR.cs new file mode 100644 index 000000000..be93432e4 --- /dev/null +++ b/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32H7_PWR.cs @@ -0,0 +1,318 @@ +// +// Copyright (c) 2024 Antmicro +// Copyright (c) 2024 Nu Quantum Ltd +// +// This file is licensed under the MIT License. +// Full license text is available in 'licenses/MIT.txt'. +// + +using Antmicro.Renode.Core; +using Antmicro.Renode.Core.Structure.Registers; +using Antmicro.Renode.Logging; + +namespace Antmicro.Renode.Peripherals.Miscellaneous +{ + public class STM32H7_PWR : BasicDoubleWordPeripheral, IKnownSize + { + public STM32H7_PWR(IMachine machine) : base(machine) + { + IRQ = new GPIO(); + + DefineRegisters(); + Reset(); + } + + public override void Reset() + { + base.Reset(); + IRQ.Unset(); + Voltage = 3.3; + prevPvdo = false; + } + + public long Size => 0x400; + public GPIO IRQ { get; } + + public double? ThresholdVoltage { get => PvdLevelToVoltage(pvdLevel.Value); } + + public double Voltage + { + get + { + return voltage; + } + set + { + voltage = value; + UpdatePvd(); + } + } + + public PvdLevelSelection PvdLevel + { + get + { + return pvdLevel.Value; + } + set + { + pvdLevel.Value = value; + UpdatePvd(); + } + } + + private void DefineRegisters() + { + Registers.Control1.Define(this, 0xF000_C000) + // The LPDS flag has no functionality because low-power run mode is not implemented + .WithFlag(0, name: "LPDS") + .WithReservedBits(1, 3) + .WithFlag(4, out pvdEnableFlag, name: "PVDE") + .WithEnumField(5, 3, out pvdLevel, writeCallback: (_, value) => + { + if(value == PvdLevelSelection.ExternalInput) + { + this.Log(LogLevel.Warning, "External PVD input selected, this is not supported"); + } + UpdatePvd(); + }, name: "PLS") + .WithFlag(8, out backupDomainDisabled, name: "DBP") + // The FLPS flag has no functionality as the respective functionality is not implemented + .WithFlag(9, name: "FLPS") + .WithReservedBits(10, 4) + .WithEnumField(14, 2, out vsosValue, name: "VSOS") + .WithFlag(16, out avdEnableFlag, name: "AVDEN") + .WithEnumField(17, 2, out avdLevel, writeCallback: (_, value) => + { + this.Log(LogLevel.Warning, $"avdLevel set to {value:X}"); + this.Log(LogLevel.Warning, $"avdLevel is {avdLevel.Value:X}"); + UpdateAvd(); + }, name: "ALS") + + .WithReservedBits(19, 13); + + Registers.ControlStatus1.Define(this, 0x0000_4000) + .WithReservedBits(0, 4) + .WithFlag(4, out pvdoFlag, FieldMode.Read, name: "PVDO") + .WithReservedBits(5, 8) + .WithFlag(13, mode: FieldMode.Read, name: "ACTVOSRDY", valueProviderCallback: _ => true) + .WithValueField(14, 2, FieldMode.Read, name: "ACTVOS", valueProviderCallback: _ => (uint)vosValue.Value) + .WithFlag(16, out avdoFlag, FieldMode.Read, name: "AVDO") + .WithReservedBits(17, 15); + + Registers.Control2.Define(this) + .WithTaggedFlag("BREN", 0) + .WithReservedBits(1, 3) + .WithTaggedFlag("MONEN", 4) + .WithReservedBits(5, 11) + .WithTaggedFlag("BRRDY", 16) + .WithReservedBits(17, 3) + .WithTaggedFlag("VBATL", 20) + .WithTaggedFlag("VBATH", 21) + .WithTaggedFlag("TEMPL", 22) + .WithTaggedFlag("TEMPH", 23) + .WithReservedBits(24, 8); + + Registers.Control3.Define(this, 0x0000_0046) + .WithFlag(0, name: "BYPASS") + .WithFlag(1, name: "LDOEN") + .WithFlag(2, name: "SCUEN") + .WithReservedBits(3, 5) + .WithFlag(8, name: "VBE") + .WithFlag(9, name: "VBRS") + .WithReservedBits(10, 14) + .WithFlag(24, name: "USB33DEN") + .WithFlag(25, name: "USBREGEN") + .WithFlag(26, name: "USB33RDY") + .WithReservedBits(27, 5); + + Registers.CPUControl1.Define(this) + .WithTaggedFlag("PDDS_D1", 0) + .WithTaggedFlag("PDDS_D2", 1) + .WithTaggedFlag("PDDS_D3", 2) + .WithReservedBits(3, 2) + .WithFlag(5, out stopFlag, FieldMode.Read, name: "STOPF") + .WithFlag(6, out standbyFlag, FieldMode.Read, name: "SBF") + .WithFlag(7, out standbyFlagD1, FieldMode.Read, name: "SBF_D1") + .WithFlag(8, out standbyFlagD2, FieldMode.Read, name: "SBF_D2") + .WithFlag(9, FieldMode.WriteOneToClear, name: "CSSF", + writeCallback: (_, __) => + { + stopFlag.Value = false; + standbyFlag.Value = false; + standbyFlagD1.Value = false; + standbyFlagD2.Value = false; + }) + .WithReservedBits(10, 1) + .WithTaggedFlag("RUN_D3", 11) + .WithReservedBits(12, 20); + + Registers.D3DomainControl.Define(this, 0x0000_4000) + .WithReservedBits(0, 13) + .WithFlag(13, mode: FieldMode.Read, name: "VOSRDY", valueProviderCallback: _ => true) + .WithEnumField(14, 2, out vosValue, name: "VOS") + .WithReservedBits(16, 16); + + Registers.WakeupFlag.Define(this) + .WithFlags(0, 6, out wakeupFlags, FieldMode.Read, name: "WKUPF") + .WithReservedBits(6, 26); + + Registers.WakeupClear.Define(this) + .WithFlags(0, 6, FieldMode.WriteOneToClear, name: "WKUPC", + writeCallback: (idx, _, __) => wakeupFlags[idx].Value = false) + .WithReservedBits(6, 26); + + Registers.WakeupEnableAndPolarity.Define(this) + .WithTaggedFlag("WKUPEN1", 0) + .WithTaggedFlag("WKUPEN2", 1) + .WithTaggedFlag("WKUPEN3", 2) + .WithTaggedFlag("WKUPEN4", 3) + .WithTaggedFlag("WKUPEN5", 4) + .WithTaggedFlag("WKUPEN6", 5) + .WithReservedBits(6, 2) + .WithTaggedFlag("WKUPP1", 8) + .WithTaggedFlag("WKUPP2", 9) + .WithTaggedFlag("WKUPP3", 10) + .WithTaggedFlag("WKUPP4", 11) + .WithTaggedFlag("WKUPP5", 12) + .WithTaggedFlag("WKUPP6", 13) + .WithReservedBits(14, 2) + .WithTaggedFlags("WKUPPUPD1", 16, 2) + .WithTaggedFlags("WKUPPUPD2", 18, 2) + .WithTaggedFlags("WKUPPUPD3", 20, 2) + .WithTaggedFlags("WKUPPUPD4", 22, 2) + .WithTaggedFlags("WKUPPUPD5", 24, 2) + .WithTaggedFlags("WKUPPUPD6", 26, 2) + .WithReservedBits(28, 4); + } + + private double? PvdLevelToVoltage(PvdLevelSelection level) + { + switch(level) + { + case PvdLevelSelection.V1_95: + return 1.95; + case PvdLevelSelection.V2_1: + return 2.1; + case PvdLevelSelection.V2_25: + return 2.25; + case PvdLevelSelection.V2_4: + return 2.4; + case PvdLevelSelection.V2_55: + return 2.55; + case PvdLevelSelection.V2_7: + return 2.7; + case PvdLevelSelection.V2_85: + return 2.85; + case PvdLevelSelection.ExternalInput: + default: + return null; + } + } + + private void UpdatePvd() + { + if(PvdLevel == PvdLevelSelection.ExternalInput) + { + // External input is not supported yet, skip updating the pvd + return; + } + + bool pvdo; + if(prevPvdo && Voltage > ThresholdVoltage + Hysteresis) + { + // PVDO should be false if the voltage was below and is above the threshold + pvdo = false; + } + else if(!prevPvdo && Voltage < ThresholdVoltage - Hysteresis) + { + // PVDO should be true if the voltage was above and is below the threshold + pvdo = true; + } + else + { + // No change (within hysteresis) + pvdo = prevPvdo; + } + prevPvdo = pvdo; + pvdo &= pvdEnableFlag.Value; + + pvdoFlag.Value = pvdo; + IRQ.Set(pvdo); + } + + private void UpdateAvd() + { + this.Log(LogLevel.Warning, "REVISIT: AVD not implemented"); + } + + private IFlagRegisterField[] wakeupFlags = new IFlagRegisterField[6]; + private IFlagRegisterField standbyFlag; + private IFlagRegisterField standbyFlagD1; + private IFlagRegisterField standbyFlagD2; + private IFlagRegisterField stopFlag; + private IFlagRegisterField avdoFlag; + private IFlagRegisterField avdEnableFlag; + private IEnumRegisterField avdLevel; + private IFlagRegisterField pvdoFlag; + private IFlagRegisterField pvdEnableFlag; + private IEnumRegisterField pvdLevel; + private IEnumRegisterField vosValue; + private IEnumRegisterField vsosValue; + private IFlagRegisterField backupDomainDisabled; + private double voltage; + private bool prevPvdo; + + private const double Hysteresis = 0.1; + + public enum AvdLevelSelection + { + V1_7 = 0b00, + V2_1 = 0b01, + V2_5 = 0b10, + V2_8 = 0b11, + } + + public enum PvdLevelSelection + { + V1_95 = 0b000, + V2_1 = 0b001, + V2_25 = 0b010, + V2_4 = 0b011, + V2_55 = 0b100, + V2_7 = 0b101, + V2_85 = 0b110, + ExternalInput = 0b111, + } + + private enum Registers + { + Control1 = 0x00, + ControlStatus1 = 0x04, + Control2 = 0x08, + Control3 = 0x0C, + CPUControl1 = 0x10, + // Reserved = 0x14, + D3DomainControl = 0x18, + WakeupClear = 0x20, + WakeupFlag = 0x24, + WakeupEnableAndPolarity = 0x28, + } + + private enum VoltageScalingRangeSelection + { + Reserved = 0b00, + Scale3 = 0b01, // Default + Scale2 = 0b10, + Scale1 = 0b11, + } + + private enum StopModeVoltageScalingSelection + { + Reserved = 0b00, + Scale5 = 0b01, + Scale4 = 0b10, + Scale3 = 0b00, + } + } +} From 643f1223f04432efa2f18d1f86cbe320d21b42f5 Mon Sep 17 00:00:00 2001 From: Shareef Jalloq Date: Mon, 5 Aug 2024 15:02:40 +0100 Subject: [PATCH 2/3] STM32H7: add missing RCC registers This commit adds some missing RCC registers causing panics in the stm32h7xx-hal. --- .../Peripherals/Miscellaneous/STM32H7_RCC.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32H7_RCC.cs b/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32H7_RCC.cs index 2d139abdf..b9571fed5 100644 --- a/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32H7_RCC.cs +++ b/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32H7_RCC.cs @@ -76,6 +76,33 @@ public STM32H7_RCC(IMachine machine) }, {(long)Registers.PLLConfigurationRegister, new DoubleWordRegister(this, 0x01FF0000) }, + {(long)Registers.Domain1KernelClockConfiguration, new DoubleWordRegister(this, 0x0) + .WithValueField(0, 2, name: "FMCSEL") + .WithReservedBits(2, 2) + .WithValueField(4, 2, name: "QSPISEL") + .WithReservedBits(6, 10) + .WithFlag(16, name: "SDMMCSEL") + .WithReservedBits(17, 11) + .WithValueField(28, 2, name: "CKPERSEL") + .WithReservedBits(30, 2) + }, + {(long)Registers.Domain2KernelClockConfiguration, new DoubleWordRegister(this, 0x0) + .WithValueField(0, 3, name: "SAI1SEL") + .WithReservedBits(3, 3) + .WithValueField(6, 3, name: "SAI23SEL") + .WithReservedBits(9, 3) + .WithValueField(12, 3, name: "SPI123SEL") + .WithReservedBits(15, 1) + .WithValueField(16, 3, name: "SPI45SEL") + .WithReservedBits(19, 1) + .WithValueField(20, 2, name: "SPDIFSEL") + .WithReservedBits(22, 2) + .WithFlag(24, name: "DFSDM1SEL") + .WithReservedBits(25, 3) + .WithValueField(28, 2, name: "FDCANSEL") + .WithReservedBits(30, 1) + .WithFlag(31, name: "SVPSEL") + }, {(long)Registers.BackupDomainControl, new DoubleWordRegister(this) .WithFlag(0, out var lseon, name: "LSEON") .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => lseon.Value, name: "LSERDY") @@ -92,6 +119,19 @@ public STM32H7_RCC(IMachine machine) .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => lsion.Value, name: "LSIRDY") .WithReservedBits(2, 30) }, + {(long)Registers.AHB2Enable, new DoubleWordRegister(this, 0x0) + .WithFlag(0, name: "DCMIEN") + .WithReservedBits(1, 3) + .WithFlag(4, name: "CRYPTEN") + .WithFlag(5, name: "HASHEN") + .WithFlag(6, name: "RNGEN") + .WithReservedBits(7, 2) + .WithFlag(9, name: "SDMMC2EN") + .WithReservedBits(10, 19) + .WithFlag(29, name: "SRAM1EN") + .WithFlag(30, name: "SRAM2EN") + .WithFlag(31, name: "SRAM3EN") + }, {(long)Registers.AHB4Enable, new DoubleWordRegister(this, 0x0) .WithFlag(0, name: "GPIOAEN") .WithFlag(1, name: "GPIOBEN") @@ -117,6 +157,18 @@ public STM32H7_RCC(IMachine machine) } }; + for(var i = 0; i < 3; ++i) + { + registersMap.Add((long)Registers.PLL1DividersConfiguration + i * 0x8, new DoubleWordRegister(this, 0x0101_0280) + .WithValueField(0, 9, name: "DIVN1") + .WithValueField(9, 7, name: "DIVP1") + .WithValueField(16, 7, name: "DIVQ1") + .WithReservedBits(23, 1) + .WithValueField(24, 7, name: "DIVR1") + .WithReservedBits(31, 1) + ); + } + for(var i = 0; i < 3; ++i) { registersMap.Add((long)Registers.PLL1FractionalDivider + i * 0x8, new DoubleWordRegister(this, 0x0) @@ -162,9 +214,13 @@ private enum Registers PLL3DividersConfiguration = 0x40, PLL3FractionalDivider = 0x44, // ... + Domain1KernelClockConfiguration = 0x4C, + Domain2KernelClockConfiguration = 0x50, + // ... BackupDomainControl = 0x70, ClockControlAndStatus = 0x74, // ... + AHB2Enable = 0xDC, AHB4Enable = 0xE0 } } From ef75902a9b244c9184ab0428ca419ab0c69aa3d2 Mon Sep 17 00:00:00 2001 From: Shareef Jalloq Date: Mon, 5 Aug 2024 15:05:16 +0100 Subject: [PATCH 3/3] STM32H7: add missing SYSCFG register This commits adds the missing CompensationCellControl register which is polled by the stm32h7xx-hal at startup. --- .../Peripherals/Miscellaneous/STM32_SYSCFG.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32_SYSCFG.cs b/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32_SYSCFG.cs index c5e09e23d..e1f36ec69 100644 --- a/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32_SYSCFG.cs +++ b/src/Emulator/Peripherals/Peripherals/Miscellaneous/STM32_SYSCFG.cs @@ -106,6 +106,17 @@ private DoubleWordRegisterCollection CreateRegisters() } map.Add((long)Registers.ExternalInterruptConfiguration1 + 4 * regNumber, reg); } + + map.Add((long)Registers.CompensationCellControl, new DoubleWordRegister(this) + .WithFlag(0, name: "EN") + .WithFlag(1, name: "CS") + .WithReservedBits(2, 6) + // READY should only be driven to 1 when the CSION flag is set in the RCC_CR register + .WithFlag(8, mode: FieldMode.Read, name: "READY", valueProviderCallback: _ => true) + .WithReservedBits(9, 7) + .WithFlag(16, name: "HSLV") + .WithReservedBits(17, 15) + ); return new DoubleWordRegisterCollection(this, map); }