diff --git a/uart_rx.sv b/uart_rx.sv index 145ff44..b95b4da 100644 --- a/uart_rx.sv +++ b/uart_rx.sv @@ -1,16 +1,19 @@ -//-------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // uart_rx.sv +// published as part of https://github.com/pConst/basic_verilog // Konstantin Pavlov, pavlovconst@gmail.com -//-------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ -// INFO -------------------------------------------------------------------------------- +// INFO ------------------------------------------------------------------------ // Straightforward yet simple UART receiver implementation -// for FPGA written in Verilog - +// for FPGA written in SystemVerilog +// // Expects at least one stop bit // Features continuous data aquisition at BAUD levels up to CLK_HZ / 2 // Features early asynchronous 'busy' reset - +// +// see also "uart_rx.v" for equivalent Verilog version +// /* --- INSTANTIATION TEMPLATE BEGIN --- @@ -62,7 +65,7 @@ delay #( logic start_bit_strobe; edge_detect rxd_fall_detector ( .clk( clk ), - .nrst( nrst ), + .anrst( nrst ), .in( rxd_s ), .falling( start_bit_strobe ) ); @@ -88,7 +91,7 @@ always_ff @ (posedge clk) begin // wait for 1,5-bit period till next sample rx_sample_cntr[15:0] <= (BAUD_DIVISOR_2 * 3 - 1'b1); rx_busy <= 1'b1; - {rx_data[7:0],rx_data_9th_bit} <= 9'b100000000; + {rx_data[7:0],rx_data_9th_bit} <= 9'b10000000_0; end end else begin diff --git a/uart_rx.v b/uart_rx.v new file mode 100644 index 0000000..1c96035 --- /dev/null +++ b/uart_rx.v @@ -0,0 +1,128 @@ +//------------------------------------------------------------------------------ +// uart_rx.v +// published as part of https://github.com/pConst/basic_verilog +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// Straightforward yet simple UART receiver implementation +// for FPGA written in Verilog +// +// Expects at least one stop bit +// Features continuous data aquisition at BAUD levels up to CLK_HZ / 2 +// Features early asynchronous 'busy' reset +// +// see also "uart_rx.sv" for equivalent SystemVerilog version +// + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +uart_rx #( + .CLK_HZ( 200_000_000 ), // in Hertz + .BAUD( 9600 ) // max. BAUD is CLK_HZ / 2 +)( + .clk( ), + .nrst( ), + + .rx_data( ), + .rx_done( ), + .rxd( ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + + +module uart_rx #( parameter + CLK_HZ = 200_000_000, + BAUD = 9600, + bit [15:0] BAUD_DIVISOR_2 = CLK_HZ / BAUD / 2 +)( + input clk, + input nrst, + + output reg [7:0] rx_data = 0, + output reg rx_busy = 1'b0, + output rx_done, // read strobe + output rx_err, + input rxd +); + + +// synchronizing external rxd pin to avoid metastability +wire rxd_s; +delay #( + .LENGTH( 2 ), + .WIDTH( 1 ) +) rxd_synch ( + .clk( clk ), + .nrst( nrst ), + .ena( 1'b1 ), + .in( rxd ), + .out( rxd_s ) +); + + +wire start_bit_strobe; +edge_detect rxd_fall_detector ( + .clk( clk ), + .anrst( nrst ), + .in( rxd_s ), + .falling( start_bit_strobe ) +); + + +reg [15:0] rx_sample_cntr = (BAUD_DIVISOR_2 - 1'b1); + +wire rx_do_sample; +assign rx_do_sample = (rx_sample_cntr[15:0] == 1'b0); + + +// {rx_data[7:0],rx_data_9th_bit} is actually a shift register +reg rx_data_9th_bit = 1'b0; +always @ (posedge clk) begin + if( ~nrst ) begin + rx_busy <= 1'b0; + rx_sample_cntr <= (BAUD_DIVISOR_2 - 1'b1); + {rx_data[7:0],rx_data_9th_bit} <= 0; + end else begin + if( ~rx_busy ) begin + if( start_bit_strobe ) begin + + // wait for 1,5-bit period till next sample + rx_sample_cntr[15:0] <= (BAUD_DIVISOR_2 * 3 - 1'b1); + rx_busy <= 1'b1; + {rx_data[7:0],rx_data_9th_bit} <= 9'b10000000_0; + + end + end else begin + + if( rx_sample_cntr[15:0] == 0 ) begin + // wait for 1-bit-period till next sample + rx_sample_cntr[15:0] <= (BAUD_DIVISOR_2 * 2 - 1'b1); + end else begin + // counting and sampling only when 'busy' + rx_sample_cntr[15:0] <= rx_sample_cntr[15:0] - 1'b1; + end + + if( rx_do_sample ) begin + if( rx_data_9th_bit == 1'b1 ) begin + // early asynchronous 'busy' reset + rx_busy <= 1'b0; + end else begin + {rx_data[7:0],rx_data_9th_bit} <= {rxd_s, rx_data[7:0]}; + end + end + + end // ~rx_busy + end // ~nrst +end + +always @* begin + // rx_done and rx_busy fall simultaneously + rx_done <= rx_data_9th_bit && rx_do_sample && rxd_s; + rx_err <= rx_data_9th_bit && rx_do_sample && ~rxd_s; +end + +endmodule + diff --git a/uart_tx.sv b/uart_tx.sv index c144098..6321e69 100644 --- a/uart_tx.sv +++ b/uart_tx.sv @@ -12,6 +12,9 @@ // Features early asynchronous 'busy' set and reset to gain time to prepare new data // If multiple UartTX instances should be inferred - make tx_sample_cntr logic // that is common for all TX instances for effective chip area usage +// +// see also "uart_tx.v" for equivalent Verilog version +// /* --- INSTANTIATION TEMPLATE BEGIN --- diff --git a/uart_tx.v b/uart_tx.v new file mode 100644 index 0000000..6a3a1e1 --- /dev/null +++ b/uart_tx.v @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +// uart_tx.v +// published as part of https://github.com/pConst/basic_verilog +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// Straightforward yet simple UART transmitter implementation +// for FPGA written in Verilog +// +// One stop bit setting is hardcoded +// Features continuous data output at BAUD levels up to CLK_HZ / 2 +// Features early asynchronous 'busy' set and reset to gain time to prepare new data +// If multiple UartTX instances should be inferred - make tx_sample_cntr logic +// that is common for all TX instances for effective chip area usage +// +// see also "uart_tx.sv" for equivalent SystemVerilog version +// + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +uart_tx #( + .CLK_HZ( 200_000_000 ), // in Hertz + .BAUD( 9600 ) // max. BAUD is CLK_HZ / 2 +) tx1 ( + .clk( ), + .nrst( 1'b1 ), + //.tx_do_sample( ), + .tx_data( ), + .tx_start( ), + .tx_busy( ), + .txd( ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + + +module uart_tx #( parameter + CLK_HZ = 200_000_000, + BAUD = 9600, + bit [15:0] BAUD_DIVISOR = CLK_HZ / BAUD +)( + input clk, + input nrst, + //input tx_do_sample, + + input [7:0] tx_data, + input tx_start, // write strobe + output reg tx_busy = 1'b0, + output reg txd = 1'b1 +); + +reg [9:0] tx_shifter = 0; +reg [15:0] tx_sample_cntr = 0; +always @ (posedge clk) begin + if( (~nrst) || (tx_sample_cntr[15:0] == 0) ) begin + tx_sample_cntr[15:0] <= (BAUD_DIVISOR-1'b1); + end else begin + tx_sample_cntr[15:0] <= tx_sample_cntr[15:0] - 1'b1; + end +end + +wire tx_do_sample; +assign tx_do_sample = (tx_sample_cntr[15:0] == 0); + + +always @ (posedge clk) begin + if( ~nrst ) begin + tx_busy <= 1'b0; + tx_shifter[9:0] <= 0; + txd <= 1'b1; + end else begin + if( ~tx_busy ) begin + // asynchronous data load and 'busy' set + if( tx_start ) begin + tx_shifter[9:0] <= { 1'b1,tx_data[7:0],1'b0 }; + tx_busy <= 1'b1; + end + end else begin + + if( tx_do_sample ) begin // next bit + // txd MUST change only on tx_do_sample although data may be loaded earlier + { tx_shifter[9:0],txd } <= { tx_shifter[9:0],txd } >> 1; + // early asynchronous 'busy' reset + if( ~|tx_shifter[9:1] ) begin + // txd still holds data, but shifter is ready to get new info + tx_busy <= 1'b0; + end + end // tx_do_sample + + end // ~tx_busy + end // ~nrst +end + +endmodule +