changeset 28:0f74428c177c

fpga/sniffer-pps: first version
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 29 Aug 2023 20:05:23 +0000
parents 990ecafdddb4
children 8be0b96b7c8d
files fpga/sniffer-pps/Makefile fpga/sniffer-pps/clk_edge.v fpga/sniffer-pps/pps_catcher.v fpga/sniffer-pps/reset_detect.v fpga/sniffer-pps/sniff_rx.v fpga/sniffer-pps/sync_inputs.v fpga/sniffer-pps/top.v fpga/sniffer-pps/uart_tx.v
diffstat 8 files changed, 444 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fpga/sniffer-pps/Makefile	Tue Aug 29 20:05:23 2023 +0000
@@ -0,0 +1,22 @@
+VSRC=	clk_edge.v pps_catcher.v reset_detect.v sniff_rx.v sync_inputs.v top.v \
+	uart_tx.v
+PCF=	../common/icestick.pcf
+PROJ=	fpga
+
+all:	${PROJ}.bin timing.rpt
+
+${PROJ}.json:	${VSRC}
+	../tools/yosys-wrap top $@ ${VSRC} | tee synthesis.rpt
+
+${PROJ}.asc:	${PROJ}.json ${PCF}
+	nextpnr-ice40 --hx1k --package tq144 --asc $@ --pcf ${PCF} \
+		--json ${PROJ}.json -l pnr.rpt
+
+${PROJ}.bin:	${PROJ}.asc
+	icepack $< $@
+
+timing.rpt:	${PROJ}.asc
+	icetime -d hx1k -mtr $@ $<
+
+clean:
+	rm -f *.json *.asc *.bin *.rpt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fpga/sniffer-pps/clk_edge.v	Tue Aug 29 20:05:23 2023 +0000
@@ -0,0 +1,19 @@
+/*
+ * This Verilog module captures the logic that detects rising edges of SIM_CLK
+ * for the purpose of counting them.
+ */
+
+module clk_edge (IntClk, SIM_CLK_sync, SIM_CLK_edge);
+
+input IntClk;
+input SIM_CLK_sync;
+output SIM_CLK_edge;
+
+reg prev_state;
+
+always @(posedge IntClk)
+	prev_state <= SIM_CLK_sync;
+
+assign SIM_CLK_edge = SIM_CLK_sync && !prev_state;
+
+endmodule
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fpga/sniffer-pps/pps_catcher.v	Tue Aug 29 20:05:23 2023 +0000
@@ -0,0 +1,165 @@
+/*
+ * This Verilog module is our PPS catcher.  It is a state machine that analyzes
+ * the stream of Rx characters from the primary sniffer and detects the
+ * positions of these two special chars:
+ *
+ * - the PPS1 byte of the card's PPS response;
+ * - the PCK byte concluding that PPS response *if* PPS1 indicated a speed
+ *   enhancement mode which we support.
+ */
+
+module pps_catcher (IntClk, SIM_RST_sync, Rx_strobe, Rx_char,
+		    pos_PPS_resp_PPS1, pos_PPS_resp_PCK);
+
+input IntClk;
+input SIM_RST_sync;
+input Rx_strobe;
+input [7:0] Rx_char;
+
+output pos_PPS_resp_PPS1, pos_PPS_resp_PCK;
+
+/* state machine */
+
+localparam
+	INITIAL		= 5'h00,
+	ATR_T0		= 5'h01,
+	ATR_TAn		= 5'h02,
+	ATR_TBn		= 5'h03,
+	ATR_TCn		= 5'h04,
+	ATR_TDn		= 5'h05,
+	ATR_HIST	= 5'h06,
+	ATR_TCK		= 5'h07,
+	READY_FOR_PPS	= 5'h08,
+	REQ_PPS0	= 5'h09,
+	REQ_PPS1	= 5'h0A,
+	REQ_PPS2	= 5'h0B,
+	REQ_PPS3	= 5'h0C,
+	REQ_PCK		= 5'h0F,
+	READY_FOR_RESP	= 5'h10,
+	RESP_PPS0	= 5'h11,
+	RESP_PPS1	= 5'h12,
+	RESP_PPS2	= 5'h13,
+	RESP_PPS3	= 5'h14,
+	RESP_PCK	= 5'h17,
+	DONE		= 5'h1F
+	;
+
+reg [4:0] state;
+reg [3:0] Y, K;
+reg have_TCK;
+
+always @(posedge IntClk)
+	if (!SIM_RST_sync)
+	    begin
+		state <= INITIAL;
+		have_TCK <= 1'b0;
+	    end
+	else case (state)
+		INITIAL:
+			if (Rx_strobe)
+			begin
+				if (Rx_char == 8'h3B)
+					state <= ATR_T0;
+				else
+					state <= DONE;
+			end
+		ATR_T0:
+			if (Rx_strobe)
+			begin
+				Y <= Rx_char[7:4];
+				K <= Rx_char[3:0];
+				state <= ATR_TAn;
+			end
+		ATR_TAn:
+			if (!Y[0] || Rx_strobe)
+				state <= ATR_TBn;
+		ATR_TBn:
+			if (!Y[1] || Rx_strobe)
+				state <= ATR_TCn;
+		ATR_TCn:
+			if (!Y[2] || Rx_strobe)
+				state <= ATR_TDn;
+		ATR_TDn:
+			if (!Y[3])
+				state <= ATR_HIST;
+			else if (Rx_strobe)
+			begin
+				Y <= Rx_char[7:4];
+				if (Rx_char[3:0] != 4'h0)
+					have_TCK <= 1'b1;
+				state <= ATR_TAn;
+			end
+		ATR_HIST:
+			if (K == 4'd0)
+				state <= ATR_TCK;
+			else if (Rx_strobe)
+				K <= K - 4'd1;
+		ATR_TCK:
+			if (!have_TCK || Rx_strobe)
+				state <= READY_FOR_PPS;
+		READY_FOR_PPS:
+			if (Rx_strobe)
+			begin
+				if (Rx_char == 8'hFF)
+					state <= REQ_PPS0;
+				else
+					state <= DONE;
+			end
+		REQ_PPS0:
+			if (Rx_strobe)
+			begin
+				Y <= Rx_char[7:4];
+				state <= REQ_PPS1;
+			end
+		REQ_PPS1:
+			if (!Y[0] || Rx_strobe)
+				state <= REQ_PPS2;
+		REQ_PPS2:
+			if (!Y[1] || Rx_strobe)
+				state <= REQ_PPS3;
+		REQ_PPS3:
+			if (!Y[2] || Rx_strobe)
+				state <= REQ_PCK;
+		REQ_PCK:
+			if (Rx_strobe)
+				state <= READY_FOR_RESP;
+		READY_FOR_RESP:
+			if (Rx_strobe)
+			begin
+				if (Rx_char == 8'hFF)
+					state <= RESP_PPS0;
+				else
+					state <= DONE;
+			end
+		RESP_PPS0:
+			if (Rx_strobe)
+			begin
+				Y <= Rx_char[7:4];
+				if (Rx_char[4])
+					state <= RESP_PPS1;
+				else
+					state <= DONE;
+			end
+		RESP_PPS1:
+			if (Rx_strobe)
+			begin
+				if (Rx_char[7:2] == 6'b100101)
+					state <= RESP_PPS2;
+				else
+					state <= DONE;
+			end
+		RESP_PPS2:
+			if (!Y[1] || Rx_strobe)
+				state <= RESP_PPS3;
+		RESP_PPS3:
+			if (!Y[2] || Rx_strobe)
+				state <= RESP_PCK;
+		RESP_PCK:
+			if (Rx_strobe)
+				state <= DONE;
+	endcase
+
+assign pos_PPS_resp_PPS1 = (state == RESP_PPS1);
+assign pos_PPS_resp_PCK = (state == RESP_PCK);
+
+endmodule
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fpga/sniffer-pps/reset_detect.v	Tue Aug 29 20:05:23 2023 +0000
@@ -0,0 +1,19 @@
+/*
+ * This Verilog module captures the logic that detects SIM_RST transitions
+ * in either direction.
+ */
+
+module reset_detect (IntClk, SIM_RST_sync, SIM_RST_toggle);
+
+input IntClk;
+input SIM_RST_sync;
+output SIM_RST_toggle;
+
+reg prev_state;
+
+always @(posedge IntClk)
+	prev_state <= SIM_RST_sync;
+
+assign SIM_RST_toggle = SIM_RST_sync != prev_state;
+
+endmodule
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fpga/sniffer-pps/sniff_rx.v	Tue Aug 29 20:05:23 2023 +0000
@@ -0,0 +1,62 @@
+/*
+ * This Verilog module captures the ISO 7816-3 character sniffing receiver.
+ */
+
+module sniff_rx (IntClk, SIM_RST_sync, SIM_CLK_sync, SIM_IO_sync,
+		 Rx_strobe, Rx_error, Rx_char, Rx_start_bit, Rx_parity_bit);
+
+input IntClk;
+input SIM_RST_sync, SIM_CLK_sync, SIM_IO_sync;
+output Rx_strobe, Rx_error;
+output [7:0] Rx_char;
+output Rx_start_bit, Rx_parity_bit;
+
+wire SIM_CLK_edge;
+
+clk_edge clk_edge (IntClk, SIM_CLK_sync, SIM_CLK_edge);
+
+wire [9:0] etu_0p5, etu_1p0, etu_1p5;
+
+/* Fi/Di=372 only for now */
+assign etu_0p5 = 10'd185;
+assign etu_1p0 = 10'd371;
+assign etu_1p5 = 10'd557;
+
+reg rx_active;
+reg [9:0] clk_count;
+reg [3:0] bit_count;
+reg [9:0] shift_reg;
+
+always @(posedge IntClk)
+	if (!SIM_RST_sync)
+		rx_active <= 1'b0;
+	else if (!rx_active && !SIM_IO_sync)
+	    begin
+		rx_active <= 1'b1;
+		clk_count <= etu_0p5;
+		bit_count <= 4'd0;
+	    end
+	else if (rx_active && SIM_CLK_edge)
+	    begin
+		if (clk_count != 10'd0)
+			clk_count <= clk_count - 10'd1;
+		else begin
+			shift_reg <= {SIM_IO_sync,shift_reg[9:1]};
+			bit_count <= bit_count + 4'd1;
+			if (bit_count == 4'd9)
+				clk_count <= etu_1p5;
+			else
+				clk_count <= etu_1p0;
+			if (bit_count == 4'd10)
+				rx_active <= 1'b0;
+		end
+	end
+
+assign Rx_strobe = rx_active && SIM_CLK_edge && clk_count == 10'd0 &&
+		   bit_count == 4'd10;
+assign Rx_error = Rx_strobe && !SIM_IO_sync;
+assign Rx_char = shift_reg[8:1];
+assign Rx_start_bit = shift_reg[0];
+assign Rx_parity_bit = shift_reg[9];
+
+endmodule
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fpga/sniffer-pps/sync_inputs.v	Tue Aug 29 20:05:23 2023 +0000
@@ -0,0 +1,35 @@
+/*
+ * This Verilog module captures the input synchronizer logic: passing all 3
+ * SIM sniffer inputs through double-DFF synchronizers to bring them into
+ * our internal clock domain.
+ */
+
+module sync_inputs (IntClk, SIM_RST_in, SIM_RST_sync, SIM_CLK_in, SIM_CLK_sync,
+		    SIM_IO_in, SIM_IO_sync);
+
+input IntClk;
+input SIM_RST_in, SIM_CLK_in, SIM_IO_in;
+output SIM_RST_sync, SIM_CLK_sync, SIM_IO_sync;
+reg SIM_RST_sync, SIM_CLK_sync, SIM_IO_sync;
+
+reg SIM_RST_sync1, SIM_CLK_sync1, SIM_IO_sync1;
+
+always @(posedge IntClk)
+	SIM_RST_sync1 <= SIM_RST_in;
+
+always @(posedge IntClk)
+	SIM_RST_sync <= SIM_RST_sync1;
+
+always @(posedge IntClk)
+	SIM_CLK_sync1 <= SIM_CLK_in;
+
+always @(posedge IntClk)
+	SIM_CLK_sync <= SIM_CLK_sync1;
+
+always @(posedge IntClk)
+	SIM_IO_sync1 <= SIM_IO_in;
+
+always @(posedge IntClk)
+	SIM_IO_sync <= SIM_IO_sync1;
+
+endmodule
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fpga/sniffer-pps/top.v	Tue Aug 29 20:05:23 2023 +0000
@@ -0,0 +1,77 @@
+module top (CLK12, LED1, LED2, LED3, LED4, LED5, UART_TxD, UART_RxD, UART_RTS,
+	    UART_CTS, UART_DTR, UART_DSR, UART_DCD, SIM_RST_in, SIM_CLK_in,
+	    SIM_IO_in, SIM_IO_out);
+
+input CLK12;
+output LED1, LED2, LED3, LED4, LED5;
+
+input UART_TxD, UART_RTS, UART_DTR;
+output UART_RxD, UART_CTS, UART_DSR, UART_DCD;
+
+input SIM_RST_in, SIM_CLK_in, SIM_IO_in;
+output SIM_IO_out;
+
+/* input synchronizers */
+
+wire SIM_RST_sync, SIM_CLK_sync, SIM_IO_sync;
+
+sync_inputs sync (CLK12, SIM_RST_in, SIM_RST_sync, SIM_CLK_in, SIM_CLK_sync,
+		  SIM_IO_in, SIM_IO_sync);
+
+/* character receiver */
+
+wire Rx_strobe, Rx_error;
+wire [7:0] Rx_char;
+wire Rx_start_bit, Rx_parity_bit;
+
+sniff_rx sniff_rx (CLK12, SIM_RST_sync, SIM_CLK_sync, SIM_IO_sync,
+		   Rx_strobe, Rx_error, Rx_char, Rx_start_bit, Rx_parity_bit);
+
+/* PPS catcher */
+
+wire pos_PPS_resp_PPS1, pos_PPS_resp_PCK;
+
+pps_catcher pps (CLK12, SIM_RST_sync, Rx_strobe, Rx_char,
+		 pos_PPS_resp_PPS1, pos_PPS_resp_PCK);
+
+/* explicit detection of RST transitions */
+
+wire SIM_RST_toggle;
+
+reset_detect reset_detect (CLK12, SIM_RST_sync, SIM_RST_toggle);
+
+/* output to the host */
+
+wire Tx_trigger;
+wire [15:0] Tx_data;
+
+assign Tx_trigger = Rx_strobe | SIM_RST_toggle;
+assign Tx_data = {SIM_RST_toggle,SIM_RST_sync,1'b0,
+		  pos_PPS_resp_PCK,pos_PPS_resp_PPS1,
+		  Rx_error,Rx_start_bit,Rx_parity_bit,Rx_char};
+
+uart_tx uart_tx (CLK12, Tx_trigger, Tx_data, UART_RxD);
+
+/* UART modem control outputs: unused */
+
+assign UART_CTS = 1'b1;
+assign UART_DSR = 1'b0;
+assign UART_DCD = 1'b0;
+
+/* board LEDs */
+
+assign LED1 = 1'b1;
+assign LED2 = 1'b0;
+assign LED3 = 1'b1;
+assign LED4 = 1'b0;
+assign LED5 = SIM_RST;
+
+/* SIM_IO_out dummy: if someone mistakenly connects an Icestick board with
+ * this FPGA image in it to a cardem pod instead of the sniffing one,
+ * we ensure that the 74LVC1G07 OD buffer remains off by feeding logic HIGH
+ * to this buffer.
+ */
+
+assign SIM_IO_out = 1'b1;
+
+endmodule
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fpga/sniffer-pps/uart_tx.v	Tue Aug 29 20:05:23 2023 +0000
@@ -0,0 +1,45 @@
+/*
+ * This Verilog module captures the UART output logic.
+ */
+
+module uart_tx (IntClk, Tx_trigger, Tx_data, UART_out);
+
+input IntClk;
+input Tx_trigger;
+input [15:0] Tx_data;
+output UART_out;
+reg UART_out;
+
+reg tx_active;
+reg [1:0] clk_div;
+reg [4:0] bit_count;
+reg [17:0] shift_reg;
+
+initial begin
+	tx_active = 1'b0;
+	UART_out = 1'b1;
+end
+
+always @(posedge IntClk)
+	if (!tx_active && Tx_trigger)
+	    begin
+		tx_active <= 1'b1;
+		UART_out <= 1'b0;
+		clk_div <= 2'd0;
+		shift_reg <= {Tx_data[15:8],2'b01,Tx_data[7:0]};
+		bit_count <= 5'd0;
+	    end
+	else if (tx_active)
+	    begin
+		clk_div <= clk_div + 2'd1;
+		if (clk_div == 2'd3)
+		    begin
+			UART_out <= shift_reg[0];
+			shift_reg <= {1,shift_reg[17:1]};
+			bit_count <= bit_count + 5'd1;
+			if (bit_count == 5'd19)
+				tx_active <= 1'b0;
+		end
+	end
+
+endmodule