diff --git a/MacPlus.qsf b/MacPlus.qsf index 56d2347..c61cccd 100644 --- a/MacPlus.qsf +++ b/MacPlus.qsf @@ -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 diff --git a/MacPlus.sv b/MacPlus.sv index 74a73a9..ee41455 100644 --- a/MacPlus.sv +++ b/MacPlus.sv @@ -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 diff --git a/files.qip b/files.qip index c76b310..5cd2109 100644 --- a/files.qip +++ b/files.qip @@ -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 diff --git a/rtl/dataController_top.v b/rtl/dataController_top.v index 44cb307..523d787 100644 --- a/rtl/dataController_top.v +++ b/rtl/dataController_top.v @@ -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), diff --git a/rtl/ps2_kbd.sv b/rtl/ps2_kbd.sv index 123708b..1a12afd 100644 --- a/rtl/ps2_kbd.sv +++ b/rtl/ps2_kbd.sv @@ -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 diff --git a/rtl/scc.v b/rtl/scc.v index bc3f8b7..acd6ae6 100644 --- a/rtl/scc.v +++ b/rtl/scc.v @@ -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 diff --git a/rtl/uart/rxuart.v b/rtl/uart/rxuart.v new file mode 100644 index 0000000..3bc3414 --- /dev/null +++ b/rtl/uart/rxuart.v @@ -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 +// 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 + + diff --git a/rtl/uart/txuart.v b/rtl/uart/txuart.v new file mode 100644 index 0000000..cf92ea3 --- /dev/null +++ b/rtl/uart/txuart.v @@ -0,0 +1,1138 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: txuart.v +// +// Project: wbuart32, a full featured UART with simulator +// +// Purpose: Transmit outputs over 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 byte +// of data you wish to transmit. Strobe the i_wr line high for one +// clock cycle, and your data will be off. Wait until the 'o_busy' +// line is low before strobing the i_wr line again--this implementation +// has NO BUFFER, so strobing i_wr while the core is busy will just +// cause your data to be lost. The output will be placed on the o_txuart +// output line. If you wish to set/send a break condition, assert the +// i_break line otherwise leave it low. +// +// 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] Set this to zero to use hardware flow control, and to +// one to ignore hardware flow control. Only works if the hardware +// flow control has been properly wired. +// +// If you don't want hardware flow control, fix the i_rts bit to +// 1'b1, and let the synthesys tools optimize out the logic. +// +// 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 +// for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +`default_nettype none +// +// +module txuart(i_clk, i_reset, i_setup, i_break, i_wr, i_data, + i_cts_n, o_uart_tx, o_busy); + parameter [30:0] INITIAL_SETUP = 31'd868; + // + localparam [3:0] TXU_BIT_ZERO = 4'h0; + localparam [3:0] TXU_BIT_ONE = 4'h1; + localparam [3:0] TXU_BIT_TWO = 4'h2; + localparam [3:0] TXU_BIT_THREE = 4'h3; + localparam [3:0] TXU_BIT_FOUR = 4'h4; + localparam [3:0] TXU_BIT_FIVE = 4'h5; + localparam [3:0] TXU_BIT_SIX = 4'h6; + localparam [3:0] TXU_BIT_SEVEN = 4'h7; + localparam [3:0] TXU_PARITY = 4'h8; + localparam [3:0] TXU_STOP = 4'h9; + localparam [3:0] TXU_SECOND_STOP = 4'ha; + // + localparam [3:0] TXU_BREAK = 4'he; + localparam [3:0] TXU_IDLE = 4'hf; + // + // + input wire i_clk, i_reset; + input wire [30:0] i_setup; + input wire i_break; + input wire i_wr; + input wire [7:0] i_data; + // Hardware flow control Ready-To-Send bit. Set this to one to use + // the core without flow control. (A more appropriate name would be + // the Ready-To-Receive bit ...) + input wire i_cts_n; + // And the UART input line itself + output reg o_uart_tx; + // A line to tell others when we are ready to accept data. If + // (i_wr)&&(!o_busy) is ever true, then the core has accepted a byte + // for transmission. + output wire o_busy; + + wire [27:0] clocks_per_baud, break_condition; + wire [1:0] i_data_bits, data_bits; + wire use_parity, parity_odd, dblstop, fixd_parity, + fixdp_value, hw_flow_control, i_parity_odd; + reg [30:0] r_setup; + assign clocks_per_baud = { 4'h0, r_setup[23:0] }; + assign break_condition = { r_setup[23:0], 4'h0 }; + assign hw_flow_control = !r_setup[30]; + assign i_data_bits = i_setup[29:28]; + 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 i_parity_odd = i_setup[24]; + assign parity_odd = r_setup[24]; + assign fixdp_value = r_setup[24]; + + reg [27:0] baud_counter; + reg [3:0] state; + reg [7:0] lcl_data; + reg calc_parity, r_busy, zero_baud_counter, last_state; + + + // First step ... handle any hardware flow control, if so enabled. + // + // Clock in the flow control data, two clocks to avoid metastability + // Default to using hardware flow control (uart_setup[30]==0 to use it). + // Set this high order bit off if you do not wish to use it. + reg q_cts_n, qq_cts_n, ck_cts; + // While we might wish to give initial values to q_rts and ck_cts, + // 1) it's not required since the transmitter starts in a long wait + // state, and 2) doing so will prevent the synthesizer from optimizing + // this pin in the case it is hard set to 1'b1 external to this + // peripheral. + // + // initial q_cts_n = 1'b1; + // initial qq_cts_n = 1'b1; + // initial ck_cts = 1'b0; + always @(posedge i_clk) + { qq_cts_n, q_cts_n } <= { q_cts_n, i_cts_n }; + always @(posedge i_clk) + ck_cts <= (!qq_cts_n)||(!hw_flow_control); + + initial o_uart_tx = 1'b1; + initial r_busy = 1'b1; + initial state = TXU_IDLE; + always @(posedge i_clk) + if (i_reset) + begin + r_busy <= 1'b1; + state <= TXU_IDLE; + end else if (i_break) + begin + state <= TXU_BREAK; + r_busy <= 1'b1; + end else if (!zero_baud_counter) + begin // r_busy needs to be set coming into here + r_busy <= 1'b1; + end else if (state == TXU_BREAK) + begin + state <= TXU_IDLE; + r_busy <= !ck_cts; + end else if (state == TXU_IDLE) // STATE_IDLE + begin + if ((i_wr)&&(!r_busy)) + begin // Immediately start us off with a start bit + r_busy <= 1'b1; + case(i_data_bits) + 2'b00: state <= TXU_BIT_ZERO; + 2'b01: state <= TXU_BIT_ONE; + 2'b10: state <= TXU_BIT_TWO; + 2'b11: state <= TXU_BIT_THREE; + endcase + end else begin // Stay in idle + r_busy <= !ck_cts; + end + end else begin + // One clock tick in each of these states ... + // baud_counter <= clocks_per_baud - 28'h01; + r_busy <= 1'b1; + if (state[3] == 0) // First 8 bits + begin + if (state == TXU_BIT_SEVEN) + state <= (use_parity)? TXU_PARITY:TXU_STOP; + else + state <= state + 1; + end else if (state == TXU_PARITY) + begin + state <= TXU_STOP; + end else if (state == TXU_STOP) + begin // two stop bit(s) + if (dblstop) + state <= TXU_SECOND_STOP; + else + state <= TXU_IDLE; + end else // `TXU_SECOND_STOP and default: + begin + state <= TXU_IDLE; // Go back to idle + // Still r_busy, since we need to wait + // for the baud clock to finish counting + // out this last bit. + end + end + + // o_busy + // + // This is a wire, designed to be true is we are ever busy above. + // originally, this was going to be true if we were ever not in the + // idle state. The logic has since become more complex, hence we have + // a register dedicated to this and just copy out that registers value. + assign o_busy = (r_busy); + + + // r_setup + // + // Our setup register. Accept changes between any pair of transmitted + // words. The register itself has many fields to it. These are + // broken out up top, and indicate what 1) our baud rate is, 2) our + // number of stop bits, 3) what type of parity we are using, and 4) + // the size of our data word. + initial r_setup = INITIAL_SETUP; + always @(posedge i_clk) + if (!o_busy) + r_setup <= i_setup; + + // lcl_data + // + // This is our working copy of the i_data register which we use + // when transmitting. It is only of interest during transmit, and is + // allowed to be whatever at any other time. Hence, if r_busy isn't + // true, we can always set it. On the one clock where r_busy isn't + // true and i_wr is, we set it and r_busy is true thereafter. + // Then, on any zero_baud_counter (i.e. change between baud intervals) + // we simple logically shift the register right to grab the next bit. + initial lcl_data = 8'hff; + always @(posedge i_clk) + if (!r_busy) + lcl_data <= i_data; + else if (zero_baud_counter) + lcl_data <= { 1'b0, lcl_data[7:1] }; + + // o_uart_tx + // + // This is the final result/output desired of this core. It's all + // centered about o_uart_tx. This is what finally needs to follow + // the UART protocol. + // + // Ok, that said, our rules are: + // 1'b0 on any break condition + // 1'b0 on a start bit (IDLE, write, and not busy) + // lcl_data[0] during any data transfer, but only at the baud + // change + // PARITY -- During the parity bit. This depends upon whether or + // not the parity bit is fixed, then what it's fixed to, + // or changing, and hence what it's calculated value is. + // 1'b1 at all other times (stop bits, idle, etc) + always @(posedge i_clk) + if (i_reset) + o_uart_tx <= 1'b1; + else if ((i_break)||((i_wr)&&(!r_busy))) + o_uart_tx <= 1'b0; + else if (zero_baud_counter) + casez(state) + 4'b0???: o_uart_tx <= lcl_data[0]; + TXU_PARITY: o_uart_tx <= calc_parity; + default: o_uart_tx <= 1'b1; + endcase + + + // calc_parity + // + // Calculate the parity to be placed into the parity bit. If the + // parity is fixed, then the parity bit is given by the fixed parity + // value (r_setup[24]). Otherwise the parity is given by the GF2 + // sum of all the data bits (plus one for even parity). + initial calc_parity = 1'b0; + always @(posedge i_clk) + if (!o_busy) + calc_parity <= i_setup[24]; + else if (fixd_parity) + calc_parity <= fixdp_value; + else if (zero_baud_counter) + begin + if (state[3] == 0) // First 8 bits of msg + calc_parity <= calc_parity ^ lcl_data[0]; + else if (state == TXU_IDLE) + calc_parity <= parity_odd; + end else if (!r_busy) + calc_parity <= parity_odd; + + + // All of the above logic is driven by the baud counter. Bits must last + // clocks_per_baud in length, and this baud counter is what we use to + // make certain of that. + // + // The basic logic is this: at the beginning of a bit interval, start + // the baud counter and set it to count clocks_per_baud. When it gets + // to zero, restart it. + // + // However, comparing a 28'bit number to zero can be rather complex-- + // especially if we wish to do anything else on that same clock. For + // that reason, we create "zero_baud_counter". zero_baud_counter is + // nothing more than a flag that is true anytime baud_counter is zero. + // It's true when the logic (above) needs to step to the next bit. + // Simple enough? + // + // I wish we could stop there, but there are some other (ugly) + // conditions to deal with that offer exceptions to this basic logic. + // + // 1. When the user has commanded a BREAK across the line, we need to + // wait several baud intervals following the break before we start + // transmitting, to give any receiver a chance to recognize that we are + // out of the break condition, and to know that the next bit will be + // a stop bit. + // + // 2. A reset is similar to a break condition--on both we wait several + // baud intervals before allowing a start bit. + // + // 3. In the idle state, we stop our counter--so that upon a request + // to transmit when idle we can start transmitting immediately, rather + // than waiting for the end of the next (fictitious and arbitrary) baud + // interval. + // + // When (i_wr)&&(!r_busy)&&(state == TXU_IDLE) then we're not only in + // the idle state, but we also just accepted a command to start writing + // the next word. At this point, the baud counter needs to be reset + // to the number of clocks per baud, and zero_baud_counter set to zero. + // + // The logic is a bit twisted here, in that it will only check for the + // above condition when zero_baud_counter is false--so as to make + // certain the STOP bit is complete. + initial zero_baud_counter = 1'b0; + initial baud_counter = 28'h05; + always @(posedge i_clk) + begin + zero_baud_counter <= (baud_counter == 28'h01); + if ((i_reset)||(i_break)) + begin + // Give ourselves 16 bauds before being ready + baud_counter <= break_condition; + zero_baud_counter <= 1'b0; + end else if (!zero_baud_counter) + baud_counter <= baud_counter - 28'h01; + else if (state == TXU_BREAK) + begin + baud_counter <= 0; + zero_baud_counter <= 1'b1; + end else if (state == TXU_IDLE) + begin + baud_counter <= 28'h0; + zero_baud_counter <= 1'b1; + if ((i_wr)&&(!r_busy)) + begin + baud_counter <= { 4'h0, i_setup[23:0]} - 28'h01; + zero_baud_counter <= 1'b0; + end + end else if (last_state) + baud_counter <= clocks_per_baud - 28'h02; + else + baud_counter <= clocks_per_baud - 28'h01; + end + + initial last_state = 1'b0; + always @(posedge i_clk) + if (dblstop) + last_state <= (state == TXU_SECOND_STOP); + else + last_state <= (state == TXU_STOP); + + // Verilator lint_off UNUSED + wire [2:0] unused; + assign unused = { i_parity_odd, data_bits }; + // Verilator lint_on UNUSED + +`ifdef FORMAL + reg fsv_parity; + reg [30:0] fsv_setup; + reg [7:0] fsv_data; + reg f_past_valid; + + initial f_past_valid = 1'b0; + always @(posedge i_clk) + f_past_valid <= 1'b1; + + always @(posedge i_clk) + if ((i_wr)&&(!o_busy)) + fsv_data <= i_data; + + initial fsv_setup = INITIAL_SETUP; + always @(posedge i_clk) + if (!o_busy) + fsv_setup <= i_setup; + + always @(*) + assert(r_setup == fsv_setup); + + + always @(posedge i_clk) + assert(zero_baud_counter == (baud_counter == 0)); + + always @(*) + if (!o_busy) + assert(zero_baud_counter); + + /* + * + * Will only pass if !i_break && !i_reset, otherwise the setup can + * change in the middle of this operation + * + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_reset))&&(!$past(i_break)) + &&(($past(o_busy))||($past(i_wr)))) + assert(baud_counter <= { fsv_setup[23:0], 4'h0 }); + */ + + // A single baud interval + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(zero_baud_counter)) + &&(!$past(i_reset))&&(!$past(i_break))) + begin + assert($stable(o_uart_tx)); + assert($stable(state)); + assert($stable(lcl_data)); + if ((state != TXU_IDLE)&&(state != TXU_BREAK)) + assert($stable(calc_parity)); + assert(baud_counter == $past(baud_counter)-1'b1); + end + + // + // Our various sequence data declarations + reg [5:0] f_five_seq; + reg [6:0] f_six_seq; + reg [7:0] f_seven_seq; + reg [8:0] f_eight_seq; + reg [2:0] f_stop_seq; // parity bit, stop bit, double stop bit + + + // + // One byte transmitted + // + // DATA = the byte that is sent + // CKS = the number of clocks per bit + // + //////////////////////////////////////////////////////////////////////// + // + // Five bit data + // + //////////////////////////////////////////////////////////////////////// + + initial f_five_seq = 0; + always @(posedge i_clk) + if ((i_reset)||(i_break)) + f_five_seq = 0; + else if ((state == TXU_IDLE)&&(i_wr)&&(!o_busy) + &&(i_data_bits == 2'b11)) // five data bits + f_five_seq <= 1; + else if (zero_baud_counter) + f_five_seq <= f_five_seq << 1; + + always @(*) + if (|f_five_seq) + begin + assert(fsv_setup[29:28] == data_bits); + assert(data_bits == 2'b11); + assert(baud_counter < fsv_setup[23:0]); + + assert(1'b0 == |f_six_seq); + assert(1'b0 == |f_seven_seq); + assert(1'b0 == |f_eight_seq); + assert(r_busy); + assert(state > 4'h2); + end + + always @(*) + case(f_five_seq) + 6'h00: begin assert(1); end + 6'h01: begin + assert(state == 4'h3); + assert(o_uart_tx == 1'b0); + assert(lcl_data[4:0] == fsv_data[4:0]); + if (!fixd_parity) + assert(calc_parity == parity_odd); + end + 6'h02: begin + assert(state == 4'h4); + assert(o_uart_tx == fsv_data[0]); + assert(lcl_data[3:0] == fsv_data[4:1]); + if (!fixd_parity) + assert(calc_parity == fsv_data[0] ^ parity_odd); + end + 6'h04: begin + assert(state == 4'h5); + assert(o_uart_tx == fsv_data[1]); + assert(lcl_data[2:0] == fsv_data[4:2]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[1:0]) ^ parity_odd); + end + 6'h08: begin + assert(state == 4'h6); + assert(o_uart_tx == fsv_data[2]); + assert(lcl_data[1:0] == fsv_data[4:3]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[2:0]) ^ parity_odd); + end + 6'h10: begin + assert(state == 4'h7); + assert(o_uart_tx == fsv_data[3]); + assert(lcl_data[0] == fsv_data[4]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[3:0]) ^ parity_odd); + end + 6'h20: begin + if (use_parity) + assert(state == 4'h8); + else + assert(state == 4'h9); + assert(o_uart_tx == fsv_data[4]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[4:0]) ^ parity_odd); + end + default: begin assert(0); end + endcase + + //////////////////////////////////////////////////////////////////////// + // + // Six bit data + // + //////////////////////////////////////////////////////////////////////// + + initial f_six_seq = 0; + always @(posedge i_clk) + if ((i_reset)||(i_break)) + f_six_seq = 0; + else if ((state == TXU_IDLE)&&(i_wr)&&(!o_busy) + &&(i_data_bits == 2'b10)) // six data bits + f_six_seq <= 1; + else if (zero_baud_counter) + f_six_seq <= f_six_seq << 1; + + always @(*) + if (|f_six_seq) + begin + assert(fsv_setup[29:28] == 2'b10); + assert(fsv_setup[29:28] == data_bits); + assert(baud_counter < fsv_setup[23:0]); + + assert(1'b0 == |f_five_seq); + assert(1'b0 == |f_seven_seq); + assert(1'b0 == |f_eight_seq); + assert(r_busy); + assert(state > 4'h1); + end + + always @(*) + case(f_six_seq) + 7'h00: begin assert(1); end + 7'h01: begin + assert(state == 4'h2); + assert(o_uart_tx == 1'b0); + assert(lcl_data[5:0] == fsv_data[5:0]); + if (!fixd_parity) + assert(calc_parity == parity_odd); + end + 7'h02: begin + assert(state == 4'h3); + assert(o_uart_tx == fsv_data[0]); + assert(lcl_data[4:0] == fsv_data[5:1]); + if (!fixd_parity) + assert(calc_parity == fsv_data[0] ^ parity_odd); + end + 7'h04: begin + assert(state == 4'h4); + assert(o_uart_tx == fsv_data[1]); + assert(lcl_data[3:0] == fsv_data[5:2]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[1:0]) ^ parity_odd); + end + 7'h08: begin + assert(state == 4'h5); + assert(o_uart_tx == fsv_data[2]); + assert(lcl_data[2:0] == fsv_data[5:3]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[2:0]) ^ parity_odd); + end + 7'h10: begin + assert(state == 4'h6); + assert(o_uart_tx == fsv_data[3]); + assert(lcl_data[1:0] == fsv_data[5:4]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[3:0]) ^ parity_odd); + end + 7'h20: begin + assert(state == 4'h7); + assert(lcl_data[0] == fsv_data[5]); + assert(o_uart_tx == fsv_data[4]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[4:0]) ^ parity_odd)); + end + 7'h40: begin + if (use_parity) + assert(state == 4'h8); + else + assert(state == 4'h9); + assert(o_uart_tx == fsv_data[5]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[5:0]) ^ parity_odd)); + end + default: begin if (f_past_valid) assert(0); end + endcase + + //////////////////////////////////////////////////////////////////////// + // + // Seven bit data + // + //////////////////////////////////////////////////////////////////////// + initial f_seven_seq = 0; + always @(posedge i_clk) + if ((i_reset)||(i_break)) + f_seven_seq = 0; + else if ((state == TXU_IDLE)&&(i_wr)&&(!o_busy) + &&(i_data_bits == 2'b01)) // seven data bits + f_seven_seq <= 1; + else if (zero_baud_counter) + f_seven_seq <= f_seven_seq << 1; + + always @(*) + if (|f_seven_seq) + begin + assert(fsv_setup[29:28] == 2'b01); + assert(fsv_setup[29:28] == data_bits); + assert(baud_counter < fsv_setup[23:0]); + + assert(1'b0 == |f_five_seq); + assert(1'b0 == |f_six_seq); + assert(1'b0 == |f_eight_seq); + assert(r_busy); + assert(state != 4'h0); + end + + always @(*) + case(f_seven_seq) + 8'h00: begin assert(1); end + 8'h01: begin + assert(state == 4'h1); + assert(o_uart_tx == 1'b0); + assert(lcl_data[6:0] == fsv_data[6:0]); + if (!fixd_parity) + assert(calc_parity == parity_odd); + end + 8'h02: begin + assert(state == 4'h2); + assert(o_uart_tx == fsv_data[0]); + assert(lcl_data[5:0] == fsv_data[6:1]); + if (!fixd_parity) + assert(calc_parity == fsv_data[0] ^ parity_odd); + end + 8'h04: begin + assert(state == 4'h3); + assert(o_uart_tx == fsv_data[1]); + assert(lcl_data[4:0] == fsv_data[6:2]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[1:0]) ^ parity_odd); + end + 8'h08: begin + assert(state == 4'h4); + assert(o_uart_tx == fsv_data[2]); + assert(lcl_data[3:0] == fsv_data[6:3]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[2:0]) ^ parity_odd); + end + 8'h10: begin + assert(state == 4'h5); + assert(o_uart_tx == fsv_data[3]); + assert(lcl_data[2:0] == fsv_data[6:4]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[3:0]) ^ parity_odd); + end + 8'h20: begin + assert(state == 4'h6); + assert(o_uart_tx == fsv_data[4]); + assert(lcl_data[1:0] == fsv_data[6:5]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[4:0]) ^ parity_odd)); + end + 8'h40: begin + assert(state == 4'h7); + assert(lcl_data[0] == fsv_data[6]); + assert(o_uart_tx == fsv_data[5]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[5:0]) ^ parity_odd)); + end + 8'h80: begin + if (use_parity) + assert(state == 4'h8); + else + assert(state == 4'h9); + assert(o_uart_tx == fsv_data[6]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[6:0]) ^ parity_odd)); + end + default: begin if (f_past_valid) assert(0); end + endcase + + + //////////////////////////////////////////////////////////////////////// + // + // Eight bit data + // + //////////////////////////////////////////////////////////////////////// + initial f_eight_seq = 0; + always @(posedge i_clk) + if ((i_reset)||(i_break)) + f_eight_seq = 0; + else if ((state == TXU_IDLE)&&(i_wr)&&(!o_busy) + &&(i_data_bits == 2'b00)) // Eight data bits + f_eight_seq <= 1; + else if (zero_baud_counter) + f_eight_seq <= f_eight_seq << 1; + + always @(*) + if (|f_eight_seq) + begin + assert(fsv_setup[29:28] == 2'b00); + assert(fsv_setup[29:28] == data_bits); + assert(baud_counter < { 6'h0, fsv_setup[23:0]}); + + assert(1'b0 == |f_five_seq); + assert(1'b0 == |f_six_seq); + assert(1'b0 == |f_seven_seq); + assert(r_busy); + end + + always @(*) + case(f_eight_seq) + 9'h000: begin assert(1); end + 9'h001: begin + assert(state == 4'h0); + assert(o_uart_tx == 1'b0); + assert(lcl_data[7:0] == fsv_data[7:0]); + if (!fixd_parity) + assert(calc_parity == parity_odd); + end + 9'h002: begin + assert(state == 4'h1); + assert(o_uart_tx == fsv_data[0]); + assert(lcl_data[6:0] == fsv_data[7:1]); + if (!fixd_parity) + assert(calc_parity == fsv_data[0] ^ parity_odd); + end + 9'h004: begin + assert(state == 4'h2); + assert(o_uart_tx == fsv_data[1]); + assert(lcl_data[5:0] == fsv_data[7:2]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[1:0]) ^ parity_odd); + end + 9'h008: begin + assert(state == 4'h3); + assert(o_uart_tx == fsv_data[2]); + assert(lcl_data[4:0] == fsv_data[7:3]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[2:0]) ^ parity_odd); + end + 9'h010: begin + assert(state == 4'h4); + assert(o_uart_tx == fsv_data[3]); + assert(lcl_data[3:0] == fsv_data[7:4]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[3:0]) ^ parity_odd); + end + 9'h020: begin + assert(state == 4'h5); + assert(o_uart_tx == fsv_data[4]); + assert(lcl_data[2:0] == fsv_data[7:5]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[4:0]) ^ parity_odd); + end + 9'h040: begin + assert(state == 4'h6); + assert(o_uart_tx == fsv_data[5]); + assert(lcl_data[1:0] == fsv_data[7:6]); + if (!fixd_parity) + assert(calc_parity == (^fsv_data[5:0]) ^ parity_odd); + end + 9'h080: begin + assert(state == 4'h7); + assert(o_uart_tx == fsv_data[6]); + assert(lcl_data[0] == fsv_data[7]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[6:0]) ^ parity_odd)); + end + 9'h100: begin + if (use_parity) + assert(state == 4'h8); + else + assert(state == 4'h9); + assert(o_uart_tx == fsv_data[7]); + if (!fixd_parity) + assert(calc_parity == ((^fsv_data[7:0]) ^ parity_odd)); + end + default: begin if (f_past_valid) assert(0); end + endcase + + //////////////////////////////////////////////////////////////////////// + // + // Combined properties for all of the data sequences + // + //////////////////////////////////////////////////////////////////////// + always @(posedge i_clk) + if (((|f_five_seq[5:0]) || (|f_six_seq[6:0]) || (|f_seven_seq[7:0]) + || (|f_eight_seq[8:0])) + && ($past(zero_baud_counter))) + assert(baud_counter == { 4'h0, fsv_setup[23:0] }-1); + + + //////////////////////////////////////////////////////////////////////// + // + // The stop sequence + // + // This consists of any parity bit, as well as one or two stop bits + //////////////////////////////////////////////////////////////////////// + initial f_stop_seq = 1'b0; + always @(posedge i_clk) + if ((i_reset)||(i_break)) + f_stop_seq <= 0; + else if (zero_baud_counter) + begin + f_stop_seq <= 0; + if (f_stop_seq[0]) // Coming from a parity bit + begin + if (dblstop) + f_stop_seq[1] <= 1'b1; + else + f_stop_seq[2] <= 1'b1; + end + + if (f_stop_seq[1]) + f_stop_seq[2] <= 1'b1; + + if (f_eight_seq[8] | f_seven_seq[7] | f_six_seq[6] + | f_five_seq[5]) + begin + if (use_parity) + f_stop_seq[0] <= 1'b1; + else if (dblstop) + f_stop_seq[1] <= 1'b1; + else + f_stop_seq[2] <= 1'b1; + end + end + + always @(*) + if (|f_stop_seq) + begin + assert(1'b0 == |f_five_seq[4:0]); + assert(1'b0 == |f_six_seq[5:0]); + assert(1'b0 == |f_seven_seq[6:0]); + assert(1'b0 == |f_eight_seq[7:0]); + + assert(r_busy); + end + + always @(*) + if (f_stop_seq[0]) + begin + // 9 if dblstop and use_parity + if (dblstop) + assert(state == TXU_STOP); + else + assert(state == TXU_STOP); + assert(use_parity); + assert(o_uart_tx == fsv_parity); + end + + always @(*) + if (f_stop_seq[1]) + begin + // if (!use_parity) + assert(state == TXU_SECOND_STOP); + assert(dblstop); + assert(o_uart_tx); + end + + always @(*) + if (f_stop_seq[2]) + begin + assert(state == 4'hf); + assert(o_uart_tx); + assert(baud_counter < fsv_setup[23:0]-1'b1); + end + + + always @(*) + if (fsv_setup[25]) + fsv_parity <= fsv_setup[24]; + else + case(fsv_setup[29:28]) + 2'b00: fsv_parity = (^fsv_data[7:0]) ^ fsv_setup[24]; + 2'b01: fsv_parity = (^fsv_data[6:0]) ^ fsv_setup[24]; + 2'b10: fsv_parity = (^fsv_data[5:0]) ^ fsv_setup[24]; + 2'b11: fsv_parity = (^fsv_data[4:0]) ^ fsv_setup[24]; + endcase + + ////////////////////////////////////////////////////////////////////// + // + // The break sequence + // + ////////////////////////////////////////////////////////////////////// + reg [1:0] f_break_seq; + initial f_break_seq = 2'b00; + always @(posedge i_clk) + if (i_reset) + f_break_seq <= 2'b00; + else if (i_break) + f_break_seq <= 2'b01; + else if (!zero_baud_counter) + f_break_seq <= { |f_break_seq, 1'b0 }; + else + f_break_seq <= 0; + + always @(posedge i_clk) + if (f_break_seq[0]) + assert(baud_counter == { $past(fsv_setup[23:0]), 4'h0 }); + always @(posedge i_clk) + if ((f_past_valid)&&($past(f_break_seq[1]))&&(state != TXU_BREAK)) + begin + assert(state == TXU_IDLE); + assert(o_uart_tx == 1'b1); + end + + always @(*) + if (|f_break_seq) + begin + assert(state == TXU_BREAK); + assert(r_busy); + assert(o_uart_tx == 1'b0); + end + + ////////////////////////////////////////////////////////////////////// + // + // Properties for use during induction if we are made a submodule of + // the rxuart + // + ////////////////////////////////////////////////////////////////////// + + // Need enough bits for reset (24+4) plus enough bits for all of the + // various characters, 24+4, so 24+5 is a minimum of this counter + // +`ifndef TXUART + reg [28:0] f_counter; + initial f_counter = 0; + always @(posedge i_clk) + if (!o_busy) + f_counter <= 1'b0; + else + f_counter <= f_counter + 1'b1; + + always @(*) + if (f_five_seq[0]|f_six_seq[0]|f_seven_seq[0]|f_eight_seq[0]) + assert(f_counter == (fsv_setup[23:0] - baud_counter - 1)); + else if (f_five_seq[1]|f_six_seq[1]|f_seven_seq[1]|f_eight_seq[1]) + assert(f_counter == ({4'h0, fsv_setup[23:0], 1'b0} - baud_counter - 1)); + else if (f_five_seq[2]|f_six_seq[2]|f_seven_seq[2]|f_eight_seq[2]) + assert(f_counter == ({4'h0, fsv_setup[23:0], 1'b0} + +{5'h0, fsv_setup[23:0]} + - baud_counter - 1)); + else if (f_five_seq[3]|f_six_seq[3]|f_seven_seq[3]|f_eight_seq[3]) + assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + - baud_counter - 1)); + else if (f_five_seq[4]|f_six_seq[4]|f_seven_seq[4]|f_eight_seq[4]) + assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + +{5'h0, fsv_setup[23:0]} + - baud_counter - 1)); + else if (f_five_seq[5]|f_six_seq[5]|f_seven_seq[5]|f_eight_seq[5]) + assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 1)); + else if (f_six_seq[6]|f_seven_seq[6]|f_eight_seq[6]) + assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + +{5'h0, fsv_setup[23:0]} + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 1)); + else if (f_seven_seq[7]|f_eight_seq[7]) + assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} // 8 + - baud_counter - 1)); + else if (f_eight_seq[8]) + assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} // 9 + +{5'h0, fsv_setup[23:0]} + - baud_counter - 1)); + else if (f_stop_seq[0] || (!use_parity && f_stop_seq[1])) + begin + // Parity bit, or first of two stop bits + case(data_bits) + 2'b00: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{4'h0, fsv_setup[23:0], 1'b0} // 10 + - baud_counter - 1)); + 2'b01: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 9 + - baud_counter - 1)); + 2'b10: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + - baud_counter - 1)); // 8 + 2'b11: assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + +{5'h0, fsv_setup[23:0]} // 7 + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 1)); + endcase + end else if (!use_parity && !dblstop && f_stop_seq[2]) + begin + // No parity, single stop bit + // Different from the one above, since the last counter is has + // one fewer items within it + case(data_bits) + 2'b00: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{4'h0, fsv_setup[23:0], 1'b0} // 10 + - baud_counter - 2)); + 2'b01: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 9 + - baud_counter - 2)); + 2'b10: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + - baud_counter - 2)); // 8 + 2'b11: assert(f_counter == ({3'h0, fsv_setup[23:0], 2'b0} + +{5'h0, fsv_setup[23:0]} // 7 + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 2)); + endcase + end else if (f_stop_seq[1]) + begin + // Parity and the first of two stop bits + assert(dblstop && use_parity); + case(data_bits) + 2'b00: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 11 + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 1)); + 2'b01: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{4'h0, fsv_setup[23:0], 1'b0} // 10 + - baud_counter - 1)); + 2'b10: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 9 + - baud_counter - 1)); + 2'b11: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + - baud_counter - 1)); // 8 + endcase + end else if ((dblstop ^ use_parity) && f_stop_seq[2]) + begin + // Parity and one stop bit + // assert(!dblstop && use_parity); + case(data_bits) + 2'b00: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 11 + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 2)); + 2'b01: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{4'h0, fsv_setup[23:0], 1'b0} // 10 + - baud_counter - 2)); + 2'b10: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 9 + - baud_counter - 2)); + 2'b11: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + - baud_counter - 2)); // 8 + endcase + end else if (f_stop_seq[2]) + begin + assert(dblstop); + assert(use_parity); + // Parity and two stop bits + case(data_bits) + 2'b00: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{3'h0, fsv_setup[23:0], 2'b00} // 12 + - baud_counter - 2)); + 2'b01: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 11 + +{4'h0, fsv_setup[23:0], 1'b0} + - baud_counter - 2)); + 2'b10: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{4'h0, fsv_setup[23:0], 1'b0} // 10 + - baud_counter - 2)); + 2'b11: assert(f_counter == ({2'h0, fsv_setup[23:0], 3'b0} + +{5'h0, fsv_setup[23:0]} // 9 + - baud_counter - 2)); + endcase + end +`endif + + ////////////////////////////////////////////////////////////////////// + // + // Other properties, not necessarily associated with any sequences + // + ////////////////////////////////////////////////////////////////////// + always @(*) + assert((state < 4'hb)||(state >= 4'he)); + ////////////////////////////////////////////////////////////////////// + // + // Careless/limiting assumption section + // + ////////////////////////////////////////////////////////////////////// + always @(*) + assume(i_setup[23:0] > 2); + always @(*) + assert(fsv_setup[23:0] > 2); + +`endif // FORMAL +endmodule +