Change Mailbox to use the RTL mailbox register interface. Change-Id: I90031927d518696220c8e4dae4680fc594e1401b
diff --git a/platforms/shodan.repl b/platforms/shodan.repl index fffb2d5..fe181bb 100644 --- a/platforms/shodan.repl +++ b/platforms/shodan.repl
@@ -4,13 +4,13 @@ // Core 0 Mailbox [‘h400F_0000 - ‘h400F_0020) 32B mailbox0: Mailbox @ sysbus 0x400F0000 - MailboxSEND -> plic@100 // TODO(aappleby) these plic lines are bogus, fix them - MailboxRECV -> plic@101 + mailbox_hiwater -> plic@100 // TODO(aappleby) these plic lines are bogus, fix them + mailbox_lowater -> plic@101 // Core 1 Mailbox [‘h400F_1000 - ‘h400F_1020) 32B mailbox1: Mailbox @ sysbus 0x400F1000 - MailboxSEND -> smc_plic@9 // TODO(aappleby) these plic lines are bogus, fix them - MailboxRECV -> smc_plic@10 + mailbox_hiwater -> smc_plic@9 // TODO(aappleby) these plic lines are bogus, fix them + mailbox_lowater -> smc_plic@10 vec_controlblock : HostReqIRQ -> smc_plic@11
diff --git a/shodan_infrastructure/Mailbox.cs b/shodan_infrastructure/Mailbox.cs index ab61bb3..5da7a84 100644 --- a/shodan_infrastructure/Mailbox.cs +++ b/shodan_infrastructure/Mailbox.cs
@@ -6,70 +6,276 @@ { public class Mailbox : IDoubleWordPeripheral, IKnownSize { + //-------------------------------------------------------------------------- + public Mailbox() { - MailboxSEND = new GPIO(); - MailboxRECV = new GPIO(); + this.Log(LogLevel.Noisy, "Mailbox()"); + mailbox_error = new GPIO(); + mailbox_hiwater = new GPIO(); + mailbox_lowater = new GPIO(); } - public void Reset() // IPeripheral + public void Reset() // IPeripheral { - for (int i = 0; i < 8; i++) message[i] = 0; + for (int i = 0; i < FIFO_SIZE; i++) fifo[i] = 0; } + //-------------------------------------------------------------------------- + public uint ReadDoubleWord(long offset) // IDoubleWordPeripheral { uint result = 0; - switch(offset) { - case 0x000: result = message[0]; break; - case 0x004: result = message[1]; break; - case 0x008: result = message[2]; break; - case 0x00C: result = message[3]; break; - case 0x010: result = message[4]; break; - case 0x014: result = message[5]; break; - case 0x018: result = message[6]; break; - case 0x01C: result = message[7]; break; - case 0x020: result = MailboxSEND.IsSet ? 1u : 0u; break; - case 0x024: result = MailboxRECV.IsSet ? 1u : 0u; break; - default: { - this.Log(LogLevel.Error, "Mailbox::ReadDoubleWord - bad offset {0}", offset); + switch (offset) + { + 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; } } + + update_regs(); + return result; } + //-------------------------------------------------------------------------- + public void WriteDoubleWord(long offset, uint value) // IDoubleWordPeripheral { - switch(offset) { - case 0x000: message[0] = value; break; - case 0x004: message[1] = value; break; - case 0x008: message[2] = value; break; - case 0x00C: message[3] = value; break; - case 0x010: message[4] = value; break; - case 0x014: message[5] = value; break; - case 0x018: message[6] = value; break; - case 0x01C: message[7] = value; break; - case 0x020: { - this.Log(LogLevel.Noisy, "Setting MailboxSEND to {0}", value != 0); - MailboxSEND.Set(value != 0); + 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 0x024: { - this.Log(LogLevel.Noisy, "Setting MailboxRECV to {0}", value != 0); - MailboxRECV.Set(value != 0); + + 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; } - default: { - this.Log(LogLevel.Error, "Mailbox::WriteDoubleWord - bad offset {0}", offset); + + 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(); } + //-------------------------------------------------------------------------- + + public void update_regs() + { + 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); + } + + //-------------------------------------------------------------------------- + + 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; + + const uint STATUS_EMPTY = 0b0001; + const uint STATUS_FULL = 0b0010; + const uint STATUS_HIWATER = 0b0100; + const uint STATUS_LOWATER = 0b1000; + const uint STATUS_MASK = 0b1111; + + const uint ERR_READ = 0b01; + const uint ERR_WRITE = 0b10; + + const uint IRQ_ERROR = 0b001; + const uint IRQ_HIWATER = 0b010; + const uint IRQ_LOWATER = 0b100; + const uint IRQ_MASK = 0b111; + + 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 - private uint[] message = new uint[8]; - public GPIO MailboxSEND { get; } - public GPIO MailboxRECV { get; } + + //-------------------------------------------------------------------------- } }