public OpenTitan_FlashController(Machine machine, MappedMemory flash) : base(machine)
        {
            ProgramEmptyIRQ     = new GPIO();
            ProgramLevelIRQ     = new GPIO();
            ReadFullIRQ         = new GPIO();
            ReadLevelIRQ        = new GPIO();
            OperationDoneIRQ    = new GPIO();
            CorrectableErrorIRQ = new GPIO();

            mpRegionEnabled      = new IFlagRegisterField[NumberOfMpRegions];
            mpRegionBase         = new IValueRegisterField[NumberOfMpRegions];
            mpRegionSize         = new IValueRegisterField[NumberOfMpRegions];
            mpRegionReadEnabled  = new IFlagRegisterField[NumberOfMpRegions];
            mpRegionProgEnabled  = new IFlagRegisterField[NumberOfMpRegions];
            mpRegionEraseEnabled = new IFlagRegisterField[NumberOfMpRegions];

            bankInfoPageEnabled        = new IFlagRegisterField[FlashNumberOfBanks, FlashNumberOfInfoTypes][];
            bankInfoPageReadEnabled    = new IFlagRegisterField[FlashNumberOfBanks, FlashNumberOfInfoTypes][];
            bankInfoPageProgramEnabled = new IFlagRegisterField[FlashNumberOfBanks, FlashNumberOfInfoTypes][];
            bankInfoPageEraseEnabled   = new IFlagRegisterField[FlashNumberOfBanks, FlashNumberOfInfoTypes][];

            for (var bankNumber = 0; bankNumber < FlashNumberOfBanks; ++bankNumber)
            {
                for (var infoType = 0; infoType < FlashNumberOfInfoTypes; ++infoType)
                {
                    bankInfoPageEnabled[bankNumber, infoType]        = new IFlagRegisterField[FlashNumberOfPagesInInfo[infoType]];
                    bankInfoPageReadEnabled[bankNumber, infoType]    = new IFlagRegisterField[FlashNumberOfPagesInInfo[infoType]];
                    bankInfoPageProgramEnabled[bankNumber, infoType] = new IFlagRegisterField[FlashNumberOfPagesInInfo[infoType]];
                    bankInfoPageEraseEnabled[bankNumber, infoType]   = new IFlagRegisterField[FlashNumberOfPagesInInfo[infoType]];
                }
            }

            Registers.InterruptState.Define(this)
            .WithFlag(0, out interruptStatusProgramEmpty, FieldMode.Read | FieldMode.WriteOneToClear, name: "prog_empty")
            .WithFlag(1, out interruptStatusProgramLevel, FieldMode.Read | FieldMode.WriteOneToClear, name: "prog_lvl")
            .WithFlag(2, out interruptStatusReadFull, FieldMode.Read | FieldMode.WriteOneToClear, name: "rd_full")
            .WithFlag(3, out interruptStatusReadLevel, FieldMode.Read | FieldMode.WriteOneToClear, name: "rd_lvl")
            .WithFlag(4, out interruptStatusOperationDone, FieldMode.Read | FieldMode.WriteOneToClear, name: "op_done")
            .WithFlag(5, out interruptStatusCorrectableError, FieldMode.Read | FieldMode.WriteOneToClear, name: "corr_err")
            .WithReservedBits(6, 1 + 31 - 6)
            .WithWriteCallback((_, __) => UpdateInterrupts());

            Registers.InterruptEnable.Define(this)
            .WithFlag(0, out interruptEnableProgramEmpty, name: "prog_empty")
            .WithFlag(1, out interruptEnableProgramLevel, name: "prog_lvl")
            .WithFlag(2, out interruptEnableReadFull, name: "rd_full")
            .WithFlag(3, out interruptEnableReadLevel, name: "rd_lvl")
            .WithFlag(4, out interruptEnableOperationDone, name: "op_done")
            .WithFlag(5, out interruptEnableCorrectableError, name: "corr_err")
            .WithReservedBits(6, 1 + 31 - 6)
            .WithWriteCallback((_, __) => UpdateInterrupts());

            Registers.InterruptTest.Define(this)
            .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { interruptStatusProgramEmpty.Value |= val; }, name: "prog_empty")
            .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { interruptStatusProgramLevel.Value |= val; }, name: "prog_lvl")
            .WithFlag(2, FieldMode.Write, writeCallback: (_, val) => { interruptStatusReadFull.Value |= val; }, name: "rd_full")
            .WithFlag(3, FieldMode.Write, writeCallback: (_, val) => { interruptStatusReadLevel.Value |= val; }, name: "rd_lvl")
            .WithFlag(4, FieldMode.Write, writeCallback: (_, val) => { interruptStatusOperationDone.Value |= val; }, name: "op_done")
            .WithFlag(5, FieldMode.Write, writeCallback: (_, val) => { interruptStatusCorrectableError.Value |= val; }, name: "corr_err")
            .WithReservedBits(6, 1 + 31 - 6)
            .WithWriteCallback((_, __) => UpdateInterrupts());

            Registers.AlertTest.Define(this)
            .WithTaggedFlag("recov_err", 0)
            .WithTaggedFlag("fatal_err", 1)
            .WithReservedBits(2, 30);

            Registers.DisableFlashFunctionality.Define(this)
            .WithTag("VAL", 0, 4)
            .WithReservedBits(4, 28);

            Registers.ExecutionFetchesEnabled.Define(this)
            .WithTag("EN", 0, 4)
            .WithReservedBits(4, 28);

            Registers.ControllerInit.Define(this)
            .WithTaggedFlag("VAL", 0)
            .WithReservedBits(1, 31);

            // TODO(julianmb): support register write enable. this isnt tested in the unittests currently
            Registers.ControlEnable.Define(this, 0x1)
            .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => true, name: "EN")
            .WithReservedBits(1, 31);

            Registers.Control.Define(this)
            .WithFlag(0, name: "START", writeCallback: (o, n) => {
                if (n)
                {
                    StartOperation();
                }
            })
            .WithReservedBits(1, 3)
            .WithEnumField <DoubleWordRegister, ControlOp>(4, 2, out operation, name: "OP")
            .WithTaggedFlag("PROG_SEL", 6)
            .WithFlag(7, out flashSelectEraseMode, name: "ERASE_SEL")
            .WithFlag(8, out flashSelectPartition, name: "PARTITION_SEL")
            .WithValueField(9, 2, out flashSelectInfo, name: "INFO_SEL")
            .WithReservedBits(11, 1 + 15 - 11)
            .WithValueField(16, 1 + 27 - 16, out controlNum, name: "NUM")
            .WithReservedBits(28, 1 + 31 - 28);

            Registers.AddressForFlashOperation.Define(this)
            .WithValueField(0, 32, out address, changeCallback: (_, address) => {
                flashAddress  = null;
                var addresses = machine.SystemBus.GetRegistrationPoints(dataFlash)
                                .Select(pint => pint.Range)
                                .Where(range => range.Contains(address))
                                .Select(range => range.StartAddress);

                if (!addresses.Any())
                {
                    this.Log(LogLevel.Warning, "Underlying data flash is not registered on the system bus, so it cannot be accessed");
                    return;
                }

                flashAddress = (long)addresses.First();
            }, name: "START");

            Registers.EnableDifferentProgramTypes.Define(this)
            .WithTaggedFlag("NORMAL", 0)
            .WithTaggedFlag("REPAIR", 1)
            .WithReservedBits(2, 30);

            // Erase is performed immediately so write to SuspendErase will never happen during erasing process.
            // Cleared immediately.
            Registers.SuspendErase.Define(this)
            .WithFlag(0, valueProviderCallback: _ => false, name: "REQ")
            .WithReservedBits(1, 31);

            for (var i = 0; i < NumberOfMpRegions; i++)
            {
                // TODO(julianmb): support register write enable. this isnt tested in the unittests currently
                RegistersCollection.AddRegister((long)Registers.RegionConfigurationEnable0 + 0x4 * i, new DoubleWordRegister(this, 0x1)
                                                .WithTaggedFlag($"REGION_{i}", 0)
                                                .WithReservedBits(1, 31));

                RegistersCollection.AddRegister((long)(Registers.RegionConfiguration0 + 0x4 * i), new DoubleWordRegister(this)
                                                .WithFlag(0, out mpRegionEnabled[i], name: $"EN_{i}")
                                                .WithFlag(1, out mpRegionReadEnabled[i], name: $"RD_EN_{i}")
                                                .WithFlag(2, out mpRegionProgEnabled[i], name: $"PROG_EN_{i}")
                                                .WithFlag(3, out mpRegionEraseEnabled[i], name: $"ERASE_EN_{i}")
                                                .WithTaggedFlag($"SCRAMBLE_EN_{i}", 4)
                                                .WithTaggedFlag($"ECC_EN_{i}", 5)
                                                .WithTaggedFlag($"HE_EN_{i}", 6)
                                                .WithReservedBits(7, 1)
                                                .WithValueField(8, 1 + 16 - 8, out mpRegionBase[i], name: $"BASE_{i}")
                                                .WithValueField(17, 1 + 26 - 17, out mpRegionSize[i], name: $"SIZE_{i}")
                                                .WithReservedBits(27, 1 + 31 - 27));
            }

            Registers.DefaultRegionConfiguration.Define(this)
            .WithFlag(0, out defaultMpRegionReadEnabled, name: "RD_EN")
            .WithFlag(1, out defaultMpRegionProgEnabled, name: "PROG_EN")
            .WithFlag(2, out defaultMpRegionEraseEnabled, name: "ERASE_EN")
            .WithTaggedFlag("SCRAMBLE_EN", 3)
            .WithTaggedFlag("ECC_EN", 4)
            .WithTaggedFlag("HE_EN", 5)
            .WithReservedBits(6, 1 + 31 - 6);

            var registerOffset = Registers.Bank0Info0Enable0;

            for (var bankNumber = 0; bankNumber < FlashNumberOfBanks; ++bankNumber)
            {
                for (var infoType = 0; infoType < FlashNumberOfInfoTypes; ++infoType)
                {
                    // For each info type, first are defined configuration enabling registers and then
                    // configuration registers.
                    for (var pageNumber = 0; pageNumber < FlashNumberOfPagesInInfo[infoType]; ++pageNumber)
                    {
                        // TODO(julianmb): support register write enable. this isnt tested in the unittests currently
                        registerOffset.Define(this, 0x1)
                        .WithTaggedFlag($"REGION_{pageNumber}", 0)
                        .WithReservedBits(1, 31);
                        registerOffset += 0x4;
                    }

                    for (var pageNumber = 0; pageNumber < FlashNumberOfPagesInInfo[infoType]; ++pageNumber)
                    {
                        registerOffset.Define(this)
                        .WithFlag(0, out bankInfoPageEnabled[bankNumber, infoType][pageNumber], name: $"EN_{pageNumber}")
                        .WithFlag(1, out bankInfoPageReadEnabled[bankNumber, infoType][pageNumber], name: $"RD_EN_{pageNumber}")
                        .WithFlag(2, out bankInfoPageProgramEnabled[bankNumber, infoType][pageNumber], name: $"PROG_EN_{pageNumber}")
                        .WithFlag(3, out bankInfoPageEraseEnabled[bankNumber, infoType][pageNumber], name: $"ERASE_EN_{pageNumber}")
                        .WithTaggedFlag($"SCRAMBLE_EN_{pageNumber}", 4)
                        .WithTaggedFlag($"ECC_EN_{pageNumber}", 5)
                        .WithTaggedFlag($"HE_EN_{pageNumber}", 6)
                        .WithReservedBits(7, 1 + 31 - 7);
                        registerOffset += 0x4;
                    }
                }
            }

            // TODO(julianmb): support register write enable. this isnt tested in the unittests currently
            Registers.BankConfigurationEnable.Define(this, 0x1)
            .WithTaggedFlag("BANK", 0)
            .WithReservedBits(1, 31);
            Registers.BankConfiguration.Define(this)
            .WithFlag(0, out eraseBank0, name: "ERASE_EN_0")
            .WithFlag(1, out eraseBank1, name: "ERASE_EN_1")
            .WithReservedBits(2, 1 + 31 - 2);

            Registers.FlashOperationStatus.Define(this)
            .WithFlag(0, out opStatusRegisterDoneFlag, name: "done")
            .WithFlag(1, out opStatusRegisterErrorFlag, name: "err")
            .WithReservedBits(2, 1 + 31 - 2);

            Registers.Status.Define(this, 0xa)
            .WithFlag(0, out statusReadFullFlag, FieldMode.Read, name: "rd_full")
            .WithFlag(1, out statusReadEmptyFlag, FieldMode.Read, name: "rd_empty")
            .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => false, name: "prog_full")
            .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => true, name: "prog_empty")
            .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => false, name: "init_wip")
            .WithReservedBits(5, 1 + 31 - 5);

            Registers.ErrorCode.Define(this)
            .WithFlag(0, out outOfBoundsError, FieldMode.Read | FieldMode.WriteOneToClear, name: "oob_err")
            .WithFlag(1, out memoryProtectionError, FieldMode.Read | FieldMode.WriteOneToClear, name: "mp_err")
            .WithTaggedFlag("rd_err", 2)
            .WithTaggedFlag("prog_win_err", 3)
            .WithTaggedFlag("prog_type_err", 4)
            .WithTaggedFlag("flash_phy_err", 5)
            .WithTaggedFlag("update_err", 6)
            .WithReservedBits(7, 1 + 31 - 7);

            Registers.FaultStatus.Define(this)
            .WithTaggedFlag("oob_err", 0)
            .WithTaggedFlag("mp_err", 1)
            .WithTaggedFlag("rd_err", 2)
            .WithTaggedFlag("prog_win_err", 3)
            .WithTaggedFlag("prog_type_err", 4)
            .WithTaggedFlag("flash_phy_err", 5)
            .WithTaggedFlag("reg_intg_err", 6)
            .WithTaggedFlag("phy_intg_err", 7)
            .WithTaggedFlag("lcmgr_err", 8)
            .WithTaggedFlag("storage_err", 9)
            .WithReservedBits(10, 1 + 31 - 10);

            Registers.ErrorAddress.Define(this)
            .WithValueField(0, 32, out errorAddress, FieldMode.Read, name: "ERR_ADDR");

            Registers.ECCSingleErrorCount.Define(this)
            .WithTag("ECC_SINGLE_ERR_CNT_0", 0, 8)
            .WithTag("ECC_SINGLE_ERR_CNT_1", 8, 8)
            .WithReservedBits(16, 16);

            Registers.ECCSingleErrorAddress0.Define(this)
            .WithTag("ECC_SINGLE_ERR_ADDR_0", 0, 20)
            .WithReservedBits(20, 12);

            Registers.ECCSingleErrorAddress1.Define(this)
            .WithTag("ECC_SINGLE_ERR_ADDR_1", 0, 20)
            .WithReservedBits(20, 12);

            Registers.PhyErrorConfigurationEnable.Define(this)
            .WithTaggedFlag("EN", 0)
            .WithReservedBits(1, 31);

            Registers.PhyErrorConfiguration.Define(this)
            .WithTaggedFlag("ECC_MULTI_ERR_DATA_EN", 0)
            .WithReservedBits(1, 31);

            Registers.PhyAlertConfiguration.Define(this)
            .WithTaggedFlag("alert_ack", 0)
            .WithTaggedFlag("alert_trig", 1)
            .WithReservedBits(2, 30);

            Registers.PhyStatus.Define(this, 0x6)
            .WithTaggedFlag("init_wip", 0)
            .WithTaggedFlag("prog_normal_avail", 1)
            .WithTaggedFlag("prog_repair_avail", 2)
            .WithReservedBits(3, 1 + 31 - 3);

            Registers.Scratch.Define(this)
            .WithTag("data", 0, 32);

            Registers.FifoLevel.Define(this, 0xf0f)
            .WithValueField(0, 5, out programFifoLevel, name: "PROG")
            .WithReservedBits(5, 1 + 7 - 5)
            .WithValueField(8, 1 + 12 - 8, out readFifoLevel, name: "RD")
            .WithReservedBits(13, 1 + 31 - 13);

            // TODO(julianmb): implement fifo reset. There isnt any unittest for this currently.
            Registers.FifoReset.Define(this)
            .WithTaggedFlag("EN", 0)
            .WithReservedBits(1, 31);

            Registers.ProgramFifo.Define(this)
            .WithValueField(0, 32, mode: FieldMode.Write, writeCallback: (_, data) =>
            {
                var isInBounds = flashAddress.HasValue && IsOffsetInBounds(programOffset);
                var isAllowed  = isInBounds && IsOperationAllowed(OperationType.ProgramData, programOffset);

                if (isInBounds && isAllowed)
                {
                    var oldData = ReadFlashDoubleWord(programOffset);
                    WriteFlashDoubleWord(programOffset, oldData & data);
                    programOffset += 4;
                }
                else
                {
                    opStatusRegisterErrorFlag.Value    = true;
                    opStatusRegisterDoneFlag.Value     = true;
                    interruptStatusOperationDone.Value = true;

                    if (isInBounds)
                    {
                        memoryProtectionError.Value = true;
                        errorAddress.Value          = (uint)(programOffset + flashAddress.Value);
                    }
                    else
                    {
                        outOfBoundsError.Value = true;
                    }
                    return;
                }

                if (TryGetOffset(out var offset))
                {
                    if (programOffset > (offset + 4 * controlNum.Value))
                    {
                        opStatusRegisterDoneFlag.Value     = true;
                        interruptStatusOperationDone.Value = true;
                    }
                    else
                    {
                        interruptStatusProgramLevel.Value = true;
                        interruptStatusProgramEmpty.Value = true;
                    }
                }
        private void DefineRegisters()
        {
            Registers.Control.Define(this)
            .WithEnumField(0, 2, changeCallback: (Mode _, Mode value) => SetMode(value), name: "MODE")
            .WithReservedBits(2, 1)
            .WithTaggedFlag("SYNC", 3)
            .WithFlag(4, changeCallback: (_, value) => innerTimer.Mode = value ? WorkMode.OneShot : WorkMode.Periodic, name: "OSMEN")
            .WithTaggedFlag("QDM", 5)
            .WithTaggedFlag("DEBUGRUN", 6)
            .WithTaggedFlag("DMACLRACT", 7)
            .WithTag("RISEA", 8, 2)
            .WithTag("FALLA", 10, 2)
            .WithReservedBits(12, 1)
            .WithTaggedFlag("X2CNT", 13)
            .WithTaggedFlag("DISSYNCOUT", 14)
            .WithReservedBits(15, 1)
            .WithTag("CLKSEL", 16, 2)
            .WithReservedBits(18, 6)
            .WithValueField(24, 4, changeCallback: (_, value) => {
                if (value <= 10)
                {
                    innerTimer.Divider = 2 << (int)value;
                }
                else
                {
                    this.Log(LogLevel.Warning, "Trying to set the prescaler to an invalid value: {0}, ignoring.", 1 << (int)value);
                }
            }, name: "PRESC")
            .WithTaggedFlag("ATI", 28)
            .WithTaggedFlag("RSSCOIST", 29)
            .WithReservedBits(30, 2)
            ;

            Registers.Command.Define(this)
            .WithFlag(0, FieldMode.Set, changeCallback: (_, __) => innerTimer.Enabled = false)
            .WithFlag(1, FieldMode.Set, changeCallback: (_, __) => innerTimer.Enabled = true)
            ;

            Registers.Status.Define(this)
            .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => innerTimer.Enabled)
            .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => innerTimer.Direction == Direction.Descending)
            .WithTaggedFlag("TOPBV", 2)
            .WithReservedBits(3, 5)
            .WithTaggedFlag("CCVBV0", 8)
            .WithTaggedFlag("CCVBV1", 9)
            .WithTaggedFlag("CCVBV2", 10)
            .WithTaggedFlag("CCVBV2", 11)
            .WithReservedBits(12, 4)
            .WithTaggedFlag("ICV0", 16)
            .WithTaggedFlag("ICV1", 17)
            .WithTaggedFlag("ICV2", 18)
            .WithTaggedFlag("ICV3", 19)
            .WithReservedBits(20, 4)
            .WithTaggedFlag("CCPOL0", 24)
            .WithTaggedFlag("CCPOL1", 25)
            .WithTaggedFlag("CCPOL2", 26)
            .WithTaggedFlag("CCPOL3", 27)
            .WithReservedBits(28, 4)
            ;

            RegistersCollection.AddRegister((long)Registers.InterruptFlag, interruptManager.GetMaskedInterruptFlagRegister <DoubleWordRegister>());
            RegistersCollection.AddRegister((long)Registers.InterruptFlagSet, interruptManager.GetInterruptSetRegister <DoubleWordRegister>());
            RegistersCollection.AddRegister((long)Registers.InterruptFlagClear, interruptManager.GetInterruptClearRegister <DoubleWordRegister>());
            RegistersCollection.AddRegister((long)Registers.InterruptEnable, interruptManager.GetInterruptEnableRegister <DoubleWordRegister>());

            Registers.CounterTopValue.Define(this)
            .WithValueField(0, (int)width, writeCallback: (_, value) => innerTimer.Limit = value, valueProviderCallback: _ => (uint)innerTimer.Limit, name: "TOP")
            ;

            Registers.CounterValue.Define(this)
            .WithValueField(0, (int)width, writeCallback: (_, value) => innerTimer.Value = value, valueProviderCallback: _ => (uint)innerTimer.Value, name: "CNT")
            ;
        }
Example #3
0
        private void DefineRegisters()
        {
            DefineTask(Registers.TxEnable, TxEnable, "TASKS_TXEN");

            DefineTask(Registers.RxEnable, RxEnable, "TASKS_RXEN");

            DefineTask(Registers.Start, Start, "TASKS_START");

            DefineTask(Registers.Disable, Disable, "TASKS_DISABLE");

            DefineEvent(Registers.Ready, () => this.Log(LogLevel.Error, "Trying to trigger READY event, not supported"), Events.Ready, "EVENTS_READY");
            DefineEvent(Registers.AddressSentOrReceived, () => this.Log(LogLevel.Error, "Trying to trigger ADDRESS event, not supported"), Events.Address, "EVENTS_ADDRESS");
            DefineEvent(Registers.PayloadSentOrReceived, () => this.Log(LogLevel.Error, "Trying to trigger PAYLOAD event, not supported"), Events.Payload, "EVENTS_PAYLOAD");
            DefineEvent(Registers.PacketSentOrReceived, () => this.Log(LogLevel.Error, "Trying to trigger END event, not supported"), Events.End, "EVENTS_END");
            DefineEvent(Registers.BitCounterMatch, () => this.Log(LogLevel.Error, "Trying to trigger BCMATCH event, not supported"), Events.BitCountMatch, "EVENTS_BCMATCH");
            DefineEvent(Registers.RSSIEnd, () => this.Log(LogLevel.Error, "Trying to trigger RSSIEnd event, not supported"), Events.RSSIEnd, "EVENTS_RSSIEND");
            DefineEvent(Registers.CRCOk, () => this.Log(LogLevel.Error, "Trying to trigger CRCOk event, not supported"), Events.CRCOk, "EVENTS_CRCOK");

            DefineEvent(Registers.RadioDisabled, Disable, Events.Disabled, "EVENTS_DISABLED");

            DefineEvent(Registers.TxReady, TxEnable, Events.TxReady, "EVENTS_TXREADY");

            DefineEvent(Registers.RxReady, RxEnable, Events.RxReady, "EVENTS_RXREADY");

            // Notice: while the whole Shorts register is implemented, we don't necessarily
            // support all the mentioned events and tasks
            Registers.Shorts.Define(this)
            .WithFlag(0, out shorts.ReadyStart, name: "READY_START")
            .WithFlag(1, out shorts.EndDisable, name: "END_DISABLE")
            .WithFlag(2, out shorts.DisabledTxEnable, name: "DISABLED_TXEN")
            .WithFlag(3, out shorts.DisabledRxEnable, name: "DISABLED_RXEN")
            .WithFlag(4, out shorts.AddressRSSIStart, name: "ADDRESS_RSSISTART")
            .WithFlag(5, out shorts.EndStart, name: "END_START")
            .WithFlag(6, out shorts.AddressBitCountStart, name: "ADDRESS_BCSTART")
            .WithFlag(8, out shorts.DisabledRSSIStop, name: "DISABLED_RSSISTOP")
            .WithFlag(11, out shorts.RxReadyCCAStart, name: "RXREADY_CCASTART")
            .WithFlag(12, out shorts.CCAIdleTxEnable, name: "CCAIDLE_TXEN")
            .WithFlag(13, out shorts.CCABusyDisable, name: "CCABUSY_DISABLE")
            .WithFlag(14, out shorts.FrameStartBitCountStart, name: "FRAMESTART_BCSTART")
            .WithFlag(15, out shorts.ReadyEnergyDetectStart, name: "READY_EDSTART")
            .WithFlag(16, out shorts.EnergyDetectEndDisable, name: "EDEND_DISABLE")
            .WithFlag(17, out shorts.CCAIdleStop, name: "CCAIDLE_STOP")
            .WithFlag(18, out shorts.TxReadyStart, name: "TXREADY_START")
            .WithFlag(19, out shorts.RxReadyStart, name: "RXREADY_START")
            .WithFlag(20, out shorts.PHYEndDisable, name: "PHYEND_DISABLE")
            .WithFlag(21, out shorts.PHYEndStart, name: "PHYEND_START")
            .WithReservedBits(22, 10)
            ;

            RegistersCollection.AddRegister((long)Registers.InterruptEnable,
                                            interruptManager.GetInterruptEnableSetRegister <DoubleWordRegister>());

            RegistersCollection.AddRegister((long)Registers.InterruptDisable,
                                            interruptManager.GetInterruptEnableClearRegister <DoubleWordRegister>());

            Registers.CRCStatus.Define(this)
            .WithFlag(0, name: "CRCSTATUS", valueProviderCallback: _ => true)    // we assume here that CRCs are always ok
            .WithReservedBits(1, 31)
            ;

            Registers.PacketPointer.Define(this)
            .WithValueField(0, 32, out packetPointer, name: "PACKETPTR")
            ;

            Registers.Frequency.Define(this, name: "FREQUENCY")
            .WithValueField(0, 7, out frequency, name: "FREQUENCY")
            .WithReservedBits(7, 1)
            .WithFlag(8, out frequencyMap, name: "MAP")
            .WithReservedBits(9, 23)
            ;

            Registers.TxPower.Define(this, name: "TXPOWER")
            .WithValueField(0, 8, name: "TXPOWER")     // just RW
            .WithReservedBits(8, 24)
            ;

            Registers.Mode.Define(this, name: "MODE")
            .WithValueField(0, 4, name: "MODE")     // just RW
            .WithReservedBits(4, 24)
            ;

            Registers.PacketConfiguration0.Define(this, name: "PCNF0")
            .WithValueField(0, 4, out lengthFieldLength, name: "LFLEN")
            .WithReservedBits(4, 4)
            .WithValueField(8, 1, out s0Length, name: "S0LEN")
            .WithReservedBits(9, 7)
            .WithValueField(16, 4, out s1Length, name: "S1LEN")
            .WithFlag(20, out s1Include, name: "S1INCL")
            .WithReservedBits(21, 1)
            .WithValueField(22, 2, out codeIndicatorLength, name: "CILEN")
            .WithTag("PLEN", 24, 2)
            .WithFlag(26, out crcIncludedInLength, name: "CRCINC")
            .WithReservedBits(27, 2)
            .WithValueField(29, 2, out termLength, name: "TERMLEN")
            .WithReservedBits(31, 1)
            ;

            Registers.PacketConfiguration1.Define(this, name: "PCNF1")
            .WithValueField(0, 8, out maxPacketLength, name: "MAXLEN")
            .WithValueField(8, 8, out staticLength, name: "STATLEN")
            .WithValueField(16, 3, out baseAddressLength, name: "BALEN")
            .WithReservedBits(19, 5)
            .WithTaggedFlag("ENDIAN", 24)
            .WithTaggedFlag("WHITEEN", 25)
            .WithReservedBits(26, 6)
            ;

            Registers.BaseAddress0.Define(this)
            .WithValueField(0, 32, out baseAddress0, name: "BASE0")
            ;

            Registers.BaseAddress1.Define(this)
            .WithValueField(0, 32, out baseAddress1, name: "BASE1")
            ;

            Registers.Prefix0.Define(this, name: "PREFIX0")
            .WithValueFields(0, 8, 4, writeCallback: (i, _, value) => addressPrefixes[i] = (byte)value, name: "AP")
            ;

            Registers.Prefix1.Define(this, name: "PREFIX1")
            .WithValueFields(0, 8, 4, writeCallback: (i, _, value) => addressPrefixes[4 + i] = (byte)value, name: "AP")
            ;

            Registers.TxAddress.Define(this, name: "TXADDRESS")
            .WithValueField(0, 3, out txAddress, name: "TXADDRESS")
            .WithReservedBits(3, 29)
            ;

            Registers.RxAddresses.Define(this, name: "RXADDRESSES")
            .WithFlags(0, 8, out rxAddressEnabled, name: "ADDR")
            ;

            Registers.CRCConfiguration.Define(this, name: "CRCCNF")
            .WithValueField(0, 2, out crcLength, name: "LEN")
            .WithReservedBits(2, 6)
            .WithEnumField(8, 2, out crcSkipAddress, name: "SKIPADDR")
            .WithReservedBits(10, 21)
            ;

            Registers.CRCPolynomial.Define(this, name: "CRCPOLY")
            .WithValueField(0, 24, out crcPolynomial, name: "CRCPOLY")
            .WithReservedBits(24, 8)
            ;

            Registers.CRCInitialValue.Define(this, name: "CRCINIT")
            .WithValueField(0, 24, out crcInitialValue, name: "CRCINIT")
            .WithReservedBits(24, 8)
            ;

            Registers.RSSISample.Define(this, name: "RSSISAMPLE")
            .WithValueField(0, 32, valueProviderCallback: _ => DefaultRSSISample, name: "RSSISAMPLE");
            ;

            Registers.State.Define(this, name: "STATE")
            .WithEnumField <DoubleWordRegister, State>(0, 4, FieldMode.Read, valueProviderCallback: _ => radioState, name: "STATE")
            .WithReservedBits(4, 28)
            ;

            Registers.BitCounterCompare.Define(this, name: "BCC")
            .WithValueField(0, 32, out bitCountCompare)
            ;

            Registers.ModeConfiguration0.Define(this, 0x200)
            .WithTaggedFlag("RU", 0)
            .WithReservedBits(1, 7)
            .WithTag("DTX", 8, 2)
            .WithReservedBits(10, 22)
            ;

            Registers.CCAControl.Define(this, 0x052D0000, name: "CCACTRL")
            .WithEnumField(0, 3, out ccaMode, name: "CCAMODE")
            .WithReservedBits(3, 5)
            .WithTag("CCAEDTHRES", 8, 8)
            .WithTag("CCACORRTHRES", 16, 8)
            .WithTag("CCACORRCNT", 24, 8)
            ;

            Registers.PowerControl.Define(this, 1, name: "POWER")
            //TODO: radio should be disabled with powerOn == false
            .WithFlag(0, out powerOn, changeCallback: (_, value) => { if (!value)
                                                                      {
                                                                          Reset();
                                                                      }
                      }, name: "POWER")
            .WithReservedBits(1, 31)
            ;
        }