diff --git a/Makefile b/Makefile index 0e41b55..88e1b8b 100644 --- a/Makefile +++ b/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 diff --git a/doc/roadmap.md b/doc/roadmap.md index 6415503..88835eb 100644 --- a/doc/roadmap.md +++ b/doc/roadmap.md @@ -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: diff --git a/test/functional_sim/uart_rx_tb.sv b/test/functional_sim/uart_rx_tb.sv new file mode 100644 index 0000000..9993eb3 --- /dev/null +++ b/test/functional_sim/uart_rx_tb.sv @@ -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 diff --git a/test/functional_sim/uart_tb/rx_uart.v b/test/functional_sim/uart_tb/rx_uart.v deleted file mode 100644 index ea35f7e..0000000 --- a/test/functional_sim/uart_tb/rx_uart.v +++ /dev/null @@ -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 diff --git a/test/functional_sim/uart_tb/tx_uart.v b/test/functional_sim/uart_tb/tx_uart.v deleted file mode 100644 index 38ec6b2..0000000 --- a/test/functional_sim/uart_tb/tx_uart.v +++ /dev/null @@ -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 diff --git a/test/functional_sim/uart_tb/uart_tb.sv b/test/functional_sim/uart_tb/uart_tb.sv deleted file mode 100644 index 5f6f398..0000000 --- a/test/functional_sim/uart_tb/uart_tb.sv +++ /dev/null @@ -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 diff --git a/test/functional_sim/uart_tb/uart_tx_tb.sv b/test/functional_sim/uart_tb/uart_tx_tb.sv deleted file mode 100644 index 29d86e9..0000000 --- a/test/functional_sim/uart_tb/uart_tx_tb.sv +++ /dev/null @@ -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 diff --git a/test/functional_sim/uart_tx_tb.sv b/test/functional_sim/uart_tx_tb.sv new file mode 100644 index 0000000..308069e --- /dev/null +++ b/test/functional_sim/uart_tx_tb.sv @@ -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