I'm trying to use the SPI connector from this commit in Renode. The connector itself is here. It hasn't been merged yet, so I have to include the files directly to get it to run. Here's my Renode script file, mostly taken directly from the tests in the commit:
include @SPISlaveModeController.cs
include @SPIMasterModeController.cs
include @SPIConnector.cs
emulation CreateSPIConnector "spi-con"
mach create "Peripheral"
machine LoadPlatformDescriptionFromString "spis: SPI.SPISlaveModeController @ sysbus 0x00000000"
connector Connect sysbus.spis "spi-con"
mach create "Controller"
machine LoadPlatformDescriptionFromString "spim: SPI.SPIMasterModeController @ sysbus 0x00000000"
connector Connect sysbus.spim "spi-con"
start
Now, using the SPIMasterModeController.cs from the example above everything works. My problem comes when I try to do anything with the Connector outside the constructor. I want to have the master call Transmit when I write to an address in it. Here's my minimal example code:
using Antmicro.Renode.Core;
using Antmicro.Renode.Core.Structure;
using Antmicro.Renode.Logging;
using Antmicro.Renode.Time;
using Antmicro.Renode.Peripherals.Bus;
using Antmicro.Renode.Peripherals.Timers;
namespace Antmicro.Renode.Peripherals.SPI
{
    public class SPIMasterModeController :
        NullRegistrationPointPeripheralContainer<ISPIPeripheral>,
        IBytePeripheral,
        IKnownSize
    {
        public SPIMasterModeController(Machine machine) : base(machine)
        {
            timer = new LimitTimer(machine.ClockSource, 1000, this,
                                   "master_clk", workMode: WorkMode.Periodic,
                                   eventEnabled: true, limit: 1, enabled: true);
            timer.LimitReached += () =>
            {
                var output = RegisteredPeripheral.Transmit(0xa5);
                this.Log(LogLevel.Info, "Master sent:{0} received:{1}", 0xa5,
                         output);
                RegisteredPeripheral.FinishTransmission();
                timer.Enabled = false;
            };
        }
        public byte ReadByte(long offset)
        {
            return 0x0;
        }
        public void WriteByte(long offset, byte value)
        {
            // if (offset == 0)
            // {
            //     timer.Enabled = true;
            // }
            // if (offset == 1)
            // {
            //     timer.Enabled = false;
            // }
            if (offset == 0)
            {
                RegisteredPeripheral.Transmit(value);
            }
            else
            {
                RegisteredPeripheral.FinishTransmission();
            }
        }
        public override void Reset()
        {
        }
        public long Size => 0x100;
        private LimitTimer timer;
    }
}
If I swap the implementation of WriteByte here, by uncommenting the commented code and commenting out the uncommented code, things work. I can send sysbus.spim WriteByte 0 0 and I get the messages from the slave about the transmission succeeding. However, as it is, when I send sysbus.spim WriteByte 0 0, Renode crashes with the following error.
[ERROR] FATAL UNHANDLED EXCEPTION: System.ArgumentException: Tried to obtain a virtual time stamp of an unregistered thread: 'Shell thread'/7
  at Antmicro.Renode.Time.TimeDomainsManager.get_VirtualTimeStamp () [0x0003c] in <b05710e063024506ba998245a09a24f0>:0 
  at Antmicro.Renode.Peripherals.SPI.SPIConnector.Transmit (System.Byte data) [0x0004d] in <df90037be0e74203ae6fcba2643fcb70>:0 
  at Antmicro.Renode.Peripherals.SPI.SPIMasterModeController.WriteByte (System.Int64 offset, System.Byte value) [0x00008] in <311068778e6543b9b3f5b2c22a93fb55>:0 
  at (wrapper dynamic-method) System.Object.CallSite.Target(System.Runtime.CompilerServices.Closure,System.Runtime.CompilerServices.CallSite,object,object,object)
  at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid3[T0,T1,T2] (System.Runtime.CompilerServices.CallSite site, T0 arg0, T1 arg1, T2 arg2) [0x00141] in <d22af090bceb4be792f53595cf074724>:0 
  at Dynamitey.Internal.Optimization.InvokeHelper.InvokeMemberAction (System.Runtime.CompilerServices.CallSite& callsite, System.Type binderType, System.Int32 knownType, Dynamitey.Internal.Optimization.InvokeHelper+LazyBinder binder, Dynamitey.InvokeMemberName name, System.Boolean staticContext, System.Type context, System.String[] argNames, System.Object target, System.Object[] args) [0x000df] in <1d0e1ef63ad2470a9ccec81105ee1302>:0 
  at Dynamitey.Internal.Optimization.InvokeHelper.InvokeMemberActionCallSite (System.Object target, Dynamitey.InvokeMemberName name, System.Object[] args, System.String[] tArgNames, System.Type tContext, System.Boolean tStaticContext, System.Runtime.CompilerServices.CallSite& callSite) [0x0004f] in <1d0e1ef63ad2470a9ccec81105ee1302>:0 
  at Dynamitey.Dynamic.InvokeMemberAction (System.Object target, Dynamitey.String_OR_InvokeMemberName name, System.Object[] args) [0x00018] in <1d0e1ef63ad2470a9ccec81105ee1302>:0 
  at Antmicro.Renode.UserInterface.Monitor.InvokeWithContext (Dynamitey.InvokeContext context, System.Reflection.MethodInfo method, System.Object[] parameters) [0x00023] in <11d556aecc3649618ce70f6971350082>:0 
  at Antmicro.Renode.UserInterface.Monitor.InvokeMethod (System.String name, System.Reflection.MethodInfo method, System.Collections.Generic.List`1[T] parameters) [0x0001a] in <11d556aecc3649618ce70f6971350082>:0 
  at Antmicro.Renode.UserInterface.Monitor.ExecuteDeviceAction (System.Type type, System.String name, System.Collections.Generic.IEnumerable`1[T] p) [0x00216] in <11d556aecc3649618ce70f6971350082>:0 
  at Antmicro.Renode.UserInterface.Monitor.ProcessDeviceAction (System.Type device, System.String name, System.Collections.Generic.IEnumerable`1[T] p, AntShell.Commands.ICommandInteraction writer) [0x00023] in <11d556aecc3649618ce70f6971350082>:0 
  at Antmicro.Renode.UserInterface.Monitor.ProcessDeviceActionByName (System.String name, System.Collections.Generic.IEnumerable`1[T] p, AntShell.Commands.ICommandInteraction writer) [0x00060] in <11d556aecc3649618ce70f6971350082>:0 
  at Antmicro.Renode.UserInterface.Monitor.ExecuteCommand (Antmicro.Renode.UserInterface.Tokenizer.Token[] com, AntShell.Commands.ICommandInteraction writer) [0x00165] in <11d556aecc3649618ce70f6971350082>:0 
  at Antmicro.Renode.UserInterface.Monitor.ParseTokens (System.Collections.Generic.IEnumerable`1[T] tokensToParse, AntShell.Commands.ICommandInteraction writer) [0x001df] in <11d556aecc3649618ce70f6971350082>:0 
  at Antmicro.Renode.UserInterface.Monitor.Parse (System.String cmd, AntShell.Commands.ICommandInteraction writer) [0x0019d] in <11d556aecc3649618ce70f6971350082>:0 
  at Antmicro.Renode.UserInterface.Monitor.HandleCommand (System.String cmd, AntShell.Commands.ICommandInteraction ci) [0x00000] in <11d556aecc3649618ce70f6971350082>:0 
  at AntShell.Shell.HandleCommand (System.String cmd, AntShell.Commands.ICommandInteraction ic) [0x000bd] in <12b5ceac888f4387a227ffd402a29e15>:0 
  at AntShell.CommandLine.HandleControlSequence (AntShell.Helpers.ControlSequence seq) [0x00aa1] in <12b5ceac888f4387a227ffd402a29e15>:0 
  at AntShell.Terminal.NavigableTerminalEmulator.HandleInput (System.Object input) [0x0002e] in <12b5ceac888f4387a227ffd402a29e15>:0 
  at AntShell.Terminal.NavigableTerminalEmulator.Run (System.Boolean stopOnError) [0x00017] in <12b5ceac888f4387a227ffd402a29e15>:0 
  at AntShell.Shell.Start (System.Boolean stopOnError) [0x00102] in <12b5ceac888f4387a227ffd402a29e15>:0 
  at Antmicro.Renode.UI.CommandLineInterface+<>c__DisplayClass0_1.<Run>b__3 (System.Object x) [0x00000] in <eb0d5c6c2ada4750b5c3a3c345d7e4f8>:0 
  at System.Threading.ThreadHelper.ThreadStart_Context (System.Object state) [0x0002c] in <12b418a7818c4ca0893feeaaf67f1e7f>:0 
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x0008d] in <12b418a7818c4ca0893feeaaf67f1e7f>:0 
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in <12b418a7818c4ca0893feeaaf67f1e7f>:0 
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state) [0x00031] in <12b418a7818c4ca0893feeaaf67f1e7f>:0 
  at System.Threading.ThreadHelper.ThreadStart (System.Object obj) [0x00012] in <12b418a7818c4ca0893feeaaf67f1e7f>:0
I'm not sufficiently familiar with C# in general or how Renode in particular works to know why this is happening. It seems like RegisteredPeripheral is only available in the constructor, but when I print it inside WriteByte it looks decidedly non-null.