diff --git a/src/manta/rx_uart.v b/src/manta/rx_uart.v new file mode 100644 index 0000000..1206348 --- /dev/null +++ b/src/manta/rx_uart.v @@ -0,0 +1,117 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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/src/manta/tx_uart.v b/src/manta/tx_uart.v new file mode 100644 index 0000000..eb5ed0d --- /dev/null +++ b/src/manta/tx_uart.v @@ -0,0 +1,180 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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/src/manta/uart_rx.v b/src/manta/uart_rx.v deleted file mode 100644 index 298bc66..0000000 --- a/src/manta/uart_rx.v +++ /dev/null @@ -1,69 +0,0 @@ -`default_nettype none -`timescale 1ns / 1ps - -module uart_rx( - input wire clk, - input wire rst, - input wire rxd, - - output reg [DATA_WIDTH - 1:0] axiod, - output reg axiov - ); - - parameter DATA_WIDTH = 0; - parameter CLK_FREQ_HZ = 0; - parameter BAUDRATE = 0; - - localparam BAUD_PERIOD = CLK_FREQ_HZ / BAUDRATE; - - reg [$clog2(BAUD_PERIOD) - 1:0] baud_counter; - reg [$clog2(DATA_WIDTH + 2):0] bit_index; - reg [DATA_WIDTH + 2 : 0] data_buf; - - reg prev_rxd; - reg busy; - - always_ff @(posedge clk) begin - prev_rxd <= rxd; - axiov <= 0; - baud_counter <= (baud_counter == BAUD_PERIOD - 1) ? 0 : baud_counter + 1; - - // reset logic - if(rst) begin - bit_index <= 0; - axiod <= 0; - busy <= 0; - baud_counter <= 0; - end - - // start receiving if we see a falling edge, and not already busy - else if (prev_rxd && ~rxd && ~busy) begin - busy <= 1; - data_buf <= 0; - baud_counter <= 0; - end - - // if we're actually receiving - else if (busy) begin - if (baud_counter == BAUD_PERIOD / 2) begin - data_buf[bit_index] <= rxd; - bit_index <= bit_index + 1; - - if (bit_index == DATA_WIDTH + 1) begin - busy <= 0; - bit_index <= 0; - - - if (rxd && ~data_buf[0]) begin - axiod <= data_buf[DATA_WIDTH : 1]; - axiov <= 1; - end - end - end - end - end - - -endmodule - -`default_nettype wire \ No newline at end of file diff --git a/test/minimal_bus_tb.sv b/test/minimal_bus_tb.sv index 472a76e..55951ac 100644 --- a/test/minimal_bus_tb.sv +++ b/test/minimal_bus_tb.sv @@ -24,23 +24,15 @@ module minimal_bus_tb; integer test_num; string msg; logic [7:0] char; - logic baud_counter; - assign baud_counter = utx.baud_counter == 0; // tb --> uart_rx signals logic tb_urx_rxd; - uart_rx #( - .DATA_WIDTH(8), - .CLK_FREQ_HZ(100_000_000), - .BAUDRATE(10_000_000) - ) urx ( - .clk(clk), - .rst(rst), - .rxd(tb_urx_rxd), - - .axiod(urx_brx_axid), - .axiov(urx_brx_axiv)); + rx_uart #(.CLOCKS_PER_BAUD(10)) urx ( + .i_clk(clk), + .i_uart_rx(tb_urx_rxd), + .o_wr(urx_brx_axiv), + .o_data(urx_brx_axid)); // uart_rx --> bridge_rx signals logic [7:0] urx_brx_axid; @@ -65,13 +57,13 @@ module minimal_bus_tb; logic brx_mem_valid; lut_mem #( - .DEPTH(8), + .DEPTH(32), .BASE_ADDR(0) ) mem ( .clk(clk), .addr_i(brx_mem_addr), .wdata_i(brx_mem_wdata), - .rdata_i(0), + .rdata_i(16'h0), .rw_i(brx_mem_rw), .valid_i(brx_mem_valid), @@ -97,45 +89,24 @@ module minimal_bus_tb; .axiod(btx_utx_axid), .axiov(btx_utx_axiv), - .axior(btx_utx_axir)); + .axior(~btx_utx_axib)); // bridge_tx --> uart_tx signals logic [7:0] btx_utx_axid; logic btx_utx_axiv; - logic btx_utx_axir; + logic btx_utx_axib; - uart_tx #( - .DATA_WIDTH(8), - .CLK_FREQ_HZ(100_000_000), - .BAUDRATE(10_000_000) - ) utx ( - .clk(clk), - .rst(rst), + tx_uart #(.CLOCKS_PER_BAUD(10)) utx( + .i_clk(clk), + .i_wr(btx_utx_axiv), + .i_data(btx_utx_axid), - .axiid(btx_utx_axid), - .axiiv(btx_utx_axiv), - .axiir(btx_utx_axir), - .txd(utx_tb_txd)); + .o_uart_tx(utx_tb_txd), + .o_busy(btx_utx_axib)); // utx --> tb signals logic utx_tb_txd; - /* - actually just for shiggles let's see what happens when you put a uart_rx on a uart_tx - */ - - uart_rx #( - .DATA_WIDTH(8), - .CLK_FREQ_HZ(100_000_000), - .BAUDRATE(10_000_000) - ) tb_decoder ( - .clk(clk), - .rst(rst), - .rxd(utx_tb_txd), - - .axiod(), - .axiov()); - always begin #`HCP clk = !clk; @@ -157,14 +128,8 @@ module minimal_bus_tb; #`HCP // throw some nonzero data in the memories just so we know that we're pulling from the right ones - mem.mem[0] = 16'h0000; - mem.mem[1] = 16'h0001; - mem.mem[2] = 16'h0002; - mem.mem[3] = 16'h0003; - mem.mem[4] = 16'h0004; - mem.mem[5] = 16'h0005; - mem.mem[6] = 16'h0006; - mem.mem[7] = 16'h0007; + + for(int i=0; i< 32; i++) mem.mem[i] = i; #(10*`CP); @@ -189,29 +154,12 @@ module minimal_bus_tb; /* ==== Test 3 Begin ==== */ $display("\n=== test 3: read from 0x0000-0x0007 for baseline functionality ==="); test_num = 3; - msg = {"M0000", 8'h0D, 8'h0A}; - `SEND_MSG_BITS(msg) - msg = {"M0001", 8'h0D, 8'h0A}; - `SEND_MSG_BITS(msg) - - msg = {"M0002", 8'h0D, 8'h0A}; - `SEND_MSG_BITS(msg) - - msg = {"M0003", 8'h0D, 8'h0A}; - `SEND_MSG_BITS(msg) - - msg = {"M0004", 8'h0D, 8'h0A}; - `SEND_MSG_BITS(msg) - - msg = {"M0005", 8'h0D, 8'h0A}; - `SEND_MSG_BITS(msg) - - msg = {"M0006", 8'h0D, 8'h0A}; - `SEND_MSG_BITS(msg) - - msg = {"M0007", 8'h0D, 8'h0A}; - `SEND_MSG_BITS(msg) + for(logic[15:0] j=0; j<32; j++) begin + $display($sformatf("M%H", j)); + msg = {$sformatf("M%H", j), 8'h0D, 8'h0A}; + `SEND_MSG_BITS(msg) + end #(10*`CP); /* ==== Test 3 End ==== */