renode-esp32s3/peripherals/ESP32S3_GPIO.cs

270 lines
11 KiB
C#

//
// Copyright (c) 2010-2021 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System.Collections.Generic;
using Antmicro.Renode.Core;
using Antmicro.Renode.Utilities;
using Antmicro.Renode.Peripherals.Bus;
using Antmicro.Renode.Core.Structure.Registers;
using Antmicro.Renode.Logging;
namespace Antmicro.Renode.Peripherals.GPIOPort
{
public class ESP32S3_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize
{
public ESP32S3_GPIO(Machine machine) : base(machine, NumberOfPins)
{
locker = new object();
IRQ = new GPIO();
registers = new DoubleWordRegisterCollection(this, BuildRegisterMap());
data = new bool[NumberOfPins];
directionOutNotIn = new bool[NumberOfPins];
interruptEnabled = new bool[NumberOfPins];
interruptRequest = new bool[NumberOfPins];
edgeSelect = new bool[NumberOfPins];
interruptConfig = new InterruptConfig[NumberOfPins];
}
public override void Reset()
{
lock(locker)
{
base.Reset();
IRQ.Unset();
registers.Reset();
for(var i = 0; i < NumberOfPins; ++i)
{
data[i] = false;
directionOutNotIn[i] = false;
interruptEnabled[i] = false;
interruptRequest[i] = false;
edgeSelect[i] = false;
interruptConfig[i] = InterruptConfig.Low;
}
}
}
public uint ReadDoubleWord(long offset)
{
lock(locker)
{
return registers.Read(offset);
}
}
public void WriteDoubleWord(long offset, uint value)
{
lock(locker)
{
registers.Write(offset, value);
}
}
public override void OnGPIO(int number, bool value)
{
// if(!CheckPinNumber(number))
// {
// return;
// }
// if(directionOutNotIn[number])
// {
// this.Log(LogLevel.Warning, "gpio {0} is set to output, signal ignored.", number);
// return;
// }
// lock(locker)
// {
// var previousState = State[number];
// base.OnGPIO(number, value);
// UpdateSingleInterruptRequest(number, value, previousState != value);
// UpdateIRQ();
// }
}
public long Size => 0x634;
public GPIO IRQ { get; }
private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
{
var registersDictionary = new Dictionary<long, DoubleWordRegister>
{
{(long)Registers.STRAP, new DoubleWordRegister(this)
.WithFlag(2, FieldMode.Read, valueProviderCallback: (_) => !EnableBootMessages, name: "GPIO46") // GND = enable boot messages
.WithFlag(3, FieldMode.Read, valueProviderCallback: (_) => !EnterBootloader, name: "GPIO0") // GND = enter bootloader
.WithFlag(4, FieldMode.Read, valueProviderCallback: (_) => false, name: "GPIO45") // GND = VDD_SPI @ 3.3V
.WithFlag(5, FieldMode.Read, valueProviderCallback: (_) => false, name: "GPIO3") // GND = JTAG from pins, not USB
},
// {(long)Registers.Data, new DoubleWordRegister(this)
// .WithFlags(0, NumberOfPins, name: "GR / GPIO data register",
// writeCallback: (id, _, val) => { data[id] = val; },
// valueProviderCallback: (id, _) =>
// {
// return (directionOutNotIn[id])
// ? data[id]
// : State[id];
// })
// .WithWriteCallback((_, __) => UpdateConnections())
// },
// {(long)Registers.Direction, new DoubleWordRegister(this)
// .WithFlags(0, NumberOfPins, name: "GDIR / GPIO direction register",
// writeCallback: (id, _, val) => { directionOutNotIn[id] = val; },
// valueProviderCallback: (id, _) => directionOutNotIn[id])
// .WithWriteCallback((_, __) => UpdateConnections())
// },
// {(long)Registers.PadStatus, new DoubleWordRegister(this)
// .WithFlags(0, NumberOfPins, FieldMode.Read, name: "PSR / GPIO pad status register",
// valueProviderCallback: (id, _) => State[id])
// },
// {(long)Registers.Mask, new DoubleWordRegister(this)
// .WithFlags(0, NumberOfPins, name: "IMR / GPIO interrupt mask register",
// writeCallback: (id, _, val) => { interruptEnabled[id] = val; },
// valueProviderCallback: (id, _) => interruptEnabled[id])
// .WithWriteCallback((_, __) => UpdateIRQ())
// },
// {(long)Registers.Status, new DoubleWordRegister(this)
// .WithFlags(0, NumberOfPins, FieldMode.Read | FieldMode.WriteOneToClear, name: "ISR / GPIO interrupt status register",
// writeCallback: (id, _, val) =>
// {
// if(val)
// {
// interruptRequest[id] = false;
// }
// },
// valueProviderCallback: (id, _) => interruptRequest[id])
// .WithWriteCallback((_, __) => UpdateIRQ())
// },
// {(long)Registers.EdgeSelect, new DoubleWordRegister(this)
// .WithFlags(0, NumberOfPins, name: "EDGE_SEL / GPIO edge select register",
// writeCallback: (id, _, val) => { edgeSelect[id] = val; },
// valueProviderCallback: (id, _) => edgeSelect[id])
// },
// {(long)Registers.DataSet, new DoubleWordRegister(this)
// .WithFlags(0, NumberOfPins, FieldMode.Write, name: "DR_SET / GPIO data register SET",
// writeCallback: (id, _, __) => { data[id] = true; })
// .WithWriteCallback((_, __) => UpdateConnections())
// },
// {(long)Registers.DataClear, new DoubleWordRegister(this)
// .WithFlags(0, NumberOfPins, FieldMode.Write, name: "DR_CLEAR / GPIO data register CLEAR",
// writeCallback: (id, _, __) => { data[id] = false; })
// .WithWriteCallback((_, __) => UpdateConnections())
// },
// {(long)Registers.DataToggle, new DoubleWordRegister(this)
// .WithFlags(0, NumberOfPins, FieldMode.Write, name: "DR_TOGGLE / GPIO data register TOGGLE",
// writeCallback: (id, _, __) => { data[id] ^= true; })
// .WithWriteCallback((_, __) => UpdateConnections())
// },
};
// var config1 = new DoubleWordRegister(this);
// var config2 = new DoubleWordRegister(this);
// var half = NumberOfPins / 2;
// for(var i = 0; i < half; ++i)
// {
// var j = i;
// config1.WithEnumField<DoubleWordRegister, InterruptConfig>(j * 2, 2,
// name: $"ICR{j} / Interrupt configuration {j}",
// writeCallback: (_, val) => { interruptConfig[j] = val; },
// valueProviderCallback: _ => interruptConfig[j]);
// config2.WithEnumField<DoubleWordRegister, InterruptConfig>(j * 2, 2,
// name: $"ICR{half + j} / Interrupt configuration {half + j}",
// writeCallback: (_, val) => { interruptConfig[half + j] = val; },
// valueProviderCallback: _ => interruptConfig[half + j]);
// }
// config1.WithWriteCallback((_, __) => UpdateAllInterruptRequests());
// config2.WithWriteCallback((_, __) => UpdateAllInterruptRequests());
// registersDictionary.Add((long)Registers.Config1, config1);
// registersDictionary.Add((long)Registers.Config2, config2);
return registersDictionary;
}
private void UpdateIRQ()
{
var flag = false;
for(var i = 0; i < NumberOfPins; ++i)
{
flag |= interruptEnabled[i] && interruptRequest[i];
}
IRQ.Set(flag);
}
private void UpdateConnections()
{
for(var i = 0; i < NumberOfPins; ++i)
{
Connections[i].Set(directionOutNotIn[i] && data[i]);
}
UpdateIRQ();
}
private void UpdateAllInterruptRequests()
{
for(var i = 0; i < NumberOfPins; ++i)
{
UpdateSingleInterruptRequest(i, State[i]);
}
UpdateIRQ();
}
private void UpdateSingleInterruptRequest(int i, bool currentState, bool stateChanged = false)
{
if(edgeSelect[i])
{
interruptRequest[i] |= stateChanged;
}
else
{
switch(interruptConfig[i])
{
case InterruptConfig.Low:
interruptRequest[i] |= !currentState;
break;
case InterruptConfig.High:
interruptRequest[i] |= currentState;
break;
case InterruptConfig.Rising:
interruptRequest[i] |= stateChanged && currentState;
break;
case InterruptConfig.Falling:
interruptRequest[i] |= stateChanged && !currentState;
break;
default:
this.Log(LogLevel.Error, "Invalid state (interruptConfig[{0}]: 0x{1:X}).", i, interruptConfig[i]);
break;
}
}
}
private readonly DoubleWordRegisterCollection registers;
private readonly object locker;
private readonly bool[] data;
private readonly bool[] directionOutNotIn;
private readonly bool[] interruptEnabled;
private readonly bool[] interruptRequest;
private readonly bool[] edgeSelect;
private readonly InterruptConfig[] interruptConfig;
public bool EnableBootMessages = true;
public bool EnterBootloader = false;
private const int NumberOfPins = 54;
private enum InterruptConfig
{
Low = 0b00,
High = 0b01,
Rising = 0b10,
Falling = 0b11,
}
private enum Registers : long
{
STRAP = 0x38,
}
}
}