using System.Linq;
using System.Collections.Generic;
using Antmicro.Renode.Core;
using Antmicro.Renode.Core.Structure;
using Antmicro.Renode.Core.Structure.Registers;
using Antmicro.Renode.Logging;
using Antmicro.Renode.Peripherals.Bus;
using Antmicro.Renode.Peripherals.Memory;
using Antmicro.Renode.Peripherals.MTD;

namespace Antmicro.Renode.Peripherals.SPI
{
    public class ESP32S3_SPIController : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
    {
        public GPIO IRQ { get; private set; }
        public DoubleWordRegisterCollection RegistersCollection { get; }
        public long Size => 0x1000; // 0xb4
        private readonly Machine machine;

        public ESP32S3_SPIController(Machine machine) : base(machine)
        {
            this.machine = machine;

            IRQ = new GPIO();

            RegistersCollection = new DoubleWordRegisterCollection(this);
            DefineRegisters();
            Reset();
        }

        private void DefineRegisters()
        {
            // SPI1 memory command register
            Registers.CMD.Define(this, 0x0)
                .WithFlag(17, FieldMode.Read | FieldMode.Write, name: "FLASH_PE") // In user mode, it is set to indicate that program/erase operation will be triggered. The bit is combined with SPI_MEM_USR bit. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(18, FieldMode.Read | FieldMode.Write, name: "USR") // User define command enable.  An operation will be triggered when the bit is set. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(19, FieldMode.Read | FieldMode.Write, name: "FLASH_HPM") // Drive Flash into high performance mode.  The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(20, FieldMode.Read | FieldMode.Write, valueProviderCallback: (_) => { var val = flashReset; flashReset = false; return val; }, writeCallback: (_, val) => { if (val) flashReset = true; }, name: "FLASH_RES") // This bit combined with SPI_MEM_RESANDRES bit releases Flash from the power-down state or high performance mode and obtains the devices ID. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(21, FieldMode.Read | FieldMode.Write, name: "FLASH_DP") // Drive Flash into power down.  An operation will be triggered when the bit is set. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(22, FieldMode.Read | FieldMode.Write, name: "FLASH_CE") // Chip erase enable. Chip erase operation will be triggered when the bit is set. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(23, FieldMode.Read | FieldMode.Write, name: "FLASH_BE") // Block erase enable(32KB) .  Block erase operation will be triggered when the bit is set. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(24, FieldMode.Read | FieldMode.Write, name: "FLASH_SE") // Sector erase enable(4KB). Sector erase operation will be triggered when the bit is set. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(25, FieldMode.Read | FieldMode.Write, name: "FLASH_PP") // Page program enable(1 byte ~64 bytes data to be programmed). Page program operation  will be triggered when the bit is set. The bit will be cleared once the operation done .1: enable 0: disable.
                .WithFlag(26, FieldMode.Read | FieldMode.Write, name: "FLASH_WRSR") // Write status register enable.   Write status operation  will be triggered when the bit is set. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(27, FieldMode.Read | FieldMode.Write, name: "FLASH_RDSR") // Read status register-1.  Read status operation will be triggered when the bit is set. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(28, FieldMode.Read | FieldMode.Write, name: "FLASH_RDID") // Read JEDEC ID . Read ID command will be sent when the bit is set. The bit will be cleared once the operation done. 1: enable 0: disable.
                .WithFlag(29, FieldMode.Read | FieldMode.Write, name: "FLASH_WRDI") // Write flash disable. Write disable command will be sent when the bit is set. The bit will be cleared once the operation done. 1: enable 0: disable.
                .WithFlag(30, FieldMode.Read | FieldMode.Write, name: "FLASH_WREN") // Write flash enable.  Write enable command will be sent when the bit is set. The bit will be cleared once the operation done. 1: enable 0: disable.
                .WithFlag(31, FieldMode.Read | FieldMode.Write, name: "FLASH_READ") // Read flash enable. Read flash operation will be triggered when the bit is set. The bit will be cleared once the operation done. 1: enable 0: disable.
            ;

            // SPI1 address register
            Registers.ADDR.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "USR_ADDR_VALUE") // In user mode, it is the memory address. other then the bit0-bit23 is the memory address, the bit24-bit31 are the byte length of a transfer.
            ;

            // SPI1 control register
            Registers.CTRL.Define(this, 0x2ca000)
                .WithFlag(3, FieldMode.Read | FieldMode.Write, name: "FDUMMY_OUT") // In the DUMMY phase the signal level of SPI bus is output by the SPI0 controller.
                .WithFlag(4, FieldMode.Read | FieldMode.Write, name: "FDOUT_OCT") // Set this bit to enable 8-bit-mode(8-bm) in DOUT phase.
                .WithFlag(5, FieldMode.Read | FieldMode.Write, name: "FDIN_OCT") // Set this bit to enable 8-bit-mode(8-bm) in DIN phase.
                .WithFlag(6, FieldMode.Read | FieldMode.Write, name: "FADDR_OCT") // Set this bit to enable 8-bit-mode(8-bm) in ADDR phase.
                .WithFlag(7, FieldMode.Read | FieldMode.Write, name: "FCMD_DUAL") // Set this bit to enable 2-bit-mode(2-bm) in CMD phase.
                .WithFlag(8, FieldMode.Read | FieldMode.Write, name: "FCMD_QUAD") // Set this bit to enable 4-bit-mode(4-bm) in CMD phase.
                .WithFlag(9, FieldMode.Read | FieldMode.Write, name: "FCMD_OCT") // Set this bit to enable 8-bit-mode(8-bm) in CMD phase.
                .WithFlag(10, FieldMode.Read | FieldMode.Write, name: "FCS_CRC_EN") // For SPI1,  initialize crc32 module before writing encrypted data to flash. Active low.
                .WithFlag(11, FieldMode.Read | FieldMode.Write, name: "TX_CRC_EN") // For SPI1,  enable crc32 when writing encrypted data to flash. 1: enable 0:disable
                .WithFlag(13, FieldMode.Read | FieldMode.Write, name: "FASTRD_MODE") // This bit should be set when SPI_MEM_FREAD_QIO, SPI_MEM_FREAD_DIO, SPI_MEM_FREAD_QUAD or SPI_MEM_FREAD_DUAL is set.
                .WithFlag(14, FieldMode.Read | FieldMode.Write, name: "FREAD_DUAL") // In hardware 0x3B read operation, DIN phase apply 2 signals. 1: enable 0: disable.
                .WithFlag(15, FieldMode.Read | FieldMode.Write, name: "RESANDRES") // The Device ID is read out to SPI_MEM_RD_STATUS register,  this bit combine with spi_mem_flash_res bit. 1: enable 0: disable.
                .WithFlag(18, FieldMode.Read | FieldMode.Write, name: "Q_POL") // The bit is used to set MISO line polarity, 1: high 0, low
                .WithFlag(19, FieldMode.Read | FieldMode.Write, name: "D_POL") // The bit is used to set MOSI line polarity, 1: high 0, low
                .WithFlag(20, FieldMode.Read | FieldMode.Write, name: "FREAD_QUAD") // In hardware 0x6B read operation, DIN phase apply 4 signals(4-bit-mode). 1: enable 0: disable.
                .WithFlag(21, FieldMode.Read | FieldMode.Write, name: "WP") // Write protect signal output when SPI is idle.  1: output high, 0: output low.
                .WithFlag(22, FieldMode.Read | FieldMode.Write, name: "WRSR_2B") // Two bytes data will be written to status register when it is set. 1: enable 0: disable.
                .WithFlag(23, FieldMode.Read | FieldMode.Write, name: "FREAD_DIO") // In hardware 0xBB read operation, ADDR phase and DIN phase apply 2 signals(2-bit-mode). 1: enable 0: disable.
                .WithFlag(24, FieldMode.Read | FieldMode.Write, name: "FREAD_QIO") // In hardware 0xEB read operation, ADDR phase and DIN phase apply 4 signals(4-bit-mode). 1: enable 0: disable.
            ;

            // SPI1 control1 register
            Registers.CTRL1.Define(this, 0xffc)
                .WithValueField(0, 2, FieldMode.Read | FieldMode.Write, name: "CLK_MODE") // SPI Bus clock (SPI_CLK) mode bits. 0: SPI Bus clock (SPI_CLK) is off when CS inactive 1: SPI_CLK is delayed one cycle after SPI_CS inactive 2: SPI_CLK is delayed two cycles after SPI_CS inactive 3: SPI_CLK is always on.
                .WithValueField(2, 10, FieldMode.Read | FieldMode.Write, name: "CS_HOLD_DLY_RES") // After RES/DP/HPM/PES/PER command is sent, SPI1 may waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 4 or * 256) SPI_CLK cycles.
            ;

            // SPI1 control2 register
            Registers.CTRL2.Define(this, 0x0)
                .WithFlag(31, FieldMode.Read | FieldMode.Write, name: "SYNC_RESET") // The FSM will be reset.
            ;

            // SPI_CLK clock division register when SPI1 accesses to flash or Ext_RAM.
            Registers.CLOCK.Define(this, 0x30103)
                .WithValueField(0, 8, FieldMode.Read | FieldMode.Write, name: "CLKCNT_L") // It must equal to the value of SPI_MEM_CLKCNT_N.
                .WithValueField(8, 8, FieldMode.Read | FieldMode.Write, name: "CLKCNT_H") // It must be a floor value of ((SPI_MEM_CLKCNT_N+1)/2-1).
                .WithValueField(16, 8, FieldMode.Read | FieldMode.Write, name: "CLKCNT_N") // When SPI1 accesses to flash or Ext_RAM, f_SPI_CLK = f_MSPI_CORE_CLK/(SPI_MEM_CLKCNT_N+1)
                .WithFlag(31, FieldMode.Read | FieldMode.Write, name: "CLK_EQU_SYSCLK") // When SPI1 access to flash or Ext_RAM, set this bit in 1-division mode, f_SPI_CLK = f_MSPI_CORE_CLK.
            ;

            // SPI1 user register.
            Registers.USER.Define(this, 0x80000000)
                .WithFlag(9, FieldMode.Read | FieldMode.Write, name: "CK_OUT_EDGE") // This bit, combined with SPI_MEM_CK_IDLE_EDGE bit, is used to change the clock mode 0~3 of SPI_CLK.
                .WithFlag(12, FieldMode.Read | FieldMode.Write, name: "FWRITE_DUAL") // Set this bit to enable 2-bm in DOUT phase in SPI1 write operation.
                .WithFlag(13, FieldMode.Read | FieldMode.Write, name: "FWRITE_QUAD") // Set this bit to enable 4-bm in DOUT phase in SPI1 write operation.
                .WithFlag(14, FieldMode.Read | FieldMode.Write, name: "FWRITE_DIO") // Set this bit to enable 2-bm in ADDR and DOUT phase in SPI1 write operation.
                .WithFlag(15, FieldMode.Read | FieldMode.Write, name: "FWRITE_QIO") // Set this bit to enable 4-bit-mode(4-bm) in ADDR and DOUT phase in SPI1 write operation.
                .WithFlag(24, FieldMode.Read | FieldMode.Write, name: "USR_MISO_HIGHPART") // DIN phase only access to high-part of the buffer SPI_MEM_W8_REG~SPI_MEM_W15_REG. 1: enable 0: disable.
                .WithFlag(25, FieldMode.Read | FieldMode.Write, name: "USR_MOSI_HIGHPART") // DOUT phase only access to high-part of the buffer SPI_MEM_W8_REG~SPI_MEM_W15_REG. 1: enable 0: disable.
                .WithFlag(26, FieldMode.Read | FieldMode.Write, name: "USR_DUMMY_IDLE") // SPI_CLK is disabled(No clock edges) in DUMMY phase when the bit is enable.
                .WithFlag(27, FieldMode.Read | FieldMode.Write, name: "USR_MOSI") // Set this bit to enable the DOUT phase of an write-data operation.
                .WithFlag(28, FieldMode.Read | FieldMode.Write, name: "USR_MISO") // Set this bit to enable enable the DIN phase of a read-data operation.
                .WithFlag(29, FieldMode.Read | FieldMode.Write, name: "USR_DUMMY") // Set this bit to enable enable the DUMMY phase of an operation.
                .WithFlag(30, FieldMode.Read | FieldMode.Write, name: "USR_ADDR") // Set this bit to enable enable the ADDR phase of an operation.
                .WithFlag(31, FieldMode.Read | FieldMode.Write, name: "USR_COMMAND") // Set this bit to enable enable the CMD phase of an operation.
            ;

            // SPI1 user1 register.
            Registers.USER1.Define(this, 0x5c000007)
                .WithValueField(0, 6, FieldMode.Read | FieldMode.Write, name: "USR_DUMMY_CYCLELEN") // The SPI_CLK cycle length minus 1 of DUMMY phase.
                .WithValueField(26, 6, FieldMode.Read | FieldMode.Write, name: "USR_ADDR_BITLEN") // The length in bits of ADDR phase. The register value shall be (bit_num-1).
            ;

            // SPI1 user2 register.
            Registers.USER2.Define(this, 0x70000000)
                .WithValueField(0, 16, FieldMode.Read | FieldMode.Write, name: "USR_COMMAND_VALUE") // The value of user defined(USR) command.
                .WithValueField(28, 4, FieldMode.Read | FieldMode.Write, name: "USR_COMMAND_BITLEN") // The length in bits of CMD phase. The register value shall be (bit_num-1)
            ;

            // SPI1 write-data bit length register.
            Registers.MOSI_DLEN.Define(this, 0x0)
                .WithValueField(0, 10, FieldMode.Read | FieldMode.Write, name: "USR_MOSI_DBITLEN") // The length in bits of DOUT phase. The register value shall be (bit_num-1).
            ;

            // SPI1 read-data bit length register.
            Registers.MISO_DLEN.Define(this, 0x0)
                .WithValueField(0, 10, FieldMode.Read | FieldMode.Write, name: "USR_MISO_DBITLEN") // The length in bits of DIN phase. The register value shall be (bit_num-1).
            ;

            // SPI1 read control register.
            Registers.RD_STATUS.Define(this, 0x0)
                .WithValueField(0, 16, FieldMode.Read | FieldMode.Write, name: "STATUS") // The value is stored when set SPI_MEM_FLASH_RDSR bit and SPI_MEM_FLASH_RES bit.
                .WithValueField(16, 8, FieldMode.Read | FieldMode.Write, name: "WB_MODE") // Mode bits in the flash fast read mode  it is combined with SPI_MEM_FASTRD_MODE bit.
            ;

            // SPI1 extended address register.
            Registers.EXT_ADDR.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "EXT_ADDR") // The register are the higher 32bits in the 64 bits address mode.
            ;

            // SPI1 misc register.
            Registers.MISC.Define(this, 0x2)
                .WithFlag(0, FieldMode.Read | FieldMode.Write, name: "CS0_DIS") // Set this bit to raise high SPI_CS pin, which means that the SPI device(flash) connected to SPI_CS is in low level when SPI1 transfer starts.
                .WithFlag(1, FieldMode.Read | FieldMode.Write, name: "CS1_DIS") // Set this bit to raise high SPI_CS1 pin, which means that the SPI device(Ext_RAM) connected to SPI_CS1 is in low level when SPI1 transfer starts.
                .WithFlag(9, FieldMode.Read | FieldMode.Write, name: "CK_IDLE_EDGE") // 1: SPI_CLK line is high when MSPI is idle. 0: SPI_CLK line is low when MSPI is idle.
                .WithFlag(10, FieldMode.Read | FieldMode.Write, name: "CS_KEEP_ACTIVE") // SPI_CS line keep low when the bit is set.
                .WithFlag(11, FieldMode.Read | FieldMode.Write, name: "AUTO_PER") // Set this bit to enable auto PER function. Hardware will sent out PER command if PES command is sent.
            ;

            // SPI1 CRC data register.
            Registers.TX_CRC.Define(this, 0xffffffff)
                .WithValueField(0, 32, FieldMode.Read, name: "DATA") // For SPI1, the value of crc32.
            ;

            // SPI1 bit mode control register.
            Registers.CACHE_FCTRL.Define(this, 0x0)
                .WithFlag(1, FieldMode.Read | FieldMode.Write, name: "CACHE_USR_CMD_4BYTE") // Set this bit to enable SPI1 transfer with 32 bits address. The value of SPI_MEM_USR_ADDR_BITLEN should be 31.
                .WithFlag(3, FieldMode.Read | FieldMode.Write, name: "FDIN_DUAL") // When SPI1 accesses to flash or Ext_RAM, set this bit to enable 2-bm in DIN phase.
                .WithFlag(4, FieldMode.Read | FieldMode.Write, name: "FDOUT_DUAL") // When SPI1 accesses to flash or Ext_RAM, set this bit to enable 2-bm in DOUT phase.
                .WithFlag(5, FieldMode.Read | FieldMode.Write, name: "FADDR_DUAL") // When SPI1 accesses to flash or Ext_RAM, set this bit to enable 2-bm in ADDR phase.
                .WithFlag(6, FieldMode.Read | FieldMode.Write, name: "FDIN_QUAD") // When SPI1 accesses to flash or Ext_RAM, set this bit to enable 4-bm in DIN phase.
                .WithFlag(7, FieldMode.Read | FieldMode.Write, name: "FDOUT_QUAD") // When SPI1 accesses to flash or Ext_RAM, set this bit to enable 4-bm in DOUT phase.
                .WithFlag(8, FieldMode.Read | FieldMode.Write, name: "FADDR_QUAD") // When SPI1 accesses to flash or Ext_RAM, set this bit to enable 4-bm in ADDR phase.
            ;

            // SPI1 state machine(FSM) status register.
            Registers.FSM.Define(this, 0x0)
                .WithValueField(0, 3, FieldMode.Read, name: "ST") // The status of SPI1 state machine. 0: idle state(IDLE), 1: preparation state(PREP), 2: send command state(CMD), 3: send address state(ADDR), 4: red data state(DIN), 5:write data state(DOUT), 6: wait state(DUMMY), 7: done state(DONE).
            ;

            // SPI1 memory data buffer0
            Registers.W0.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF0") // data buffer
            ;

            // SPI1 memory data buffer1
            Registers.W1.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF1") // data buffer
            ;

            // SPI1 memory data buffer2
            Registers.W2.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF2") // data buffer
            ;

            // SPI1 memory data buffer3
            Registers.W3.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF3") // data buffer
            ;

            // SPI1 memory data buffer4
            Registers.W4.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF4") // data buffer
            ;

            // SPI1 memory data buffer5
            Registers.W5.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF5") // data buffer
            ;

            // SPI1 memory data buffer6
            Registers.W6.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF6") // data buffer
            ;

            // SPI1 memory data buffer7
            Registers.W7.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF7") // data buffer
            ;

            // SPI1 memory data buffer8
            Registers.W8.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF8") // data buffer
            ;

            // SPI1 memory data buffer9
            Registers.W9.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF9") // data buffer
            ;

            // SPI1 memory data buffer10
            Registers.W10.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF10") // data buffer
            ;

            // SPI1 memory data buffer11
            Registers.W11.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF11") // data buffer
            ;

            // SPI1 memory data buffer12
            Registers.W12.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF12") // data buffer
            ;

            // SPI1 memory data buffer13
            Registers.W13.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF13") // data buffer
            ;

            // SPI1 memory data buffer14
            Registers.W14.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF14") // data buffer
            ;

            // SPI1 memory data buffer15
            Registers.W15.Define(this, 0x0)
                .WithValueField(0, 32, FieldMode.Read | FieldMode.Write, name: "BUF15") // data buffer
            ;

            // SPI1 wait idle control register
            Registers.FLASH_WAITI_CTRL.Define(this, 0x14)
                .WithFlag(0, FieldMode.Read | FieldMode.Write, name: "WAITI_EN") // Set this bit to enable auto-waiting flash idle operation when PP/SE/BE/CE/WRSR/PES command is sent.
                .WithFlag(1, FieldMode.Read | FieldMode.Write, name: "WAITI_DUMMY") // Set this bit to enable DUMMY phase in auto wait flash idle transfer(RDSR).
                .WithValueField(2, 8, FieldMode.Read | FieldMode.Write, name: "WAITI_CMD") // The command value of auto wait flash idle transfer(RDSR).
                .WithValueField(10, 6, FieldMode.Read | FieldMode.Write, name: "WAITI_DUMMY_CYCLELEN") // The dummy cycle length when wait flash idle(RDSR).
            ;

            // SPI1 flash suspend control register
            Registers.FLASH_SUS_CMD.Define(this, 0x0)
                .WithFlag(0, FieldMode.Read | FieldMode.Write, name: "FLASH_PER") // program erase resume bit, program erase suspend operation will be triggered when the bit is set. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(1, FieldMode.Read | FieldMode.Write, name: "FLASH_PES") // program erase suspend bit, program erase suspend operation will be triggered when the bit is set. The bit will be cleared once the operation done.1: enable 0: disable.
                .WithFlag(2, FieldMode.Read | FieldMode.Write, name: "FLASH_PER_WAIT_EN") // Set this bit to add delay time after program erase resume(PER) is sent.
                .WithFlag(3, FieldMode.Read | FieldMode.Write, name: "FLASH_PES_WAIT_EN") // Set this bit to add delay time after program erase suspend(PES) command is sent.
                .WithFlag(4, FieldMode.Read | FieldMode.Write, name: "PES_PER_EN") // Set this bit to enable PES transfer trigger PES transfer option.
                .WithFlag(5, FieldMode.Read | FieldMode.Write, name: "PESR_IDLE_EN") // 1: Separate PER flash wait idle and PES flash wait idle. 0: Not separate.
            ;

            // SPI1 flash suspend command register
            Registers.FLASH_SUS_CTRL.Define(this, 0xeaf4)
                .WithFlag(0, FieldMode.Read | FieldMode.Write, name: "FLASH_PES_EN") // Set this bit to enable auto-suspend function.
                .WithValueField(1, 8, FieldMode.Read | FieldMode.Write, name: "FLASH_PER_COMMAND") // Program/Erase resume command value.
                .WithValueField(9, 8, FieldMode.Read | FieldMode.Write, name: "FLASH_PES_COMMAND") // Program/Erase suspend command value.
            ;

            // SPI1 flash suspend status register
            Registers.SUS_STATUS.Define(this, 0x0)
                .WithFlag(0, FieldMode.Read | FieldMode.Write, name: "FLASH_SUS") // The status of flash suspend. This bit is set when PES command is sent, and cleared when PER is sent. Only used in SPI1.
                .WithFlag(2, FieldMode.Read | FieldMode.Write, name: "FLASH_HPM_DLY_256") // 1: SPI1 waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 256) SPI_CLK cycles after HPM command is sent. 0: SPI1 waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 4) SPI_CLK cycles after HPM command is sent.
                .WithFlag(3, FieldMode.Read | FieldMode.Write, name: "FLASH_RES_DLY_256") // 1: SPI1 waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 256) SPI_CLK cycles after RES command is sent. 0: SPI1 waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 4) SPI_CLK cycles after RES command is sent.
                .WithFlag(4, FieldMode.Read | FieldMode.Write, name: "FLASH_DP_DLY_256") // 1: SPI1 waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 256) SPI_CLK cycles after DP command is sent. 0: SPI1 waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 4) SPI_CLK cycles after DP command is sent.
                .WithFlag(5, FieldMode.Read | FieldMode.Write, name: "FLASH_PER_DLY_256") // Valid when SPI_MEM_FLASH_PER_WAIT_EN is 1. 1: SPI1 waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 256) SPI_CLK cycles after PER command is sent. 0: SPI1 waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 4) SPI_CLK cycles after PER command is sent.
                .WithFlag(6, FieldMode.Read | FieldMode.Write, name: "FLASH_PES_DLY_256") // Valid when SPI_MEM_FLASH_PES_WAIT_EN is 1. 1: SPI1 waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 256) SPI_CLK cycles after PES command is sent. 0: SPI1 waits (SPI_MEM_CS_HOLD_DELAY_RES[9:0] * 4) SPI_CLK cycles after PES command is sent.
            ;

            // SPI1 timing compensation register when accesses to flash or Ext_RAM.
            Registers.TIMING_CALI.Define(this, 0x0)
                .WithFlag(1, FieldMode.Read | FieldMode.Write, name: "TIMING_CALI") // Set this bit to add extra SPI_CLK cycles in DUMMY phase for all reading operations.
                .WithValueField(2, 3, FieldMode.Read | FieldMode.Write, name: "EXTRA_DUMMY_CYCLELEN") // Extra SPI_CLK cycles added in DUMMY phase for timing compensation. Active when SPI_MEM_TIMING_CALI bit is set.
            ;

            // SPI1 DDR control register
            Registers.DDR.Define(this, 0x20)
                .WithFlag(0, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_DDR_EN") // 1: in DDR mode,  0: in SDR mode.
                .WithFlag(1, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_VAR_DUMMY") // Set the bit to enable variable dummy cycle in DDRmode.
                .WithFlag(2, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_DDR_RDAT_SWP") // Set the bit to reorder RX data of the word in DDR mode.
                .WithFlag(3, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_DDR_WDAT_SWP") // Set the bit to reorder TX data of the word in DDR mode.
                .WithFlag(4, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_DDR_CMD_DIS") // the bit is used to disable dual edge in command phase when DDR mode.
                .WithValueField(5, 7, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_OUTMINBYTELEN") // It is the minimum output data length in the panda device.
                .WithValueField(14, 7, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_USR_DDR_DQS_THD") // The delay number of data strobe which from memory based on SPI_CLK.
                .WithFlag(21, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_DDR_DQS_LOOP") // 1: Use internal signal  as data strobe, the strobe can not be delayed by input timing module. 0: Use input SPI_DQS signal from PAD as data strobe, the strobe can be delayed by input timing module
                .WithFlag(22, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_DDR_DQS_LOOP_MODE") // When SPI_FMEM_DDR_DQS_LOOP and SPI_FMEM_DDR_EN are set, 1: Use internal SPI_CLK  as data strobe. 0: Use internal ~SPI_CLK  as data strobe. Otherwise this bit is not active.
                .WithFlag(24, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_CLK_DIFF_EN") // Set this bit to enable the differential SPI_CLK#.
                .WithFlag(25, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_HYPERBUS_MODE") // Set this bit to enable the SPI HyperBus mode.
                .WithFlag(26, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_DQS_CA_IN") // Set this bit to enable the input of SPI_DQS signal in SPI phases of CMD and ADDR.
                .WithFlag(27, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_HYPERBUS_DUMMY_2X") // Set this bit to enable the vary dummy function in SPI HyperBus mode, when SPI0 accesses flash or SPI1 accesses flash or sram.
                .WithFlag(28, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_CLK_DIFF_INV") // Set this bit to invert SPI_DIFF when accesses to flash. .
                .WithFlag(29, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_OCTA_RAM_ADDR") // Set this bit to enable octa_ram address out when accesses to flash, which means ADDR_OUT[31:0] = {spi_usr_addr_value[25:4], 6'd0, spi_usr_addr_value[3:1], 1'b0}.
                .WithFlag(30, FieldMode.Read | FieldMode.Write, name: "SPI_FMEM_HYPERBUS_CA") // Set this bit to enable HyperRAM address out when accesses to flash, which means ADDR_OUT[31:0] = {spi_usr_addr_value[19:4], 13'd0, spi_usr_addr_value[3:1]}.
            ;

            // SPI1 clk_gate register
            Registers.CLOCK_GATE.Define(this, 0x1)
                .WithFlag(0, FieldMode.Read | FieldMode.Write, name: "CLK_EN") // Register clock gate enable signal. 1: Enable. 0: Disable.
            ;

            // SPI1 interrupt enable register
            Registers.INT_ENA.Define(this, 0x0)
                .WithFlag(0, FieldMode.Read | FieldMode.Write, name: "PER_END_INT_ENA") // The enable bit for SPI_MEM_PER_END_INT interrupt.
                .WithFlag(1, FieldMode.Read | FieldMode.Write, name: "PES_END_INT_ENA") // The enable bit for SPI_MEM_PES_END_INT interrupt.
                .WithFlag(2, FieldMode.Read | FieldMode.Write, name: "TOTAL_TRANS_END_INT_ENA") // The enable bit for SPI_MEM_TOTAL_TRANS_END_INT interrupt.
                .WithFlag(3, FieldMode.Read | FieldMode.Write, name: "BROWN_OUT_INT_ENA") // The enable bit for SPI_MEM_BROWN_OUT_INT interrupt.
            ;

            // SPI1 interrupt clear register
            Registers.INT_CLR.Define(this, 0x0)
                .WithFlag(0, FieldMode.Write, name: "PER_END_INT_CLR") // The clear bit for SPI_MEM_PER_END_INT interrupt.
                .WithFlag(1, FieldMode.Write, name: "PES_END_INT_CLR") // The clear bit for SPI_MEM_PES_END_INT interrupt.
                .WithFlag(2, FieldMode.Write, name: "TOTAL_TRANS_END_INT_CLR") // The clear bit for SPI_MEM_TOTAL_TRANS_END_INT interrupt.
                .WithFlag(3, FieldMode.Write, name: "BROWN_OUT_INT_CLR") // The status bit for SPI_MEM_BROWN_OUT_INT interrupt.
            ;

            // SPI1 interrupt raw register
            Registers.INT_RAW.Define(this, 0x0)
                .WithFlag(0, FieldMode.Read | FieldMode.Write, name: "PER_END_INT_RAW") // The raw bit for SPI_MEM_PER_END_INT interrupt. 1: Triggered when Auto Resume command (0x7A) is sent and flash is resumed successfully. 0: Others.
                .WithFlag(1, FieldMode.Read | FieldMode.Write, name: "PES_END_INT_RAW") // The raw bit for SPI_MEM_PES_END_INT interrupt.1: Triggered when Auto Suspend command (0x75) is sent and flash is suspended successfully. 0: Others.
                .WithFlag(2, FieldMode.Read | FieldMode.Write, name: "TOTAL_TRANS_END_INT_RAW") // The raw bit for SPI_MEM_TOTAL_TRANS_END_INT interrupt. 1: Triggered when SPI1 transfer is done and flash is already idle. When WRSR/PP/SE/BE/CE is sent and PES/PER command is sent, this bit is set when WRSR/PP/SE/BE/CE is success.  0: Others.
                .WithFlag(3, FieldMode.Read | FieldMode.Write, name: "BROWN_OUT_INT_RAW") // The raw bit for SPI_MEM_BROWN_OUT_INT interrupt. 1: Triggered condition is that chip is loosing power and RTC module sends out brown out close flash request to SPI1. After SPI1 sends out suspend command to flash, this interrupt is triggered and MSPI returns to idle state. 0: Others.
            ;

            // SPI1 interrupt status register
            Registers.INT_ST.Define(this, 0x0)
                .WithFlag(0, FieldMode.Read, name: "PER_END_INT_ST") // The status bit for SPI_MEM_PER_END_INT interrupt.
                .WithFlag(1, FieldMode.Read, name: "PES_END_INT_ST") // The status bit for SPI_MEM_PES_END_INT interrupt.
                .WithFlag(2, FieldMode.Read, name: "TOTAL_TRANS_END_INT_ST") // The status bit for SPI_MEM_TOTAL_TRANS_END_INT interrupt.
                .WithFlag(3, FieldMode.Read, name: "BROWN_OUT_INT_ST") // The status bit for SPI_MEM_BROWN_OUT_INT interrupt.
            ;

            // SPI0 version control register
            Registers.DATE.Define(this, 0x2101040)
                .WithValueField(0, 28, FieldMode.Read | FieldMode.Write, name: "DATE") // SPI register version.
            ;

        }

        public override void Reset()
        {
            RegistersCollection.Reset();
            UpdateInterrupts();
        }

        public uint ReadDoubleWord(long offset)
        {
            return RegistersCollection.Read(offset);
        }

        public void WriteDoubleWord(long offset, uint value)
        {
            RegistersCollection.Write(offset, value);
        }
        private void UpdateInterrupts()
        {
            var status = false;
            this.Log(LogLevel.Noisy, "Setting IRQ to {0}", status);
            IRQ.Set(status);
        }

        private bool flashReset;

        private enum Registers
        {
            CMD = 0x0,
            ADDR = 0x4,
            CTRL = 0x8,
            CTRL1 = 0xc,
            CTRL2 = 0x10,
            CLOCK = 0x14,
            USER = 0x18,
            USER1 = 0x1c,
            USER2 = 0x20,
            MOSI_DLEN = 0x24,
            MISO_DLEN = 0x28,
            RD_STATUS = 0x2c,
            EXT_ADDR = 0x30,
            MISC = 0x34,
            TX_CRC = 0x38,
            CACHE_FCTRL = 0x3c,
            FSM = 0x54,
            W0 = 0x58,
            W1 = 0x5c,
            W2 = 0x60,
            W3 = 0x64,
            W4 = 0x68,
            W5 = 0x6c,
            W6 = 0x70,
            W7 = 0x74,
            W8 = 0x78,
            W9 = 0x7c,
            W10 = 0x80,
            W11 = 0x84,
            W12 = 0x88,
            W13 = 0x8c,
            W14 = 0x90,
            W15 = 0x94,
            FLASH_WAITI_CTRL = 0x98,
            FLASH_SUS_CMD = 0x9c,
            FLASH_SUS_CTRL = 0xa0,
            SUS_STATUS = 0xa4,
            TIMING_CALI = 0xa8,
            DDR = 0xe0,
            CLOCK_GATE = 0xe8,
            INT_ENA = 0xf0,
            INT_CLR = 0xf4,
            INT_RAW = 0xf8,
            INT_ST = 0xfc,
            DATE = 0x3fc,
        }
    }
}
