Now that you’ve completed a UART transmitter, in this lab you will
design a UART receiver, implement and test it alongside your
transmitter, similar to what was done in the serial_interface and SPI_WRITE assignments.
As a reminder, the UART specs for this lab are:
uart_rxDesign a UART receiver module with these I/O ports:
clk // system clock
rst_l // active-low reset
rx // UART serial data input
d_in // received data (8 bits)
ready // "data is ready" handshake signal
ack // "data acknowledged" handshake signal
The UART RX module is very similar to the TX design, except the flow of information is reversed. Use the TX state machine as a starting point, and design a modified state machine to produce the receiver’s behavior. Note that this is very similar to what was done in previous assignments.
For handshaking on the application side, use a procedure similar to
the S2P module from the serial_interface assignment. Include
handshaking signals in your state machine.
Scan an image of your state transition diagram and add/commit it to the repository.
Program your state machine in the uart_rx module.
The UART RX is very similar to the SPI RX, with one important difference: since the UART channel has no clock, you should initialize the UART clock divider so that it has a rising edge immediately when the transmission begins.
To support this timing synchronization requirement, modify the clock
divider. Supposing your divider has a UART clock named div_clk with a counter named div_count, you should modify the logic
as follows:
WAIT state and rx is 1, hold both
div_clk and div_count at 0. (In other
words, the 9600Hz UART clock is stalled until the start bit
occurs).WAIT state and rx is 0 and div_count is 0, then toggle
div_clk to 1 and
increment div_count.else) the
divider acts normally: if div_count >= N
then toggle div_clk and reset
div_count; else increment div_count.One approach is to use a clock_divider submodule with a sync input. Whenever sync goes high, it forces the clock
divider output to zero. When sync goes low again, the clock divider
resets its phase with a rising edge a short delay after sync event. In the example below, the
delay is set to 1000 cycles of the main system clock. The purpose of
this delay is to let the rx
signal settle to a stable value before reading its value.
module clock_divider
#(
parameter N=5208
)
(
input clk,
input rst_l,
input sync,
output reg div_clk
);
integer clk_count;
initial begin
div_clk = 0;
clk_count = 0;
end
always @(posedge clk, negedge rst_l) begin
if (!rst_l) begin
div_clk <= 0;
clk_count <= 0;
end
else begin
//---- sync behavior -----------------//
if (sync) begin
div_clk <= 0; // set div_clk low
clk_count <= N-1000; // delay 1000 cycles before rising edge
end
//---- normal clock divider below ----//
else if (clk_count == N) begin
div_clk <= ~div_clk;
clk_count <= 0;
end
else begin
clk_count <= clk_count + 1;
end
//-------------------------------------//
end
end
endmodule // clock_divideruart_rxModify the testbench from uart_tx so that it sends a
transmission. Declare the necessary wire and reg signals
that correspond to the uart_rx
ports. Remove or comment out unused signals from the tx module.
The section to focus on is this one:
rx = 1; // <--- add this line to initialize rx in the idle mode
ack = 0; // <--- add this line to initialize handshake
#1000 send = 1; // <--- remove this line
while (1) begin
d_out = $random(); // <--- add this line to set data to send
// Wait for a signal: // <--- remove
while (tx) begin // all
// Delay 10us between checks // of
#10000; // these
end // lines
$write("Sending %x\n",d_out); // <-- add this
$fwrite(fid,"Sending %x\n",d_out); // <-- and this
$write("Start bit. TX="); // <--- change "TX" to "RX"
$fwrite(fid,"Start bit. TX=");
rx=0; // <--- add this line to send START BIT
// SEND eight bits:
for (i=0; i<8; i=i+1) begin
#104170;
d_in[i] = tx; // <-- Change to rx = d_out[i]
$write("%b",tx); // <-- tx to rx
$fwrite(fid,"%b",tx); // <-- ditto
end
// Wait for stop bit:
#104170;
if (tx) begin // <-- remove the `if` line
$write(". Stop bit. ");
$fwrite(fid,". Stop bit. ");
end // <-- remove
else begin // <-- remove
$write(". BAD STOP BIT. "); // <-- remove
$fwrite(fid,". BAD STOP BIT. "); // <-- remove
end // <-- remove
// Add these lines:
while (!ready) begin
#104170; // wait for 'ready' signal
end
ack = 1;
// Keep these lines:
$write("Got %xh('%c')\n",d_in,d_in);
$fwrite(fid,"Got %xh ('%c')\n",d_in,d_in);
// Add these lines to test handshake:
while (ready) begin
#104170; // wait for 'ready' signal to drop
end
ack = 0; // clear ack
#104170; // wait one more UART clock cycle before sending again
tx_count = tx_count + 1;
endComplete your testbench, run the simulation and verify that your module receives the same data that was transmitted.
top moduleCreate a top module based on
the SPI_WRITE assignment.
Instead of the SPI modules,
place one instance of uart_tx
and one instance of uart_rx, and
connect the tx port from one to
the rx port of the other. The
uart_tx module will send data
from the switches, and uart_rx
will write data to the LEDs.
Use buttons for the start and
ack signals. Use modified
debouncers to implement the handshaking protocols.
Create a new testbench based on the SPI_WRITE assignment to test your
top module. Don’t delete the
other testbench that you made; instead, name this one top_testbench and edit the
Makefile by adding these lines:
top: compile elaborate_top simulate
elaborate_top: compile
xelab -debug typical -s sim top_testbench
Important note: there should be a TAB prior to the xelab command, not
spaces.
Then run your simulation using the command make top.
Create build.tcl and XDC
files for your top module,
implement it and program onto the Basys3 board.
After the design is debugged and verified to work properly, record a short video showing your design work and post the video to Canvas.
Turn in your work using git:
git add src/*.v *.v *.rpt *.txt *.tcl *.bit *.xdc
git commit . -m "Complete"
git push origin main