URL
https://opencores.org/ocsvn/wbuart32/wbuart32/trunk
Subversion Repositories wbuart32
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 20 to Rev 21
- ↔ Reverse comparison
Rev 20 → Rev 21
/wbuart32/trunk/rtl/rxuartlite.v
21,7 → 21,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// Copyright (C) 2015-2018, 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 |
56,10 → 56,13
`define RXUL_BIT_SIX 4'h6 |
`define RXUL_BIT_SEVEN 4'h7 |
`define RXUL_STOP 4'h8 |
`define RXUL_WAIT 4'h9 |
`define RXUL_IDLE 4'hf |
|
module rxuartlite(i_clk, i_uart_rx, o_wr, o_data); |
parameter [23:0] CLOCKS_PER_BAUD = 24'd868; |
parameter TIMER_BITS = 10; |
parameter [(TIMER_BITS-1):0] CLOCKS_PER_BAUD = 868; // 115200 MBaud at 100MHz |
localparam TB = TIMER_BITS; |
input wire i_clk; |
input wire i_uart_rx; |
output reg o_wr; |
66,12 → 69,12
output reg [7:0] o_data; |
|
|
wire [23:0] half_baud; |
reg [3:0] state; |
wire [(TB-1):0] half_baud; |
reg [3:0] state; |
|
assign half_baud = { 1'b0, CLOCKS_PER_BAUD[23:1] } - 24'h1; |
reg [23:0] baud_counter; |
reg zero_baud_counter; |
assign half_baud = { 1'b0, CLOCKS_PER_BAUD[(TB-1):1] }; |
reg [(TB-1):0] baud_counter; |
reg zero_baud_counter; |
|
|
// Since this is an asynchronous receiver, we need to register our |
79,26 → 82,22
// 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; |
initial q_uart = 1'b1; |
initial qq_uart = 1'b1; |
initial ck_uart = 1'b1; |
always @(posedge i_clk) |
begin |
q_uart <= i_uart_rx; |
qq_uart <= q_uart; |
ck_uart <= qq_uart; |
end |
{ ck_uart, qq_uart, q_uart } <= { qq_uart, q_uart, i_uart_rx }; |
|
// 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 [23:0] chg_counter; |
initial chg_counter = 24'h00; |
reg [(TB-1):0] chg_counter; |
initial chg_counter = {(TB){1'b1}}; |
always @(posedge i_clk) |
if (qq_uart != ck_uart) |
chg_counter <= 24'h00; |
else |
chg_counter <= 0; |
else if (chg_counter != { (TB){1'b1} }) |
chg_counter <= chg_counter + 1; |
|
// Are we in the middle of a baud iterval? Specifically, are we |
108,7 → 107,7
reg half_baud_time; |
initial half_baud_time = 0; |
always @(posedge i_clk) |
half_baud_time <= (~ck_uart)&&(chg_counter >= half_baud); |
half_baud_time <= (!ck_uart)&&(chg_counter >= half_baud-1'b1-1'b1); |
|
|
initial state = `RXUL_IDLE; |
118,19 → 117,19
begin // Idle state, independent of baud counter |
// By default, just stay in the IDLE state |
state <= `RXUL_IDLE; |
if ((~ck_uart)&&(half_baud_time)) |
if ((!ck_uart)&&(half_baud_time)) |
// UNLESS: We are in the center of a valid |
// start bit |
state <= `RXUL_BIT_ZERO; |
end else if (zero_baud_counter) |
end else if ((state >= `RXUL_WAIT)&&(ck_uart)) |
state <= `RXUL_IDLE; |
else if (zero_baud_counter) |
begin |
if (state < `RXUL_STOP) |
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; |
else // Wait for the next character |
state <= `RXUL_IDLE; |
end |
end |
|
142,8 → 141,8
// data in all other cases. Hence, let's keep it real simple. |
reg [7:0] data_reg; |
always @(posedge i_clk) |
if (zero_baud_counter) |
data_reg <= { ck_uart, data_reg[7:1] }; |
if ((zero_baud_counter)&&(state != `RXUL_STOP)) |
data_reg <= { qq_uart, data_reg[7:1] }; |
|
// 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 |
151,9 → 150,10
// 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)) |
if ((zero_baud_counter)&&(state == `RXUL_STOP)&&(ck_uart)) |
begin |
o_wr <= 1'b1; |
o_data <= data_reg; |
166,10 → 166,15
// 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 ((zero_baud_counter)|||(state == `RXUL_IDLE)) |
if (((state==`RXUL_IDLE))&&(!ck_uart)&&(half_baud_time)) |
baud_counter <= CLOCKS_PER_BAUD-1'b1; |
else |
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 |
178,14 → 183,518
// (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; |
initial zero_baud_counter = 1'b1; |
always @(posedge i_clk) |
if (state == `RXUL_IDLE) |
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; |
|
`ifdef FORMAL |
`define FORMAL_VERILATOR |
`else |
`ifdef VERILATOR |
`define FORMAL_VERILATOR |
`endif |
`endif |
|
`ifdef FORMAL |
|
// `define PHASE_TWO // SymbiYosys controls this definition |
`define PHASE_ONE_ASSERT assert |
`define PHASE_TWO_ASSERT assert |
|
`ifdef PHASE_TWO |
`undef PHASE_ONE_ASSERT |
`define PHASE_ONE_ASSERT assume |
`endif |
|
localparam F_CKRES = 10; |
|
wire f_tx_start, f_tx_busy; |
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; |
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; |
|
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]); |
|
|
|
/////////////////////////////////////////////////////////// |
// |
// |
// Generate 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); |
|
assign f_tx_step = $anyconst; |
// 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)); |
|
// initial rx_clock = $anyseq; |
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); |
|
|
////////////////////////////////////////////// |
// |
// |
// Build a simulated transmitter |
// |
// |
////////////////////////////////////////////// |
// |
// First, the simulated timing generator |
|
// parameter TIMER_BITS = 10; |
// parameter [(TIMER_BITS-1):0] CLOCKS_PER_BAUD = 868; |
// localparam TB = TIMER_BITS; |
|
assign f_tx_start = $anyseq; |
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 @(*) |
`PHASE_ONE_ASSERT(f_tx_baud < CLOCKS_PER_BAUD); |
|
always @(*) |
if (!f_tx_busy) |
`PHASE_ONE_ASSERT(f_tx_baud == 0); |
|
assign f_tx_zbaud = (f_tx_baud == 0); |
|
// Pick some data to transmit |
assign f_tx_data = $anyseq; |
|
// 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 |
|
// |
// |
// |
|
reg [9:0] f_tx_reg; |
reg f_tx_busy; |
|
// 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 |
`PHASE_ONE_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]) |
`PHASE_ONE_ASSERT(f_tx_reg[8:0] == { f_tx_data, 1'b0 }); |
else if (f_tx_reg[8]) |
`PHASE_ONE_ASSERT(f_tx_reg[7:0] == f_tx_data[7:0] ); |
else if (f_tx_reg[7]) |
`PHASE_ONE_ASSERT(f_tx_reg[6:0] == f_tx_data[7:1] ); |
else if (f_tx_reg[6]) |
`PHASE_ONE_ASSERT(f_tx_reg[5:0] == f_tx_data[7:2] ); |
else if (f_tx_reg[5]) |
`PHASE_ONE_ASSERT(f_tx_reg[4:0] == f_tx_data[7:3] ); |
else if (f_tx_reg[4]) |
`PHASE_ONE_ASSERT(f_tx_reg[3:0] == f_tx_data[7:4] ); |
else if (f_tx_reg[3]) |
`PHASE_ONE_ASSERT(f_tx_reg[2:0] == f_tx_data[7:5] ); |
else if (f_tx_reg[2]) |
`PHASE_ONE_ASSERT(f_tx_reg[1:0] == f_tx_data[7:6] ); |
else if (f_tx_reg[1]) |
`PHASE_ONE_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))) |
`PHASE_ONE_ASSERT(f_tx_count == 0); |
end else if (f_tx_reg[9]) |
`PHASE_ONE_ASSERT(f_tx_count == |
CLOCKS_PER_BAUD -1 -f_tx_baud); |
else if (f_tx_reg[8]) |
`PHASE_ONE_ASSERT(f_tx_count == |
2 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
else if (f_tx_reg[7]) |
`PHASE_ONE_ASSERT(f_tx_count == |
3 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
else if (f_tx_reg[6]) |
`PHASE_ONE_ASSERT(f_tx_count == |
4 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
else if (f_tx_reg[5]) |
`PHASE_ONE_ASSERT(f_tx_count == |
5 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
else if (f_tx_reg[4]) |
`PHASE_ONE_ASSERT(f_tx_count == |
6 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
else if (f_tx_reg[3]) |
`PHASE_ONE_ASSERT(f_tx_count == |
7 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
else if (f_tx_reg[2]) |
`PHASE_ONE_ASSERT(f_tx_count == |
8 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
else if (f_tx_reg[1]) |
`PHASE_ONE_ASSERT(f_tx_count == |
9 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
else if (f_tx_reg[0]) |
`PHASE_ONE_ASSERT(f_tx_count == |
10 * CLOCKS_PER_BAUD -1 -f_tx_baud); |
else |
`PHASE_ONE_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) |
`PHASE_ONE_ASSERT(f_rx_count |
== half_baud + (CLOCKS_PER_BAUD-baud_counter)); |
else if (state == 1) |
`PHASE_ONE_ASSERT(f_rx_count == half_baud + 2 * CLOCKS_PER_BAUD |
- baud_counter); |
else if (state == 2) |
`PHASE_ONE_ASSERT(f_rx_count == half_baud + 3 * CLOCKS_PER_BAUD |
- baud_counter); |
else if (state == 3) |
`PHASE_ONE_ASSERT(f_rx_count == half_baud + 4 * CLOCKS_PER_BAUD |
- baud_counter); |
else if (state == 4) |
`PHASE_ONE_ASSERT(f_rx_count == half_baud + 5 * CLOCKS_PER_BAUD |
- baud_counter); |
else if (state == 5) |
`PHASE_ONE_ASSERT(f_rx_count == half_baud + 6 * CLOCKS_PER_BAUD |
- baud_counter); |
else if (state == 6) |
`PHASE_ONE_ASSERT(f_rx_count == half_baud + 7 * CLOCKS_PER_BAUD |
- baud_counter); |
else if (state == 7) |
`PHASE_ONE_ASSERT(f_rx_count == half_baud + 8 * CLOCKS_PER_BAUD |
- baud_counter); |
else if (state == 8) |
`PHASE_ONE_ASSERT((f_rx_count == half_baud + 9 * CLOCKS_PER_BAUD |
- baud_counter) |
||(f_rx_count == half_baud + 10 * CLOCKS_PER_BAUD |
- baud_counter)); |
|
always @(*) |
`PHASE_ONE_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) |
`PHASE_ONE_ASSERT((state == `RXUL_IDLE)&&(baud_counter == 0) |
&&(zero_baud_counter)); |
|
always @(*) |
begin |
`PHASE_ONE_ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h2); |
`PHASE_ONE_ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h4); |
`PHASE_ONE_ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h5); |
`PHASE_ONE_ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h6); |
`PHASE_ONE_ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'h9); |
`PHASE_ONE_ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'ha); |
`PHASE_ONE_ASSERT({ ck_uart,qq_uart,q_uart,i_uart_rx } != 4'hb); |
`PHASE_ONE_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))) |
`PHASE_ONE_ASSERT(state == `RXUL_IDLE); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(state) >= `RXUL_WAIT) |
&&(($past(state) != `RXUL_IDLE)||(state == `RXUL_IDLE))) |
`PHASE_ONE_ASSERT(zero_baud_counter); |
|
// Calculate an absolute value of the difference between the two baud |
// clocks |
`ifdef PHASE_TWO |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(state)==`RXUL_IDLE)&&(state == `RXUL_IDLE)) |
begin |
`PHASE_TWO_ASSERT(($past(ck_uart)) |
||(chg_counter <= |
{ 1'b0, CLOCKS_PER_BAUD[(TB-1):1] })); |
end |
|
always @(posedge f_txclk) |
if (!f_past_valid_tx) |
`PHASE_TWO_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; |
|
wire [(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 }; |
|
|
wire [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}} }; |
|
wire [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) |
`PHASE_TWO_ASSERT((!f_tx_busy)||(f_tx_reg[9:1] == 0)); |
|
always @($global_clock) |
if (state == `RXUL_IDLE) |
begin |
`PHASE_TWO_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 |
zero_baud_counter <= (baud_counter == 24'h01); |
`PHASE_TWO_ASSERT((f_tx_reg[9:1]==0)||(f_tx_count < (3 + CLOCKS_PER_BAUD/2))); |
end else if (state == 0) |
`PHASE_TWO_ASSERT(f_sub_baud_difference |
<= 2 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
else if (state == 1) |
`PHASE_TWO_ASSERT(f_sub_baud_difference |
<= 3 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
else if (state == 2) |
`PHASE_TWO_ASSERT(f_sub_baud_difference |
<= 4 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
else if (state == 3) |
`PHASE_TWO_ASSERT(f_sub_baud_difference |
<= 5 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
else if (state == 4) |
`PHASE_TWO_ASSERT(f_sub_baud_difference |
<= 6 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
else if (state == 5) |
`PHASE_TWO_ASSERT(f_sub_baud_difference |
<= 7 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
else if (state == 6) |
`PHASE_TWO_ASSERT(f_sub_baud_difference |
<= 8 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
else if (state == 7) |
`PHASE_TWO_ASSERT(f_sub_baud_difference |
<= 9 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
else if (state == 8) |
`PHASE_TWO_ASSERT(f_sub_baud_difference |
<= 10 * ((CLOCKS_PER_BAUD<<F_CKRES)/20)); |
|
always @(posedge i_clk) |
if (o_wr) |
`PHASE_TWO_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) |
`PHASE_TWO_ASSERT(!data_reg[7]); |
|
if (state == 4'h1) |
`PHASE_TWO_ASSERT((data_reg[7] |
== $past(f_tx_data[0]))&&(!data_reg[6])); |
|
if (state == 4'h2) |
`PHASE_TWO_ASSERT(data_reg[7:6] |
== $past(f_tx_data[1:0])); |
|
if (state == 4'h3) |
`PHASE_TWO_ASSERT(data_reg[7:5] == $past(f_tx_data[2:0])); |
|
if (state == 4'h4) |
`PHASE_TWO_ASSERT(data_reg[7:4] == $past(f_tx_data[3:0])); |
|
if (state == 4'h5) |
`PHASE_TWO_ASSERT(data_reg[7:3] == $past(f_tx_data[4:0])); |
|
if (state == 4'h6) |
`PHASE_TWO_ASSERT(data_reg[7:2] == $past(f_tx_data[5:0])); |
|
if (state == 4'h7) |
`PHASE_TWO_ASSERT(data_reg[7:1] == $past(f_tx_data[6:0])); |
|
if (state == 4'h8) |
`PHASE_TWO_ASSERT(data_reg[7:0] == $past(f_tx_data[7:0])); |
end |
|
always @(posedge i_clk) |
cover(o_wr); |
`endif |
`endif |
`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 |
|
|
/wbuart32/trunk/rtl/txuartlite.v
64,7 → 64,10
// |
// |
module txuartlite(i_clk, i_wr, i_data, o_uart_tx, o_busy); |
parameter [23:0] CLOCKS_PER_BAUD = 24'd8; // 24'd868; |
parameter [4:0] TIMING_BITS = 5'd24; |
localparam TB = TIMING_BITS; |
parameter [(TB-1):0] CLOCKS_PER_BAUD = 8; // 24'd868; |
parameter [0:0] F_OPT_CLK2FFLOGIC = 1'b0; |
input wire i_clk; |
input wire i_wr; |
input wire [7:0] i_data; |
75,7 → 78,7
// for transmission. |
output wire o_busy; |
|
reg [23:0] baud_counter; |
reg [(TB-1):0] baud_counter; |
reg [3:0] state; |
reg [7:0] lcl_data; |
reg r_busy, zero_baud_counter; |
87,8 → 90,9
if (!zero_baud_counter) |
// r_busy needs to be set coming into here |
r_busy <= 1'b1; |
else if (state == `TXUL_IDLE) // STATE_IDLE |
else if (state > `TXUL_STOP) // STATE_IDLE |
begin |
state <= `TXUL_IDLE; |
r_busy <= 1'b0; |
if ((i_wr)&&(!r_busy)) |
begin // Immediately start us off with a start bit |
99,7 → 103,7
// One clock tick in each of these states ... |
r_busy <= 1'b1; |
if (state <=`TXUL_STOP) // start bit, 8-d bits, stop-b |
state <= state + 1; |
state <= state + 1'b1; |
else |
state <= `TXUL_IDLE; |
end |
184,8 → 188,8
// 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 = 24'h05; |
initial zero_baud_counter = 1'b1; |
initial baud_counter = 0; |
always @(posedge i_clk) |
begin |
zero_baud_counter <= (baud_counter == 24'h01); |
198,6 → 202,10
baud_counter <= CLOCKS_PER_BAUD - 24'h01; |
zero_baud_counter <= 1'b0; |
end |
end else if ((zero_baud_counter)&&(state == 4'h9)) |
begin |
baud_counter <= 0; |
zero_baud_counter <= 1'b1; |
end else if (!zero_baud_counter) |
baud_counter <= baud_counter - 24'h01; |
else |
222,21 → 230,27
|
reg f_past_valid, f_last_clk; |
|
always @($global_clock) |
generate if (F_OPT_CLK2FFLOGIC) |
begin |
restrict(i_clk == !f_last_clk); |
f_last_clk <= i_clk; |
if (!$rose(i_clk)) |
|
always @($global_clock) |
begin |
`ASSUME($stable(i_wr)); |
`ASSUME($stable(i_data)); |
restrict(i_clk == !f_last_clk); |
f_last_clk <= i_clk; |
if (!$rose(i_clk)) |
begin |
`ASSUME($stable(i_wr)); |
`ASSUME($stable(i_data)); |
end |
end |
end |
|
end endgenerate |
|
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
|
initial `ASSUME(!i_wr); |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_wr))&&($past(o_busy))) |
begin |
246,8 → 260,7
|
// Check the baud counter |
always @(posedge i_clk) |
if (zero_baud_counter) |
assert(baud_counter == 0); |
assert(zero_baud_counter == (baud_counter == 0)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(baud_counter != 0))&&($past(state != `TXUL_IDLE))) |
257,7 → 270,7
if ((f_past_valid)&&(!$past(zero_baud_counter))&&($past(state != `TXUL_IDLE))) |
assert($stable(o_uart_tx)); |
|
reg [23:0] f_baud_count; |
reg [(TB-1):0] f_baud_count; |
initial f_baud_count = 1'b0; |
always @(posedge i_clk) |
if (zero_baud_counter) |
278,11 → 291,14
if (zero_baud_counter) |
f_txbits <= { o_uart_tx, f_txbits[9:1] }; |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(zero_baud_counter)) |
&&(!$past(state==`TXUL_IDLE))) |
assert(state == $past(state)); |
|
reg [3:0] f_bitcount; |
initial f_bitcount = 0; |
always @(posedge i_clk) |
//if (baud_counter == CLOCKS_PER_BAUD - 24'h01) |
//f_bitcount <= f_bitcount + 1'b1; |
if ((!f_past_valid)||(!$past(f_past_valid))) |
f_bitcount <= 0; |
else if ((state == `TXUL_IDLE)&&(zero_baud_counter)) |
303,15 → 319,8
always @(posedge i_clk) |
if (f_bitcount > 0) |
assert(!f_txbits[subcount]); |
/* |
|
always @(posedge i_clk) |
if ((f_bitcount > 2)&&(f_bitcount <= 10)) |
assert(f_txbits[f_bitcount-2:0] |
== f_request_tx_data[7:(9-f_bitcount)]); |
*/ |
|
always @(posedge i_clk) |
if (f_bitcount == 4'ha) |
begin |
assert(f_txbits[8:1] == f_request_tx_data); |
320,9 → 329,100
|
always @(posedge i_clk) |
assert((state <= `TXUL_STOP + 1'b1)||(state == `TXUL_IDLE)); |
// |
// |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(f_past_valid))&&($past(o_busy))) |
cover(!o_busy); |
|
`endif // FORMAL |
`ifdef VERIFIC_SVA |
reg [7:0] fsv_data; |
|
// |
// Grab a copy of the data any time we are sent a new byte to transmit |
// We'll use this in a moment to compare the item transmitted against |
// what is supposed to be transmitted |
// |
always @(posedge i_clk) |
if ((i_wr)&&(!o_busy)) |
fsv_data <= i_data; |
|
// |
// One baud interval |
// |
// 1. The UART output is constant at DAT |
// 2. The internal state remains constant at ST |
// 3. CKS = the number of clocks per bit. |
// |
// Everything stays constant during the CKS clocks with the exception |
// of (zero_baud_counter), which is *only* raised on the last clock |
// interval |
sequence BAUD_INTERVAL(CKS, DAT, SR, ST); |
((o_uart_tx == DAT)&&(state == ST) |
&&(lcl_data == SR) |
&&(!zero_baud_counter))[*(CKS-1)] |
##1 (o_uart_tx == DAT)&&(state == ST) |
&&(lcl_data == SR) |
&&(zero_baud_counter); |
endsequence |
|
// |
// One byte transmitted |
// |
// DATA = the byte that is sent |
// CKS = the number of clocks per bit |
// |
sequence SEND(CKS, DATA); |
BAUD_INTERVAL(CKS, 1'b0, DATA, 4'h0) |
##1 BAUD_INTERVAL(CKS, DATA[0], {{(1){1'b1}},DATA[7:1]}, 4'h1) |
##1 BAUD_INTERVAL(CKS, DATA[1], {{(2){1'b1}},DATA[7:2]}, 4'h2) |
##1 BAUD_INTERVAL(CKS, DATA[2], {{(3){1'b1}},DATA[7:3]}, 4'h3) |
##1 BAUD_INTERVAL(CKS, DATA[3], {{(4){1'b1}},DATA[7:4]}, 4'h4) |
##1 BAUD_INTERVAL(CKS, DATA[4], {{(5){1'b1}},DATA[7:5]}, 4'h5) |
##1 BAUD_INTERVAL(CKS, DATA[5], {{(6){1'b1}},DATA[7:6]}, 4'h6) |
##1 BAUD_INTERVAL(CKS, DATA[6], {{(7){1'b1}},DATA[7:7]}, 4'h7) |
##1 BAUD_INTERVAL(CKS, DATA[7], 8'hff, 4'h8) |
##1 BAUD_INTERVAL(CKS, 1'b1, 8'hff, 4'h9); |
endsequence |
|
// |
// Transmit one byte |
// |
// Once the byte is transmitted, make certain we return to |
// idle |
// |
assert property ( |
@(posedge i_clk) |
(i_wr)&&(!o_busy) |
|=> ((o_busy) throughout SEND(CLOCKS_PER_BAUD,fsv_data)) |
##1 (!o_busy)&&(o_uart_tx)&&(zero_baud_counter)); |
|
assume property ( |
@(posedge i_clk) |
(i_wr)&&(o_busy) |=> |
(i_wr)&&(o_busy)&&($stable(i_data))); |
|
// |
// Make certain that o_busy is true any time zero_baud_counter is |
// non-zero |
// |
always @(*) |
assert((o_busy)||(zero_baud_counter) ); |
|
// If and only if zero_baud_counter is true, baud_counter must be zero |
// Insist on that relationship here. |
always @(*) |
assert(zero_baud_counter == (baud_counter == 0)); |
|
// To make certain baud_counter stays below CLOCKS_PER_BAUD |
always @(*) |
assert(baud_counter < CLOCKS_PER_BAUD); |
|
// |
// Insist that we are only ever in a valid state |
always @(*) |
assert((state <= `TXUL_STOP+1'b1)||(state == `TXUL_IDLE)); |
|
`endif // Verific SVA |
endmodule |
|
/wbuart32/trunk/rtl/ufifo.v
18,7 → 18,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// Copyright (C) 2015-2018, 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 |
48,6 → 48,7
parameter BW=8; // Byte/data width |
parameter [3:0] LGFLEN=4; |
parameter RXFIFO=1'b0; |
parameter [0:0] F_OPT_CLK2FFLOGIC = 1'b0; |
input wire i_clk, i_rst; |
input wire i_wr; |
input wire [(BW-1):0] i_data; |
296,18 → 297,21
|
initial restrict(i_rst); |
|
always @($global_clock) |
generate if (F_OPT_CLK2FFLOGIC) |
begin |
restrict(i_clk == !f_last_clk); |
f_last_clk <= i_clk; |
if (!$rose(i_clk)) |
always @($global_clock) |
begin |
`ASSUME($stable(i_rst)); |
`ASSUME($stable(i_wr)); |
`ASSUME($stable(i_data)); |
`ASSUME($stable(i_rd)); |
restrict(i_clk == !f_last_clk); |
f_last_clk <= i_clk; |
if (!$rose(i_clk)) |
begin |
`ASSUME($stable(i_rst)); |
`ASSUME($stable(i_wr)); |
`ASSUME($stable(i_data)); |
`ASSUME($stable(i_rd)); |
end |
end |
end |
end endgenerate |
|
// |
// Underflows are a very real possibility, should the user wish to |
/wbuart32/trunk/rtl/wbuart.v
179,7 → 179,7
|
// The clear to send line, which may be ignored, but which we set here |
// to be true any time the FIFO has fewer than N-2 items in it. |
// Why N-1? Because at N-1 we are totally full, but already so full |
// Why not N-1? Because at N-1 we are totally full, but already so full |
// that if the transmit end starts sending we won't have a location to |
// receive it. (Transmit might've started on the next character by the |
// time we set this--thus we need to set it to one, one character before |