Rewrite Mailbox.cs to match TLUL spec - one mailbox with two endpoints.

The previous impl had two mailboxes, each with both "ends" connected to
the same register block. This was wrong - each "end" of each mailbox
fifo should be connected to different register blocks. This makes the
simulation slightly trickier, but saner from the user perspective.

Change-Id: I3699630ed86a4decb25d8611d1a8f2c57118ea98
diff --git a/platforms/shodan.repl b/platforms/shodan.repl
index 226e302..809aea3 100644
--- a/platforms/shodan.repl
+++ b/platforms/shodan.repl
@@ -2,15 +2,30 @@
 using "sim/config/platforms/secure.repl"
 using "sim/config/platforms/springbok.repl"
 
-// Core 0 Mailbox           [‘h400F_0000 - ‘h400F_0020)   32B
-mailbox0: Mailbox @ sysbus 0x400F0000
-    mailbox_hiwater -> plic@100 // TODO(aappleby) these plic lines are bogus, fix them
-    mailbox_lowater -> plic@101
+// To model the TLUL mailbox spec, we need a Renode peripheral that listens
+// to address ranges for both endpoints of the mailbox. This is the current
+// best way to do that, per Renode dev's recommendations.
 
-// Core 1 Mailbox           [‘h400F_1000 - ‘h400F_1020)   32B
-mailbox1: Mailbox @ sysbus 0x400F1000
-    mailbox_hiwater -> smc_plic@9 // TODO(aappleby) these plic lines are bogus, fix them
-    mailbox_lowater -> smc_plic@10
+mailbox : Mailbox @ {
+        sysbus new Bus.BusMultiRegistration {
+            address: 0x40800000;  // TOP_MATCHA_MAILBOX_SEC_BASE_ADDR
+            size: 0x28;
+            region: "endpoint_a"
+        };
+        sysbus new Bus.BusMultiRegistration {
+            address: 0x540F1000;  // TOP_MATCHA_MAILBOX_SMC_BASE_ADDR
+            size: 0x28;
+            region: "endpoint_b"
+        }
+    }
+    endpoint_a_name: "SEC"
+    endpoint_b_name: "SMC"
+    wtirq_A -> plic@181    // kTopMatchaPlicIrqIdMailboxSecWtirq
+    rtirq_A -> plic@182    // kTopMatchaPlicIrqIdMailboxSecRtirq
+    eirq_A  -> plic@183    // kTopMatchaPlicIrqIdMailboxSecEirq
+    wtirq_B -> smc_plic@10 // kTopMatchaPlicIrqIdMailboxSmcWtirq
+    rtirq_B -> smc_plic@11 // kTopMatchaPlicIrqIdMailboxSmcRtirq
+    eirq_B  -> smc_plic@12 // kTopMatchaPlicIrqIdMailboxSmcEirq
 
 vec_controlblock :
     HostReqIRQ -> smc_plic@11
diff --git a/shodan_infrastructure/Mailbox.cs b/shodan_infrastructure/Mailbox.cs
index 5da7a84..4e74358 100644
--- a/shodan_infrastructure/Mailbox.cs
+++ b/shodan_infrastructure/Mailbox.cs
@@ -4,278 +4,393 @@
 
 namespace Antmicro.Renode.Peripherals
 {
-  public class Mailbox : IDoubleWordPeripheral, IKnownSize
+  // NOTE: Log messages don't seem to work correctly if called from a sub-object
+  // of a peripheral, so the per-endpoint implementation is in Mailbox below.
+
+  public class MailboxEndpoint
   {
-    //--------------------------------------------------------------------------
-
-    public Mailbox()
+    public MailboxEndpoint(string name, MailboxFifo fifo_r, MailboxFifo fifo_w,
+                           GPIO wtirq, GPIO rtirq, GPIO eirq)
     {
-      this.Log(LogLevel.Noisy, "Mailbox()");
-      mailbox_error = new GPIO();
-      mailbox_hiwater = new GPIO();
-      mailbox_lowater = new GPIO();
+      this.name = name;
+      this.fifo_r = fifo_r;
+      this.fifo_w = fifo_w;
+      this.wtirq = wtirq;
+      this.rtirq = rtirq;
+      this.eirq = eirq;
     }
 
-    public void Reset() // IPeripheral 
+    public void Reset()
     {
-      for (int i = 0; i < FIFO_SIZE; i++) fifo[i] = 0;
+      intr_state = 0;
+      intr_enable = 0;
+      intr_test = 0;
+      error = 0;
+      wirqt = 0;
+      rirqt = 0;
     }
 
-    //--------------------------------------------------------------------------
+    public string name;
+    public MailboxFifo fifo_r;
+    public MailboxFifo fifo_w;
+    public GPIO wtirq;
+    public GPIO rtirq;
+    public GPIO eirq;
 
-    public uint ReadDoubleWord(long offset) // IDoubleWordPeripheral
+    public uint intr_state;
+    public uint intr_enable;
+    public uint intr_test;
+    public uint error;
+    public uint wirqt; // Write fifo IRQ threshold
+    public uint rirqt; // Read fifo IRQ threshold
+  }
+
+  //----------------------------------------------------------------------------
+
+  public class MailboxFifo
+  {
+    public void Push(uint value)
+    {
+      if (!Full())
+      {
+        fifo[cursor_w] = value;
+        cursor_w = (cursor_w + 1) & FIFO_MASK;
+        count++;
+      }
+    }
+
+    public uint Pop()
     {
       uint result = 0;
-      switch (offset)
+      if (!Empty())
       {
-        case REG_ISTATE: {
-          this.Log(LogLevel.Noisy, "read reg_istate = 0b{0}", System.Convert.ToString(reg_istate, 2));
-          result = reg_istate;
-          break;
-        }
-
-        case REG_IENABLE: {
-          this.Log(LogLevel.Noisy, "read reg_ienable = 0b{0}", System.Convert.ToString(reg_ienable, 2));
-          result = reg_ienable;
-          break;
-        }
-
-        case REG_ITEST: {
-          this.Log(LogLevel.Noisy, "read reg_itest = 0b{0}", System.Convert.ToString(reg_itest, 2));
-          result = reg_itest;
-          break;
-        }
-
-        case REG_WRITE: {
-          this.Log(LogLevel.Error, "Tried to read from the mailbox write register");
-          reg_istate |= IRQ_ERROR; break;
-        }
-
-        case REG_READ:
-          if (fifo_count == 0)
-          {
-            this.Log(LogLevel.Error, "Tried to read from empty mailbox");
-            reg_istate |= IRQ_ERROR;
-            reg_error |= ERR_READ;
-            break;
-          }
-          else
-          {
-            this.Log(LogLevel.Noisy, "read fifo[{0}] = 0x{1:X8}, fifo_count {2}", cursor_r, fifo[cursor_r], fifo_count - 1);
-            result = fifo[cursor_r];
-            cursor_r = (cursor_r + 1) & FIFO_MASK;
-            fifo_count--;
-            if (fifo_count <= reg_lowater) reg_istate |= IRQ_LOWATER;
-            break;
-          }
-
-        case REG_STATUS: {
-          this.Log(LogLevel.Noisy, "read reg_status = 0b{0}", System.Convert.ToString(reg_status, 2));
-          result = reg_status;
-          break;
-        }
-
-        case REG_ERROR: {
-          // Reading clears pending errors.
-          this.Log(LogLevel.Noisy, "read reg_error = 0b{0}", System.Convert.ToString(reg_error, 2));
-          result = reg_error;
-          reg_error = 0;
-          break;
-        }
-
-        case REG_HIWATER: {
-          this.Log(LogLevel.Noisy, "read reg_hiwater = {0}", reg_hiwater);
-          result = reg_hiwater;
-          break;
-        }
-
-        case REG_LOWATER: {
-          this.Log(LogLevel.Noisy, "read reg_lowater = {0}", reg_lowater);
-          result = reg_lowater;
-          break;
-        }
-
-        case REG_FLUSH: {
-          this.Log(LogLevel.Error, "Tried to read from the mailbox flush register");
-          reg_istate |= IRQ_ERROR;
-          break;
-        }
+        result = fifo[cursor_r];
+        cursor_r = (cursor_r + 1) & FIFO_MASK;
+        count--;
       }
-
-      update_regs();
-
       return result;
     }
 
-    //--------------------------------------------------------------------------
-
-    public void WriteDoubleWord(long offset, uint value) // IDoubleWordPeripheral
+    public bool Empty()
     {
-      switch (offset)
-      {
-        case REG_ISTATE: {
-          // Interrupt state is write-to-clear.
-          uint new_istate = (reg_istate & ~value) & IRQ_MASK;
-          this.Log(LogLevel.Noisy, "write reg_istate 0b{0} -> 0b{1}", System.Convert.ToString(reg_istate, 2), System.Convert.ToString(new_istate, 2));
-          reg_istate = new_istate;
-          break;
-        }
-
-        case REG_IENABLE: {
-          this.Log(LogLevel.Noisy, "write reg_ienable 0b{0} -> 0b{1}", System.Convert.ToString(reg_ienable, 2), System.Convert.ToString(value & IRQ_MASK, 2));
-          reg_ienable = value & IRQ_MASK;
-          break;
-        }
-
-        case REG_ITEST: {
-          this.Log(LogLevel.Noisy, "write reg_itest 0b{0} -> 0b{1}", System.Convert.ToString(reg_itest, 2), System.Convert.ToString(value & IRQ_MASK, 2));
-          reg_itest = value & IRQ_MASK;
-          break;
-        }
-
-        case REG_WRITE:
-          if (fifo_count == FIFO_SIZE)
-          {
-            this.Log(LogLevel.Error, "Tried to write to full mailbox");
-            reg_istate |= IRQ_ERROR;
-            reg_error |= ERR_WRITE;
-            break;
-          }
-          else
-          {
-            this.Log(LogLevel.Noisy, "write fifo[{0}] = 0x{1:X8}, fifo_count {2}", cursor_w, value, fifo_count + 1);
-            fifo[cursor_w] = value;
-            cursor_w = (cursor_w + 1) & FIFO_MASK;
-            fifo_count++;
-            if (fifo_count >= reg_hiwater) reg_istate |= IRQ_HIWATER;
-            break;
-          }
-
-        case REG_READ: {
-          this.Log(LogLevel.Error, "Tried to write 0x{0:X8} to the mailbox read register", value);
-          reg_istate |= IRQ_ERROR;
-          break;
-        }
-
-        case REG_STATUS: {
-          this.Log(LogLevel.Noisy, "write 0x{0:X8} to reg_status", value);
-          reg_istate |= IRQ_ERROR;
-          break;
-        }
-
-        case REG_ERROR: {
-          this.Log(LogLevel.Noisy, "write 0x{0:X8} to reg_status", value);
-          reg_istate |= IRQ_ERROR;
-          break;
-        }
-
-        case REG_HIWATER: {
-          this.Log(LogLevel.Noisy, "write reg_hiwater {0} -> {1}", reg_hiwater, value);
-          reg_hiwater = System.Math.Min(value, FIFO_SIZE - 1);
-          break;
-        }
-
-        case REG_LOWATER: {
-          this.Log(LogLevel.Noisy, "write reg_lowater {0} -> {1}", reg_lowater, value);
-          reg_lowater = System.Math.Min(value, FIFO_SIZE - 1);
-          break;
-        }
-
-        case REG_FLUSH:
-          this.Log(LogLevel.Noisy, "write reg_flush = 0b{0}", System.Convert.ToString(value, 2));
-          // FIXME - not 100% sure that flushing makes sense w/ this setup...
-          if ((value & FLUSH_READ) != 0) fifo_count = 0;
-          if ((value & FLUSH_WRITE) != 0) fifo_count = 0;
-          break;
-      }
-
-      update_regs();
+      return count == 0;
     }
 
-    //--------------------------------------------------------------------------
-
-    public void update_regs()
+    public bool Full()
     {
-      reg_status = 0;
-      if (fifo_count == 0) reg_status |= STATUS_EMPTY;
-      if (fifo_count == FIFO_SIZE) reg_status |= STATUS_FULL;
-      if (fifo_count <= reg_lowater) reg_status |= STATUS_LOWATER;
-      if (fifo_count >= reg_hiwater) reg_status |= STATUS_HIWATER;
-
-      bool new_irq_error   = ((reg_ienable & IRQ_ERROR) != 0)   && ((reg_istate & IRQ_ERROR) != 0);
-      bool new_irq_hiwater = ((reg_ienable & IRQ_HIWATER) != 0) && ((reg_istate & IRQ_HIWATER) != 0);
-      bool new_irq_lowater = ((reg_ienable & IRQ_LOWATER) != 0) && ((reg_istate & IRQ_LOWATER) != 0);
-
-      if (!mailbox_error.IsSet   && new_irq_error)   this.Log(LogLevel.Noisy, "Triggering error IRQ");
-      if (!mailbox_hiwater.IsSet && new_irq_hiwater) this.Log(LogLevel.Noisy, "Triggering hiwater IRQ");
-      if (!mailbox_lowater.IsSet && new_irq_lowater) this.Log(LogLevel.Noisy, "Triggering lowater IRQ");
-
-      if (mailbox_error.IsSet   && !new_irq_error)   this.Log(LogLevel.Noisy, "Clearing error IRQ");
-      if (mailbox_hiwater.IsSet && !new_irq_hiwater) this.Log(LogLevel.Noisy, "Clearing hiwater IRQ");
-      if (mailbox_lowater.IsSet && !new_irq_lowater) this.Log(LogLevel.Noisy, "Clearing lowater IRQ");
-
-      mailbox_error.Set(new_irq_error);
-      mailbox_hiwater.Set(new_irq_hiwater);
-      mailbox_lowater.Set(new_irq_lowater);
+      return count == FIFO_SIZE;
     }
 
-    //--------------------------------------------------------------------------
+    public void Flush()
+    {
+      cursor_r = 0;
+      cursor_w = 0;
+      count = 0;
+      for (int i = 0; i < FIFO_SIZE; i++)
+        fifo[i] = 0;
+    }
 
     const uint FIFO_SIZE = 8;
     const uint FIFO_MASK = FIFO_SIZE - 1;
 
-    const uint REG_ISTATE = 0x000;
-    const uint REG_IENABLE = 0x004;
-    const uint REG_ITEST = 0x008;
-    const uint REG_WRITE = 0x00C;
-    const uint REG_READ = 0x010;
-    const uint REG_STATUS = 0x014;
-    const uint REG_ERROR = 0x018;
-    const uint REG_HIWATER = 0x01C;
-    const uint REG_LOWATER = 0x020;
-    const uint REG_FLUSH = 0x024;
+    public uint cursor_r = 0;
+    public uint cursor_w = 0;
+    public uint count = 0;
+    public uint[] fifo = new uint[FIFO_SIZE];
+  }
 
-    const uint STATUS_EMPTY = 0b0001;
-    const uint STATUS_FULL = 0b0010;
-    const uint STATUS_HIWATER = 0b0100;
-    const uint STATUS_LOWATER = 0b1000;
+  //----------------------------------------------------------------------------
+
+  public class Mailbox : IBusPeripheral
+  {
+    MailboxEndpoint endpoint_A;
+    MailboxEndpoint endpoint_B;
+    MailboxFifo fifo_AB;
+    MailboxFifo fifo_BA;
+
+    public GPIO wtirq_A { get; }
+    public GPIO rtirq_A { get; }
+    public GPIO eirq_A { get; }
+
+    public GPIO wtirq_B { get; }
+    public GPIO rtirq_B { get; }
+    public GPIO eirq_B { get; }
+
+    public Mailbox(string endpoint_a_name, string endpoint_b_name)
+    {
+      this.Log(LogLevel.Noisy, "Mailbox()");
+
+      wtirq_A = new GPIO();
+      rtirq_A = new GPIO();
+      eirq_A = new GPIO();
+
+      wtirq_B = new GPIO();
+      rtirq_B = new GPIO();
+      eirq_B = new GPIO();
+
+      fifo_AB = new MailboxFifo();
+      fifo_BA = new MailboxFifo();
+
+      endpoint_A = new MailboxEndpoint(endpoint_a_name, fifo_BA, fifo_AB,
+                                       wtirq_A, rtirq_A, eirq_A);
+      endpoint_B = new MailboxEndpoint(endpoint_b_name, fifo_AB, fifo_BA,
+                                       wtirq_B, rtirq_B, eirq_B);
+
+      this.Log(LogLevel.Noisy, "Mailbox() done");
+    }
+
+    public void Reset()
+    {
+      endpoint_A.Reset();
+      endpoint_B.Reset();
+      fifo_AB.Flush();
+      fifo_BA.Flush();
+    }
+
+    [ConnectionRegion("endpoint_a")]
+    public uint ReadDoubleWord_A(long offset)
+    {
+      uint value = ReadDoubleWord(endpoint_A, offset);
+      UpdateRegisters(endpoint_A);
+      UpdateRegisters(endpoint_B);
+      return value;
+    }
+
+    [ConnectionRegion("endpoint_a")]
+    public void WriteDoubleWord_A(long offset, uint value)
+    {
+      WriteDoubleWord(endpoint_A, offset, value);
+      UpdateRegisters(endpoint_A);
+      UpdateRegisters(endpoint_B);
+    }
+
+    [ConnectionRegion("endpoint_b")]
+    public uint ReadDoubleWord_B(long offset)
+    {
+      uint value = ReadDoubleWord(endpoint_B, offset);
+      UpdateRegisters(endpoint_A);
+      UpdateRegisters(endpoint_B);
+      return value;
+    }
+
+    [ConnectionRegion("endpoint_b")]
+    public void WriteDoubleWord_B(long offset, uint value)
+    {
+      WriteDoubleWord(endpoint_B, offset, value);
+      UpdateRegisters(endpoint_A);
+      UpdateRegisters(endpoint_B);
+    }
+
+    public uint ReadDoubleWord(MailboxEndpoint ep, long offset)
+    {
+      uint value = 0;
+
+      switch (offset)
+      {
+        case REG_INTR_STATE:
+          value = ep.intr_state;
+          break;
+        case REG_INTR_ENABLE:
+          value = ep.intr_enable;
+          break;
+        case REG_INTR_TEST:
+          value = ep.intr_test;
+          break;
+        case REG_MBOXW:
+          this.Log(LogLevel.Error, "Tried to read from {0}'s write port",
+                   ep.name);
+          break;
+        case REG_MBOXR:
+          if (!ep.fifo_r.Empty())
+          {
+            value = ep.fifo_r.Pop();
+          }
+          else
+          {
+            this.Log(LogLevel.Error, "Tried to read from {0}'s empty fifo",
+                     ep.name);
+            ep.error |= ERROR_BIT_READ;
+            ep.intr_state |= INTR_STATE_BIT_EIRQ;
+          }
+          break;
+        case REG_STATUS:
+          if (ep.fifo_r.Empty())
+            value |= STATUS_BIT_EMPTY;
+          if (ep.fifo_w.Full())
+            value |= STATUS_BIT_FULL;
+          if (ep.fifo_w.count > ep.wirqt)
+            value |= STATUS_BIT_WFIFOL;
+          if (ep.fifo_r.count > ep.rirqt)
+            value |= STATUS_BIT_RFIFOL;
+          break;
+        case REG_ERROR:
+          break;
+        case REG_WIRQT:
+          value = ep.wirqt;
+          break;
+        case REG_RIRQT:
+          value = ep.rirqt;
+          break;
+        case REG_CTRL:
+          // "On write, the fifo is cleared and the register is reset"
+          // So this can only read as 0.
+          value = 0;
+          break;
+      }
+
+      this.Log(LogLevel.Noisy, "{0}.ReadDoubleWord({1}) = 0x{2:X8}", ep.name,
+               reg_names[offset >> 2], value);
+      return value;
+    }
+
+    public void WriteDoubleWord(MailboxEndpoint ep, long offset, uint value)
+    {
+      switch (offset)
+      {
+        case REG_INTR_STATE:
+          ep.intr_state &= ~(value & INTR_STATE_MASK);
+          break;
+        case REG_INTR_ENABLE:
+          ep.intr_enable = value & INTR_ENABLE_MASK;
+          break;
+        case REG_INTR_TEST:
+          ep.intr_test = value & INTR_TEST_MASK;
+          ep.intr_state |= value & INTR_STATE_MASK;
+          break;
+        case REG_MBOXW:
+          if (!ep.fifo_w.Full())
+          {
+            ep.fifo_w.Push(value);
+          }
+          else
+          {
+            this.Log(LogLevel.Error, "Tried to write to {0}'s full fifo",
+                     ep.name);
+            ep.error |= ERROR_BIT_WRITE;
+            ep.intr_state |= INTR_STATE_BIT_EIRQ;
+          }
+          break;
+        case REG_MBOXR:
+          this.Log(LogLevel.Error, "Tried to write to {0}'s read port",
+                   ep.name);
+          ep.intr_state |= INTR_STATE_BIT_EIRQ;
+          break;
+        case REG_STATUS:
+          this.Log(LogLevel.Error, "Tried to write to {0}'s status register",
+                   ep.name);
+          ep.intr_state |= INTR_STATE_BIT_EIRQ;
+          break;
+        case REG_ERROR:
+          break;
+        case REG_WIRQT:
+          ep.wirqt = value & WIRQT_MASK;
+          break;
+        case REG_RIRQT:
+          ep.rirqt = value & RIRQT_MASK;
+          break;
+        case REG_CTRL:
+          if ((value & CTRL_BIT_FLUSH_WFIFO) != 0)
+            ep.fifo_w.Flush();
+          if ((value & CTRL_BIT_FLUSH_RFIFO) != 0)
+            ep.fifo_r.Flush();
+          break;
+      }
+
+      this.Log(LogLevel.Noisy, "{0}.WriteDoubleWord({1}) = 0x{2:X8}", ep.name,
+               reg_names[offset >> 2], value);
+    }
+
+    public void UpdateRegisters(MailboxEndpoint ep)
+    {
+      if (ep.fifo_w.count > ep.wirqt)
+        ep.intr_state |= INTR_STATE_BIT_WTIRQ;
+      if (ep.fifo_r.count > ep.rirqt)
+        ep.intr_state |= INTR_STATE_BIT_RTIRQ;
+
+      bool new_wtirq =
+          (ep.intr_enable & ep.intr_state & INTR_ENABLE_BIT_WTIRQ) != 0;
+      bool new_rtirq =
+          (ep.intr_enable & ep.intr_state & INTR_ENABLE_BIT_RTIRQ) != 0;
+      bool new_eirq =
+          (ep.intr_enable & ep.intr_state & INTR_ENABLE_BIT_EIRQ) != 0;
+
+      if (!ep.wtirq.IsSet && new_wtirq)
+        this.Log(LogLevel.Noisy, "{0}.WIRQ triggered", ep.name);
+      if (!ep.rtirq.IsSet && new_rtirq)
+        this.Log(LogLevel.Noisy, "{0}.RIRQ triggered", ep.name);
+      if (!ep.eirq.IsSet && new_eirq)
+        this.Log(LogLevel.Noisy, "{0}.EIRQ triggered", ep.name);
+
+      if (ep.wtirq.IsSet && !new_wtirq)
+        this.Log(LogLevel.Noisy, "{0}.WIRQ cleared", ep.name);
+      if (ep.rtirq.IsSet && !new_rtirq)
+        this.Log(LogLevel.Noisy, "{0}.RIRQ cleared", ep.name);
+      if (ep.eirq.IsSet && !new_eirq)
+        this.Log(LogLevel.Noisy, "{0}.EIRQ cleared", ep.name);
+
+      ep.eirq.Set(new_eirq);
+      ep.wtirq.Set(new_wtirq);
+      ep.rtirq.Set(new_rtirq);
+    }
+
+    public static readonly string[] reg_names = {
+      "INTR_STATE",
+      "INTR_ENABLE",
+      "INTR_TEST",
+      "MBOXW",
+      "MBOXR",
+      "STATUS",
+      "ERROR",
+      "WIRQT",
+      "RIRQT",
+      "CTRL"
+    };
+
+    const uint REG_INTR_STATE = 0x000;  // R/W1C
+    const uint REG_INTR_ENABLE = 0x004; // R/W
+    const uint REG_INTR_TEST = 0x008;   // R/W
+    const uint REG_MBOXW = 0x00C;       // W
+    const uint REG_MBOXR = 0x010;       // R
+    const uint REG_STATUS = 0x014;      // R
+    const uint REG_ERROR = 0x018;       // R
+    const uint REG_WIRQT = 0x01C;       // R/W
+    const uint REG_RIRQT = 0x020;       // R/W
+    const uint REG_CTRL = 0x024;        // R/W
+
+    const uint INTR_STATE_BIT_WTIRQ = 0b001;
+    const uint INTR_STATE_BIT_RTIRQ = 0b010;
+    const uint INTR_STATE_BIT_EIRQ = 0b100;
+    const uint INTR_STATE_MASK = 0b111;
+
+    const uint INTR_ENABLE_BIT_WTIRQ = 0b001;
+    const uint INTR_ENABLE_BIT_RTIRQ = 0b010;
+    const uint INTR_ENABLE_BIT_EIRQ = 0b100;
+    const uint INTR_ENABLE_MASK = 0b111;
+
+    const uint INTR_TEST_BIT_WTIRQ = 0b001;
+    const uint INTR_TEST_BIT_RTIRQ = 0b010;
+    const uint INTR_TEST_BIT_EIRQ = 0b100;
+    const uint INTR_TEST_MASK = 0b111;
+
+    const uint STATUS_BIT_EMPTY = 0b0001;
+    const uint STATUS_BIT_FULL = 0b0010;
+    const uint STATUS_BIT_WFIFOL = 0b0100;
+    const uint STATUS_BIT_RFIFOL = 0b1000;
     const uint STATUS_MASK = 0b1111;
 
-    const uint ERR_READ = 0b01;
-    const uint ERR_WRITE = 0b10;
+    const uint ERROR_BIT_READ = 0b01;
+    const uint ERROR_BIT_WRITE = 0b10;
+    const uint ERROR_MASK = 0b11;
 
-    const uint IRQ_ERROR = 0b001;
-    const uint IRQ_HIWATER = 0b010;
-    const uint IRQ_LOWATER = 0b100;
-    const uint IRQ_MASK = 0b111;
+    const uint FIFO_SIZE = 8;
+    const uint FIFO_MASK = FIFO_SIZE - 1;
+    const uint WIRQT_MASK = FIFO_MASK;
+    const uint RIRQT_MASK = FIFO_MASK;
 
-    const uint FLUSH_READ = 0b01;
-    const uint FLUSH_WRITE = 0b10;
-
-    //--------------------------------------------------------------------------
-
-    private uint reg_istate;
-    private uint reg_ienable;
-    private uint reg_itest;
-
-    private uint reg_mboxw;
-    private uint reg_mboxr;
-    private uint reg_status;
-    private uint reg_error;
-    private uint reg_lowater;
-    private uint reg_hiwater;
-
-    private uint cursor_r = 0;
-    private uint cursor_w = 0;
-    private uint fifo_count = 0;
-    private uint[] fifo = new uint[FIFO_SIZE];
-
-    public GPIO mailbox_error { get; }
-    public GPIO mailbox_hiwater { get; }
-    public GPIO mailbox_lowater { get; }
-
-    public long Size => 40; // IKnownSize
-
-    //--------------------------------------------------------------------------
+    const uint CTRL_BIT_FLUSH_WFIFO = 0b01;
+    const uint CTRL_BIT_FLUSH_RFIFO = 0b10;
+    const uint CTRL_MASK = 0b11;
   }
+
+  //----------------------------------------------------------------------------
 }