Merge "testing: Unify the platform names to be more programmatic."
diff --git a/shodan_infrastructure/MatchaI2S.cs b/shodan_infrastructure/MatchaI2S.cs index 037ab35..d6fc044 100644 --- a/shodan_infrastructure/MatchaI2S.cs +++ b/shodan_infrastructure/MatchaI2S.cs
@@ -67,12 +67,18 @@ public bool Empty() { - return count == 0; + lock (synclock) + { + return count == 0; + } } public bool Full() { - return count == FIFO_SIZE; + lock (synclock) + { + return count == FIFO_SIZE; + } } public void Flush() @@ -87,13 +93,15 @@ } } - const uint FIFO_SIZE = 64 * 2; + public uint Count { get { lock(synclock) { return count; } } } + + const uint FIFO_SIZE = 63; const uint FIFO_MASK = FIFO_SIZE - 1; - public uint cursor_r = 0; - public uint cursor_w = 0; - public uint count = 0; - public ulong[] fifo = new ulong[FIFO_SIZE]; + private uint cursor_r = 0; + private uint cursor_w = 0; + private uint count = 0; + private ulong[] fifo = new ulong[FIFO_SIZE]; private readonly object synclock = new object(); } @@ -175,12 +183,15 @@ decoder.LoadFile(InputFile); this.Log(LogLevel.Info, "Starting recording."); - rxThread = machine.ObtainManagedThread(InputFrames, SampleFrequencyHz); + rxThread = machine.ObtainManagedThread(ReceiveFrame, SampleFrequencyHz); rxThread.Start(); } private void StartTx() { + this.Log(LogLevel.Info, "Starting playback."); + txThread = machine.ObtainManagedThread(TransmitFrame, SampleFrequencyHz); + txThread.Start(); } private void StopThread(ref IManagedThread thread) @@ -189,39 +200,65 @@ thread = null; } - private void OutputFrames() + // Note: this is not a real thread! This is simply a function that is + // called at a known frequency from the machine simulation. It must + // always complete and never loop! + private void TransmitFrame() { - } - - private void InputFrames() - { - while (!rxBuffer.Full()) - { - // Decode left on the upper half of the sample - ulong sample = (decoder.GetSingleSample() << 16); - sample |= decoder.GetSingleSample(); - - if (!rxBuffer.Full()) { - rxBuffer.Write(sample); - - if ((rxBuffer.count == mappedRxTriggerLevel) && (rxWatermarkIrqEnabled.Value)) - { - this.Log(LogLevel.Debug, "I2S RX FIFO Watermark."); - Connections[(int)Events.RxWatermarkIRQ].Blink(); - } - } + if (txBuffer.Empty()) { + // FIFO is empty -- nothing to do except set the IRQ and return. + this.Log(LogLevel.Debug, "I2S TX FIFO Empty.", txBuffer.Count, mappedTxTriggerLevel); + TxEmptyIRQ.Set(txEmptyIrqEnabled.Value); + return; } - if (rxOverflowIrqEnabled.Value) { - Connections[(int)Events.RxOverflowIRQ].Blink(); + txBuffer.Read(); + this.Log(LogLevel.Noisy, "Removed sample. Level: {0}", txBuffer.Count); + + if (txBuffer.Count < mappedTxTriggerLevel) + { + this.Log(LogLevel.Debug, "I2S TX FIFO Watermark (count {0} < {1}).", txBuffer.Count, mappedTxTriggerLevel); + TxWatermarkIRQ.Set(txWatermarkIrqEnabled.Value); + } + } + + // Note: this is not a real thread! This is simply a function that is + // called at a known frequency from the machine simulation. It must + // always complete and never loop! + private void ReceiveFrame() + { + // Decode left on the upper half of the sample + ulong sample = (decoder.GetSingleSample() << 16); + sample |= decoder.GetSingleSample(); + + if (rxBuffer.Full()) + { + this.Log(LogLevel.Noisy, "RX buffer full"); + if (rxOverflowIrqEnabled.Value) + { + this.Log(LogLevel.Debug, "I2S RX FIFO Overflow (count {0}).", rxBuffer.Count); + RxOverflowIRQ.Set(rxOverflowIrqEnabled.Value); + } + + // FIFO is full -- drop the sample + return; + } + + rxBuffer.Write(sample); + this.Log(LogLevel.Noisy, "Added sample. Level: {0}", rxBuffer.Count); + + if (rxBuffer.Count >= mappedRxTriggerLevel) + { + this.Log(LogLevel.Debug, "I2S RX FIFO Watermark (count {0} == {1}).", rxBuffer.Count, mappedRxTriggerLevel); + RxWatermarkIRQ.Set(rxWatermarkIrqEnabled.Value); } } private void CreateRegisters() { Registers.IrqState.Define(this) - .WithFlag(0, name: "TX_WATERMARK", valueProviderCallback: _ => { return txBuffer.count >= mappedTxTriggerLevel; }) - .WithFlag(1, name: "RX_WATERMARK", valueProviderCallback: _ => { return rxBuffer.count >= mappedRxTriggerLevel; }) + .WithFlag(0, name: "TX_WATERMARK", valueProviderCallback: _ => { return txBuffer.Count >= mappedTxTriggerLevel; }) + .WithFlag(1, name: "RX_WATERMARK", valueProviderCallback: _ => { return rxBuffer.Count >= mappedRxTriggerLevel; }) .WithFlag(2, name: "TX_EMPTY", valueProviderCallback: _ => { return txBuffer.Empty(); }) .WithFlag(3, name: "RX_OVERFLOW", valueProviderCallback: _ => { return rxBuffer.Full(); }); @@ -233,13 +270,13 @@ Registers.IrqTest.Define(this) .WithFlag(0, name: "TEST_TX_WATERMARK", - writeCallback: (_, __) => { Connections[(int)Events.TxWatermarkIRQ].Blink(); }) + writeCallback: (_, val) => { TxWatermarkIRQ.Set(val); }) .WithFlag(1, name: "TEST_RX_WATERMARK", - writeCallback: (_, __) => { Connections[(int)Events.RxWatermarkIRQ].Blink(); }) + writeCallback: (_, val) => { RxWatermarkIRQ.Set(val); }) .WithFlag(2, name: "TEST_TX_EMPTY", - writeCallback: (_, __) => { Connections[(int)Events.TxEmptyIRQ].Blink(); }) + writeCallback: (_, val) => { TxEmptyIRQ.Set(val); }) .WithFlag(3, name: "TEST_RX_OVERFLOW", - writeCallback: (_, __) => { Connections[(int)Events.RxOverflowIRQ].Blink(); }); + writeCallback: (_, val) => { RxOverflowIRQ.Set(val); }); Registers.Control.Define(this) .WithFlag(0, out txEnable, name: "TX", @@ -271,12 +308,30 @@ .WithReservedBits(4, 27); Registers.ReadData.Define32(this) - .WithValueField(0, 31, FieldMode.Read, name: "RDATA", - valueProviderCallback: _ => { return rxBuffer.Read(); }); + .WithValueField(0, 32, FieldMode.Read, name: "RDATA", + valueProviderCallback: _ => { + ulong sample = rxBuffer.Read(); + if (rxBuffer.Count < mappedRxTriggerLevel) { + RxWatermarkIRQ.Set(false); + this.Log(LogLevel.Noisy, "RX Watermark cleared"); + } + RxOverflowIRQ.Set(false); + this.Log(LogLevel.Noisy, "Removed sample. Level: {0}", rxBuffer.Count); + + return sample; + }); Registers.WriteData.Define32(this) - .WithValueField(0, 31, name: "WDATA", - writeCallback: (_, val) => { txBuffer.Write(val); }); + .WithValueField(0, 32, name: "WDATA", + writeCallback: (_, val) => { + txBuffer.Write(val); + if (txBuffer.Count > mappedTxTriggerLevel) { + TxWatermarkIRQ.Set(false); + this.Log(LogLevel.Noisy, "TX Watermark cleared"); + } + TxEmptyIRQ.Set(false); + this.Log(LogLevel.Noisy, "Added sample. Level: {0}", txBuffer.Count); + }); Registers.FifoControl.Define32(this) .WithFlag(0, name: "RXRST", writeCallback: (_, val) => { if (val) ResetRx(); }) @@ -316,10 +371,10 @@ Registers.FifoStatus.Define32(this) .WithValueField(0, 6, name: "TXLVL", - valueProviderCallback: _ => { return txBuffer.count; }) + valueProviderCallback: _ => { return txBuffer.Count; }) .WithReservedBits(7, 9) .WithValueField(16, 6, name: "RXLVL", - valueProviderCallback: _ => { return rxBuffer.count; }) + valueProviderCallback: _ => { return rxBuffer.Count; }) .WithReservedBits(22, 10); }