From 1a6a0e9b80d1ea600d2ab0f44568d6b2dab455bb Mon Sep 17 00:00:00 2001 From: Andrew Kay Date: Sat, 19 Feb 2022 10:04:17 -0600 Subject: [PATCH] Add SNOOPIE module --- interface2/firmware/include/coax.h | 4 + interface2/firmware/include/interface.h | 4 + interface2/firmware/src/coax.cpp | 37 ++++ interface2/firmware/src/interface.cpp | 37 ++++ interface2/fpga/rtl/coax_syn.prj | 1 + interface2/fpga/rtl/control.v | 76 ++++++++- interface2/fpga/rtl/snoopie.v | 68 ++++++++ interface2/fpga/rtl/top.v | 24 ++- interface2/fpga/tests/Makefile | 1 + interface2/fpga/tests/regression_johann_tb.v | 169 +++++++++++++++++++ pycoax/coax/serial_interface.py | 40 +++++ pycoax/tests/test_serial_interface.py | 2 + 12 files changed, 461 insertions(+), 2 deletions(-) create mode 100644 interface2/fpga/rtl/snoopie.v create mode 100644 interface2/fpga/tests/regression_johann_tb.v diff --git a/interface2/firmware/include/coax.h b/interface2/firmware/include/coax.h index f7c17a8..565f1a7 100644 --- a/interface2/firmware/include/coax.h +++ b/interface2/firmware/include/coax.h @@ -61,6 +61,8 @@ public: void handleInterrupt(); + int snoopie(uint16_t *buffer, size_t bufferSize, uint8_t *writeIndex); + private: SPICoaxTransceiver &_spiCoaxTransceiver; CoaxProtocol _txProtocol; @@ -129,6 +131,8 @@ public: return readRegister(COAX_REGISTER_STATUS) & COAX_REGISTER_STATUS_RX_ACTIVE; }; + int snoopie(uint16_t *buffer, size_t bufferSize, uint8_t *writeIndex); + private: void spiTransfer(const uint8_t *transmitBuffer, uint8_t *receiveBuffer, size_t count); }; diff --git a/interface2/firmware/include/interface.h b/interface2/firmware/include/interface.h index 53c27cc..cdf2858 100644 --- a/interface2/firmware/include/interface.h +++ b/interface2/firmware/include/interface.h @@ -25,6 +25,7 @@ #define COMMAND_INFO 0xf0 #define COMMAND_TEST 0xf1 #define COMMAND_DFU 0xf2 +#define COMMAND_SNOOPIE_REPORT 0xf3 #define INFO_SUPPORTED_QUERIES 0x01 #define INFO_HARDWARE_TYPE 0x02 @@ -59,4 +60,7 @@ private: void handleInfo(uint8_t *buffer, size_t bufferCount); void handleTest(uint8_t *buffer, size_t bufferCount); void handleDFU(uint8_t *buffer, size_t bufferCount); + + void snoopieTeamAway(); + void handleSnoopieReport(uint8_t *buffer, size_t bufferCount); }; diff --git a/interface2/firmware/src/coax.cpp b/interface2/firmware/src/coax.cpp index c13215b..a5f778e 100644 --- a/interface2/firmware/src/coax.cpp +++ b/interface2/firmware/src/coax.cpp @@ -246,10 +246,16 @@ void Coax::handleInterrupt() } } +int Coax::snoopie(uint16_t *buffer, size_t bufferSize, uint8_t *writeIndex) +{ + return _spiCoaxTransceiver.snoopie(buffer, bufferSize, writeIndex); +} + #define COAX_COMMAND_READ_REGISTER 0x2 #define COAX_COMMAND_WRITE_REGISTER 0x3 #define COAX_COMMAND_TX 0x4 #define COAX_COMMAND_RX 0x5 +#define COAX_COMMAND_SNOOPIE 0x6 #define COAX_COMMAND_RESET 0xff #define NOP asm volatile("nop\n\t") @@ -511,6 +517,37 @@ void SPICoaxTransceiver::setRXParity(CoaxParity parity) writeRegister(COAX_REGISTER_CONTROL, parity == CoaxParity::Even ? COAX_REGISTER_CONTROL_RX_PARITY : 0, COAX_REGISTER_CONTROL_RX_PARITY); } +int SPICoaxTransceiver::snoopie(uint16_t *buffer, size_t bufferSize, uint8_t *writeIndex) +{ + uint8_t transmitBuffer[2] = { COAX_COMMAND_SNOOPIE }; + uint8_t receiveBuffer[2]; + + ATOMIC_BLOCK_START; + LL_GPIO_ResetOutputPin(ICE40_CS_GPIO_Port, ICE40_CS_Pin); + + spiTransfer(transmitBuffer, NULL, 1); + + transmitBuffer[0] = 0x00; + transmitBuffer[1] = 0x00; + + spiTransfer(transmitBuffer, receiveBuffer, 1); + + *writeIndex = receiveBuffer[0]; + + for (size_t index = 0; index < bufferSize; index++) { + spiTransfer(transmitBuffer, receiveBuffer, 2); + + uint16_t value = (receiveBuffer[0] << 8) | receiveBuffer[1]; + + buffer[index] = value; + } + + LL_GPIO_SetOutputPin(ICE40_CS_GPIO_Port, ICE40_CS_Pin); + ATOMIC_BLOCK_END; + + return 0; +} + void SPICoaxTransceiver::spiTransfer(const uint8_t *transmitBuffer, uint8_t *receiveBuffer, size_t count) { diff --git a/interface2/firmware/src/interface.cpp b/interface2/firmware/src/interface.cpp index 926b74f..00ca403 100644 --- a/interface2/firmware/src/interface.cpp +++ b/interface2/firmware/src/interface.cpp @@ -84,6 +84,8 @@ void Interface::handleMessage(uint8_t *buffer, size_t bufferCount) handleTest(buffer + 3, count - 1); } else if (command == COMMAND_DFU) { handleDFU(buffer + 3, count - 1); + } else if (command == COMMAND_SNOOPIE_REPORT) { + handleSnoopieReport(buffer + 3, count - 1); } else { sendErrorMessage(ERROR_UNKNOWN_COMMAND, NULL); } @@ -178,6 +180,10 @@ void Interface::handleTransmitReceive(uint8_t *buffer, size_t bufferCount) if (receiveCount < 0) { Debug::trap(403, "error = %d", receiveCount); + // vvv + snoopieTeamAway(); + // ^^^ + _indicators.error(); // Convert the error to legacy interface error for compatability. @@ -292,3 +298,34 @@ void Interface::handleDFU(uint8_t *buffer, size_t bufferCount) resetToBootloader(); } + +uint16_t snoopieBuffer[256]; +uint8_t snoopieWriteIndex; + +void Interface::snoopieTeamAway() +{ + printf("\r\n\r\nSNOOPIE +++\r\n"); + + _coax.snoopie((uint16_t *) &snoopieBuffer, 256, &snoopieWriteIndex); + + printf("writeIndex = %d\r\n", snoopieWriteIndex); + + for (size_t index = 0; index < 256; index++) { + uint16_t counter = (snoopieBuffer[index] & 0xfff0) >> 4; + uint8_t probes = snoopieBuffer[index] & 0x0f; + + printf("%d %d\r\n", counter, probes); + } + + printf("SNOOPIE ---\r\n"); +} + +void Interface::handleSnoopieReport(uint8_t *buffer, size_t bufferCount) +{ + buffer[0] = 0x01; + buffer[1] = snoopieWriteIndex; + + memcpy(buffer + 2, &snoopieBuffer, 256 * sizeof(uint16_t)); + + MessageSender::send(buffer, 2 + (256 * sizeof(uint16_t))); +} diff --git a/interface2/fpga/rtl/coax_syn.prj b/interface2/fpga/rtl/coax_syn.prj index 3fc1a3e..da9c109 100644 --- a/interface2/fpga/rtl/coax_syn.prj +++ b/interface2/fpga/rtl/coax_syn.prj @@ -19,6 +19,7 @@ add_file -verilog -lib work "third_party/ram_sdp.v" add_file -verilog -lib work "strobe_cdc.v" add_file -verilog -lib work "top.v" add_file -verilog -lib work "dual_clock_spi_device.v" +add_file -verilog -lib work "snoopie.v" add_file -constraint -lib work "clocks.sdc" #implementation: "coax_Implmnt" impl -add coax_Implmnt -type fpga diff --git a/interface2/fpga/rtl/control.v b/interface2/fpga/rtl/control.v index 2dccfdf..8d36dba 100644 --- a/interface2/fpga/rtl/control.v +++ b/interface2/fpga/rtl/control.v @@ -47,7 +47,12 @@ module control ( output reg rx_read_strobe, input rx_empty, output rx_protocol, - output rx_parity + output rx_parity, + + output reg snoopie_enable, + input [15:0] snoopie_read_data, + output reg snoopie_read_strobe, + input [7:0] snoopie_write_address ); parameter DEFAULT_CONTROL_REGISTER = 8'b01001000; @@ -64,6 +69,11 @@ module control ( localparam STATE_RX_3 = 10; localparam STATE_RX_4 = 11; localparam STATE_RESET = 12; + localparam STATE_SNOOPIE_1 = 13; + localparam STATE_SNOOPIE_2 = 14; + localparam STATE_SNOOPIE_3 = 15; + localparam STATE_SNOOPIE_4 = 16; + localparam STATE_SNOOPIE_5 = 17; reg [7:0] state = STATE_IDLE; reg [7:0] next_state; @@ -94,6 +104,9 @@ module control ( reg [15:0] rx_buffer; reg [15:0] next_rx_buffer; + reg next_snoopie_enable; + reg next_snoopie_read_strobe; + reg [1:0] spi_cs_n_d; always @(posedge clk) @@ -124,6 +137,9 @@ module control ( next_rx_read_strobe = 0; next_rx_buffer = rx_buffer; + next_snoopie_enable = 1; + next_snoopie_read_strobe = 0; + case (state) STATE_IDLE: begin @@ -136,6 +152,7 @@ module control ( 4'h3: next_state = STATE_WRITE_REGISTER_1; 4'h4: next_state = STATE_TX_1; 4'h5: next_state = STATE_RX_1; + 4'h6: next_state = STATE_SNOOPIE_1; 4'hf: next_state = STATE_RESET; endcase end @@ -280,6 +297,57 @@ module control ( next_state = STATE_IDLE; end + + STATE_SNOOPIE_1: + begin + next_snoopie_enable = 0; + + next_spi_tx_data = snoopie_write_address; + next_spi_tx_strobe = 1; + + next_state = STATE_SNOOPIE_2; + end + + STATE_SNOOPIE_2: + begin + next_snoopie_enable = 0; + + if (spi_rx_strobe) + next_state = STATE_SNOOPIE_3; + end + + STATE_SNOOPIE_3: + begin + next_snoopie_enable = 0; + + next_spi_tx_data = snoopie_read_data[15:8]; + next_spi_tx_strobe = 1; + + next_state = STATE_SNOOPIE_4; + end + + STATE_SNOOPIE_4: + begin + next_snoopie_enable = 0; + + if (spi_rx_strobe) + begin + next_snoopie_read_strobe = 1; + + next_spi_tx_data = snoopie_read_data[7:0]; + next_spi_tx_strobe = 1; + + next_state = STATE_SNOOPIE_5; + end + end + + STATE_SNOOPIE_5: + begin + next_snoopie_enable = 0; + + if (spi_rx_strobe) + next_state = STATE_SNOOPIE_3; + end endcase if (spi_cs_n_d[1]) @@ -317,6 +385,9 @@ module control ( rx_read_strobe <= next_rx_read_strobe; rx_buffer <= next_rx_buffer; + snoopie_enable <= next_snoopie_enable; + snoopie_read_strobe <= next_snoopie_read_strobe; + if (reset) begin state <= STATE_IDLE; @@ -337,6 +408,9 @@ module control ( rx_reset <= 0; rx_read_strobe <= 0; rx_buffer <= 0; + + snoopie_enable <= 1; + snoopie_read_strobe <= 0; end previous_tx_active <= tx_active; diff --git a/interface2/fpga/rtl/snoopie.v b/interface2/fpga/rtl/snoopie.v new file mode 100644 index 0000000..ab2d20d --- /dev/null +++ b/interface2/fpga/rtl/snoopie.v @@ -0,0 +1,68 @@ +`default_nettype none + +module snoopie ( + input clk, + input enable, + + input [3:0] probes, + + output [7:0] xxx_write_address, + output [15:0] read_data, + input read_strobe +); +reg previous_enable = 0; + reg [11:0] counter; + + reg [3:0] previous_probes; + + reg [7:0] write_address; + reg [15:0] write_data; + reg write_enable; + + reg [7:0] read_address; + + ram_sdp #( + .AWIDTH(8), + .DWIDTH(16) + ) ram ( + .clk(clk), + + .wr_addr(write_address), + .wr_data(write_data), + .wr_ena(write_enable), + + .rd_addr(read_address), + .rd_data(read_data), + .rd_ena(1'b1) + ); + + always @(posedge clk) + begin + counter <= counter + 1; + + // Writer... + if (enable && !previous_enable) + write_address <= 0; + else if (write_enable) + write_address <= write_address + 1; + + write_enable <= 0; + + if (enable && probes != previous_probes) + begin + write_data <= { counter[11:0], probes[3:0] }; + write_enable <= 1; + end + + // Reader... + if (enable && !previous_enable) + read_address <= 0; + else if (read_strobe) + read_address <= read_address + 1; + + previous_probes <= probes; + previous_enable <= enable; + end + + assign xxx_write_address = write_address; +endmodule diff --git a/interface2/fpga/rtl/top.v b/interface2/fpga/rtl/top.v index 2eed75e..c5d19fe 100644 --- a/interface2/fpga/rtl/top.v +++ b/interface2/fpga/rtl/top.v @@ -168,6 +168,23 @@ module top ( .rx_debug(rx_debug) ); + wire snoopie_enable; + wire [15:0] snoopie_read_data; + wire snoopie_read_strobe; + wire [7:0] snoopie_write_address; + + snoopie snoopie ( + .clk(clk), + .enable(snoopie_enable), + + .probes({ internal_rx, rx_error, 2'b00 }), + + .read_data(snoopie_read_data), + .read_strobe(snoopie_read_strobe), + + .xxx_write_address(snoopie_write_address) + ); + control control ( .clk(clk), .reset(reset), @@ -198,7 +215,12 @@ module top ( .rx_read_strobe(rx_read_strobe), .rx_empty(rx_empty), .rx_protocol(rx_protocol), - .rx_parity(rx_parity) + .rx_parity(rx_parity), + + .snoopie_enable(snoopie_enable), + .snoopie_read_data(snoopie_read_data), + .snoopie_read_strobe(snoopie_read_strobe), + .snoopie_write_address(snoopie_write_address) ); assign irq = rx_active || rx_error; diff --git a/interface2/fpga/tests/Makefile b/interface2/fpga/tests/Makefile index 6fe974d..aebc951 100644 --- a/interface2/fpga/tests/Makefile +++ b/interface2/fpga/tests/Makefile @@ -17,6 +17,7 @@ coax_tx_rx_frontend_tb: coax_tx_rx_frontend_tb.v $(RTL)/coax_tx_rx_frontend.v $( control_tb: control_tb.v $(RTL)/control.v $(RTL)/coax_buffered_tx.v $(RTL)/coax_tx.v $(RTL)/coax_tx_bit_timer.v $(RTL)/coax_buffer.v $(RTL)/third_party/*.v tx_rx_loopback_tb: tx_rx_loopback_tb.v $(RTL)/coax_tx.v $(RTL)/coax_tx_bit_timer.v $(RTL)/coax_rx.v $(RTL)/coax_rx_ss_detector.v regression_memorex_tb: regression_memorex_tb.v $(RTL)/coax_rx.v $(RTL)/coax_rx_ss_detector.v +regression_johann_tb: regression_johann_tb.v $(RTL)/coax_rx.v $(RTL)/coax_rx_ss_detector.v test: $(TESTS) ./run_tests.sh diff --git a/interface2/fpga/tests/regression_johann_tb.v b/interface2/fpga/tests/regression_johann_tb.v new file mode 100644 index 0000000..f4df872 --- /dev/null +++ b/interface2/fpga/tests/regression_johann_tb.v @@ -0,0 +1,169 @@ +`default_nettype none + +`include "assert.v" + +module regression_johann_tb; + reg clk = 0; + + initial + begin + forever + begin + #1 clk <= ~clk; + end + end + + reg probe_rx = 0; + reg probe_error = 0; + + reg reset = 0; + + coax_rx #( + .CLOCKS_PER_BIT(16) + ) dut ( + .clk(clk), + .reset(reset), + .rx(probe_rx), + .protocol(1'b0), + .parity(1'b1) + ); + + initial + begin + $dumpfile("regression_johann_tb.vcd"); + $dumpvars(0, regression_johann_tb); + + test_1; + + $finish; + end + + task test_1; + begin + $display("START: test_1"); + + `assert_equal(dut.state, dut.STATE_IDLE, "state should be STATE_IDLE"); + + // ... | vcd2v -s 2 -t nnnn probe_rx=top.internal_rx probe_error=top.rx_error + // + // vvv +#200; +probe_rx = 1; +probe_error = 0; +#18; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#16; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#16; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#16; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#16; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#16; +probe_rx = 0; +probe_error = 0; +#48; +probe_rx = 1; +probe_error = 0; +#48; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#32; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#16; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#16; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#16; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#16; +probe_rx = 0; +probe_error = 0; +#16; +probe_rx = 1; +probe_error = 0; +#16; +probe_rx = 0; +probe_error = 0; +#32; +probe_rx = 1; +probe_error = 0; +#32; +probe_rx = 0; +probe_error = 0; +#32; +probe_rx = 1; +probe_error = 0; +#32; +probe_rx = 0; +probe_error = 0; +#32; +probe_rx = 1; +probe_error = 0; +#4; +probe_rx = 1; +probe_error = 1; +#28; +probe_rx = 0; +probe_error = 1; +#16; +probe_rx = 1; +probe_error = 1; +#82; +probe_rx = 0; +probe_error = 1; +#258; +probe_rx = 0; +probe_error = 0; + // ^^^ + + #64; + + `assert_equal(dut.state, dut.STATE_IDLE, "state should be STATE_IDLE"); + + `assert_equal(dut.data, 10'b0000001010, "data not correct") + + $display("END: test_1"); + end + endtask +endmodule diff --git a/pycoax/coax/serial_interface.py b/pycoax/coax/serial_interface.py index 105e315..17cb6aa 100644 --- a/pycoax/coax/serial_interface.py +++ b/pycoax/coax/serial_interface.py @@ -80,6 +80,42 @@ class SerialInterface(Interface): if message[0] != 0x01: raise _convert_error(message) + def snoopie_report(self): + message = bytes([0xf3]) + + self._write_message(message) + + message = self._read_message() + + if message[0] != 0x01: + raise Exception('Uh, some sort of problem with the SNOOPIE report') + + if len(message) != 1 + 1 + (2 * 256): + raise Exception('Uh, length of SNOOPIE report is not correct') + + write_index = message[1] + buffer = message[2:] + + print() + print() + + print('* ' * 40) + + print(f'write_index = {write_index}') + + for i in range(256): + x = buffer[i * 2] | (buffer[(i * 2) + 1] << 8) + + counter = (x & 0xfff0) >> 4 + probes = x & 0x0f + + print(f'{counter} {probes}') + + print('* ' * 40) + + print() + print() + def _get_features(self): """Get interface features.""" message = bytes([0xf0, 0x07]) @@ -125,6 +161,10 @@ class SerialInterface(Interface): if not isinstance(error, (ReceiveError, ReceiveTimeout)): raise error + # vvv + self.snoopie_report() + # ^^^ + response = error responses.append(response) diff --git a/pycoax/tests/test_serial_interface.py b/pycoax/tests/test_serial_interface.py index 278c3f9..a92d205 100644 --- a/pycoax/tests/test_serial_interface.py +++ b/pycoax/tests/test_serial_interface.py @@ -91,6 +91,8 @@ class SerialInterfaceTransmitReceiveTestCase(unittest.TestCase): self.interface._write_message = Mock(wraps=self.interface._write_message) self.interface._read_message = Mock() + self.interface.snoopie_report = Mock() + def test_words_frame(self): # Arrange self.interface._read_message.return_value=bytes.fromhex('01 00 00')