polish uart testbenches
This commit is contained in:
parent
0132d8fab0
commit
7ed4a9e6b8
10
Makefile
10
Makefile
|
|
@ -88,6 +88,16 @@ bridge_tx_tb:
|
|||
vvp sim.out
|
||||
rm sim.out
|
||||
|
||||
uart_rx_tb:
|
||||
iverilog -g2012 -o sim.out -y src/manta/uart_iface test/functional_sim/uart_rx_tb.sv
|
||||
vvp sim.out
|
||||
rm sim.out
|
||||
|
||||
uart_tx_tb:
|
||||
iverilog -g2012 -o sim.out -y src/manta/uart_iface test/functional_sim/uart_tx_tb.sv
|
||||
vvp sim.out
|
||||
rm sim.out
|
||||
|
||||
# Formal Verification
|
||||
formal:
|
||||
sby -f test/formal_verification/uart_rx.sby
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
## Prior to v1.0.0 release:
|
||||
_targeting August 2023_
|
||||
|
||||
- Clean up UART testbenches, make them actually test things
|
||||
- Pull text from thesis into documentation site
|
||||
- Update docs with API reference
|
||||
- Make super super sure everything works (need hardware for that)
|
||||
- Port logic analyzer examples to the icestick
|
||||
- __IO Core:__ Clock domain crossing
|
||||
- __IO Core:__ Clock domain crossing, check that >16 bit probes work
|
||||
- __Logic Analyzer Core:__ CDC, trigger modes, external trigger
|
||||
|
||||
## Prior to v1.1.0 release:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
`default_nettype none
|
||||
|
||||
`define CP 10
|
||||
`define HCP 5
|
||||
|
||||
task automatic test_receive (
|
||||
input [7:0] data,
|
||||
input integer CLOCKS_PER_BAUD
|
||||
);
|
||||
|
||||
// send a byte to uart_rx, and check that it receives properly
|
||||
|
||||
integer data_bit = 0;
|
||||
logic valid_has_been_asserted = 0;
|
||||
|
||||
for(int i=0; i < (10*CLOCKS_PER_BAUD); i++) begin
|
||||
|
||||
// clock out data bits on each baud period
|
||||
data_bit = i / CLOCKS_PER_BAUD;
|
||||
if (data_bit == 0) uart_rx_tb.tb_urx_rx = 0;
|
||||
else if ((data_bit > 0) && (data_bit < 9)) uart_rx_tb.tb_urx_rx = data[data_bit-1];
|
||||
else uart_rx_tb.tb_urx_rx = 1;
|
||||
|
||||
|
||||
// every cycle, run checks on uart_rx:
|
||||
|
||||
// make sure valid isn't asserted before end of byte
|
||||
if (data_bit < 9) begin
|
||||
assert(uart_rx_tb.urx_tb_valid == 0) else $fatal(0, "valid asserted before end of byte!");
|
||||
end
|
||||
|
||||
// make sure valid is only asserted once
|
||||
if (valid_has_been_asserted) begin
|
||||
assert(uart_rx_tb.urx_tb_valid == 0) else $fatal(0, "valid asserted more than once!");
|
||||
end
|
||||
|
||||
// make sure byte is presented once last bit has been clocked out
|
||||
if (uart_rx_tb.urx_tb_valid) begin
|
||||
assert(data_bit == 9) else $fatal(0, "byte presented before it is complete");
|
||||
assert(uart_rx_tb.urx_tb_data == data) else $fatal(0, "wrong data!");
|
||||
valid_has_been_asserted = 1;
|
||||
end
|
||||
|
||||
#`CP;
|
||||
end
|
||||
|
||||
// make sure valid was asserted at some point
|
||||
assert (valid_has_been_asserted) else $fatal(0, "valid not asserted!");
|
||||
endtask
|
||||
|
||||
module uart_rx_tb();
|
||||
logic clk;
|
||||
integer test_num;
|
||||
|
||||
logic tb_urx_rx;
|
||||
logic [7:0] urx_tb_data;
|
||||
logic urx_tb_valid;
|
||||
uart_rx #(.CLOCKS_PER_BAUD(10)) urx (
|
||||
.clk(clk),
|
||||
.rx(tb_urx_rx),
|
||||
.data_o(urx_tb_data),
|
||||
.valid_o(urx_tb_valid));
|
||||
|
||||
always begin
|
||||
#`HCP
|
||||
clk = !clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
$dumpfile("uart_rx_tb.vcd");
|
||||
$dumpvars(0, uart_rx_tb);
|
||||
clk = 0;
|
||||
test_num = 0;
|
||||
tb_urx_rx = 1;
|
||||
#`HCP;
|
||||
|
||||
// test all possible bytes
|
||||
test_num = test_num + 1;
|
||||
for(int i=0; i < 256; i++) begin
|
||||
test_receive(i, 10);
|
||||
#(100*`CP);
|
||||
end
|
||||
|
||||
// test all possible bytes (no delay between them)
|
||||
test_num = test_num + 1;
|
||||
for(int i=0; i < 256; i++) begin
|
||||
test_receive(i, 10);
|
||||
end
|
||||
|
||||
$finish();
|
||||
end
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Filename: rxuart.v
|
||||
//
|
||||
// Project: Verilog Tutorial Example file
|
||||
//
|
||||
// Purpose: Receives a character from a UART (serial port) wire. Key
|
||||
// features of this core include:
|
||||
//
|
||||
// - The baud rate is constant, and set by the CLOCKS_PER_BAUD parameter.
|
||||
// To be successful, one baud interval must be (approximately)
|
||||
// equal to CLOCKS_PER_BAUD / CLOCK_RATE_HZ seconds long.
|
||||
//
|
||||
// - The protocol used is the basic 8N1: 8 data bits, 1 stop bit, and no
|
||||
// parity.
|
||||
//
|
||||
// - This core has no reset
|
||||
// - This core has no error detection for frame errors
|
||||
// - This core cannot detect, report, or even recover from, a break
|
||||
// condition on the line. A break condition is defined as a
|
||||
// period of time where the i_uart_rx line is held low for longer
|
||||
// than one data byte (10 baud intervals)
|
||||
//
|
||||
// - There's no clock rate detection in this core
|
||||
//
|
||||
// Perhaps one of the nicer features of this core is that it (can be)
|
||||
// formally verified. It depends upon a separate (formally verified)
|
||||
// transmit core for this purpose.
|
||||
//
|
||||
// As with the other cores within this tutorial, there may (or may not) be
|
||||
// bugs within this design for you to find.
|
||||
//
|
||||
//
|
||||
// Creator: Dan Gisselquist, Ph.D.
|
||||
// Gisselquist Technology, LLC
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Written and distributed by Gisselquist Technology, LLC
|
||||
//
|
||||
// This program is hereby granted to the public domain.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
`default_nettype none
|
||||
|
||||
module rx_uart(
|
||||
input wire i_clk,
|
||||
input wire i_uart_rx,
|
||||
output reg o_wr,
|
||||
output reg [7:0] o_data);
|
||||
|
||||
parameter [15:0] CLOCKS_PER_BAUD = 868;
|
||||
localparam [3:0] IDLE = 4'h0;
|
||||
localparam [3:0] BIT_ZERO = 4'h1;
|
||||
// localparam [3:0] BIT_ONE = 4'h2;
|
||||
// localparam [3:0] BIT_TWO = 4'h3;
|
||||
// localparam [3:0] BIT_THREE = 4'h4;
|
||||
// localparam [3:0] BIT_FOUR = 4'h5;
|
||||
// localparam [3:0] BIT_FIVE = 4'h6;
|
||||
// localparam [3:0] BIT_SIX = 4'h7;
|
||||
// localparam [3:0] BIT_SEVEN = 4'h8;
|
||||
localparam [3:0] STOP_BIT = 4'h9;
|
||||
|
||||
reg [3:0] state;
|
||||
reg [15:0] baud_counter;
|
||||
reg zero_baud_counter;
|
||||
|
||||
// 2FF Synchronizer
|
||||
//
|
||||
reg ck_uart;
|
||||
reg q_uart;
|
||||
initial { ck_uart, q_uart } = -1;
|
||||
always @(posedge i_clk)
|
||||
{ ck_uart, q_uart } <= { q_uart, i_uart_rx };
|
||||
|
||||
initial state = IDLE;
|
||||
initial baud_counter = 0;
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (state == IDLE) begin
|
||||
state <= IDLE;
|
||||
baud_counter <= 0;
|
||||
if (!ck_uart) begin
|
||||
state <= BIT_ZERO;
|
||||
baud_counter <= CLOCKS_PER_BAUD+CLOCKS_PER_BAUD/2-1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
else if (zero_baud_counter) begin
|
||||
state <= state + 1;
|
||||
baud_counter <= CLOCKS_PER_BAUD-1'b1;
|
||||
if (state == STOP_BIT) begin
|
||||
state <= IDLE;
|
||||
baud_counter <= 0;
|
||||
end
|
||||
end
|
||||
|
||||
else baud_counter <= baud_counter - 1'b1;
|
||||
|
||||
always @(*)
|
||||
zero_baud_counter = (baud_counter == 0);
|
||||
|
||||
always @(posedge i_clk)
|
||||
if ((zero_baud_counter)&&(state != STOP_BIT))
|
||||
o_data <= { ck_uart, o_data[7:1] };
|
||||
|
||||
initial o_wr = 1'b0;
|
||||
always @(posedge i_clk)
|
||||
o_wr <= ((zero_baud_counter)&&(state == STOP_BIT));
|
||||
|
||||
endmodule
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Filename: txuart.v
|
||||
//
|
||||
// Project: Verilog Tutorial Example file
|
||||
//
|
||||
// Purpose: Transmit outputs over a single UART line. This particular UART
|
||||
// implementation has been extremely simplified: it does not handle
|
||||
// generating break conditions, nor does it handle anything other than the
|
||||
// 8N1 (8 data bits, no parity, 1 stop bit) UART sub-protocol.
|
||||
//
|
||||
// To interface with this module, connect it to your system clock, and
|
||||
// pass it the byte of data you wish to transmit. Strobe the i_wr line
|
||||
// high for one 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
|
||||
// get ignored. The output will be placed on the o_txuart output line.
|
||||
//
|
||||
// There are known deficiencies in the formal proof found within this
|
||||
// module. These have been left behind for you (the student) to fix.
|
||||
//
|
||||
// Creator: Dan Gisselquist, Ph.D.
|
||||
// Gisselquist Technology, LLC
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Written and distributed by Gisselquist Technology, LLC
|
||||
//
|
||||
// This program is hereby granted to the public domain.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
`default_nettype none
|
||||
//
|
||||
//
|
||||
//
|
||||
module tx_uart(
|
||||
input wire i_clk,
|
||||
input wire i_wr,
|
||||
input wire [7:0] i_data,
|
||||
output reg o_uart_tx,
|
||||
output reg o_busy);
|
||||
|
||||
parameter [23:0] CLOCKS_PER_BAUD = 24'd868;
|
||||
|
||||
// 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.
|
||||
|
||||
// Define several states
|
||||
localparam [3:0] START = 4'h0,
|
||||
BIT_ZERO = 4'h1,
|
||||
BIT_ONE = 4'h2,
|
||||
BIT_TWO = 4'h3,
|
||||
BIT_THREE = 4'h4,
|
||||
BIT_FOUR = 4'h5,
|
||||
BIT_FIVE = 4'h6,
|
||||
BIT_SIX = 4'h7,
|
||||
BIT_SEVEN = 4'h8,
|
||||
LAST = 4'h8,
|
||||
IDLE = 4'hf;
|
||||
|
||||
reg [23:0] counter;
|
||||
reg [3:0] state;
|
||||
reg [8:0] lcl_data;
|
||||
reg baud_stb;
|
||||
|
||||
// o_busy
|
||||
//
|
||||
// This is a register, 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.
|
||||
|
||||
initial o_busy = 1'b0;
|
||||
initial state = IDLE;
|
||||
always @(posedge i_clk)
|
||||
if ((i_wr)&&(!o_busy))
|
||||
// Immediately start us off with a start bit
|
||||
{ o_busy, state } <= { 1'b1, START };
|
||||
else if (baud_stb)
|
||||
begin
|
||||
if (state == IDLE) // Stay in IDLE
|
||||
{ o_busy, state } <= { 1'b0, IDLE };
|
||||
else if (state < LAST) begin
|
||||
o_busy <= 1'b1;
|
||||
state <= state + 1'b1;
|
||||
end else // Wait for IDLE
|
||||
{ o_busy, state } <= { 1'b1, IDLE };
|
||||
end
|
||||
|
||||
|
||||
|
||||
// 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 o_busy isn't
|
||||
// true, we can always set it. On the one clock where o_busy isn't
|
||||
// true and i_wr is, we set it and o_busy is true thereafter.
|
||||
// Then, on any baud_stb (i.e. change between baud intervals)
|
||||
// we simple logically shift the register right to grab the next bit.
|
||||
initial lcl_data = 9'h1ff;
|
||||
always @(posedge i_clk)
|
||||
if ((i_wr)&&(!o_busy))
|
||||
lcl_data <= { i_data, 1'b0 };
|
||||
else if (baud_stb)
|
||||
lcl_data <= { 1'b1, lcl_data[8: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.
|
||||
//
|
||||
assign o_uart_tx = lcl_data[0];
|
||||
|
||||
|
||||
// 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 "baud_stb". baud_stb 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)&&(!o_busy)&&(state == 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 baud_stb set to zero.
|
||||
//
|
||||
// The logic is a bit twisted here, in that it will only check for the
|
||||
// above condition when baud_stb is false--so as to make
|
||||
// certain the STOP bit is complete.
|
||||
initial baud_stb = 1'b1;
|
||||
initial counter = 0;
|
||||
always @(posedge i_clk)
|
||||
if ((i_wr)&&(!o_busy))
|
||||
begin
|
||||
counter <= CLOCKS_PER_BAUD - 1'b1;
|
||||
baud_stb <= 1'b0;
|
||||
end else if (!baud_stb)
|
||||
begin
|
||||
baud_stb <= (counter == 24'h01);
|
||||
counter <= counter - 1'b1;
|
||||
end else if (state != IDLE)
|
||||
begin
|
||||
counter <= CLOCKS_PER_BAUD - 1'b1;
|
||||
baud_stb <= 1'b0;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
`default_nettype none
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module uart_tb();
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
logic [7:0] tx_data;
|
||||
logic tx_start;
|
||||
|
||||
// transmitters
|
||||
logic tx_done_manta;
|
||||
logic txd_manta;
|
||||
uart_tx #(.CLOCKS_PER_BAUD(10)) tx_manta (
|
||||
.clk(clk),
|
||||
|
||||
.data_i(tx_data),
|
||||
.start_i(tx_start),
|
||||
.done_o(tx_done_manta),
|
||||
|
||||
.tx(txd_manta));
|
||||
|
||||
logic tx_busy_zipcpu;
|
||||
logic tx_done_zipcpu;
|
||||
logic txd_zipcpu;
|
||||
assign tx_done_zipcpu = ~tx_busy_zipcpu;
|
||||
tx_uart #(.CLOCKS_PER_BAUD(10)) tx_zipcpu (
|
||||
.i_clk(clk),
|
||||
|
||||
.i_wr(tx_start),
|
||||
.i_data(tx_data),
|
||||
.o_uart_tx(txd_zipcpu),
|
||||
.o_busy(tx_busy_zipcpu));
|
||||
|
||||
// receivers
|
||||
logic [7:0] rx_data_manta;
|
||||
logic rx_valid_manta;
|
||||
uart_rx #(.CLOCKS_PER_BAUD(10)) rx_manta (
|
||||
.clk(clk),
|
||||
.rx(txd_manta),
|
||||
.data_o(rx_data_manta),
|
||||
.valid_o(rx_valid_manta));
|
||||
|
||||
logic [7:0] rx_data_zipcpu;
|
||||
logic rx_valid_zipcpu;
|
||||
rx_uart #(.CLOCKS_PER_BAUD(10)) rx_zipcpu (
|
||||
.i_clk(clk),
|
||||
.i_uart_rx(txd_zipcpu),
|
||||
.o_wr(rx_valid_zipcpu),
|
||||
.o_data(rx_data_zipcpu));
|
||||
|
||||
always begin
|
||||
#5;
|
||||
clk = !clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
$dumpfile("uart.vcd");
|
||||
$dumpvars(0, uart_tb);
|
||||
clk = 0;
|
||||
|
||||
tx_data = 'hFF;
|
||||
tx_start = 0;
|
||||
#10;
|
||||
rst = 0;
|
||||
#10;
|
||||
tx_start = 1;
|
||||
#10;
|
||||
tx_start = 0;
|
||||
#10000;
|
||||
|
||||
// send another byte!
|
||||
tx_data = 'b0100_1101;
|
||||
tx_start = 1;
|
||||
#3000;
|
||||
tx_start = 0;
|
||||
#10000;
|
||||
|
||||
$finish();
|
||||
end
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
`default_nettype none
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
`define CP 10
|
||||
`define HCP 5
|
||||
|
||||
module uart_tx_tb();
|
||||
logic clk;
|
||||
|
||||
logic [7:0] tb_utx_data;
|
||||
logic tb_utx_valid;
|
||||
logic utx_tb_busy;
|
||||
|
||||
logic utx_tb_tx;
|
||||
|
||||
uart_tx #(.CLOCKS_PER_BAUD(10)) utx (
|
||||
.clk(clk),
|
||||
|
||||
.data(tb_utx_data),
|
||||
.valid(tb_utx_valid),
|
||||
.busy(utx_tb_busy),
|
||||
|
||||
.tx(utx_tb_tx));
|
||||
|
||||
|
||||
logic zcpu_tb_tx;
|
||||
logic zcpu_tb_busy;
|
||||
|
||||
tx_uart #(.CLOCKS_PER_BAUD(10)) zcpu_utx (
|
||||
.i_clk(clk),
|
||||
|
||||
.i_wr(tb_utx_valid),
|
||||
.i_data(tb_utx_data),
|
||||
|
||||
.o_uart_tx(zcpu_tb_tx),
|
||||
.o_busy(zcpu_tb_busy));
|
||||
|
||||
logic zcpu_urx_valid;
|
||||
logic[7:0] zcpu_urx_data;
|
||||
|
||||
rx_uart #(.CLOCKS_PER_BAUD(10)) zcpu_urx (
|
||||
.i_clk(clk),
|
||||
|
||||
.i_uart_rx(utx_tb_tx),
|
||||
.o_wr(zcpu_urx_valid),
|
||||
.o_data(zcpu_urx_data));
|
||||
|
||||
always begin
|
||||
#`HCP
|
||||
clk = !clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
$dumpfile("uart_tx.vcd");
|
||||
$dumpvars(0, uart_tx_tb);
|
||||
clk = 0;
|
||||
tb_utx_data = 0;
|
||||
tb_utx_valid = 0;
|
||||
#`HCP;
|
||||
|
||||
#(10*`CP);
|
||||
|
||||
$display("send a byte");
|
||||
tb_utx_data = 8'h69;
|
||||
tb_utx_valid = 1;
|
||||
#`CP;
|
||||
tb_utx_valid = 0;
|
||||
|
||||
#(150*`CP);
|
||||
|
||||
$display("send another byte");
|
||||
tb_utx_data = 8'h42;
|
||||
tb_utx_valid = 1;
|
||||
#`CP;
|
||||
tb_utx_valid = 0;
|
||||
|
||||
#(150*`CP);
|
||||
|
||||
$display("send two bytes back to back");
|
||||
tb_utx_data = 8'h69;
|
||||
tb_utx_valid = 1;
|
||||
#`CP;
|
||||
tb_utx_valid = 0;
|
||||
|
||||
#(99*`CP);
|
||||
|
||||
tb_utx_data = 8'h42;
|
||||
tb_utx_valid = 1;
|
||||
#`CP;
|
||||
tb_utx_valid = 0;
|
||||
|
||||
#(150*`CP);
|
||||
|
||||
$display("send two bytes back to back, but keep valid asserted");
|
||||
tb_utx_data = 8'h69;
|
||||
tb_utx_valid = 1;
|
||||
#`CP;
|
||||
|
||||
#(99*`CP);
|
||||
|
||||
tb_utx_data = 8'h42;
|
||||
tb_utx_valid = 1;
|
||||
#`CP;
|
||||
tb_utx_valid = 0;
|
||||
|
||||
#(150*`CP);
|
||||
|
||||
$finish();
|
||||
end
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
`default_nettype none
|
||||
|
||||
`define CP 10
|
||||
`define HCP 5
|
||||
|
||||
task automatic transmit_byte (
|
||||
input [7:0] data,
|
||||
input integer CLOCKS_PER_BAUD
|
||||
);
|
||||
|
||||
// send a byte from uart_tx, and check that it transmits properly
|
||||
|
||||
integer data_bit = 0;
|
||||
for(int i=0; i < (10*CLOCKS_PER_BAUD)-1; i++) begin
|
||||
|
||||
// check that data bit is correct on every baud period
|
||||
data_bit = i / CLOCKS_PER_BAUD;
|
||||
if (data_bit == 0) begin
|
||||
assert(uart_tx_tb.utx_tb_tx == 0) else $fatal(0, "wrong start bit!");
|
||||
end
|
||||
|
||||
else if ((data_bit > 0) && (data_bit < 9)) begin
|
||||
assert(uart_tx_tb.utx_tb_tx == data[data_bit-1]) else $fatal(0, "wrong data bit!");
|
||||
end
|
||||
|
||||
else begin
|
||||
assert(uart_tx_tb.utx_tb_tx == 1) else $fatal(0, "wrong stop bit!");
|
||||
end
|
||||
|
||||
|
||||
// check that done is not asserted during transmisison
|
||||
assert(!uart_tx_tb.utx_tb_done) else $fatal(0, "wrong done!");
|
||||
#`CP;
|
||||
end
|
||||
|
||||
// assert that done is asserted at end of transmission
|
||||
assert(uart_tx_tb.utx_tb_done) else $fatal(0, "wrong done!");
|
||||
endtask
|
||||
|
||||
module uart_tx_tb();
|
||||
logic clk;
|
||||
integer test_num;
|
||||
|
||||
logic [7:0] tb_utx_data;
|
||||
logic tb_utx_start;
|
||||
logic utx_tb_done;
|
||||
logic utx_tb_tx;
|
||||
|
||||
uart_tx #(.CLOCKS_PER_BAUD(10)) utx (
|
||||
.clk(clk),
|
||||
|
||||
.data_i(tb_utx_data),
|
||||
.start_i(tb_utx_start),
|
||||
.done_o(utx_tb_done),
|
||||
|
||||
.tx(utx_tb_tx));
|
||||
|
||||
always begin
|
||||
#`HCP
|
||||
clk = !clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
$dumpfile("uart_tx_tb.vcd");
|
||||
$dumpvars(0, uart_tx_tb);
|
||||
clk = 0;
|
||||
test_num = 0;
|
||||
|
||||
tb_utx_data = 0;
|
||||
tb_utx_start = 0;
|
||||
#`HCP;
|
||||
|
||||
// test all possible bytes
|
||||
test_num = test_num + 1;
|
||||
for(int i=0; i < 256; i++) begin
|
||||
tb_utx_start = 1;
|
||||
tb_utx_data = i;
|
||||
#`CP;
|
||||
tb_utx_start = 0;
|
||||
tb_utx_data = 0;
|
||||
transmit_byte(i, 10);
|
||||
#(100*`CP);
|
||||
end
|
||||
|
||||
// test all possible bytes (no delay between them)
|
||||
test_num = test_num + 1;
|
||||
for(int i=0; i < 256; i++) begin
|
||||
tb_utx_start = 1;
|
||||
tb_utx_data = i;
|
||||
#`CP;
|
||||
tb_utx_data = 0;
|
||||
transmit_byte(i, 10);
|
||||
end
|
||||
|
||||
$finish();
|
||||
end
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
Loading…
Reference in New Issue