Просмотр исходного кода

serial.sv: mer generell serieport med Rx och Tx

En mer generell serieport-IP med separat konfigurerbart Rx och Tx fõr
data och BREAK, och fast eller variabel baudrate.
H. Peter Anvin 3 лет назад
Родитель
Сommit
0cdd243825

+ 2 - 2
fpga/max80.qpf

@@ -19,12 +19,12 @@
 #
 # Quartus Prime
 # Version 21.1.0 Build 842 10/21/2021 SJ Lite Edition
-# Date created = 10:23:34  February 28, 2022
+# Date created = 01:21:21  March 05, 2022
 #
 # -------------------------------------------------------------------------- #
 
 QUARTUS_VERSION = "21.1"
-DATE = "10:23:34  February 28, 2022"
+DATE = "01:21:21  March 05, 2022"
 
 # Revisions
 

+ 107 - 97
fpga/max80.sv

@@ -13,124 +13,124 @@ module max80
     parameter logic [7:0] fpga_ver)
    (
     // Clock oscillator
-    input 	  master_clk,	// 336 MHz from PLL2
-    input 	  slow_clk,	// ~12 MHz clock from PLL2
-    input 	  master_pll_locked, // PLL2 is locked, master_clk is good
-    output 	  reset_plls,	// Reset all PLLs including PLL2
+    input	  master_clk,	// 336 MHz from PLL2
+    input	  slow_clk,	// ~12 MHz clock from PLL2
+    input	  master_pll_locked, // PLL2 is locked, master_clk is good
+    output	  reset_plls,	// Reset all PLLs including PLL2
 
-    input 	  board_id, // This better match the firmware
+    input	  board_id, // This better match the firmware
 
     // ABC-bus
-    inout 	  abc_clk, // ABC-bus 3 MHz clock
+    inout	  abc_clk, // ABC-bus 3 MHz clock
     inout [15:0]  abc_a, // ABC address bus
     inout [7:0]   abc_d, // ABC data bus
-    output 	  abc_d_oe, // Data bus output enable
-    inout 	  abc_rst_n, // ABC bus reset strobe
-    inout 	  abc_cs_n, // ABC card select strobe
+    output	  abc_d_oe, // Data bus output enable
+    inout	  abc_rst_n, // ABC bus reset strobe
+    inout	  abc_cs_n, // ABC card select strobe
     inout [4:0]   abc_out_n, // OUT, C1-C4 strobe
     inout [1:0]   abc_inp_n, // INP, STATUS strobe
-    inout 	  abc_xmemfl_n, // Memory read strobe
-    inout 	  abc_xmemw800_n, // Memory write strobe (ABC800)
-    inout 	  abc_xmemw80_n, // Memory write strobe (ABC80)
-    inout 	  abc_xinpstb_n, // I/O read strobe (ABC800)
-    inout 	  abc_xoutpstb_n, // I/O write strobe (ABC80)
+    inout	  abc_xmemfl_n, // Memory read strobe
+    inout	  abc_xmemw800_n, // Memory write strobe (ABC800)
+    inout	  abc_xmemw80_n, // Memory write strobe (ABC80)
+    inout	  abc_xinpstb_n, // I/O read strobe (ABC800)
+    inout	  abc_xoutpstb_n, // I/O write strobe (ABC80)
     // The following are inverted versus the bus IF
     // the corresponding MOSFETs are installed
-    inout 	  abc_rdy_x, // RDY = WAIT#
-    inout 	  abc_resin_x, // System reset request
-    inout 	  abc_int80_x, // System INT request (ABC80)
-    inout 	  abc_int800_x, // System INT request (ABC800)
-    inout 	  abc_nmi_x, // System NMI request (ABC800)
-    inout 	  abc_xm_x, // System memory override (ABC800)
+    inout	  abc_rdy_x, // RDY = WAIT#
+    inout	  abc_resin_x, // System reset request
+    inout	  abc_int80_x, // System INT request (ABC80)
+    inout	  abc_int800_x, // System INT request (ABC800)
+    inout	  abc_nmi_x, // System NMI request (ABC800)
+    inout	  abc_xm_x, // System memory override (ABC800)
     // Host/device control
-    output 	  abc_host, // 1 = host, 0 = target
+    output	  abc_host, // 1 = host, 0 = target
 
     // ABC-bus extension header
     // (Note: cannot use an array here because HC and HH are
     // input only.)
-    inout 	  exth_ha,
-    inout 	  exth_hb,
-    input 	  exth_hc,
-    inout 	  exth_hd,
-    inout 	  exth_he,
-    inout 	  exth_hf,
-    inout 	  exth_hg,
-    input 	  exth_hh,
+    inout	  exth_ha,
+    inout	  exth_hb,
+    input	  exth_hc,
+    inout	  exth_hd,
+    inout	  exth_he,
+    inout	  exth_hf,
+    inout	  exth_hg,
+    input	  exth_hh,
 
     // SDRAM bus
-    output 	  sr_clk,
+    output	  sr_clk,
     output [1:0]  sr_ba, // Bank address
     output [12:0] sr_a, // Address within bank
     inout [15:0]  sr_dq, // Also known as D or IO
     output [1:0]  sr_dqm, // DQML and DQMH
-    output 	  sr_cs_n,
-    output 	  sr_we_n,
-    output 	  sr_cas_n,
-    output 	  sr_ras_n,
+    output	  sr_cs_n,
+    output	  sr_we_n,
+    output	  sr_cas_n,
+    output	  sr_ras_n,
 
     // SD card
-    input 	  sd_cd_n,
-    output 	  sd_cs_n,
-    output 	  sd_clk,
-    output 	  sd_di,
-    input 	  sd_do,
+    input	  sd_cd_n,
+    output	  sd_cs_n,
+    output	  sd_clk,
+    output	  sd_di,
+    input	  sd_do,
 
     // Serial console (naming is FPGA as DCE)
-    input 	  tty_txd,
-    output 	  tty_rxd,
-    input 	  tty_rts,
-    output 	  tty_cts,
-    input 	  tty_dtr,
+    input	  tty_txd,
+    output	  tty_rxd,
+    input	  tty_rts,
+    output	  tty_cts,
+    input	  tty_dtr,
 
     // SPI flash memory (also configuration)
-    output 	  flash_cs_n,
-    output 	  flash_sck,
+    output	  flash_cs_n,
+    output	  flash_sck,
     inout [1:0]   flash_io,
 
     // SPI bus (connected to ESP32 so can be bidirectional)
-    inout 	  spi_clk,
-    inout 	  spi_miso,
-    inout 	  spi_mosi,
-    inout 	  spi_cs_esp_n, // ESP32 IO10
-    inout 	  spi_cs_flash_n, // ESP32 IO01
+    inout	  spi_clk,
+    inout	  spi_miso,
+    inout	  spi_mosi,
+    inout	  spi_cs_esp_n, // ESP32 IO10
+    inout	  spi_cs_flash_n, // ESP32 IO01
 
     // Other ESP32 connections
-    inout 	  esp_io0, // ESP32 IO00
-    inout 	  esp_int, // ESP32 IO09
+    inout	  esp_io0, // ESP32 IO00
+    inout	  esp_int, // ESP32 IO09
 
     // I2C bus (RTC and external)
-    inout 	  i2c_scl,
-    inout 	  i2c_sda,
-    input 	  rtc_32khz,
-    input 	  rtc_int_n,
+    inout	  i2c_scl,
+    inout	  i2c_sda,
+    input	  rtc_32khz,
+    input	  rtc_int_n,
 
     // LEDs
     output [2:0]  led,
 
     // USB
-    inout 	  usb_dp,
-    inout 	  usb_dn,
-    output 	  usb_pu,
-    input 	  usb_rx,
-    input 	  usb_rx_ok,
+    inout	  usb_dp,
+    inout	  usb_dn,
+    output	  usb_pu,
+    input	  usb_rx,
+    input	  usb_rx_ok,
 
     // HDMI
     output [2:0]  hdmi_d,
-    output 	  hdmi_clk,
-    inout 	  hdmi_scl,
-    inout 	  hdmi_sda,
-    inout 	  hdmi_hpd,
+    output	  hdmi_clk,
+    inout	  hdmi_scl,
+    inout	  hdmi_sda,
+    inout	  hdmi_hpd,
 
     // Unconnected pins with pullups, used for randomness
     inout [2:0]   rngio,
 
     // Various clocks available to the top level as well as internally
-    output 	  sdram_clk, // 168 MHz SDRAM clock
-    output 	  sys_clk, //  84 MHz System clock
-    output 	  flash_clk, // 134 MHz Serial flash ROM clock
-    output 	  usb_clk, //  48 MHz USB clock
-    output 	  vid_clk, //  56 MHz Video pixel clock
-    output 	  vid_hdmiclk	// 280 MHz HDMI serializer clock = vid_clk x 5
+    output	  sdram_clk, // 168 MHz SDRAM clock
+    output	  sys_clk, //  84 MHz System clock
+    output	  flash_clk, // 134 MHz Serial flash ROM clock
+    output	  usb_clk, //  48 MHz USB clock
+    output	  vid_clk, //  56 MHz Video pixel clock
+    output	  vid_hdmiclk	// 280 MHz HDMI serializer clock = vid_clk x 5
     );
 
    // -----------------------------------------------------------------------
@@ -138,10 +138,10 @@ module max80
    // -----------------------------------------------------------------------
    reg			    rst_n        = 1'b0; // Internal system reset
    reg			    hard_rst_n   = 1'b0; // Strict POR reset only
-   wire 		    reconfig_rst;	 // Reconfigure FPGA
+   wire			    reconfig_rst;	 // Reconfigure FPGA
 
    assign reset_plls = 1'b0;
-   
+
    tri1 [4:1]		    pll_locked;
 
    assign   pll_locked[2] = master_pll_locked;
@@ -219,7 +219,7 @@ module max80
    reg [3:1]  reset_cmd_q = 3'b0;
 
    assign reconfig_rst = reset_cmd_q[3];
-   
+
    always @(negedge all_plls_locked or posedge sys_clk)
      if (~all_plls_locked)
        begin
@@ -287,7 +287,7 @@ module max80
    // Internal CPU bus
    //
    wire			      cpu_mem_valid;
-   wire 		      cpu_mem_ready;
+   wire			      cpu_mem_ready;
    wire			      cpu_mem_instr;
    wire [ 3:0]		      cpu_mem_wstrb;
    wire [31:0]                cpu_mem_addr;
@@ -487,13 +487,13 @@ module max80
 
    // Reading the register shows the current set of pending interrupts.
    assign sysreg_rdata[4] = cpu_irq;
-   
+
    // CPU permanently hung?
    wire	       cpu_trap;
 
    // Request to halt the CPU on the next instruction boundary
    wire        cpu_halt;
-   
+
    always @(negedge rst_n or posedge sys_clk)
      if (~rst_n)
        begin
@@ -574,10 +574,10 @@ module max80
    wire [31:0] fast_mem_rdata;
 
    wire [SRAM_BITS-1:2] vjtag_sram_addr;
-   wire 		vjtag_sram_read;
-   wire 		vjtag_sram_write;
-   wire [31:0] 		vjtag_sram_rdata;
-   wire [31:0] 		vjtag_sram_wdata;
+   wire			vjtag_sram_read;
+   wire			vjtag_sram_write;
+   wire [31:0]		vjtag_sram_rdata;
+   wire [31:0]		vjtag_sram_wdata;
 
    fast_mem #(.words_lg2(SRAM_BITS-2),
 	      .data_file("mif/sram.mif"))
@@ -756,21 +756,31 @@ module max80
    wire        serial_tx_full;
    wire        serial_rx_break;
 
-   serial serial_tty (
-		      .rst_n    ( hard_rst_n ),
-		      .clk      ( sys_clk ),
-
-		      .tx_valid ( iodev_valid_tty &
-				  cpu_mem_addr[6:2] == 5'b00000 &
-				  cpu_mem_wstrb[0] ),
-		      .tx_data  ( cpu_mem_wdata[7:0] ),
-
-		      .tty_rx   ( tty_data_in ),
-		      .tty_tx   ( tty_data_out ),
-
-		      .tx_full  ( serial_tx_full ),
-		      .rx_break ( tty_rxd_break_rst )
-		      );
+   serial #(.ENABLE_RX_DATA    (1'b0),
+	    .ENABLE_RX_BREAK   (1'b1),
+	    .ENABLE_TX_DATA    (1'b1),
+	    .ENABLE_TX_BREAK   (1'b0),
+	    .BAUDRATE_SETTABLE (1'b0),
+	    .BAUDRATE          (921600),
+	    .TTY_CLK           (84000000))
+   (
+    .rst_n    ( hard_rst_n ),
+    .clk      ( sys_clk ),
+
+    // Snoops USB TTY channel 0
+    .tx_wstrb ( iodev_valid_tty &
+		cpu_mem_addr[6:2] == 5'b00000 &
+		cpu_mem_wstrb[0] ),
+    .tx_data  ( cpu_mem_wdata[7:0] ),
+    .tx_flush ( 1'b0 ),
+    .rx_flush ( 1'b0 ),
+
+    .tty_rx   ( tty_data_in ),
+    .tty_tx   ( tty_data_out ),
+
+    .tx_full  ( serial_tx_full ),
+    .rx_break ( tty_rxd_break_rst )
+    );
 
    // If DTR# is asserted, block on full serial Tx FIFO; this allows
    // us to not lose debugging messages.

BIN
fpga/output/jtagupd/v1.rbf.gz


BIN
fpga/output/jtagupd/v1.sof


BIN
fpga/output/jtagupd/v1.svf.gz


BIN
fpga/output/jtagupd/v2.rbf.gz


BIN
fpga/output/jtagupd/v2.sof


BIN
fpga/output/jtagupd/v2.svf.gz


BIN
fpga/output/v1.fw


BIN
fpga/output/v1.jic


BIN
fpga/output/v1.rbf.gz


BIN
fpga/output/v1.rpd.gz


BIN
fpga/output/v1.sof


BIN
fpga/output/v1.svf.gz


BIN
fpga/output/v1.update.svf.gz


BIN
fpga/output/v1.update.xsvf.gz


BIN
fpga/output/v1.xsvf.gz


BIN
fpga/output/v2.fw


BIN
fpga/output/v2.jic


BIN
fpga/output/v2.rbf.gz


BIN
fpga/output/v2.rpd.gz


BIN
fpga/output/v2.sof


BIN
fpga/output/v2.svf.gz


BIN
fpga/output/v2.update.svf.gz


BIN
fpga/output/v2.update.xsvf.gz


BIN
fpga/output/v2.xsvf.gz


+ 255 - 101
fpga/serial.sv

@@ -1,7 +1,5 @@
 //
-// Serial port "snoop" of the console (otherwise sent via USB.)
-//
-// The only receive support is BREAK.
+// Simple serial port.
 //
 // The transmission rate is fixed to 1 Mbps.
 //
@@ -10,19 +8,42 @@
 // if desired.
 //
 
-module serial (
-	    input 	rst_n,
-	    input 	clk,
+module serial
+  #(
+    parameter logic  ENABLE_RX_DATA    = 1'b1,
+    parameter logic  ENABLE_RX_BREAK   = 1'b1,
+    parameter logic  ENABLE_TX_DATA    = 1'b1,
+    parameter logic  ENABLE_TX_BREAK   = 1'b1,
+    parameter logic  BAUDRATE_SETTABLE = 1'b0,
+    parameter [31:0] BAUDRATE          = 115200,
+    parameter [31:0] TTY_CLK           = 84000000,
+    parameter        NCO_BITS          = 24,
+    parameter        BREAK_BITS        = 16 // Bit times for BREAK detect
+    )
+   (
+    input tri1		 rst_n,
+    input		 clk,
+
+    output		 tty_tx,
+    input		 tty_rx,
 
-	    input 	tx_valid,
-	    input [7:0] tx_data,
+    input		 tx_wstrb,
+    input [7:0]		 tx_data,
+    input		 tx_break, // BREAK is asserted as long as this is true
+    output		 tx_full,
+    output		 tx_empty,
+    input tri0		 tx_flush,
 
-	    input 	tty_rx,
-	    output 	tty_tx,
+    input		 rx_rstrb,
+    output [7:0]	 rx_data,
+    output		 rx_break,
+    output		 rx_full,
+    output		 rx_empty,
+    input tri0		 rx_flush,
 
-	    output reg	rx_break,
-	    output 	tx_full
-	    );
+    input [NCO_BITS-1:0] divisor, // If divisor is settable
+    input tri0		 divisor_wstrb
+    );
 
    `include "functions.sv"	// For ModelSim
 
@@ -37,124 +58,257 @@ module serial (
    // The term "divisor" here is probably misleading, since it is
    // actually a fixed-point *multiplier* which is <= 1.
    //
-   parameter [31:0]	BAUDRATE = 921600;
-   parameter [31:0]	TTY_CLK  = 84000000;
-   parameter		NCO_BITS = 24;
-   parameter            BREAK_BITS = 19; // 2^19/(BAUDRATE*16) ~ 33 ms
-
-   reg  [NCO_BITS-1:0]  divisor
-			= round_div(BAUDRATE << NCO_BITS, TTY_CLK >> 4) - 1'b1;
+   localparam [NCO_BITS-1:0] default_divisor =
+		  round_div(BAUDRATE << NCO_BITS, TTY_CLK >> 4) - 1'b1;
+   reg  [NCO_BITS-1:0]	divisor_q = default_divisor;
    reg  [NCO_BITS-1:0]  nco_q;
    reg			tty_clk_en; // tty clock tick (clock enable)
 
    always @(posedge clk)
-	{ tty_clk_en, nco_q } <= nco_q + divisor + 1'b1;
+     { tty_clk_en, nco_q } <= nco_q + divisor_q + 1'b1;
+
+   always @(negedge rst_n or posedge clk)
+     if (~rst_n)
+       divisor_q <= default_divisor;
+     else if ( BAUDRATE_SETTABLE & divisor_wstrb )
+       divisor_q <= divisor;
 
    //
-   // Tx FIFO
+   // **** Transmitter section ****
    //
-   reg  tx_rdack;
-   wire tx_wrreq;
-   wire tx_rdempty;
-   wire [7:0] tx_out_data;
+   reg		tx_rdack;
+   wire [7:0]	tx_out_data;
 
-   reg 	      tx_valid_q;
+   //
+   // FIFO
+   //
+   generate if (ENABLE_TX_DATA)
+     begin
+	//
+	// Tx FIFO
+	//
 
-   always @(negedge rst_n or posedge clk)
-     if (~rst_n)
-       tx_valid_q <= 1'b0;
-     else
-       tx_valid_q <= tx_valid;
-
-   fifo txfifo (
-		.aclr ( ~rst_n ),
-		.clock ( clk ),
-		.data ( tx_data ),
-		.rdreq ( tx_rdack ),
-		.sclr ( 1'b0 ),	// Flush FIFO command
-		.wrreq ( tx_valid & ~tx_valid_q ),
-		.empty ( tx_rdempty ),
-		.full ( tx_full ),
-		.q ( tx_out_data ),
-		.usedw ( )
-		);
+	reg tx_wstrb_q;
+	always @(negedge rst_n or posedge clk)
+	  if (~rst_n)
+	    tx_wstrb_q <= 1'b0;
+	  else
+	    tx_wstrb_q <= tx_wstrb;
+
+	fifo txfifo (
+		     .aclr ( ~rst_n ),
+		     .clock ( clk ),
+		     .data ( tx_data ),
+		     .wrreq ( tx_wstrb & ~tx_wstrb_q ),
+		     .sclr ( tx_flush ),
+		     .empty ( tx_empty ),
+		     .full ( tx_full ),
+		     .rdreq ( tx_rdack ),
+		     .q ( tx_out_data ),
+		     .usedw ( )
+		     );
+     end // if (ENABLE_TX_DATA)
+   else
+     begin
+	assign tx_empty    = 1'b1;
+	assign tx_full     = 1'b1;
+	assign tx_out_data = 8'hxx;
+     end
+   endgenerate
 
    //
    // Transmitter
    //
-   reg [3:0]  tx_phase;
-   reg [3:0]  tx_bits;
-   reg [9:0]  tx_sr = ~10'b0;		// Shift register
-   assign tty_tx = tx_sr[0];
+   generate if (ENABLE_TX_DATA | ENABLE_TX_BREAK)
+     begin
+	reg [3:0] tx_phase;
+	reg [3:0] tx_ctr;
+	reg [9:0] tx_sr = ~10'b0;
+	reg	  tx_sending_break;
 
-   always @(negedge rst_n or posedge clk)
-     if (~rst_n)
-       begin
-	  tx_phase <= 4'h0;
-	  tx_bits  <= 4'd0;
-	  tx_sr    <= ~10'b0;	// Line idle
-	  tx_rdack <= 1'b0;
-       end
-     else
-       begin
-	  tx_rdack <= 1'b0;
-
-	  if ( tty_clk_en )
+	always @(negedge rst_n or posedge clk)
+	  if (~rst_n)
 	    begin
-	       tx_phase <= tx_phase + 1'b1;
+	       tx_phase <= 4'h0;
+	       tx_ctr  <= 'd0;
+	       tx_sr    <= 10'h3ff;	// Line idle
+	       tx_rdack <= 1'b0;
+	       tty_tx   <= 1'b1;
+	       tx_sending_break <= 1'b0;
+	    end
+	  else
+	    begin
+	       tx_rdack <= 1'b0;
+	       tty_tx   <= tx_sr[0] & ~tx_sending_break;
 
-	       if (tx_phase == 4'hF)
+	       if ( tty_clk_en )
 		 begin
-		    tx_sr[8:0] <= tx_sr[9:1];
-		    tx_sr[9]   <= 1'b1; // Stop bit/idle
+		    tx_phase <= tx_phase + 1'b1;
 
-		    if (tx_bits == 4'd0)
+		    if (&tx_phase)
 		      begin
-			 if ( ~tx_rdempty )
+			 tx_sr[8:0] <= tx_sr[9:1];
+			 tx_sr[9]   <= 1'b1; // Stop bit/idle
+
+			 if (|tx_ctr)
+			   begin
+			      tx_ctr <= tx_ctr - 1'b1;
+			   end
+			 else if ( ENABLE_TX_BREAK &
+				   (tx_break | tx_sending_break) )
+			   begin
+			      // Make sure we get an idle bit after break
+			      tx_sending_break <= tx_break;
+			   end
+			 else if ( ~tx_empty )
 			   begin
 			      tx_sr[9:2] <= tx_out_data;
 			      tx_sr[1]   <= 1'b0; // Start bit
 			      // 10 = start bit + data + stop bit
-			      tx_bits    <= 4'd10;
+			      tx_ctr     <= 'd10;
 			      tx_rdack   <= 1'b1; // Remove from FIFO
 			   end
-		      end
-		    else
-		      begin
-			 tx_bits <= tx_bits - 1'b1;
-		      end // else: !if(tx_bits == 4'd0)
-		 end // if (tx_phase == 4'hF)
-	    end // if ( tty_clk_en )
-       end // else: !if(~rst_n)
+		      end // if (tx_phase == 4'hF)
+		 end // if ( tty_clk_en )
+	    end // else: !if(~rst_n)
+     end // if (ENABLE_TX_DATA | ENABLE_TX_BREAK)
+   else
+     begin
+	assign tty_tx = 1'b1;
+     end // else: !if(ENABLE_TX_DATA | ENABLE_TX_BREAK)
+   endgenerate
+
+   //
+   // **** Receiver section ****
+   //
+
+   //
+   // Bit phase counter
+   //
+   generate if (ENABLE_RX_DATA | ENABLE_RX_BREAK)
+     begin
+	reg [3:0] rx_phase;
+	reg	  rx_phase_start = 1'b0;
+
+	always @(negedge rst_n or posedge clk)
+	  if (~rst_n)
+	    rx_phase <= 4'b0;
+	  else if ( rx_phase_start )
+	    rx_phase <= 4'h8;	// Start bit flank detected
+	  else
+	    rx_phase <= rx_phase + tty_clk_en;
+     end // if (ENABLE_RX_DATA | ENABLE_RX_BREAK)
+   endgenerate
 
    //
    // BREAK detect
    //
-   reg  [BREAK_BITS-1:0] rx_break_ctr;
-   wire   [BREAK_BITS:0] rx_break_ctr_next = rx_break_ctr + 1'b1;
-   
-   always @(negedge rst_n or posedge clk)
-     if (~rst_n)
-       begin
-	  rx_break_ctr <= 'b0;
-	  rx_break     <= 1'b0;
-       end
-     else
-       begin
-	  if ( tty_clk_en )
+
+   generate if (ENABLE_RX_BREAK)
+     begin
+	reg  [BREAK_BITS-1:0] rx_break_ctr;
+	wire [BREAK_BITS:0]   rx_break_ctr_next = rx_break_ctr + 1'b1;
+
+	always @(negedge rst_n or posedge clk)
+	  if (~rst_n)
+	    begin
+	       rx_break_ctr <= 'b0;
+	       rx_break     <= 1'b0;
+	    end
+	  else
 	    begin
-	       if ( tty_rx )
+	       if ( tty_clk_en )
 		 begin
-		    // Rx high = not a break
-		    rx_break_ctr <= 'b0;
-		    rx_break     <= 1'b0;
-		 end
-	       else
+		    if ( tty_rx )
+		      begin
+			 // Rx high = not a break
+			 rx_break_ctr <= 'b0;
+			 rx_break     <= 1'b0;
+		      end
+		    else if ( &rx_phase )
+		      begin
+			 rx_break_ctr <= rx_break_ctr_next;
+			 rx_break     <= rx_break | rx_break_ctr_next[BREAK_BITS];
+		      end // else: !if(tty_rxd)
+		 end // if ( tty_clk_en )
+	    end // else: !if(~rst_n)
+     end // if (ENABLE_RX_BREAK)
+   else
+     begin
+	assign rx_break = 1'b0;
+     end
+   endgenerate
+
+   //
+   // Data receive
+   //
+   generate if (ENABLE_RX_DATA)
+     begin
+	reg rx_recv_strb;
+	reg rx_rstrb_q;
+
+	fifo rxfifo (
+		     .aclr ( ~rst_n ),
+		     .clock ( clk ),
+		     .rdreq ( ~rx_rstrb & rx_rstrb_q ), // Advance after read
+		     .q ( rx_data ),
+		     .sclr ( rx_flush ),
+		     .empty ( rx_empty ),
+		     .full ( rx_full ),
+		     .wrreq ( rx_recv_strb ),
+		     .data ( rx_sr ),
+		     .usedw ( )
+		     );
+
+	reg [3:0] rx_ctr;
+	reg [2:0] rx_bits;
+	reg [7:0] rx_sr;
+	reg	  tty_clk_en_q;
+
+	always @(negedge rst_n or posedge clk)
+	  if (~rst_n)
+	    begin
+	       rx_ctr         <= 4'b0;
+	       rx_bits        <= 3'b111;
+	       rx_sr          <= 8'hxx;
+	       tty_clk_en_q   <= 1'b0;
+	       rx_rstrb_q      <= 1'b0;
+	       rx_recv_strb    <= 1'b0;
+	       rx_phase_start <= 1'b0;
+	    end
+	  else
+	    begin
+	       rx_rstrb_q    <= rx_rstrb;
+	       tty_clk_en_q <= tty_clk_en;
+	       rx_recv_strb  <= 1'b0;
+
+	       if ( tty_clk_en )
+		 rx_bits <= { rx_bits[1:0], tty_rx };
+
+	       if ( tty_clk_en_q )
 		 begin
-		    rx_break_ctr <= rx_break_ctr_next[BREAK_BITS-1:0];
-		    rx_break     <= rx_break | rx_break_ctr_next[BREAK_BITS];
-		 end // else: !if(tty_rxd)
-	    end // if ( tty_clk_en )
-       end // else: !if(~rst_n)
-   
+		    if ( !rx_ctr )
+		      begin
+			 if ( rx_bits == 3'b100 )
+			   begin
+			      rx_ctr <= 4'd10;
+			      rx_phase_start <= 1'b1;
+			   end
+		      end
+		    else if ( !rx_phase )
+		      begin
+			 rx_sr[6:0] <= rx_sr[7:1];
+			 // Majority vote of three samples
+			 rx_sr[7] <= (rx_bits[1] & rx_bits[0]) |
+				     (rx_bits[2] & rx_bits[0]) |
+				     (rx_bits[2] & rx_bits[1]);
+
+			 rx_ctr <= rx_ctr - 1'b1;
+			 // Push to FIFO if this was the bit before stop
+			 rx_recv_strb <= rx_ctr == 4'd2;
+		      end // if ( &rx_phase )
+		 end // if ( tty_clk_en_q )
+	    end // else: !if(~rst_n)
+     end // if (ENABLE_RX_DATA)
+   endgenerate
 endmodule // serial