270 lines
11 KiB
C#
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,
|
|
}
|
|
}
|
|
}
|