diff --git a/uart_rx_shifter.sv b/uart_rx_shifter.sv new file mode 100644 index 0000000..21a0afd --- /dev/null +++ b/uart_rx_shifter.sv @@ -0,0 +1,105 @@ +//------------------------------------------------------------------------------ +// uart_rx_shifter.sv +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// UART-like shifter for simple synchronous messaging inside the FPGA or between FPGAs +// See also `uart_tx_shifter.sv` for TX part +// +// TX and RX parts should share one clock source +// Capable of continious stream transfer when tx_start is held constant 1'b1 +// Any reasonable start bit count,data bit count, stop bit count +// + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +uart_rx_shifter #( + .START_BITS( 1 ), + .DATA_BITS( 8 ), + .STOP_BITS( 2 ), + .SYNCHRONIZE_RXD( 0 ) // 0 - disabled; 1 - enabled +) rx1 ( + .clk( clk ), + .nrst( 1'b1 ), + .rx_data( ), + .rx_valid( ), + .rxd( ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + + +module uart_rx_shifter #( + + bit [7:0] START_BITS = 1, // must be >=1 + bit [7:0] DATA_BITS = 4, // must be >=1 + bit [7:0] STOP_BITS = 2, // must be >=1 + + bit SYNCHRONIZE_RXD = 0 // its better to synchronize when rxd input + // is actually an FPGA pin +)( + input clk, // transmitter and receiver should use + input nrst, // the same clock + + output logic [DATA_BITS-1:0] rx_data = '0, // output data + output logic rx_valid = '0, // read strobe + + input rxd +); + +localparam TOTAL_BITS = START_BITS + DATA_BITS + STOP_BITS; + +logic [TOTAL_BITS-1:0] rx_data_buf = '1; + +logic rxd_sync; +delay #( + .LENGTH( 2 ), + .WIDTH( 1 ) +) rxd_SYNC_ATTR ( + .clk( clk ), + .nrst( 1'b1 ), + .ena( 1'b1 ), + + .in( rxd ), + .out( rxd_sync ) +); + +logic start_detected; +assign start_detected = ~|rx_data_buf[DATA_BITS+STOP_BITS+:START_BITS]; +logic stop_detected; +assign stop_detected = &rx_data_buf[0+:STOP_BITS]; +logic data_valid; +assign data_valid = start_detected && stop_detected; + +always_ff @(posedge clk) begin + if( ~nrst ) begin + rx_data_buf[TOTAL_BITS-1:0] <= '1; + + rx_data[DATA_BITS-1:0] <= '0; + rx_valid <= 1'b0; + end else begin + if( data_valid ) begin + // clear rx_data_buf if valid message is already detected + rx_data_buf[TOTAL_BITS-1:0] <= { {(TOTAL_BITS-1){1'b1}}, + (SYNCHRONIZE_RXD ? rxd_sync : rxd) }; + end else begin + // simple shifter, MSB first + rx_data_buf[TOTAL_BITS-1:0] <= { rx_data_buf[TOTAL_BITS-2:0], + (SYNCHRONIZE_RXD ? rxd_sync : rxd) }; + end + + // buffering valid messages + if( data_valid ) begin + rx_data[DATA_BITS-1:0] <= rx_data_buf[STOP_BITS+:DATA_BITS]; + rx_valid <= 1'b1; + end else begin + rx_valid <= 1'b0; + end + end +end + + +endmodule + diff --git a/uart_tx_rx_shifter_tb.png b/uart_tx_rx_shifter_tb.png new file mode 100644 index 0000000..0dbd302 Binary files /dev/null and b/uart_tx_rx_shifter_tb.png differ diff --git a/uart_tx_rx_shifter_tb.sv b/uart_tx_rx_shifter_tb.sv new file mode 100644 index 0000000..c0aa44e --- /dev/null +++ b/uart_tx_rx_shifter_tb.sv @@ -0,0 +1,166 @@ +//------------------------------------------------------------------------------ +// uart_tx_rx_shifter_tb.sv +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// testbench for uart_tx_rx_shifter_tb.sv module + +`timescale 1ns / 1ps + +module uart_tx_rx_shifter_tb(); + +logic clk200; +initial begin + #0 clk200 = 1'b0; + forever + #2.5 clk200 = ~clk200; +end + +logic clk400; +initial begin + #0 clk400 = 1'b0; + forever + #1.25 clk400 = ~clk400; +end + +logic clk33; +initial begin + #0 clk33 = 1'b0; + forever + #15.151 clk33 = ~clk33; +end + +logic rst; +initial begin + #0 rst = 1'b0; + #10.2 rst = 1'b1; + #5 rst = 1'b0; +end + +logic nrst; +assign nrst = ~rst; + +logic rst_once; +initial begin + #0 rst_once = 1'b0; + #10.2 rst_once = 1'b1; + #5 rst_once = 1'b0; +end + +logic nrst_once; +assign nrst_once = ~rst_once; + +logic [31:0] DerivedClocks; +clk_divider #( + .WIDTH( 32 ) +) cd1 ( + .clk( clk200 ), + .nrst( nrst_once ), + .ena( 1'b1 ), + .out( DerivedClocks[31:0] ) +); + +logic [31:0] E_DerivedClocks; +edge_detect ed1[31:0] ( + .clk( {32{clk200}} ), + .nrst( {32{nrst_once}} ), + .in( DerivedClocks[31:0] ), + .rising( E_DerivedClocks[31:0] ), + .falling( ), + .both( ) +); + +logic [31:0] RandomNumber1; +c_rand rng1 ( + .clk( clk200 ), + .rst( 1'b0 ), + .reseed( rst_once ), + .seed_val( DerivedClocks[31:0] ^ (DerivedClocks[31:0] << 1) ), + .out( RandomNumber1[15:0] ) +); + +c_rand rng2 ( + .clk( clk200 ), + .rst( 1'b0 ), + .reseed( rst_once ), + .seed_val( DerivedClocks[31:0] ^ (DerivedClocks[31:0] << 2) ), + .out( RandomNumber1[31:16] ) +); + + +// Module under test ========================================================== + +`define STB 1 +`define DB 8 +`define SPB 2 + +logic tx_busy; +logic serial_data; + +logic start; + +// continious transfer (no automatic data check implemented) +//assign start = 1'b1; + +// random transfer (features automatic data check) +assign start = ~tx_busy && &RandomNumber1[11:8]; + +uart_tx_shifter #( + .START_BITS( `STB ), + .DATA_BITS( `DB ), + .STOP_BITS( `SPB ) +) tx1 ( + .clk( clk200 ), + .nrst( nrst_once ), + .tx_data( RandomNumber1[`DB-1:0] ), + .tx_start( start ), + .tx_busy( tx_busy ), + .txd( serial_data ) +); + +logic data_valid; +logic [`DB-1:0] data_rcvd; +uart_rx_shifter #( + .START_BITS( `STB ), + .DATA_BITS( `DB ), + .STOP_BITS( `SPB ), + .SYNCHRONIZE_RXD( 1 ) // 0 - disabled; 1 - enabled +) rx1 ( + .clk( clk200 ), + .nrst( nrst_once ), + .rx_data( data_rcvd[`DB-1:0] ), + .rx_valid( data_valid ), + .rxd( serial_data ) +); + + +logic [`DB-1:0] data_sent; +fifo #( + .DEPTH( 8 ), + .DATA_W( `DB ) +) data_check_fifo ( + .clk( clk200 ), + .rst( 1'b0 ), + + .w_req( start ), + .w_data( RandomNumber1[`DB-1:0] ), + + .r_req( data_valid ), + .r_data( data_sent[`DB-1:0] ), + + .cnt( ), + .empty( ), + .full( ) +); + +logic success = 1'b1; +always_ff @(posedge clk200) begin + if( data_valid ) begin + if( data_sent[`DB-1:0] != data_rcvd[`DB-1:0] ) begin + success <= 1'b0; + end + end +end + +endmodule diff --git a/uart_tx_shifter.sv b/uart_tx_shifter.sv new file mode 100644 index 0000000..05f0198 --- /dev/null +++ b/uart_tx_shifter.sv @@ -0,0 +1,122 @@ +//------------------------------------------------------------------------------ +// uart_tx_shifter.sv +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// UART-like shifter for simple synchronous messaging inside the FPGA or between FPGAs +// See also `uart_rx_shifter.sv` for RX part +// +// TX and RX parts should share one clock source +// Capable of continious stream transfer when tx_start is held constant 1'b1 +// Any reasonable start bit count,data bit count, stop bit count +// + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +uart_tx_shifter #( + .START_BITS( 1 ), + .DATA_BITS( 8 ), + .STOP_BITS( 2 ) +) tx1 ( + .clk( clk ), + .nrst( 1'b1 ), + .tx_data( ), + .tx_start( ), + .tx_busy( ), + .txd( ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + + +module uart_tx_shifter #( + + bit [7:0] START_BITS = 1, // must be >=1 + bit [7:0] DATA_BITS = 4, // must be >=1 + bit [7:0] STOP_BITS = 2 // must be >=1 +)( + input clk, // transmitter and receiver should use + input nrst, // the same clock + + input [DATA_BITS-1:0] tx_data, // input data get captured on write strobe + input tx_start, // write strobe itself + output tx_busy, // tx_busy fall on the last stop bit + + output logic txd = 1'b1 +); + +logic [DATA_BITS-1:0] tx_data_buf = '0; +logic [7:0] state_cntr = '0; + +enum int unsigned { STOP, START, DATA } tx_state = STOP; + +always_ff @(posedge clk) begin + if( ~nrst ) begin + tx_state <= STOP; + + tx_data_buf[DATA_BITS-1:0] <= '0; + state_cntr[7:0] <= '0; + + txd <= 1'b1; + end else begin + + case( tx_state ) + STOP: begin + + txd <= 1'b1; + if( state_cntr[7:0] != '0 ) begin + // holding stop bits + state_cntr[7:0]--; + end else begin + // idle state after stop bits + + // no need for edge detector here because tx_state changes instantly + // after the first active tx_start cycle + if( tx_start ) begin + // buffering input data + tx_data_buf[DATA_BITS-1:0] <= tx_data[DATA_BITS-1:0]; + state_cntr[7:0] <= START_BITS-1; + tx_state <= tx_state.next(); + end // tx_start + end // state_cntr + + end + START: begin + + txd <= 1'b0; + if( state_cntr[7:0] != '0 ) begin + // holding start bits + state_cntr[7:0]--; + end else begin + // transition + state_cntr[7:0] <= DATA_BITS-1; + tx_state <= tx_state.next(); + end // state_cntr + + end + DATA: begin + + // setting data, MSB first + txd <= tx_data_buf[state_cntr[7:0]]; + + if( state_cntr[7:0] != '0 ) begin + state_cntr[7:0]--; + end else begin + // transition + state_cntr[7:0] <= STOP_BITS-1; + tx_state <= tx_state.next(); + end // state_cntr + + end + endcase // tx_state + + end +end + +assign tx_busy = ~( (tx_state == STOP) && (state_cntr[7:0] == '0) ); + + +endmodule +