summaryrefslogtreecommitdiffstats
path: root/serial.v
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2010-10-20 04:19:22 -0700
committerH. Peter Anvin <hpa@zytor.com>2010-10-20 05:52:25 -0700
commitbfeb24f4336341a7af9301d841421df150598e7b (patch)
treebd16354a70860cc8cb6275ae30f32b6e36eb6460 /serial.v
parent67d21151e94089870aa4739a93ead71736365127 (diff)
downloadabc8000-bfeb24f4336341a7af9301d841421df150598e7b.tar.gz
abc8000-bfeb24f4336341a7af9301d841421df150598e7b.tar.xz
abc8000-bfeb24f4336341a7af9301d841421df150598e7b.zip
Add serial device; project now at least compiles
Add a serial device, and at least make it plausible for the project to compile. If we have insane luck we might have a working ROM, CPU and serial port.
Diffstat (limited to 'serial.v')
-rw-r--r--serial.v215
1 files changed, 215 insertions, 0 deletions
diff --git a/serial.v b/serial.v
new file mode 100644
index 0000000..f48f199
--- /dev/null
+++ b/serial.v
@@ -0,0 +1,215 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2004-2010 H. Peter Anvin - All Rights Reserved
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+// Bostom MA 02111-1307, USA; either version 2 of the License, or
+// (at your option) any later version; incorporated herein by reference.
+//
+// -----------------------------------------------------------------------
+
+//
+// serial.v
+//
+// Simple fixed-speed serial port
+//
+// The following registers are implemented:
+//
+// R 0 - data in
+// W 0 - data out
+// R 1 - status:
+// bit 0 - data available in input FIFO
+// bit 1 - space available in output FIFO
+// bit 2 - input FIFO full
+// bit 3 - output FIFO empty
+// W 1 - control:
+// bit 0 - clean input FIFO
+// bit 1 - clear output FIFO
+//
+// On the 68000 side these are mapped to the low half of two separate
+// bytes, so a[0] is actually a[1].
+//
+
+module serial (
+ input rst_n, // Global reset
+ input clk, // Clock (25 MHz)
+
+ output tty_txd, // Serial port data out
+ input tty_rxd, // Serial port data in
+
+ input cs_n, // Unit (card/chip) select, AS#
+ input cpu_a, // Address bit
+ input [7:0] cpu_do, // CPU data out (cpu->card)
+ output [7:0] cpu_di, // CPU data in (card->cpu)
+ input cpu_r_wn // R/W#
+ );
+
+ // CPU strobes
+ wire cpu_rd = ~cs_n & cpu_r_wn;
+ wire cpu_wr = ~cs_n & ~cpu_r_wn;
+ wire cpu_rdd = cpu_rd & ~cpu_a;
+ wire cpu_wrd = cpu_wr & ~cpu_a;
+
+ // Data out
+ reg [9:0] tx_data_sr; // Serial port shift register
+ reg tx_data_out; // Serial port data out
+ assign tty_txd = tx_data_out;
+
+ // Baud rate divider
+ parameter baud_rate = 115200;
+ parameter baud_div = (25000000/baud_rate)-1;
+ reg [7:0] baud_rate_ctr;
+ // Time to advance the serial register
+ wire advance_bit = ~|baud_rate_ctr;
+
+ // BUSY counter
+ reg [3:0] tx_busy_ctr;
+ wire tx_busy = |tx_busy_ctr;
+
+ // Status generation
+ wire rx_empty;
+ wire [7:0] status = { 6'b0, ~rx_empty, tx_busy };
+ wire [7:0] rx_cpu_data;
+ assign cpu_di = cpu_rd ? (cpu_a ? status : rx_cpu_data) : 8'hFF;
+
+ // Control register
+ wire rx_clear = cpu_wr & cpu_a & cpu_do[0];
+
+ // Edge detect
+ reg cpu_wrd_q;
+ reg cpu_rdd_q;
+
+ always @(negedge rst_n or posedge clk)
+ begin
+ if ( ~rst_n )
+ begin
+ baud_rate_ctr <= 0;
+ tx_data_sr <= ~9'b0;
+ tx_data_out <= 1'b1;
+ tx_busy_ctr <= ~0;
+ cpu_wrd_q <= 1'b1;
+ cpu_rdd_q <= 1'b1;
+ end
+ else
+ begin
+ if ( cpu_wrd & ~cpu_wrd_q )
+ tx_data_sr <= { 1'b1, cpu_do, 1'b0 };
+ else if ( advance_bit )
+ tx_data_sr <= { 1'b1, tx_data_sr[8:1] };
+
+ if ( advance_bit )
+ tx_data_out <= tx_data_sr[0];
+
+ if ( advance_bit )
+ baud_rate_ctr <= baud_div;
+ else
+ baud_rate_ctr <= baud_rate_ctr - 1;
+
+ if ( cpu_wrd & ~cpu_wrd_q )
+ tx_busy_ctr <= 4'd10;
+ else if ( advance_bit & tx_busy )
+ tx_busy_ctr <= tx_busy_ctr - 1;
+
+ cpu_rdd_q <= cpu_rdd;
+ cpu_wrd_q <= cpu_wrd;
+ end // else: !if( ~rst_n )
+ end // always @ (negedge rst_n or posedge clk)
+
+ // Deglitch for the input
+ reg [3:0] rx_deglitch;
+ reg rx_deglitched;
+ reg tty_rxd_q;
+
+ always @(negedge rst_n or posedge clk)
+ if ( ~rst_n )
+ begin
+ rx_deglitch <= 4'b1111;
+ tty_rxd_q <= 1'b1;
+ end
+ else
+ begin
+ tty_rxd_q <= tty_rxd;
+ if ( tty_rxd_q )
+ begin
+ if ( ~&rx_deglitch )
+ rx_deglitch <= rx_deglitch + 1;
+ else
+ rx_deglitched <= 1'b1;
+ end
+ else
+ begin
+ if ( |rx_deglitch )
+ rx_deglitch <= rx_deglitch - 1;
+ else
+ rx_deglitched <= 1'b0;
+ end // else: !if( tty_rxd_q )
+ end // else: !if( ~rst_n )
+
+ // Receive logic
+ reg [3:0] rx_bit_ctr;
+ reg [8:0] rx_data;
+ reg [7:0] rx_wait_ctr;
+ reg rx_write_stb;
+
+ always @(negedge rst_n or posedge clk)
+ if (~rst_n)
+ begin
+ rx_bit_ctr <= 4'd0;
+ rx_data <= 9'hxxx;
+ rx_wait_ctr <= 9'hxxx;
+ rx_write_stb <= 1'b0;
+ end
+ else
+ begin
+ rx_write_stb <= 1'b0;
+ if (~|rx_bit_ctr)
+ begin
+ if (~rx_deglitched)
+ begin
+ rx_bit_ctr <= 4'd1;
+ // Sample near the center point; half a cycle delay
+ rx_wait_ctr <= {1'b0, baud_div[7:1]};
+ end
+ end
+ else
+ begin
+ if (|rx_wait_ctr)
+ rx_wait_ctr <= rx_wait_ctr - 1;
+ else
+ begin
+ rx_wait_ctr <= baud_div;
+ rx_data <= { rx_deglitched, rx_data[8:1] };
+ if (rx_bit_ctr[3] & rx_bit_ctr[1])
+ begin
+ // rx_data[0] and rx_deglitched are the start and
+ // stop bits, respectively. Verify that both
+ // were properly detected.
+ rx_write_stb <= ~rx_data[0] & rx_deglitched;
+ rx_bit_ctr <= 4'd0;
+ end
+ else
+ rx_bit_ctr <= rx_bit_ctr + 1;
+ end // else: !if(&rx_wait_ctr)
+ end // else: !if(~|rx_ctr)
+ end // else: !if(~rst_n)
+
+ //
+ // Serial receive FIFO
+ //
+ bytefifo serrxfifo (
+ .clock (clk),
+ .data (rx_data[7:0]),
+ .wrreq (rx_write_stb),
+ .sclr (~rst_n | rx_clear),
+ .almost_full ( ),
+ .empty (rx_empty),
+ .full ( ),
+ .rdreq (cpu_rdd & ~cpu_rdd_q),
+ .q (rx_cpu_data),
+ .usedw ( )
+ );
+
+endmodule // serial
+