FreeCalypso > hg > fc-sim-sniff
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