working serial port for console and ppp (#6)

This commit is contained in:
Alan Steremberg 2021-03-30 13:19:58 -07:00 committed by GitHub
parent 19b22bdc6d
commit 4cd1724296
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 2038 additions and 32 deletions

View File

@ -13,7 +13,7 @@ set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
set_global_assignment -name LAST_QUARTUS_VERSION "17.0.2 Standard Edition"
set_global_assignment -name LAST_QUARTUS_VERSION "17.0.2 Lite Edition"
set_global_assignment -name GENERATE_RBF_FILE ON
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files

View File

@ -175,7 +175,7 @@ module emu
assign ADC_BUS = 'Z;
assign USER_OUT = '1;
assign {UART_RTS, UART_TXD, UART_DTR} = 0;
assign {DDRAM_CLK, DDRAM_BURSTCNT, DDRAM_ADDR, DDRAM_DIN, DDRAM_BE, DDRAM_RD, DDRAM_WE} = 0;
assign {SD_SCK, SD_MOSI, SD_CS} = 'Z;
@ -201,7 +201,7 @@ video_freak video_freak
`include "build_id.v"
localparam CONF_STR = {
"MACPLUS;;",
"MACPLUS;UART115200;",
"-;",
"F1,DSK,Mount Pri Floppy;",
"F2,DSK,Mount Sec Floppy;",
@ -216,6 +216,8 @@ localparam CONF_STR = {
"ODE,CPU,68000,68010,68020;",
"O4,Memory,1MB,4MB;",
"-;",
//"OA,Serial,Off,On;",
//"-;",
"R0,Reset & Apply CPU+Memory;",
"V,v",`BUILD_DATE
};
@ -358,11 +360,44 @@ assign AUDIO_MIX = 0;
// set the real-world inputs to sane defaults
localparam serialIn = 1'b0,
configROMSize = 1'b1; // 128K ROM
localparam configROMSize = 1'b1; // 128K ROM
wire [1:0] configRAMSize = status_mem?2'b11:2'b10; // 1MB/4MB
//
// Serial Ports
//
wire serialOut;
wire serialIn;
wire serialCTS;
wire serialRTS;
/*
assign serialIn = ~status[10] ? 0 : UART_RXD;
assign UART_TXD = serialOut;
assign serialCTS = UART_CTS;
assign UART_RTS = serialRTS;
assign UART_DTR = UART_DSR;
*/
//assign serialIn = ~status[10] ? 0 : UART_RXD;
assign serialIn = UART_RXD;
assign UART_TXD = serialOut;
//assign UART_RTS = UART_CTS;
assign UART_RTS = serialRTS ;
assign UART_DTR = UART_DSR;
//assign {UART_RTS, UART_TXD, UART_DTR} = 0;
/*
input UART_CTS,
output UART_RTS,
input UART_RXD,
output UART_TXD,
output UART_DTR,
input UART_DSR,
*/
// interconnects
// CPU
wire clk8, _cpuReset, _cpuReset_o, _cpuUDS, _cpuLDS, _cpuRW, _cpuAS;
@ -618,7 +653,13 @@ dataController_top dc0
.ps2_key(ps2_key),
.capslock(capslock),
.ps2_mouse(ps2_mouse),
// serial uart
.serialIn(serialIn),
.serialOut(serialOut),
.serialCTS(serialCTS),
.serialRTS(serialRTS),
// rtc unix ticks
.timestamp(TIMESTAMP),
// video

View File

@ -8,6 +8,8 @@ set_global_assignment -name VERILOG_FILE rtl/floppy.v
set_global_assignment -name SYSTEMVERILOG_FILE rtl/ps2_kbd.sv
set_global_assignment -name VERILOG_FILE rtl/ps2_mouse.v
set_global_assignment -name VERILOG_FILE rtl/scc.v
set_global_assignment -name VERILOG_FILE rtl/uart/txuart.v
set_global_assignment -name VERILOG_FILE rtl/uart/rxuart.v
set_global_assignment -name VERILOG_FILE rtl/iwm.v
set_global_assignment -name VHDL_FILE rtl/via6522.vhd
set_global_assignment -name VERILOG_FILE rtl/addrDecoder.v

View File

@ -47,6 +47,8 @@ module dataController_top(
// serial:
input serialIn,
output serialOut,
input serialCTS,
output serialRTS,
// RTC
input [32:0] timestamp,
@ -148,8 +150,6 @@ module dataController_top(
!_sccIrq?3'b101:
3'b111;
// Serial port
assign serialOut = 0;
reg [15:0] cpu_data;
always @(posedge clk32) if (cpuBusControl && memoryLatch) cpu_data <= memoryDataIn;
@ -409,8 +409,13 @@ module dataController_top(
._irq(_sccIrq),
.dcd_a(mouseX1),
.dcd_b(mouseY1),
.wreq(sccWReq));
.wreq(sccWReq),
.txd(serialOut),
.rxd(serialIn),
.cts(serialCTS),
.rts(serialRTS)
);
// Video
videoShifter vs(
.clk32(clk32),

View File

@ -22,7 +22,7 @@ module ps2_kbd
reg [8:0] keymac;
reg key_pending;
reg [21:0] pacetimer;
reg [19:0] pacetimer;
reg inquiry_active;
reg cmd_inquiry;
reg cmd_instant;
@ -68,8 +68,8 @@ always@(posedge clk or posedge reset) begin
end
end
wire tick_long = pacetimer == 22'h3fffff;
wire tick_short = pacetimer == 22'h000fff;
wire tick_long = pacetimer == 20'hfffff;
wire tick_short = pacetimer == 20'h00fff;
/* Delay inquiry responses to after tick_short */
always@(posedge clk or posedge reset) begin

393
rtl/scc.v
View File

@ -62,7 +62,8 @@ module scc
wire reset;
/* Data registers */
reg [7:0] data_a = 0;
// reg [7:0] data_a = 0;
wire[7:0] data_a ;
reg [7:0] data_b = 0;
/* Read registers */
@ -84,6 +85,10 @@ module scc
reg [7:0] wr1_a;
reg [7:0] wr1_b;
reg [7:0] wr2;
reg [7:0] wr3_a; /* synthesis keep */
reg [7:0] wr3_b;
reg [7:0] wr4_a;
reg [7:0] wr4_b;
reg [7:0] wr5_a;
reg [7:0] wr5_b;
reg [7:0] wr6_a;
@ -105,8 +110,10 @@ module scc
/* Status latches */
reg latch_open_a;
reg latch_open_b;
reg cts_latch_a;
reg dcd_latch_a;
reg dcd_latch_b;
wire cts_ip_a;
wire dcd_ip_a;
wire dcd_ip_b;
wire do_latch_a;
@ -125,6 +132,10 @@ module scc
reg ex_irq_ip_b;
wire [2:0] rr2_vec_stat;
reg [7:0] tx_data_a;
reg wr8_wr_a;
reg wr8_wr_b;
/* Register/Data access helpers */
assign wreg_a = cs & we & (~rs[1]) & rs[0];
assign wreg_b = cs & we & (~rs[1]) & ~rs[0];
@ -136,11 +147,28 @@ module scc
/* Register index is set by a write to WR0 and reset
* after any subsequent write. We ignore the side
*/
always@(posedge clk or posedge reset) begin
reg wr_data_a;
reg wr_data_b;
reg rx_wr_a_latch;
reg rx_first_a=1;
always@(posedge clk /*or posedge reset*/) begin
if (rx_wr_a) begin
rx_wr_a_latch<=1;
end
wr_data_a<=0;
wr_data_b<=0;
if (reset) begin
rindex_latch <= 0;
data_a <= 0;
data_b <= 0;
//data_a <= 0;
tx_data_a<=0;
data_b <= 0;
rx_wr_a_latch<=0;
wr_data_a<=0;
rx_first_a<=1;
end else if (cen && cs) begin
if (!rs[1]) begin
/* Default, reset index */
@ -153,12 +181,33 @@ module scc
/* Add point high */
rindex_latch[3] <= (wdata[5:3] == 3'b001);
/* enable int on next rx char */
if (wdata[5:3] == 3'b100)
rx_first_a<=1;
end
end else begin
if (we) begin
if (rs[0]) data_a <= wdata;
else data_b <= wdata;
end
if (rs[0]) begin
//data_a <= wdata;
tx_data_a <= wdata;
wr_data_a<=1;
end
else
begin
data_b <= wdata;
wr_data_b<=1;
end
end
else begin
// clear the read register?
if (rs[0]) begin
rx_wr_a_latch<=0;
rx_first_a<=0;
end
else begin
end
end
end
end
end
@ -207,6 +256,37 @@ module scc
wr2 <= wdata;
end
/* WR3
* Reset: unchanged
*/
always@(posedge clk or posedge reset_hw) begin
if (reset_hw)
wr3_a <= 0;
else if (cen && wreg_a && rindex == 3)
wr3_a <= wdata;
end
always@(posedge clk or posedge reset_hw) begin
if (reset_hw)
wr3_b <= 0;
else if (cen && wreg_b && rindex == 3)
wr3_b <= wdata;
end
/* WR4
* Reset: unchanged
*/
always@(posedge clk or posedge reset_hw) begin
if (reset_hw)
wr4_a <= 0;
else if (cen && wreg_a && rindex == 4)
wr4_a <= wdata;
end
always@(posedge clk or posedge reset_hw) begin
if (reset_hw)
wr4_b <= 0;
else if (cen && wreg_b && rindex == 4)
wr4_b <= wdata;
end
/* WR5
* Reset: Bits 7,4,3,2,1 to 0
*/
@ -231,6 +311,39 @@ module scc
end
end
/* WR8 : write data to serial port -- a or b?
*
*/
always@(posedge clk or posedge reset_hw) begin
if (reset_hw) begin
wr8_a <= 0;
wr8_wr_a <= 1'b0;
end
else if (cen && (rs[1] & we ) && rindex == 8) begin
wr8_wr_a <= 1'b1;
wr8_a <= wdata;
end
else begin
wr8_wr_a <= 1'b0;
end
end
always@(posedge clk or posedge reset_hw) begin
if (reset_hw) begin
wr8_b <= 0;
wr8_wr_b <= 1'b0;
end
else if (cen && (wreg_b ) && rindex == 8)
begin
wr8_wr_b <= 1'b1;
wr8_b <= wdata;
end
else
begin
wr8_wr_b <= 1'b0;
end
end
/* WR9. Special: top bits are reset, handled separately, bottom
* bits are only reset by a hw reset
*/
@ -378,12 +491,13 @@ module scc
/* RR0 */
assign rr0_a = { 1'b0, /* Break */
1'b1, /* Tx Underrun/EOM */
1'b0, /* CTS */
wr15_a[5] ? cts_latch_a : cts_a, /* CTS */
1'b0, /* Sync/Hunt */
wr15_a[3] ? dcd_latch_a : dcd_a, /* DCD */
1'b1, /* Tx Empty */
//1'b1, /*TX EMPTY */
~tx_busy_a, /* Tx Empty */
1'b0, /* Zero Count */
1'b0 /* Rx Available */
rx_wr_a_latch /* Rx Available */
};
assign rr0_b = { 1'b0, /* Break */
1'b1, /* Tx Underrun/EOM */
@ -397,13 +511,13 @@ module scc
/* RR1 */
assign rr1_a = { 1'b0, /* End of frame */
1'b0, /* CRC/Framing error */
1'b0,//frame_err_a, /* CRC/Framing error */
1'b0, /* Rx Overrun error */
1'b0, /* Parity error */
1'b0,//parity_err_a, /* Parity error */
1'b0, /* Residue code 0 */
1'b1, /* Residue code 1 */
1'b1, /* Residue code 2 */
1'b1 /* All sent */
~tx_busy_a /* All sent */
};
assign rr1_b = { 1'b0, /* End of frame */
@ -483,15 +597,110 @@ module scc
*
* Need to add latches. Tx irq is latched when buffer goes from full->empty,
* it's not a permanent state. For now keep it clear. Will have to fix that.
* TODO: AJS - look at tx and interrupt logic
*/
assign rx_irq_pend_a = 0;
assign tx_irq_pend_a = 0 /*& wr1_a[1]*/; /* Tx always empty for now */
assign ex_irq_pend_a = ex_irq_ip_a;
/*
The TxIP is reset either by writing data to the transmit buffer or by issuing the Reset Tx Int command in WR0
*/
reg tx_fin_pre;
reg tx_ip;
reg tx_mip;
/*
reg tx_ie;
always @(posedge clk) begin
if (reset_a|reset_hw|reset)
tx_ie<=0;
else if (wreg_a & (rindex == 1) )
tx_ie<=wdata[1];
end
*/
always @(posedge clk) begin
if (reset) begin
tx_ip<=0;
tx_mip<=0;
end
else begin
tx_fin_pre<=tx_busy_a;
if (wr5_a[3] & wr1_a[1] & tx_busy_a & ~tx_fin_pre) begin
tx_ip<=~tx_mip;
tx_mip<=0;
end
if (wreg_a & (rindex == 0) & (wdata[5:3] == 3'b111)) begin
tx_ip<=0;
end
if (wreg_a & (rindex == 0) & (wdata[5:3] == 3'b101)) begin
// If CIP=1, inhibit generation of next TX interrupt
// Actually, "Reset TxInt pend." clears current interrupt
tx_mip<= ~tx_ip;
tx_ip<=0;
end
if (wr5_a[3]==0)begin
tx_mip<=0;
tx_ip<=0;
end
end
end
reg tx_busy_a_r;
reg tx_latch_a;
always @(posedge clk) begin
tx_busy_a_r <= tx_busy_a;
// when we transition from empty to full, we create an interrupt
if (reset | reset_hw | reset_a)
tx_latch_a<=0;
else if (tx_busy_a_r ==1 && tx_busy_a==0)
tx_latch_a<=1;
// cleared when we write again
else if (wr_data_a)
tx_latch_a<=0;
// or when we set the reset in wr0
else if (wreg_a & (rindex == 0) & (wdata[5:3] == 3'b010))
tx_latch_a<=0;
//else if (wreg_a & (rindex == 0) & (wdata[5:3] == 3'b111)) // clear highest under service?
end
wire wreq_n;
//assign rx_irq_pend_a = rx_wr_a_latch & ( (wr1_a[3] && ~wr1_a[4])|| (~wr1_a[3] && wr1_a[4])) & wr3_a[0]; /* figure out the interrupt on / off */
//assign rx_irq_pend_a = rx_wr_a_latch & ( (wr1_a[3] & ~wr1_a[4])| (~wr1_a[3] & wr1_a[4])) & wr3_a[0]; /* figure out the interrupt on / off */
/* figure out the interrupt on / off */
/* rx enable: wr3_a[0] */
/* wr1_a 4 3
0 0 = rx int disable
0 1 = rx int on first char or special
1 0 = rx int on all rx chars or special
1 1 = rx int on special cond only
*/
// rx enable char waiting 01,10 only first char
assign rx_irq_pend_a = wr3_a[0] & rx_wr_a_latch & (wr1_a[3] ^ wr1_a[4]) & ((wr1_a[3] & rx_first_a )|(wr1_a[4]));
// assign tx_irq_pend_a = 0;
// assign tx_irq_pend_a = tx_busy_a & wr1_a[1];
assign tx_irq_pend_a = tx_ip;
//assign tx_irq_pend_a = wr1_a[1]; /* Tx always empty for now */
wire cts_interrupt = wr1_a[0] && wr15_a[5] || (tx_busy_a_r ==1 && tx_busy_a==0) || (tx_busy_a_r ==0 && tx_busy_a==1);/* if cts changes */
assign ex_irq_pend_a = ex_irq_ip_a ;
assign rx_irq_pend_b = 0;
assign tx_irq_pend_b = 0 /*& wr1_b[1]*/; /* Tx always empty for now */
assign ex_irq_pend_b = ex_irq_ip_b;
assign _irq = ~(wr9[3] & (rx_irq_pend_a |
rx_irq_pend_b |
tx_irq_pend_a |
tx_irq_pend_b |
@ -514,12 +723,13 @@ module scc
* corresponding interrupt is enabled in WR15
*/
assign dcd_ip_a = (dcd_a != dcd_latch_a) & wr15_a[3];
assign cts_ip_a = (cts_a != cts_latch_a) & wr15_a[5];
assign dcd_ip_b = (dcd_b != dcd_latch_b) & wr15_b[3];
/* Latches close when an enabled IP bit is set and latches
* are currently open
*/
assign do_latch_a = latch_open_a & (dcd_ip_a /* | cts... */);
assign do_latch_a = latch_open_a & (dcd_ip_a | cts_ip_a /* | cts... */);
assign do_latch_b = latch_open_b & (dcd_ip_b /* | cts... */);
/* "Master" interrupt, set when latch close & WR1[0] is set */
@ -570,10 +780,12 @@ module scc
always@(posedge clk or posedge reset) begin
if (reset) begin
dcd_latch_a <= 0;
cts_latch_a <= 0;
/* cts ... */
end else if(cep) begin
if (do_latch_a)
dcd_latch_a <= dcd_a;
cts_latch_a <= cts_a;
/* cts ... */
end
end
@ -587,10 +799,151 @@ module scc
/* cts ... */
end
end
/* NYI */
assign txd = 1;
assign rts = 1;
// assign txd = 1;
// assign rts = 1;
assign wreq = 1;
/* UART */
//wr_3_a
//wr_3_b
// bit
wire parity_ena_a= wr4_a[0];
wire parity_even_a= wr4_a[1];
reg [1:0] stop_bits_a= 2'b00;
reg [1:0] bit_per_char_a = 2'b00;
/*
76543210
data>>2 & 3
wr4_a[3:2]
case(wr4_a[3:2])
2'b00:
// sync mode enable
2'b01:
// 1 stop bit
stop_bits_a <= 2'b0;
2'b10:
// 1.5 stop bit
stop_bits_a <= 2'b0;
2'b11:
// 2 stop bit
stop_bits_a <= 2'b1;
default:
stop_bits_a <= 2'b0;
endcase
*/
/*
76543210
^__ 76
wr_3_a[7:6] -- bits per char
case (wr_3_a[7:6]})
2'b00: // 5
bit_per_char_a <= 2'b11;
2'b01: // 7
bit_per_char_a <= 2'b01;
2'b10: // 6
bit_per_char_a <= 2'b10;
2'b11: // 8
bit_per_char_a <= 2'b00;
endcase
*/
/*
300 -- 62.668800 / = 208896
600 -- 62.668800 / = 104448
1200-- 62.668800 / = 69632
2400 -- 62.668800 / 2400 = 26112
4800 -- 62.668800 / 4800 = 13056
9600 -- 62.668800 / 9600 = 6528
1440 -- 62.668800 / 14400 = 4352
19200 -- 62.668800 / 19200= 3264
38400 -- 62.668800 / 28800 = 2176
38400 -- 62.668800 / 38400 = 1632
57600 -- 62.668800 / 57600 = 1088
115200 -- 62.668800 / 115200 = 544
230400 -- 62.668800 / 230400 = 272
32.5 / 115200 =
*/
// case the baud rate based on wr12_a and 13_a
// wr_12_a -- contains the baud rate lower byte
// wr_13_a -- contains the baud rate high byte
/*
always @(posedge clk) begin
case ({wr13_a,wr12_a})
16'd380: // 300 baud
baud_divid_speed_a <= 24'd108333;
16'd94: // 1200 baud
baud_divid_speed_a <= 24'd27083;
16'd46: // 2400 baud
baud_divid_speed_a <= 24'd13542;
16'd22: // 4800 baud
baud_divid_speed_a <= 24'd6770;
16'd10: // 9600 baud
baud_divid_speed_a <= 24'd3385;
16'd6: // 14400 baud
baud_divid_speed_a <= 24'd2257;
16'd4: // 19200 baud
baud_divid_speed_a <= 24'd1693;
16'd2: // 28800 baud
baud_divid_speed_a <= 24'd1128;
16'd1: // 38400 baud
baud_divid_speed_a <= 24'd846;
16'd0: // 57600 baud
baud_divid_speed_a <= 24'd564;
default:
baud_divid_speed_a <= 24'd282;
endcase
end
*/
//reg [23:0] baud_divid_speed_a = 24'd1088;
//reg [23:0] baud_divid_speed_a = 24'd544;
reg [23:0] baud_divid_speed_a = 24'd282;
//reg [23:0] baud_divid_speed_a = 24'd564;
wire tx_busy_a;
wire rx_wr_a;
wire [30:0] uart_setup_rx_a = { 1'b0, bit_per_char_a, 1'b0, parity_ena_a, 1'b0, parity_even_a, baud_divid_speed_a } ;
wire [30:0] uart_setup_tx_a = { 1'b0, bit_per_char_a, 1'b0, parity_ena_a, 1'b0, parity_even_a, baud_divid_speed_a } ;
//wire [30:0] uart_setup_rx_a = { 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, baud_divid_speed_a } ;
//wire [30:0] uart_setup_tx_a = { 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, baud_divid_speed_a } ;
rxuart rxuart_a (
.i_clk(clk),
.i_reset(reset_a|reset_hw),
.i_setup(uart_setup_rx_a),
.i_uart_rx(rxd),
.o_wr(rx_wr_a), // TODO -- check on this flag
.o_data(data_a), // TODO we need to save this off only if wreq is set, and mux it into data_a in the right spot
.o_break(break_a),
.o_parity_err(parity_err_a),
.o_frame_err(frame_err_a),
.o_ck_uart()
);
txuart txuart_a
(
.i_clk(clk),
.i_reset(reset_a|reset_hw),
.i_setup(uart_setup_tx_a),
.i_break(1'b0),
.i_wr(wr_data_a), // TODO -- we need to send data when we get the register command i guess???
.i_data(tx_data_a),
//.i_cts_n(~cts),
.i_cts_n(1'b0),
.o_uart_tx(txd),
.o_busy(tx_busy_a)); // TODO -- do we need this busy line?? probably
wire cts_a = ~tx_busy_a;
// RTS and CTS are active low
assign rts = rx_wr_a_latch;
assign wreq=1;
endmodule

467
rtl/uart/rxuart.v Normal file
View File

@ -0,0 +1,467 @@
////////////////////////////////////////////////////////////////////////////////
//
// Filename: rxuart.v
//
// Project: wbuart32, a full featured UART with simulator
//
// Purpose: Receive and decode inputs from a single UART line.
//
//
// To interface with this module, connect it to your system clock,
// pass it the 32 bit setup register (defined below) and the UART
// input. When data becomes available, the o_wr line will be asserted
// for one clock cycle. On parity or frame errors, the o_parity_err
// or o_frame_err lines will be asserted. Likewise, on a break
// condition, o_break will be asserted. These lines are self clearing.
//
// There is a synchronous reset line, logic high.
//
// Now for the setup register. The register is 32 bits, so that this
// UART may be set up over a 32-bit bus.
//
// i_setup[30] True if we are not using hardware flow control. This bit
// is ignored within this module, as any receive hardware flow
// control will need to be implemented elsewhere.
//
// i_setup[29:28] Indicates the number of data bits per word. This will
// either be 2'b00 for an 8-bit word, 2'b01 for a 7-bit word, 2'b10
// for a six bit word, or 2'b11 for a five bit word.
//
// i_setup[27] Indicates whether or not to use one or two stop bits.
// Set this to one to expect two stop bits, zero for one.
//
// i_setup[26] Indicates whether or not a parity bit exists. Set this
// to 1'b1 to include parity.
//
// i_setup[25] Indicates whether or not the parity bit is fixed. Set
// to 1'b1 to include a fixed bit of parity, 1'b0 to allow the
// parity to be set based upon data. (Both assume the parity
// enable value is set.)
//
// i_setup[24] This bit is ignored if parity is not used. Otherwise,
// in the case of a fixed parity bit, this bit indicates whether
// mark (1'b1) or space (1'b0) parity is used. Likewise if the
// parity is not fixed, a 1'b1 selects even parity, and 1'b0
// selects odd.
//
// i_setup[23:0] Indicates the speed of the UART in terms of clocks.
// So, for example, if you have a 200 MHz clock and wish to
// run your UART at 9600 baud, you would take 200 MHz and divide
// by 9600 to set this value to 24'd20834. Likewise if you wished
// to run this serial port at 115200 baud from a 200 MHz clock,
// you would set the value to 24'd1736
//
// Thus, to set the UART for the common setting of an 8-bit word,
// one stop bit, no parity, and 115200 baud over a 200 MHz clock, you
// would want to set the setup value to:
//
// 32'h0006c8 // For 115,200 baud, 8 bit, no parity
// 32'h005161 // For 9600 baud, 8 bit, no parity
//
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2019, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
// States: (@ baud counter == 0)
// 0 First bit arrives
// ..7 Bits arrive
// 8 Stop bit (x1)
// 9 Stop bit (x2)
// c break condition
// d Waiting for the channel to go high
// e Waiting for the reset to complete
// f Idle state
`define RXU_BIT_ZERO 4'h0
`define RXU_BIT_ONE 4'h1
`define RXU_BIT_TWO 4'h2
`define RXU_BIT_THREE 4'h3
`define RXU_BIT_FOUR 4'h4
`define RXU_BIT_FIVE 4'h5
`define RXU_BIT_SIX 4'h6
`define RXU_BIT_SEVEN 4'h7
`define RXU_PARITY 4'h8
`define RXU_STOP 4'h9
`define RXU_SECOND_STOP 4'ha
// Unused 4'hb
// Unused 4'hc
`define RXU_BREAK 4'hd
`define RXU_RESET_IDLE 4'he
`define RXU_IDLE 4'hf
module rxuart(i_clk, i_reset, i_setup, i_uart_rx, o_wr, o_data, o_break,
o_parity_err, o_frame_err, o_ck_uart);
parameter [30:0] INITIAL_SETUP = 31'd868;
// 8 data bits, no parity, (at least 1) stop bit
input wire i_clk, i_reset;
/* verilator lint_off UNUSED */
input wire [30:0] i_setup;
/* verilator lint_on UNUSED */
input wire i_uart_rx;
output reg o_wr;
output reg [7:0] o_data;
output reg o_break;
output reg o_parity_err, o_frame_err;
output wire o_ck_uart;
wire [27:0] clocks_per_baud, break_condition, half_baud;
wire [1:0] data_bits;
wire use_parity, parity_even, dblstop, fixd_parity;
reg [29:0] r_setup;
reg [3:0] state;
assign clocks_per_baud = { 4'h0, r_setup[23:0] };
// assign hw_flow_control = !r_setup[30];
assign data_bits = r_setup[29:28];
assign dblstop = r_setup[27];
assign use_parity = r_setup[26];
assign fixd_parity = r_setup[25];
assign parity_even = r_setup[24];
assign break_condition = { r_setup[23:0], 4'h0 };
assign half_baud = { 5'h00, r_setup[23:1] }-28'h1;
reg [27:0] baud_counter;
reg zero_baud_counter;
// Since this is an asynchronous receiver, we need to register our
// input a couple of clocks over to avoid any problems with
// metastability. We do that here, and then ignore all but the
// ck_uart wire.
reg q_uart, qq_uart, ck_uart;
initial q_uart = 1'b0;
initial qq_uart = 1'b0;
initial ck_uart = 1'b0;
always @(posedge i_clk)
begin
q_uart <= i_uart_rx;
qq_uart <= q_uart;
ck_uart <= qq_uart;
end
// In case anyone else wants this clocked, stabilized value, we
// offer it on our output.
assign o_ck_uart = ck_uart;
// Keep track of the number of clocks since the last change.
//
// This is used to determine if we are in either a break or an idle
// condition, as discussed further below.
reg [27:0] chg_counter;
initial chg_counter = 28'h00;
always @(posedge i_clk)
if (i_reset)
chg_counter <= 28'h00;
else if (qq_uart != ck_uart)
chg_counter <= 28'h00;
else if (chg_counter < break_condition)
chg_counter <= chg_counter + 1;
// Are we in a break condition?
//
// A break condition exists if the line is held low for longer than
// a data word. Hence, we keep track of when the last change occurred.
// If it was more than break_condition clocks ago, and the current input
// value is a 0, then we're in a break--and nothing can be read until
// the line idles again.
initial o_break = 1'b0;
always @(posedge i_clk)
o_break <= ((chg_counter >= break_condition)&&(~ck_uart))? 1'b1:1'b0;
// Are we between characters?
//
// The opposite of a break condition is where the line is held high
// for more clocks than would be in a character. When this happens,
// we know we have synchronization--otherwise, we might be sampling
// from within a data word.
//
// This logic is used later to hold the RXUART in a reset condition
// until we know we are between data words. At that point, we should
// be able to hold on to our synchronization.
reg line_synch;
initial line_synch = 1'b0;
always @(posedge i_clk)
line_synch <= ((chg_counter >= break_condition)&&(ck_uart));
// Are we in the middle of a baud iterval? Specifically, are we
// in the middle of a start bit? Set this to high if so. We'll use
// this within our state machine to transition out of the IDLE
// state.
reg half_baud_time;
initial half_baud_time = 0;
always @(posedge i_clk)
half_baud_time <= (~ck_uart)&&(chg_counter >= half_baud);
// Allow our controlling processor to change our setup at any time
// outside of receiving/processing a character.
initial r_setup = INITIAL_SETUP[29:0];
always @(posedge i_clk)
if (state >= `RXU_RESET_IDLE)
r_setup <= i_setup[29:0];
// Our monster state machine. YIKES!
//
// Yeah, this may be more complicated than it needs to be. The basic
// progression is:
// RESET -> RESET_IDLE -> (when line is idle) -> IDLE
// IDLE -> bit 0 -> bit 1 -> bit_{ndatabits} ->
// (optional) PARITY -> STOP -> (optional) SECOND_STOP
// -> IDLE
// ANY -> (on break) BREAK -> IDLE
//
// There are 16 states, although all are not used. These are listed
// at the top of this file.
//
// Logic inputs (12): (I've tried to minimize this number)
// state (4)
// i_reset
// line_synch
// o_break
// ckuart
// half_baud_time
// zero_baud_counter
// use_parity
// dblstop
// Logic outputs (4):
// state
//
initial state = `RXU_RESET_IDLE;
always @(posedge i_clk)
begin
if (i_reset)
state <= `RXU_RESET_IDLE;
else if (state == `RXU_RESET_IDLE)
begin
if (line_synch)
// Goto idle state from a reset
state <= `RXU_IDLE;
else // Otherwise, stay in this condition 'til reset
state <= `RXU_RESET_IDLE;
end else if (o_break)
begin // We are in a break condition
state <= `RXU_BREAK;
end else if (state == `RXU_BREAK)
begin // Goto idle state following return ck_uart going high
if (ck_uart)
state <= `RXU_IDLE;
else
state <= `RXU_BREAK;
end else if (state == `RXU_IDLE)
begin // Idle state, independent of baud counter
if ((~ck_uart)&&(half_baud_time))
begin
// We are in the center of a valid start bit
case (data_bits)
2'b00: state <= `RXU_BIT_ZERO;
2'b01: state <= `RXU_BIT_ONE;
2'b10: state <= `RXU_BIT_TWO;
2'b11: state <= `RXU_BIT_THREE;
endcase
end else // Otherwise, just stay here in idle
state <= `RXU_IDLE;
end else if (zero_baud_counter)
begin
if (state < `RXU_BIT_SEVEN)
// Data arrives least significant bit first.
// By the time this is clocked in, it's what
// you'll have.
state <= state + 1;
else if (state == `RXU_BIT_SEVEN)
state <= (use_parity) ? `RXU_PARITY:`RXU_STOP;
else if (state == `RXU_PARITY)
state <= `RXU_STOP;
else if (state == `RXU_STOP)
begin // Stop (or parity) bit(s)
if (~ck_uart) // On frame error, wait 4 ch idle
state <= `RXU_RESET_IDLE;
else if (dblstop)
state <= `RXU_SECOND_STOP;
else
state <= `RXU_IDLE;
end else // state must equal RX_SECOND_STOP
begin
if (~ck_uart) // On frame error, wait 4 ch idle
state <= `RXU_RESET_IDLE;
else
state <= `RXU_IDLE;
end
end
end
// Data bit capture logic.
//
// This is drastically simplified from the state machine above, based
// upon: 1) it doesn't matter what it is until the end of a captured
// byte, and 2) the data register will flush itself of any invalid
// data in all other cases. Hence, let's keep it real simple.
// The only trick, though, is that if we have parity, then the data
// register needs to be held through that state without getting
// updated.
reg [7:0] data_reg;
always @(posedge i_clk)
if ((zero_baud_counter)&&(state != `RXU_PARITY))
data_reg <= { ck_uart, data_reg[7:1] };
// Parity calculation logic
//
// As with the data capture logic, all that must be known about this
// bit is that it is the exclusive-OR of all bits prior. The first
// of those will follow idle, so we set ourselves to zero on idle.
// Then, as we walk through the states of a bit, all will adjust this
// value up until the parity bit, where the value will be read. Setting
// it then or after will be irrelevant, so ... this should be good
// and simplified. Note--we don't need to adjust this on reset either,
// since the reset state will lead to the idle state where we'll be
// reset before any transmission takes place.
reg calc_parity;
always @(posedge i_clk)
if (state == `RXU_IDLE)
calc_parity <= 0;
else if (zero_baud_counter)
calc_parity <= calc_parity ^ ck_uart;
// Parity error logic
//
// Set during the parity bit interval, read during the last stop bit
// interval, cleared on BREAK, RESET_IDLE, or IDLE states.
initial o_parity_err = 1'b0;
always @(posedge i_clk)
if ((zero_baud_counter)&&(state == `RXU_PARITY))
begin
if (fixd_parity)
// Fixed parity bit--independent of any dat
// value.
o_parity_err <= (ck_uart ^ parity_even);
else if (parity_even)
// Parity even: The XOR of all bits including
// the parity bit must be zero.
o_parity_err <= (calc_parity != ck_uart);
else
// Parity odd: the parity bit must equal the
// XOR of all the data bits.
o_parity_err <= (calc_parity == ck_uart);
end else if (state >= `RXU_BREAK)
o_parity_err <= 1'b0;
// Frame error determination
//
// For the purpose of this controller, a frame error is defined as a
// stop bit (or second stop bit, if so enabled) not being high midway
// through the stop baud interval. The frame error value is
// immediately read, so we can clear it under all other circumstances.
// Specifically, we want it clear in RXU_BREAK, RXU_RESET_IDLE, and
// most importantly in RXU_IDLE.
initial o_frame_err = 1'b0;
always @(posedge i_clk)
if ((zero_baud_counter)&&((state == `RXU_STOP)
||(state == `RXU_SECOND_STOP)))
o_frame_err <= (o_frame_err)||(~ck_uart);
else if ((zero_baud_counter)||(state >= `RXU_BREAK))
o_frame_err <= 1'b0;
// Our data bit logic doesn't need nearly the complexity of all that
// work above. Indeed, we only need to know if we are at the end of
// a stop bit, in which case we copy the data_reg into our output
// data register, o_data.
//
// We would also set o_wr to be true when this is the case, but ... we
// won't know if there is a frame error on the second stop bit for
// another baud interval yet. So, instead, we set up the logic so that
// we know on the next zero baud counter that we can write out. That's
// the purpose of pre_wr.
initial o_data = 8'h00;
reg pre_wr;
initial pre_wr = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
pre_wr <= 1'b0;
o_data <= 8'h00;
end else if ((zero_baud_counter)&&(state == `RXU_STOP))
begin
pre_wr <= 1'b1;
case (data_bits)
2'b00: o_data <= data_reg;
2'b01: o_data <= { 1'b0, data_reg[7:1] };
2'b10: o_data <= { 2'b0, data_reg[7:2] };
2'b11: o_data <= { 3'b0, data_reg[7:3] };
endcase
end else if ((zero_baud_counter)||(state == `RXU_IDLE))
pre_wr <= 1'b0;
// Create an output strobe, true for one clock only, once we know
// all we need to know. o_data will be set on the last baud interval,
// o_parity_err on the last parity baud interval (if it existed,
// cleared otherwise, so ... we should be good to go here.)
initial o_wr = 1'b0;
always @(posedge i_clk)
if ((zero_baud_counter)||(state == `RXU_IDLE))
o_wr <= (pre_wr)&&(!i_reset);
else
o_wr <= 1'b0;
// The baud counter
//
// This is used as a "clock divider" if you will, but the clock needs
// to be reset before any byte can be decoded. In all other respects,
// we set ourselves up for clocks_per_baud counts between baud
// intervals.
always @(posedge i_clk)
if (i_reset)
baud_counter <= clocks_per_baud-28'h01;
else if (zero_baud_counter)
baud_counter <= clocks_per_baud-28'h01;
else case(state)
`RXU_RESET_IDLE:baud_counter <= clocks_per_baud-28'h01;
`RXU_BREAK: baud_counter <= clocks_per_baud-28'h01;
`RXU_IDLE: baud_counter <= clocks_per_baud-28'h01;
default: baud_counter <= baud_counter-28'h01;
endcase
// zero_baud_counter
//
// Rather than testing whether or not (baud_counter == 0) within our
// (already too complicated) state transition tables, we use
// zero_baud_counter to pre-charge that test on the clock
// before--cleaning up some otherwise difficult timing dependencies.
initial zero_baud_counter = 1'b0;
always @(posedge i_clk)
if (state == `RXU_IDLE)
zero_baud_counter <= 1'b0;
else
zero_baud_counter <= (baud_counter == 28'h01);
endmodule

1138
rtl/uart/txuart.v Normal file

File diff suppressed because it is too large Load Diff