| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Filename: rxuartlite.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, |
| // and a UART input. Set the parameter to the number of clocks per |
| // baud. When data becomes available, the o_wr line will be asserted |
| // for one clock cycle. |
| // |
| // This interface only handles 8N1 serial port communications. It does |
| // not handle the break, parity, or frame error conditions. |
| // |
| // |
| // Creator: Dan Gisselquist, Ph.D. |
| // Gisselquist Technology, LLC |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| // }}} |
| // Copyright (C) 2015-2021, 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 |
| // }}} |
| module rxuartlite #( |
| // {{{ |
| parameter TIMER_BITS = 10, |
| `ifdef FORMAL |
| parameter [(TIMER_BITS-1):0] CLOCKS_PER_BAUD = 16, // Necessary for formal proof |
| `else |
| parameter [(TIMER_BITS-1):0] CLOCKS_PER_BAUD = 868 // 115200 MBaud at 100MHz |
| `endif |
| |
| // }}} |
| ) ( |
| // {{{ |
| input wire i_clk, |
| input wire i_uart_rx, |
| output reg o_wr, |
| output reg [7:0] o_data |
| // }}} |
| ); |
| |
| |
| localparam TB = TIMER_BITS; |
| // |
| localparam [3:0] RXUL_BIT_ZERO = 4'h0; |
| // Verilator lint_off UNUSED |
| // These are used by the formal solver |
| localparam [3:0] RXUL_BIT_ONE = 4'h1; |
| localparam [3:0] RXUL_BIT_TWO = 4'h2; |
| localparam [3:0] RXUL_BIT_THREE = 4'h3; |
| localparam [3:0] RXUL_BIT_FOUR = 4'h4; |
| localparam [3:0] RXUL_BIT_FIVE = 4'h5; |
| localparam [3:0] RXUL_BIT_SIX = 4'h6; |
| localparam [3:0] RXUL_BIT_SEVEN = 4'h7; |
| // Verilator lint_on UNUSED |
| localparam [3:0] RXUL_STOP = 4'h8; |
| localparam [3:0] RXUL_WAIT = 4'h9; |
| localparam [3:0] RXUL_IDLE = 4'hf; |
| |
| // Signal/register declarations |
| // {{{ |
| wire [(TB-1):0] half_baud; |
| reg [3:0] state; |
| |
| assign half_baud = { 1'b0, CLOCKS_PER_BAUD[(TB-1):1] }; |
| reg [(TB-1):0] baud_counter; |
| reg zero_baud_counter; |
| |
| reg q_uart, qq_uart, ck_uart; |
| reg [(TB-1):0] chg_counter; |
| reg half_baud_time; |
| reg [7:0] data_reg; |
| // }}} |
| |
| // ck_uart |
| // {{{ |
| // 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. |
| initial q_uart = 1'b1; |
| initial qq_uart = 1'b1; |
| initial ck_uart = 1'b1; |
| always @(posedge i_clk) |
| { ck_uart, qq_uart, q_uart } <= { qq_uart, q_uart, i_uart_rx }; |
| // }}} |
| |
| // chg_counter |
| // {{{ |
| // 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. |
| initial chg_counter = {(TB){1'b1}}; |
| always @(posedge i_clk) |
| if (qq_uart != ck_uart) |
| chg_counter <= 0; |
| else if (chg_counter != { (TB){1'b1} }) |
| chg_counter <= chg_counter + 1; |
| // }}} |
| |
| // half_baud_time |
| // {{{ |
| // 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. |
| initial half_baud_time = 0; |
| always @(posedge i_clk) |
| half_baud_time <= (!ck_uart)&&(chg_counter >= half_baud-1'b1-1'b1); |
| // }}} |
| |
| // state |
| // {{{ |
| initial state = RXUL_IDLE; |
| always @(posedge i_clk) |
| if (state == RXUL_IDLE) |
| begin // Idle state, independent of baud counter |
| // {{{ |
| // By default, just stay in the IDLE state |
| state <= RXUL_IDLE; |
| if ((!ck_uart)&&(half_baud_time)) |
| // UNLESS: We are in the center of a valid |
| // start bit |
| state <= RXUL_BIT_ZERO; |
| // }}} |
| end else if ((state >= RXUL_WAIT)&&(ck_uart)) |
| state <= RXUL_IDLE; |
| else if (zero_baud_counter) |
| begin |
| // {{{ |
| if (state <= RXUL_STOP) |
| // Data arrives least significant bit first. |
| // By the time this is clocked in, it's what |
| // you'll have. |
| state <= state + 1; |
| // }}} |
| end |
| // }}} |
| |
| // data_reg |
| // {{{ |
| // 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. |
| always @(posedge i_clk) |
| if ((zero_baud_counter)&&(state != RXUL_STOP)) |
| data_reg <= { qq_uart, data_reg[7:1] }; |
| // }}} |
| |
| // o_wr, o_data |
| // {{{ |
| // 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, and tell others (for one clock) that data is |
| // available. |
| // |
| initial o_wr = 1'b0; |
| initial o_data = 8'h00; |
| always @(posedge i_clk) |
| if ((zero_baud_counter)&&(state == RXUL_STOP)&&(ck_uart)) |
| begin |
| o_wr <= 1'b1; |
| o_data <= data_reg; |
| end else |
| o_wr <= 1'b0; |
| // }}} |
| |
| // baud_counter -- 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. |
| initial baud_counter = 0; |
| always @(posedge i_clk) |
| if (((state==RXUL_IDLE))&&(!ck_uart)&&(half_baud_time)) |
| baud_counter <= CLOCKS_PER_BAUD-1'b1; |
| else if (state == RXUL_WAIT) |
| baud_counter <= 0; |
| else if ((zero_baud_counter)&&(state < RXUL_STOP)) |
| baud_counter <= CLOCKS_PER_BAUD-1'b1; |
| else if (!zero_baud_counter) |
| baud_counter <= baud_counter-1'b1; |
| // }}} |
| |
| // 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'b1; |
| always @(posedge i_clk) |
| if ((state == RXUL_IDLE)&&(!ck_uart)&&(half_baud_time)) |
| zero_baud_counter <= 1'b0; |
| else if (state == RXUL_WAIT) |
| zero_baud_counter <= 1'b1; |
| else if ((zero_baud_counter)&&(state < RXUL_STOP)) |
| zero_baud_counter <= 1'b0; |
| else if (baud_counter == 1) |
| zero_baud_counter <= 1'b1; |
| // }}} |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Formal properties |
| // {{{ |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| // Declarations |
| // {{{ |
| `ifdef FORMAL |
| `define FORMAL_VERILATOR |
| `else |
| `ifdef VERILATOR |
| `define FORMAL_VERILATOR |
| `endif |
| `endif |
| |
| `ifdef FORMAL |
| `define ASSUME assume |
| `define ASSERT assert |
| `ifdef VERIFIC |
| // We need this to use $global_clock below |
| (* gclk *) wire gbl_clk; |
| global clocking @(posedge gbl_clk); endclocking |
| `endif |
| |
| |
| localparam F_CKRES = 10; |
| |
| (* anyseq *) wire f_tx_start; |
| (* anyconst *) wire [(F_CKRES-1):0] f_tx_step; |
| reg f_tx_zclk; |
| reg [(TB-1):0] f_tx_timer; |
| wire [7:0] f_rx_newdata; |
| reg [(TB-1):0] f_tx_baud; |
| wire f_tx_zbaud; |
| |
| wire [(TB-1):0] f_max_baud_difference; |
| reg [(TB-1):0] f_baud_difference; |
| reg [(TB+3):0] f_tx_count, f_rx_count; |
| (* anyseq *) wire [7:0] f_tx_data; |
| |
| wire f_txclk; |
| reg [1:0] f_rx_clock; |
| reg [(F_CKRES-1):0] f_tx_clock; |
| reg f_past_valid, f_past_valid_tx; |
| |
| reg [9:0] f_tx_reg; |
| reg f_tx_busy; |
| |
| // }}} |
| |
| initial f_past_valid = 1'b0; |
| always @(posedge i_clk) |
| f_past_valid <= 1'b1; |
| |
| initial f_rx_clock = 3'h0; |
| always @($global_clock) |
| f_rx_clock <= f_rx_clock + 1'b1; |
| |
| always @(*) |
| assume(i_clk == f_rx_clock[1]); |
| |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Assume a transmitted signal |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| // First, calculate the transmit clock |
| localparam [(F_CKRES-1):0] F_MIDSTEP = { 2'b01, {(F_CKRES-2){1'b0}} }; |
| // |
| // Need to allow us to slip by half a baud clock over 10 baud intervals |
| // |
| // (F_STEP / (2^F_CKRES)) * (CLOCKS_PER_BAUD)*10 < CLOCKS_PER_BAUD/2 |
| // F_STEP * 2 * 10 < 2^F_CKRES |
| localparam [(F_CKRES-1):0] F_HALFSTEP= F_MIDSTEP/32; |
| localparam [(F_CKRES-1):0] F_MINSTEP = F_MIDSTEP - F_HALFSTEP + 1; |
| localparam [(F_CKRES-1):0] F_MAXSTEP = F_MIDSTEP + F_HALFSTEP - 1; |
| |
| initial assert(F_MINSTEP <= F_MIDSTEP); |
| initial assert(F_MIDSTEP <= F_MAXSTEP); |
| |
| // assume((f_tx_step >= F_MINSTEP)&&(f_tx_step <= F_MAXSTEP)); |
| // |
| // |
| always @(*) assume((f_tx_step == F_MINSTEP) |
| ||(f_tx_step == F_MIDSTEP) |
| ||(f_tx_step == F_MAXSTEP)); |
| |
| always @($global_clock) |
| f_tx_clock <= f_tx_clock + f_tx_step; |
| |
| assign f_txclk = f_tx_clock[F_CKRES-1]; |
| // |
| initial f_past_valid_tx = 1'b0; |
| always @(posedge f_txclk) |
| f_past_valid_tx <= 1'b1; |
| |
| initial assume(i_uart_rx); |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // The simulated timing generator |
| |
| always @(*) |
| if (f_tx_busy) |
| assume(!f_tx_start); |
| |
| initial f_tx_baud = 0; |
| always @(posedge f_txclk) |
| if ((f_tx_zbaud)&&((f_tx_busy)||(f_tx_start))) |
| f_tx_baud <= CLOCKS_PER_BAUD-1'b1; |
| else if (!f_tx_zbaud) |
| f_tx_baud <= f_tx_baud - 1'b1; |
| |
| always @(*) |
| `ASSERT(f_tx_baud < CLOCKS_PER_BAUD); |
| |
| always @(*) |
| if (!f_tx_busy) |
| `ASSERT(f_tx_baud == 0); |
| |
| assign f_tx_zbaud = (f_tx_baud == 0); |
| |
| // But only if we aren't busy |
| initial assume(f_tx_data == 0); |
| always @(posedge f_txclk) |
| if ((!f_tx_zbaud)||(f_tx_busy)||(!f_tx_start)) |
| assume(f_tx_data == $past(f_tx_data)); |
| |
| // Force the data to change on a clock only |
| always @($global_clock) |
| if ((f_past_valid)&&(!$rose(f_txclk))) |
| assume($stable(f_tx_data)); |
| else if (f_tx_busy) |
| assume($stable(f_tx_data)); |
| |
| // |
| always @($global_clock) |
| if ((!f_past_valid)||(!$rose(f_txclk))) |
| begin |
| assume($stable(f_tx_start)); |
| assume($stable(f_tx_data)); |
| end |
| |
| // |
| // |
| // |
| |
| // Here's the transmitter itself (roughly) |
| initial f_tx_busy = 1'b0; |
| initial f_tx_reg = 0; |
| always @(posedge f_txclk) |
| if (!f_tx_zbaud) |
| begin |
| `ASSERT(f_tx_busy); |
| end else begin |
| f_tx_reg <= { 1'b0, f_tx_reg[9:1] }; |
| if (f_tx_start) |
| f_tx_reg <= { 1'b1, f_tx_data, 1'b0 }; |
| end |
| |
| // Create a busy flag that we'll use |
| always @(*) |
| if (!f_tx_zbaud) |
| f_tx_busy <= 1'b1; |
| else if (|f_tx_reg) |
| f_tx_busy <= 1'b1; |
| else |
| f_tx_busy <= 1'b0; |
| |
| // |
| // Tie the TX register to the TX data |
| always @(posedge f_txclk) |
| if (f_tx_reg[9]) |
| `ASSERT(f_tx_reg[8:0] == { f_tx_data, 1'b0 }); |
| else if (f_tx_reg[8]) |
| `ASSERT(f_tx_reg[7:0] == f_tx_data[7:0] ); |
| else if (f_tx_reg[7]) |
| `ASSERT(f_tx_reg[6:0] == f_tx_data[7:1] ); |
| else if (f_tx_reg[6]) |
| `ASSERT(f_tx_reg[5:0] == f_tx_data[7:2] ); |
| else if (f_tx_reg[5]) |
| `ASSERT(f_tx_reg[4:0] == f_tx_data[7:3] ); |
| else if (f_tx_reg[4]) |
| `ASSERT(f_tx_reg[3:0] == f_tx_data[7:4] ); |
| else if (f_tx_reg[3]) |
| `ASSERT(f_tx_reg[2:0] == f_tx_data[7:5] ); |
| else if (f_tx_reg[2]) |
| `ASSERT(f_tx_reg[1:0] == f_tx_data[7:6] ); |
| else if (f_tx_reg[1]) |
| `ASSERT(f_tx_reg[0] == f_tx_data[7]); |
| |
| // Our counter since we start |
| initial f_tx_count = 0; |
| always @(posedge f_txclk) |
| if (!f_tx_busy) |
| f_tx_count <= 0; |
| else |
| f_tx_count <= f_tx_count + 1'b1; |
| |
| always @(*) |
| if (f_tx_reg == 10'h0) |
| assume(i_uart_rx); |
| else |
| assume(i_uart_rx == f_tx_reg[0]); |
| |
| // |
| // Make sure the absolute transmit clock timer matches our state |
| // |
| always @(posedge f_txclk) |
| if (!f_tx_busy) |
| begin |
| if ((!f_past_valid_tx)||(!$past(f_tx_busy))) |
| `ASSERT(f_tx_count == 0); |
| end else if (f_tx_reg[9]) |
| `ASSERT(f_tx_count == |
| CLOCKS_PER_BAUD -1 -f_tx_baud); |
| else if (f_tx_reg[8]) |
| `ASSERT(f_tx_count == |
| 2 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
| else if (f_tx_reg[7]) |
| `ASSERT(f_tx_count == |
| 3 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
| else if (f_tx_reg[6]) |
| `ASSERT(f_tx_count == |
| 4 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
| else if (f_tx_reg[5]) |
| `ASSERT(f_tx_count == |
| 5 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
| else if (f_tx_reg[4]) |
| `ASSERT(f_tx_count == |
| 6 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
| else if (f_tx_reg[3]) |
| `ASSERT(f_tx_count == |
| 7 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
| else if (f_tx_reg[2]) |
| `ASSERT(f_tx_count == |
| 8 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
| else if (f_tx_reg[1]) |
| `ASSERT(f_tx_count == |
| 9 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
| else if (f_tx_reg[0]) |
| `ASSERT(f_tx_count == |
| 10 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
| else |
| `ASSERT(f_tx_count == |
| 11 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
| |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Receiver |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| // Count RX clocks since the start of the first stop bit, measured in |
| // rx clocks |
| initial f_rx_count = 0; |
| always @(posedge i_clk) |
| if (state == RXUL_IDLE) |
| f_rx_count = (!ck_uart) ? (chg_counter+2) : 0; |
| else |
| f_rx_count <= f_rx_count + 1'b1; |
| always @(posedge i_clk) |
| if (state == 0) |
| `ASSERT(f_rx_count |
| == half_baud + (CLOCKS_PER_BAUD-baud_counter)); |
| else if (state == 1) |
| `ASSERT(f_rx_count == half_baud + 2 * CLOCKS_PER_BAUD |
| - baud_counter); |
| else if (state == 2) |
| `ASSERT(f_rx_count == half_baud + 3 * CLOCKS_PER_BAUD |
| - baud_counter); |
| else if (state == 3) |
| `ASSERT(f_rx_count == half_baud + 4 * CLOCKS_PER_BAUD |
| - baud_counter); |
| else if (state == 4) |
| `ASSERT(f_rx_count == half_baud + 5 * CLOCKS_PER_BAUD |
| - baud_counter); |
| else if (state == 5) |
| `ASSERT(f_rx_count == half_baud + 6 * CLOCKS_PER_BAUD |
| - baud_counter); |
| else if (state == 6) |
| `ASSERT(f_rx_count == half_baud + 7 * CLOCKS_PER_BAUD |
| - baud_counter); |
| else if (state == 7) |
| `ASSERT(f_rx_count == half_baud + 8 * CLOCKS_PER_BAUD |
| - baud_counter); |
| else if (state == 8) |
| `ASSERT((f_rx_count == half_baud + 9 * CLOCKS_PER_BAUD |
| - baud_counter) |
| ||(f_rx_count == half_baud + 10 * CLOCKS_PER_BAUD |
| - baud_counter)); |
| |
| always @(*) |
| `ASSERT( ((!zero_baud_counter) |
| &&(state == RXUL_IDLE) |
| &&(baud_counter == 0)) |
| ||((zero_baud_counter)&&(baud_counter == 0)) |
| ||((!zero_baud_counter)&&(baud_counter != 0))); |
| |
| always @(posedge i_clk) |
| if (!f_past_valid) |
| `ASSERT((state == RXUL_IDLE)&&(baud_counter == 0) |
| &&(zero_baud_counter)); |
| |
| always @(*) |
| begin |
| `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h2); |
| `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h4); |
| `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h5); |
| `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h6); |
| `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h9); |
| `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'ha); |
| `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'hb); |
| `ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'hd); |
| end |
| |
| always @(posedge i_clk) |
| if ((f_past_valid)&&($past(state) >= RXUL_WAIT)&&($past(ck_uart))) |
| `ASSERT(state == RXUL_IDLE); |
| |
| always @(posedge i_clk) |
| if ((f_past_valid)&&($past(state) >= RXUL_WAIT) |
| &&(($past(state) != RXUL_IDLE)||(state == RXUL_IDLE))) |
| `ASSERT(zero_baud_counter); |
| |
| // Calculate an absolute value of the difference between the two baud |
| // clocks |
| always @(posedge i_clk) |
| if ((f_past_valid)&&($past(state)==RXUL_IDLE)&&(state == RXUL_IDLE)) |
| begin |
| `ASSERT(($past(ck_uart)) |
| ||(chg_counter <= |
| { 1'b0, CLOCKS_PER_BAUD[(TB-1):1] })); |
| end |
| |
| always @(posedge f_txclk) |
| if (!f_past_valid_tx) |
| `ASSERT((state == RXUL_IDLE)&&(baud_counter == 0) |
| &&(zero_baud_counter)&&(!f_tx_busy)); |
| |
| wire [(TB+3):0] f_tx_count_two_clocks_ago; |
| assign f_tx_count_two_clocks_ago = f_tx_count - 2; |
| always @(*) |
| if (f_tx_count >= f_rx_count + 2) |
| f_baud_difference = f_tx_count_two_clocks_ago - f_rx_count; |
| else |
| f_baud_difference = f_rx_count - f_tx_count_two_clocks_ago; |
| |
| localparam F_SYNC_DLY = 8; |
| |
| reg [(TB+4+F_CKRES-1):0] f_sub_baud_difference; |
| reg [F_CKRES-1:0] ck_tx_clock; |
| reg [((F_SYNC_DLY-1)*F_CKRES)-1:0] q_tx_clock; |
| reg [TB+3:0] ck_tx_count; |
| reg [(F_SYNC_DLY-1)*(TB+4)-1:0] q_tx_count; |
| initial q_tx_count = 0; |
| initial ck_tx_count = 0; |
| initial q_tx_clock = 0; |
| initial ck_tx_clock = 0; |
| always @($global_clock) |
| { ck_tx_clock, q_tx_clock } <= { q_tx_clock, f_tx_clock }; |
| always @($global_clock) |
| { ck_tx_count, q_tx_count } <= { q_tx_count, f_tx_count }; |
| |
| |
| reg [TB+4+F_CKRES-1:0] f_ck_tx_time, f_rx_time; |
| always @(*) |
| f_ck_tx_time = { ck_tx_count, !ck_tx_clock[F_CKRES-1], |
| ck_tx_clock[F_CKRES-2:0] }; |
| always @(*) |
| f_rx_time = { f_rx_count, !f_rx_clock[1], f_rx_clock[0], |
| {(F_CKRES-2){1'b0}} }; |
| |
| reg [TB+4+F_CKRES-1:0] f_signed_difference; |
| always @(*) |
| f_signed_difference = f_ck_tx_time - f_rx_time; |
| |
| always @(*) |
| if (f_signed_difference[TB+4+F_CKRES-1]) |
| f_sub_baud_difference = -f_signed_difference; |
| else |
| f_sub_baud_difference = f_signed_difference; |
| |
| always @($global_clock) |
| if (state == RXUL_WAIT) |
| `ASSERT((!f_tx_busy)||(f_tx_reg[9:1] == 0)); |
| |
| always @($global_clock) |
| if (state == RXUL_IDLE) |
| begin |
| `ASSERT((!f_tx_busy)||(f_tx_reg[9])||(f_tx_reg[9:1]==0)); |
| if (!ck_uart) |
| ;//`PHASE_TWO_ASSERT((f_rx_count < 4)||(f_sub_baud_difference <= ((CLOCKS_PER_BAUD<<F_CKRES)/20))); |
| else |
| `ASSERT((f_tx_reg[9:1]==0)||(f_tx_count < (3 + CLOCKS_PER_BAUD/2))); |
| end else if (state == 0) |
| `ASSERT(f_sub_baud_difference |
| <= 2 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
| else if (state == 1) |
| `ASSERT(f_sub_baud_difference |
| <= 3 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
| else if (state == 2) |
| `ASSERT(f_sub_baud_difference |
| <= 4 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
| else if (state == 3) |
| `ASSERT(f_sub_baud_difference |
| <= 5 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
| else if (state == 4) |
| `ASSERT(f_sub_baud_difference |
| <= 6 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
| else if (state == 5) |
| `ASSERT(f_sub_baud_difference |
| <= 7 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
| else if (state == 6) |
| `ASSERT(f_sub_baud_difference |
| <= 8 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
| else if (state == 7) |
| `ASSERT(f_sub_baud_difference |
| <= 9 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
| else if (state == 8) |
| `ASSERT(f_sub_baud_difference |
| <= 10 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
| |
| always @(posedge i_clk) |
| if (o_wr) |
| `ASSERT(o_data == $past(f_tx_data,4)); |
| |
| // always @(posedge i_clk) |
| // if ((zero_baud_counter)&&(state != 4'hf)&&(CLOCKS_PER_BAUD > 6)) |
| // assert(i_uart_rx == ck_uart); |
| |
| // Make sure the data register matches |
| always @(posedge i_clk) |
| // if ((f_past_valid)&&(state != $past(state))) |
| begin |
| if (state == 4'h0) |
| `ASSERT(!data_reg[7]); |
| |
| if (state == 4'h1) |
| `ASSERT((data_reg[7] |
| == $past(f_tx_data[0]))&&(!data_reg[6])); |
| |
| if (state == 4'h2) |
| `ASSERT(data_reg[7:6] |
| == $past(f_tx_data[1:0])); |
| |
| if (state == 4'h3) |
| `ASSERT(data_reg[7:5] == $past(f_tx_data[2:0])); |
| |
| if (state == 4'h4) |
| `ASSERT(data_reg[7:4] == $past(f_tx_data[3:0])); |
| |
| if (state == 4'h5) |
| `ASSERT(data_reg[7:3] == $past(f_tx_data[4:0])); |
| |
| if (state == 4'h6) |
| `ASSERT(data_reg[7:2] == $past(f_tx_data[5:0])); |
| |
| if (state == 4'h7) |
| `ASSERT(data_reg[7:1] == $past(f_tx_data[6:0])); |
| |
| if (state == 4'h8) |
| `ASSERT(data_reg[7:0] == $past(f_tx_data[7:0])); |
| end |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Cover properties |
| // {{{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| always @(posedge i_clk) |
| cover(o_wr); // Step 626, takes about 20mins |
| |
| always @(posedge i_clk) |
| begin |
| cover(!ck_uart); |
| cover((f_past_valid)&&($rose(ck_uart))); // 82 |
| cover((zero_baud_counter)&&(state == RXUL_BIT_ZERO)); // 110 |
| cover((zero_baud_counter)&&(state == RXUL_BIT_ONE)); // 174 |
| cover((zero_baud_counter)&&(state == RXUL_BIT_TWO)); // 238 |
| cover((zero_baud_counter)&&(state == RXUL_BIT_THREE));// 302 |
| cover((zero_baud_counter)&&(state == RXUL_BIT_FOUR)); // 366 |
| cover((zero_baud_counter)&&(state == RXUL_BIT_FIVE)); // 430 |
| cover((zero_baud_counter)&&(state == RXUL_BIT_SIX)); // 494 |
| cover((zero_baud_counter)&&(state == RXUL_BIT_SEVEN));// 558 |
| cover((zero_baud_counter)&&(state == RXUL_STOP)); // 622 |
| cover((zero_baud_counter)&&(state == RXUL_WAIT)); // 626 |
| end |
| `endif |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Properties to test via Verilator *and* formal |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| `ifdef FORMAL_VERILATOR |
| // FORMAL properties which can be tested via Verilator as well as |
| // Yosys FORMAL |
| always @(*) |
| assert((state == 4'hf)||(state <= RXUL_WAIT)); |
| always @(*) |
| assert(zero_baud_counter == (baud_counter == 0)? 1'b1:1'b0); |
| always @(*) |
| assert(baud_counter <= CLOCKS_PER_BAUD-1'b1); |
| // }}} |
| `endif |
| // }}} |
| endmodule |