并口flash

This commit is contained in:
liuwb
2026-04-27 09:08:34 +08:00
parent f5a2fa7725
commit d9c0f0f07c

318
YB29LV160Flash.cs Normal file
View File

@@ -0,0 +1,318 @@
using System;
using Antmicro.Renode.Core;
using Antmicro.Renode.Logging;
using Antmicro.Renode.Peripherals.Bus;
namespace Antmicro.Renode.Peripherals.CustomPeripherals
{
public class YB29LV160Flash : IDoubleWordPeripheral, IWordPeripheral, IBytePeripheral, IKnownSize, IGPIOReceiver
{
public YB29LV160Flash(IMachine machine, long size = DefaultFlashSize)
{
if(size <= 0 || size > int.MaxValue)
{
throw new ArgumentException($"Invalid flash size: {size}", nameof(size));
}
Size = size;
storage = new byte[(int)size];
Ready = new GPIO();
Array.Fill(storage, ErasedValue);
Reset();
}
public byte ReadByte(long offset)
{
if(!TryValidateRange(offset, 1))
{
return 0xFF;
}
return storage[(int)offset];
}
public ushort ReadWord(long offset)
{
if(!TryValidateRange(offset, 2))
{
return 0xFFFF;
}
var index = (int)offset;
return (ushort)(storage[index] | (storage[index + 1] << 8));
}
public uint ReadDoubleWord(long offset)
{
if(!TryValidateRange(offset, 4))
{
return 0xFFFFFFFF;
}
var index = (int)offset;
return (uint)(
storage[index]
| (storage[index + 1] << 8)
| (storage[index + 2] << 16)
| (storage[index + 3] << 24)
);
}
public void WriteByte(long offset, byte value)
{
this.Log(LogLevel.Warning, "Unhandled byte write at offset 0x{0:X}: 0x{1:X2}; firmware is expected to use 32-bit accesses", offset, value);
commandState = FlashCommandState.ReadArray;
}
public void WriteWord(long offset, ushort value)
{
this.Log(LogLevel.Warning, "Unhandled word write at offset 0x{0:X}: 0x{1:X4}; firmware is expected to use 32-bit accesses", offset, value);
commandState = FlashCommandState.ReadArray;
}
public void WriteDoubleWord(long offset, uint value)
{
if(!TryValidateRange(offset, 4))
{
commandState = FlashCommandState.ReadArray;
return;
}
this.Log(LogLevel.Noisy, "Flash write offset 0x{0:X}, value 0x{1:X8}, state {2}", offset, value, commandState);
switch(commandState)
{
case FlashCommandState.ReadArray:
if(IsUnlock1(offset, value))
{
commandState = FlashCommandState.GotAA;
return;
}
break;
case FlashCommandState.GotAA:
if(IsUnlock2(offset, value))
{
commandState = FlashCommandState.GotAA55;
return;
}
break;
case FlashCommandState.GotAA55:
if(IsProgramSetup(offset, value))
{
commandState = FlashCommandState.ProgramSetup;
return;
}
if(IsEraseSetup(offset, value))
{
commandState = FlashCommandState.EraseSetup;
return;
}
break;
case FlashCommandState.ProgramSetup:
ProgramDoubleWord(offset, value);
commandState = FlashCommandState.ReadArray;
return;
case FlashCommandState.EraseSetup:
if(IsUnlock1(offset, value))
{
commandState = FlashCommandState.EraseGotAA;
return;
}
break;
case FlashCommandState.EraseGotAA:
if(IsUnlock2(offset, value))
{
commandState = FlashCommandState.EraseGotAA55;
return;
}
break;
case FlashCommandState.EraseGotAA55:
if(value == SectorEraseConfirm)
{
EraseSectorContaining(offset);
commandState = FlashCommandState.ReadArray;
return;
}
break;
}
this.Log(LogLevel.Warning, "Unexpected flash command write at offset 0x{0:X}: 0x{1:X8} in state {2}", offset, value, commandState);
commandState = FlashCommandState.ReadArray;
}
public void OnGPIO(int number, bool value)
{
switch(number)
{
case WriteProtectInput:
// The board-level WP pin is active low in the C driver.
writeProtected = !value;
this.Log(LogLevel.Debug, "Hardware write protect {0}", writeProtected ? "enabled" : "disabled");
break;
default:
this.Log(LogLevel.Warning, "Unhandled GPIO #{0} value {1}", number, value);
break;
}
}
public void Reset()
{
commandState = FlashCommandState.ReadArray;
writeProtected = true;
SetReady(true);
}
public void SetWriteProtect(bool enabled)
{
writeProtected = enabled;
}
public GPIO Ready { get; }
public long Size { get; }
private void ProgramDoubleWord(long offset, uint value)
{
if(writeProtected)
{
this.Log(LogLevel.Warning, "Ignoring program command at 0x{0:X}; hardware write protect is enabled", offset);
return;
}
// NOR programming can only drive bits from 1 to 0.
var index = (int)offset;
storage[index] = (byte)(storage[index] & (value & 0xFF));
storage[index + 1] = (byte)(storage[index + 1] & ((value >> 8) & 0xFF));
storage[index + 2] = (byte)(storage[index + 2] & ((value >> 16) & 0xFF));
storage[index + 3] = (byte)(storage[index + 3] & ((value >> 24) & 0xFF));
this.Log(LogLevel.Debug, "Programmed flash at 0x{0:X} with 0x{1:X8}", offset, value);
}
private void EraseSectorContaining(long offset)
{
if(writeProtected)
{
this.Log(LogLevel.Warning, "Ignoring sector erase at 0x{0:X}; hardware write protect is enabled", offset);
return;
}
var sectorStart = GetSectorStart(offset, out var sectorSize);
if(sectorSize == 0)
{
this.Log(LogLevel.Warning, "Cannot erase sector for offset 0x{0:X}", offset);
return;
}
for(var i = 0; i < sectorSize; i++)
{
storage[sectorStart + i] = ErasedValue;
}
this.Log(LogLevel.Debug, "Erased sector at 0x{0:X}, size 0x{1:X}", sectorStart, sectorSize);
}
private void SetReady(bool value)
{
Ready.Set(value);
}
private int GetSectorStart(long offset, out int sectorSize)
{
if(offset < 0 || offset >= Size)
{
sectorSize = 0;
return 0;
}
// Mirror the firmware's get_flash_sector_addr_size() layout.
if(offset >= 0x3F8000)
{
sectorSize = 0x8000;
return 0x3F8000;
}
if(offset >= 0x3F4000)
{
sectorSize = 0x4000;
return 0x3F4000;
}
if(offset >= 0x3F0000)
{
sectorSize = 0x4000;
return 0x3F0000;
}
if(offset >= 0x3E0000)
{
sectorSize = 0x10000;
return 0x3E0000;
}
sectorSize = 0x20000;
return (int)(offset / sectorSize) * sectorSize;
}
private bool TryValidateRange(long offset, int width)
{
if(offset < 0 || offset + width > Size)
{
this.Log(LogLevel.Warning, "Out-of-range flash access at offset 0x{0:X}, width {1}", offset, width);
return false;
}
return true;
}
private static bool IsUnlock1(long offset, uint value)
{
return offset == UnlockAddress1 && value == UnlockData1;
}
private static bool IsUnlock2(long offset, uint value)
{
return offset == UnlockAddress2 && value == UnlockData2;
}
private static bool IsProgramSetup(long offset, uint value)
{
return offset == UnlockAddress1 && value == ProgramSetupData;
}
private static bool IsEraseSetup(long offset, uint value)
{
return offset == UnlockAddress1 && value == EraseSetupData;
}
private readonly byte[] storage;
private FlashCommandState commandState;
private bool writeProtected;
private enum FlashCommandState
{
ReadArray,
GotAA,
GotAA55,
ProgramSetup,
EraseSetup,
EraseGotAA,
EraseGotAA55
}
private const long DefaultFlashSize = 0x400000;
private const byte ErasedValue = 0xFF;
private const int WriteProtectInput = 0;
private const long UnlockAddress1 = 0x1554;
private const long UnlockAddress2 = 0x0AA8;
private const uint UnlockData1 = 0x00AA00AA;
private const uint UnlockData2 = 0x00550055;
private const uint ProgramSetupData = 0x00A000A0;
private const uint EraseSetupData = 0x00800080;
private const uint SectorEraseConfirm = 0x00300030;
}
}