|
| 1 | +`default_nettype none |
| 2 | + |
| 3 | +module buart( |
| 4 | + input clk, // The master clock for this module |
| 5 | + input resetq, // Synchronous reset, active low |
| 6 | + input rx, // Incoming serial line |
| 7 | + output tx, // Outgoing serial line |
| 8 | + input rd, // read strobe -- used only to clear valid flag. |
| 9 | + input wr, // write strobe |
| 10 | + output reg valid, // Indicates a new byte is available. clears on read. |
| 11 | + output reg busy, // Low when transmit line is idle. |
| 12 | + input [7:0] tx_data, // Byte to transmit |
| 13 | + output reg [7:0] rx_data, // *Most recent* byte received -- whether or not the last was collected. |
| 14 | + output reg error // reception error |
| 15 | + ); |
| 16 | +// you can override these on a per-port basis, looks like: |
| 17 | +// buart #(.BAUD(115200)) _youruart (.clk(clk)...etc); |
| 18 | +// or |
| 19 | +// buart #(.CLOCK_DIVIDE(312)) _uart1 (... |
| 20 | +// The latter might be better for designs with non-48MHz clocks. |
| 21 | +parameter BAUD = 9600; |
| 22 | +parameter CLKFREQ = 48000000; // frequency of incoming signal 'clk' |
| 23 | +parameter CLOCK_DIVIDE = (CLKFREQ / (BAUD * 4)); // clock rate (48Mhz) / (baud rate (460800) * 4) |
| 24 | +// will probably want to support at least down to 9600 baud, which will require a CLOCK_DIVIDE == 1250 |
| 25 | + |
| 26 | +localparam CDSIZE = $clog2(CLOCK_DIVIDE)+1; // one more to accomodate assumed signed arithmatic |
| 27 | +reg [5:0] bytephase; |
| 28 | +reg [CDSIZE-1:0] rxclkcounter; |
| 29 | +wire rxqtick = rxclkcounter == CLOCK_DIVIDE; // strobes high one clk every 1/4 bit time |
| 30 | +wire rxrst = rx & (~|bytephase); // rx goes low with the beginning of the start bit. synchronous to system clk, not sample clk. |
| 31 | +always @(posedge clk) rxclkcounter <= rxrst | rxqtick ? 1 : rxclkcounter + 1; // initially held in reset |
| 32 | +// very important: idle rx line holds rxrst asserted, |
| 33 | +// this goes on *until* the start edge is found. |
| 34 | +// thus synchronising further sampling to that edge, rather than remaining in phase with however it was reset. |
| 35 | + |
| 36 | +wire rxstop = bytephase == 6'd40; // 11th sample 'tick' would have been at 42. |
| 37 | +wire nonstarter; |
| 38 | +always @(posedge clk) bytephase <= rxstop|nonstarter ? 0 : rxqtick ? bytephase + 1 : bytephase; |
| 39 | +wire sample = (bytephase[1:0] == 2'b10) & rxqtick; // one clk for each of ten bits |
| 40 | +// note sample is false while rxrst is true. |
| 41 | +assign nonstarter = (bytephase == 6'd2) & rx; // start bit should still be low when sample strobes first. |
| 42 | +// if it isn't, then it will go back to a rxrst state. |
| 43 | + |
| 44 | +// after this point, we have a sample strobe, a rxstop strobe |
| 45 | +reg [9:0] capture; always @(posedge clk) capture <= sample ? {rx, capture[9:1]} : capture; |
| 46 | +// note bits are sent least-significant first. |
| 47 | +wire startbit = capture[0]; // valid when rxstop strobes, and until rxrst releases for the next byte. |
| 48 | +wire stopbit = capture[9]; |
| 49 | +wire good = stopbit&~startbit; // valid when rxstop is asserted. stop bit should be 1, start bit should have been zero. |
| 50 | +always @(posedge clk) |
| 51 | +begin |
| 52 | +valid <= rd ? 1'b0 : rxstop & good ? 1'b1 : valid; |
| 53 | +rx_data <= rxstop & good ? capture[8:1] : rx_data; |
| 54 | +error <= nonstarter ? 1'b1 : rxstop ? ~good : error ; |
| 55 | +end |
| 56 | +// tx parts |
| 57 | +reg [CDSIZE+1:0] txclkcounter; // note, two extra bits to accomodate a limit 4x as large. |
| 58 | +wire txtick = txclkcounter == 4*CLOCK_DIVIDE; // ticks for a clk once every bit, not every quarter bit. |
| 59 | +always @(posedge clk) txclkcounter <= txtick ? 1 : txclkcounter + 1; |
| 60 | +// note txclkcounter never needs to be reset out-of-phase with itself. |
| 61 | +reg [3:0] sentbits; |
| 62 | +wire done = sentbits == 4'd10; // eventually stays 'done'. Reset to zero again when wr strobes. |
| 63 | +always @(posedge clk) sentbits <= txtick & ~done ? sentbits + 1 : wr ? 4'd0 : sentbits ; |
| 64 | +reg [9:0] sender; |
| 65 | +// wr strobe might come any clk, not synchronous to txtick. No real need to force txtick to be synchronous to it either. |
| 66 | +always @(posedge clk) sender <= wr ? {tx_data, 1'b0, 1'b1} : txtick ? {1'b1, sender[9:1]} : sender; |
| 67 | +assign tx = sender[0]; // wr loads this 1, because tranmission doesn't start until the next txtick, whenever it arrives. |
| 68 | +assign busy = ~done; |
| 69 | +endmodule |
0 commit comments