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; } + + //---------------------------------------------------------------------------- }