URL
https://opencores.org/ocsvn/wbuart32/wbuart32/trunk
Subversion Repositories wbuart32
Compare Revisions
- This comparison shows the changes necessary to convert path
/wbuart32/trunk
- from Rev 4 to Rev 5
- ↔ Reverse comparison
Rev 4 → Rev 5
/Makefile
56,6 → 56,7
.PHONY: test |
test: bench |
bench/cpp/linetest |
# bench/cpp/speechtest bench/cpp/speech.txt |
|
.PHONY: clean |
clean: |
/README.md
0,0 → 1,25
# Another Wishbone Controlled UART |
|
_Forasmuch as many have taken in hand to set forth_ a UART core, ... _It seemed |
good to me also, having had ~~perfect~~_ (a good) _understanding of ~~all~~ |
things from the very first, to write_ ... my own UART core. |
|
- This Verilog core contains two UART modules, one for transmit and one for receive. Each can be configured via one 32-bit word for just about any baud rate, one or two stop bits, five through eight data bits, and odd, even, mark, or space parity. If you are looking for an example Verilog UART module containing all these features, then you've just found it. |
|
- The module goes beyond simple transmit and receive, however, to also include a fairly generic synchronous FIFO. For those looking for a fairly simple FIFO, you've also just found it. |
|
- If you are looking for a wishbone--enabled peripheral, this module offers two configuration methods: one that can be included in another, larger, wishbone module, and another wish is complete in its own right--together with FIFO and a FIFO status register. |
|
- If you are familiar with other UART setup protocols, you'll find this one even easier to setup. Unlike the 16550 serial port, this serial port can be set up by just writing to and setting a single 32--bit register. Once set, either at startup or by writing the the port, and your UART is fully configured. Changes will take place on the next byte to be transmitted. |
|
- If you would rather test your own UART transmitter and/or receiver, this core contains within it a Verilator enabled UART simulator which can be used in test-benches of your own UART implementation to know if you've done it right or not. |
|
- Finally, the test benches within bench/verilog of this directory can be used as very simple test benches to test for UART functionality on a board with only two pins (clock and output UART), or three pins (adding the input UART). Thus, if you are just trying to start up a project and need a demonstration that will prove if your UART will work, you can find such a demonstration project in this code. Further, two of those test benches will also create VCD files that can be inspected via gtkwave, so you can get a feel for how the whole works. |
|
At one time, the biggest drawback to the files in these directories was that |
there wasn't a version of this UART interface containing a FIFO. Well, no |
more. Now there is a wbuart.v file that can be interacted with from a |
wishbone/B4/pipeline bus, and it contains a FIFO with a parameterized length |
that can extend up to 1023 entries. |
|
Thus this is a very simple and easy to use controller. |
/bench/README.md
0,0 → 1,25
There are two bench testing directories, one for Verilator sources, and one for C++ simulation sources that will work with Verilator. The Verilog sources may or may not be used with Verilator. Indeed, they would work nicely as stand--alone top--level files that may be used to test whether or not the UART on a given board works. |
/bench/cpp/Makefile
4,8 → 4,43
## |
## Project: wbuart32, a full featured UART with simulator |
## |
## Purpose: |
## Purpose: To test a group of Verilator modules: txuart (UART transmitter), |
## rxuart (UART receiver/sink) and wbuart (UART module, containing |
## both receiver and transmitter, with FIFOs, controlled via wishbone). |
## |
## |
## Targets: |
## test |
## Perform both tests. The end result should be either a PASS |
## or a FAIL. |
## |
## helloworld |
## A non-automated, and less interactive test than the others. In |
## this test, the UART simply produces a Hello World message to the |
## screen over and over again. |
## |
## linetest |
## An automated test of both txuart and rxuart. The test works |
## by sending a message through the rxuart, and receiving the |
## message via the txuart. This depends upon a Verilog test |
## infrastructure, linetest.v. |
## |
## This test may be ran in an interactive mode. In this mode, |
## characters written to the UART will be reflected back upon |
## the entrance of a return character. |
## |
## speechtest |
## An automated test of the wbuart, txuart, and fifo. In this |
## case, the test RTL produces a copy of the Gettysburg address, |
## filling the FIFO at 12/16 at a time. In automated mode, the |
## speechtest will compare the output against against a text copy |
## of the speech, and report upon any success or failure. |
## |
## In interactive mode, the test will repeatedly print out the |
## Gettysburg address until stopped. (It may take a significant |
## amount of time between copies of the Gettysburg address ...) |
## |
## |
## Creator: Dan Gisselquist, Ph.D. |
## Gisselquist Technology, LLC |
## |
39,14 → 74,26
FLAGS := -Wall -Og -g |
OBJDIR := obj-pc |
RTLD := ../verilog |
VERILATOR_ROOT ?= $(shell bash -c 'verilator -V|grep VERILATOR_ROOT | head -1 | sed -e " s/^.*=\s*//"') |
VROOT := $(VERILATOR_ROOT) |
INCS := -I$(RTLD)/obj_dir/ -I/usr/share/verilator/include |
SOURCES := linetest.cpp |
SOURCES := helloworld.cpp linetest.cpp uartsim.cpp uartsim.h |
VOBJDR := $(RTLD)/obj_dir |
VLIB := /usr/share/verilator/include/verilated.cpp |
SIMSRCS := linetest.cpp uartsim.cpp |
SIMOBJ := $(subst .cpp,.o,$(SIMSRCS)) |
SIMOBJS:= $(addprefix $(OBJDIR)/,$(SIMOBJ)) |
all: $(OBJDIR)/ linetest |
SYSVDR := /usr/share/verilator/include |
VLIB := $(SYSVDR)/verilated.cpp $(SYSVDR)/verilated_vcd_c.cpp |
# Sources necessary to build the linetest program (rxuart-txuart test) |
LINSRCS := linetest.cpp uartsim.cpp |
LINOBJ := $(subst .cpp,.o,$(LINSRCS)) |
LINOBJS:= $(addprefix $(OBJDIR)/,$(LINOBJ)) |
# Sources necessary to build the helloworld test (txuart test) |
HLOSRCS := helloworld.cpp uartsim.cpp |
HLOOBJ := $(subst .cpp,.o,$(HLOSRCS)) |
HLOOBJS:= $(addprefix $(OBJDIR)/,$(HLOOBJ)) |
# Sources necessary to build the speech test (wbuart test) |
SPCHSRCS:= speechtest.cpp uartsim.cpp |
SPCHOBJ := $(subst .cpp,.o,$(SPCHSRCS)) |
SPCHOBJS:= $(addprefix $(OBJDIR)/,$(SPCHOBJ)) |
all: $(OBJDIR)/ linetest helloworld speechtest test |
|
$(OBJDIR)/: |
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi" |
56,11 → 103,42
$(OBJDIR)/%.o: %.cpp |
$(CXX) $(FLAGS) $(INCS) -c $< -o $@ |
|
linetest: $(OBJDIR)/linetest.o $(OBJDIR)/uartsim.o $(VOBJDR)/Vlinetest__ALL.a |
linetest: $(LINOBJS) $(VOBJDR)/Vlinetest__ALL.a |
$(CXX) $(FLAGS) $(INCS) $^ $(VLIB) -o $@ |
|
helloworld: $(HLOOBJS) $(VOBJDR)/Vhelloworld__ALL.a |
$(CXX) $(FLAGS) $(INCS) $^ $(VLIB) -o $@ |
|
# |
# The speech test program depends upon a copy of the Gettysburg Address, |
# turned into a hex file format which will be read by the Verilog/RTL |
# $readmemh function. However, we need to create that hex file that will |
# written. That's the purpose of mkspeech--to make a file that can be read |
# by $readmemh. |
# |
mkspeech: mkspeech.cpp |
$(CXX) mkspeech.cpp -o $@ |
|
# Now that mkspeech is available, use it to produce a speech.hex file from |
# the speech.txt file. Be careful if you adjust this speech: the speechfifo.v |
# verilog file depends upon the exact number of characters--its not a portable |
# dependency, but ... it is what it is. |
speech.hex: mkspeech speech.txt |
./mkspeech speech.txt |
|
# Now, if the speech.hex file is available, then we can perform our final build. |
# Actually, we could've done this without the speech file being available, but |
# this works. |
speechtest: speech.hex $(SPCHOBJS) $(VOBJDR)/Vspeechfifo__ALL.a |
$(CXX) $(FLAGS) $(INCS) $(SPCHOBJS) $(VOBJDR)/Vspeechfifo__ALL.a $(VLIB) -o $@ |
|
test: linetest speechtest |
./linetest |
./speechtest |
|
.PHONY: clean |
clean: |
rm -f ./linetest |
rm -f ./linetest ./helloworld ./speechtest |
rm -f ./mkspeech ./speech.hex |
rm -rf $(OBJDIR)/ |
|
/bench/cpp/README.md
0,0 → 1,21
+ C++ source files |
|
Items within this directory include: |
|
- uartsim defines a C++ class that can be used for simulating a UART within |
Verilator. This class can be used both to generate valid UART signaling, |
to determine if your configuration can receive it properly, as well as to decode |
valid UART signaling to determine if your configuration is properly setting the |
UART signaling wire. |
|
- speech.txt, and the associated speech.hex file, is the text that speechfifo |
will transmit. It is currently set to the Gettysburg Address. While you are welcome to change this, the length of this file is hard coded within the verilog file that references it. |
|
- mkspeech, a Verilog hex file generator--although it also converts newlines to |
carriage-return newline pairs |
|
- Demonstration projects using these: |
-- helloworld, exercises and tests the helloworld.v test bench |
-- linetest, exercises and tests the linetest.v test bench. This also creates a .VCD file which can be viewed via GTKwave |
-- speechtest, exercises and tests the speechfifo test bench. When run with the -i option, speechtest will also generate a .VCD file for use with GTKwave. |
|
/bench/cpp/helloworld.cpp
0,0 → 1,72
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: helloworld.cpp |
// |
// Project: wbuart32, a full featured UART with simulator |
// |
// Purpose: To demonstrate a useful Verilog file which could be used as a |
// toplevel program later, to demo the transmit UART. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// 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. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include <stdio.h> |
#include <fcntl.h> |
#include <unistd.h> |
#include <string.h> |
#include <time.h> |
#include <sys/types.h> |
#include <signal.h> |
#include "verilated.h" |
#include "Vhelloworld.h" |
#include "uartsim.h" |
|
int main(int argc, char **argv) { |
Verilated::commandArgs(argc, argv); |
Vhelloworld tb; |
UARTSIM *uart; |
int port = 0; |
unsigned setup = 25, testcount = 0; |
|
tb.i_setup = setup; |
uart = new UARTSIM(port); |
uart->setup(tb.i_setup); |
|
while(testcount++ < 0x7f000000) { |
|
tb.i_clk = 1; |
tb.eval(); |
tb.i_clk = 0; |
tb.eval(); |
|
(*uart)(tb.o_uart_tx); |
} |
|
printf("\n\nSimulation complete\n"); |
} |
/bench/cpp/linetest.cpp
8,7 → 8,7
// which can be exercised/proven via Verilator. |
// |
// If you run this program with no arguments, it will run an automatic |
// test, returning "SUCCESS" on success, or "FAIL" on failure as a last |
// test, returning "PASS" on success, or "FAIL" on failure as a last |
// output line--hence it should support automated testing. |
// |
// If you run with a '-i' argument, the program will run interactively. |
50,9 → 50,11
#include <string.h> |
#include <time.h> |
#include <sys/types.h> |
#include <sys/wait.h> |
#include <signal.h> |
#include "verilated.h" |
#include "Vlinetest.h" |
#include "verilated_vcd_c.h" |
#include "uartsim.h" |
|
int main(int argc, char **argv) { |
62,6 → 64,7
bool run_interactively = false; |
int port = 0; |
unsigned setup = 25; |
char string[] = "This is a UART testing string\r\n"; |
|
for(int argn=1; argn<argc; argn++) { |
if (argv[argn][0] == '-') for(int j=1; (j<1000)&&(argv[argn][j]); j++) |
83,7 → 86,8
} |
|
tb.i_setup = setup; |
tb.i_uart = 1; |
int baudclocks = setup & 0x0ffffff; |
tb.i_uart_rx = 1; |
if (run_interactively) { |
uart = new UARTSIM(port); |
uart->setup(tb.i_setup); |
95,7 → 99,7
tb.i_clk = 0; |
tb.eval(); |
|
tb.i_uart = (*uart)(tb.o_uart); |
tb.i_uart_rx = (*uart)(tb.o_uart_tx); |
} |
|
} else { |
108,9 → 112,9
exit(EXIT_FAILURE); |
} |
|
pid_t pid = fork(); |
pid_t childs_pid = fork(); |
|
if (pid < 0) { |
if (childs_pid < 0) { |
fprintf(stderr, "ERR setting up child process\n"); |
perror("O/S ERR"); |
printf("TEST FAILURE\n"); |
117,7 → 121,7
exit(EXIT_FAILURE); |
} |
|
if (pid) { |
if (childs_pid) { |
int nr=-2, nw; |
|
// We are the parent |
124,7 → 128,6
close(childs_stdin[ 0]); // Close the read end |
close(childs_stdout[1]); // Close the write end |
|
char string[] = "This is a UART testing string\r\n"; |
char test[256]; |
|
nw = write(childs_stdin[1], string, strlen(string)); |
142,14 → 145,37
printf("Successfully read %d characters: %s\n", nr, test); |
} |
|
// We are done, kill our child if not already dead |
kill(pid, SIGTERM); |
int status = 0, rv = -1; |
|
// Give the child the oppoortunity to take another |
// 60 seconds to finish closing itself |
for(int waitcount=0; waitcount < 60; waitcount++) { |
rv = waitpid(-1, &status, WNOHANG); |
if (rv == childs_pid) |
break; |
else if (rv < 0) |
break; |
else // rv == 0 |
sleep(1); |
} |
|
if (rv != childs_pid) { |
kill(childs_pid, SIGTERM); |
printf("WARNING: Child/simulator did not terminate normally\n"); |
} |
|
if (WEXITSTATUS(status) != EXIT_SUCCESS) { |
printf("WARNING: Child/simulator exit status does not indicate success\n"); |
} |
|
if ((nr == nw)&&(nw == (int)strlen(string)) |
&&(strcmp(test, string) == 0)) |
printf("SUCCESS!\n"); |
else |
&&(strcmp(test, string) == 0)) { |
printf("PASS!\n"); |
exit(EXIT_SUCCESS); |
} else { |
printf("TEST FAILED\n"); |
exit(EXIT_FAILURE); |
} |
} else { |
close(childs_stdin[ 1]); |
close(childs_stdout[0]); |
173,9 → 199,25
// Make sure we don't run longer than 4 seconds ... |
time_t start = time(NULL); |
int iterations_before_check = 2048; |
unsigned clocks = 0; |
bool done = false; |
|
for(int i=0; i<200000; i++) { |
#define VCDTRACE |
#ifdef VCDTRACE |
Verilated::traceEverOn(true); |
VerilatedVcdC* tfp = new VerilatedVcdC; |
tb.trace(tfp, 99); |
tfp->open("linetest.vcd"); |
#define TRACE_POSEDGE tfp->dump(10*clocks) |
#define TRACE_NEGEDGE tfp->dump(10*clocks+5) |
#define TRACE_CLOSE tfp->close() |
#else |
#define TRACE_POSEDGE while(0) |
#define TRACE_NEGEDGE while(0) |
#define TRACE_CLOSE while(0) |
#endif |
|
for(int i=0; i<(baudclocks*24); i++) { |
// Clear any initial break condition |
tb.i_clk = 1; |
tb.eval(); |
182,18 → 224,22
tb.i_clk = 0; |
tb.eval(); |
|
tb.i_uart = 1; |
tb.i_uart_rx = 1; |
} |
|
while(!done) { |
while(clocks < 2*(baudclocks*16)*strlen(string)) { |
tb.i_clk = 1; |
tb.eval(); |
TRACE_POSEDGE; |
tb.i_clk = 0; |
tb.eval(); |
TRACE_NEGEDGE; |
clocks++; |
|
tb.i_uart = (*uart)(tb.o_uart); |
tb.i_uart_rx = (*uart)(tb.o_uart_tx); |
|
if (false) { |
/* |
static long counts = 0; |
static int lasti = 1, lasto = 1; |
bool writeout = false; |
220,7 → 266,7
tb.v__DOT__transmitter__DOT__state, |
tb.v__DOT__transmitter__DOT__zero_baud_counter, |
tb.v__DOT__transmitter__DOT__baud_counter); |
} |
} */ |
} |
|
if (iterations_before_check-- <= 0) { |
230,6 → 276,10
fprintf(stderr, "CHILD-TIMEOUT\n"); |
} |
} |
} |
|
TRACE_CLOSE; |
|
exit(EXIT_SUCCESS); |
} |
} |
} |
/bench/cpp/mkspeech.cpp
0,0 → 1,98
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: mkspeech.cpp |
// |
// Project: wbuart32, a full featured UART with simulator |
// |
// Purpose: To turn a text file (i.e. the Gettysburg address) into a |
// hex file that can be included via readmemh. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// 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. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include <stdio.h> |
#include <stdlib.h> |
#include <unistd.h> |
#include <string.h> |
|
int main(int argc, char **argv) { |
FILE *fp, *fout; |
|
if (argc != 2) { |
fprintf(stderr, "Err: USAGE is mkspeech <filename>.txt\n"); |
exit(EXIT_FAILURE); |
} else if ((!argv[1])||(strlen(argv[1])<5) |
||(strcmp(&argv[1][strlen(argv[1])-4], ".txt")!=0)) { |
fprintf(stderr, "Err: %s is an invalid text file name\n", argv[1]); |
exit(EXIT_FAILURE); |
} else if (access(argv[1], F_OK)!=0) { |
fprintf(stderr, "Err: %s is not a file\n", argv[1]); |
exit(EXIT_FAILURE); |
} else if (access(argv[1], R_OK)!=0) { |
fprintf(stderr, "Err: Cannot read %s\n", argv[1]); |
exit(EXIT_FAILURE); |
} |
|
fp = fopen(argv[1], "r"); |
if (fp == NULL) { |
fprintf(stderr, "Err: Cannot read %s\n", argv[1]); |
exit(EXIT_FAILURE); |
} |
|
fout = fopen("speech.hex", "w"); |
if (fout == NULL) { |
fprintf(stderr, "Err: Cannot write %s\n", "speech.hex"); |
exit(EXIT_FAILURE); |
} |
|
int linelen = 0; |
int ch, addr = 0; |
|
fprintf(fout, "@%08x ", addr); linelen += 4+6; |
while((ch = fgetc(fp))!=EOF) { |
if (ch == '\n') { |
fprintf(fout, "%02x ", '\r' & 0x0ff); linelen += 3; addr++; |
if (linelen >= 77) { |
fprintf(fout, "\n"); |
linelen = 0; |
fprintf(fout, "@%08x ", addr); linelen += 4+6; |
} |
} |
fprintf(fout, "%02x ", ch & 0x0ff); linelen += 3; addr++; |
|
if (linelen >= 77) { |
fprintf(fout, "\n"); |
linelen = 0; |
fprintf(fout, "@%08x ", addr); linelen += 4+6; |
} |
} fprintf(fout, "\n"); |
|
fclose(fp); |
fclose(fout); |
} |
/bench/cpp/speech.txt
0,0 → 1,24
Four score and seven years ago our fathers brought forth on this continent, a |
new nation, conceived in Liberty, and dedicated to the proposition that all men |
are created equal. |
|
Now we are engaged in a great civil war, testing whether that nation, or any |
nation so conceived and so dedicated, can long endure. We are met on a great |
battle-field of that war. We have come to dedicate a portion of that field, as |
a final resting place for those who here gave their lives that that nation |
might live. It is altogether fitting and proper that we should do this. |
|
But, in a larger sense, we can not dedicate-we can not consecrate-we can not |
hallow-this ground. The brave men, living and dead, who struggled here, have |
consecrated it, far above our poor power to add or detract. The world will |
little note, nor long remember what we say here, but it can never forget what |
they did here. It is for us the living, rather, to be dedicated here to the |
unfinished work which they who fought here have thus far so nobly advanced. It |
is rather for us to be here dedicated to the great task remaining before |
us-that from these honored dead we take increased devotion to that cause for |
which they gave the last full measure of devotion-that we here highly resolve |
that these dead shall not have died in vain-that this nation, under God, shall |
have a new birth of freedom-and that government of the people, by the people, |
for the people, shall not perish from the earth. |
|
|
/bench/cpp/speechtest.cpp
0,0 → 1,397
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: speechtest.cpp |
// |
// Project: wbuart32, a full featured UART with simulator |
// |
// Purpose: To demonstrate a useful Verilog file which could be used as a |
// toplevel program later, to demo the transmit UART as it might |
// be commanded from a WB bus, and having a FIFO. |
// |
// If all goes well, the program will write out the words of the Gettysburg |
// address in interactive mode. In non-interactive mode, the program will |
// read its own output and report on whether or not it worked well. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// 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. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include <stdio.h> |
#include <fcntl.h> |
#include <unistd.h> |
#include <string.h> |
#include <time.h> |
#include <sys/types.h> |
#include <signal.h> |
#include <ctype.h> |
#include "verilated.h" |
#include "Vspeechfifo.h" |
#include "uartsim.h" |
#include "verilated_vcd_c.h" |
|
void usage(void) { |
fprintf(stderr, "USAGE: speechtest [-i] [<matchfile>.txt]\n"); |
fprintf(stderr, "\n" |
"\tWhere ... \n" |
"\t-i\tis an optional argument, instructing speechtest to run\n" |
"\t\tinteractively. This mode offers no checkin against any possible\n" |
"\t\ttruth or match file.\n" |
"\n" |
"\t<matchfile.txt>\t is the name of a file which will be compared against\n" |
"\t\tthe output of the simulation. If the output matches the match\n" |
"\t\tfile, the simulation will exit with success. Only the number of\n" |
"\t\tcharacters in the match file will be tested.\n\n"); |
}; |
|
int main(int argc, char **argv) { |
Verilated::commandArgs(argc, argv); |
Vspeechfifo tb; |
UARTSIM *uart; |
int port = 0; |
unsigned setup = 25, testcount = 0, baudclocks; |
const char *matchfile = "speech.txt"; |
bool run_interactively = false; |
|
for(int argn=1; argn<argc; argn++) { |
if (argv[argn][0]=='-') for(int j=1; (j<1000)&&(argv[argn][j]); j++) |
switch(argv[argn][j]) { |
case 'i': run_interactively = true; |
break; |
default: |
printf("Undefined option, -%c\n", argv[argn][j]); |
usage(); |
exit(EXIT_FAILURE); |
} else { |
matchfile = argv[argn]; |
} |
} |
|
tb.i_setup = setup; |
baudclocks = setup & 0x0ffffff; |
|
if (run_interactively) { |
// |
// The difference between the non-interactive mode and the |
// interactive mode is that in the interactive mode we don't |
// get to observe the speech being output to stdout. Thus, |
// we blindly run for a period of clocks, and then stop. |
// |
// The cool part of the interactive mode is that we can |
// output internals from the simulation, for the purpose of |
// debug by printf. We can also dump things to a VCD file, |
// should you wish to run GTKwave. |
// |
uart = new UARTSIM(port); |
uart->setup(tb.i_setup); |
|
Verilated::traceEverOn(true); |
VerilatedVcdC* tfp = new VerilatedVcdC; |
tb.trace(tfp, 99); |
tfp->open("speechtrace.vcd"); |
|
testcount = 0; |
while(testcount < baudclocks * 16 * 2048) { |
// Run one tick of the clock. |
|
tb.i_clk = 1; // Positive edge |
tb.eval(); |
tfp->dump(5*(2*testcount)); |
tb.i_clk = 0; // Negative edge |
tb.eval(); |
|
// Now, evaluate the UART, throwing away the received |
// value since the SpeechTest doesnt use it. |
(*uart)(tb.o_uart_tx); |
|
tfp->dump(5*(2*testcount+1)); |
testcount++; |
|
// #define DEBUG |
#ifdef DEBUG |
// |
// Here are my notes from my last attempt at debug by printf. |
printf("%08x ", |
tb.v__DOT__restart_counter); |
printf("%s %s@%d<-%08x[%c/%4d] (%s%s,%08x,%2d,%2d,%2d,%c,%s) %s,%02x >%d\n", |
(tb.v__DOT__restart)?"RST":" ", |
(tb.v__DOT__wb_stb)?"STB":" ", |
(tb.v__DOT__wb_addr), |
(tb.v__DOT__wb_data), |
isgraph(tb.v__DOT__wb_data&0x0ff)? |
(tb.v__DOT__wb_data&0x0ff) : '.', |
(tb.v__DOT__msg_index), |
(tb.v__DOT__wbuarti__DOT____Vcellinp__txfifo____pinNumber2)?"RST":" ", |
(tb.v__DOT__wbuarti__DOT__txf_wb_write)?"WR":" ", |
(tb.v__DOT__wbuarti__DOT__txfifo__DOT__r_fill), |
(tb.v__DOT__wbuarti__DOT__txfifo__DOT__r_first), |
(tb.v__DOT__wbuarti__DOT__txfifo__DOT__w_first_plus_one), |
(tb.v__DOT__wbuarti__DOT__txfifo__DOT__r_last), |
isgraph(tb.v__DOT__wbuarti__DOT__tx_data&0x0ff)? |
(tb.v__DOT__wbuarti__DOT__tx_data&0x0ff) : '.', |
(tb.v__DOT__wbuarti__DOT____Vcellinp__txfifo____pinNumber5)?"RD":" ", |
(tb.v__DOT__wbuarti__DOT__tx_empty_n)?"TXI":"EMP", |
(tb.v__DOT__wbuarti__DOT__tx_data), |
(tb.o_uart_tx)); |
#endif |
} |
|
tfp->close(); |
|
// |
// *IF* we ever get here, then at least explain to the user |
// why we stopped. |
// |
printf("\n\nSimulation complete\n"); |
} else { |
// |
// Non-interactive mode is more difficult. In this case, we |
// must figure out how to determine if the test was successful |
// or not. Since uartsim dumps the UART output to standard |
// out, we then need to do a bit of work to capture that. |
// |
// In particular, we are going to fork ourselves and set up our |
// child process so that we can read from its standard out |
// (and write to its standard in--although we don't). |
int childs_stdin[2], childs_stdout[2]; |
FILE *fp = fopen(matchfile, "r"); |
long flen = 0; |
|
// |
// Before forking (and getting complicated), let's read the |
// file describing the data we are supposed to read. Our goal |
// will basically be to do an strncmp with the data in this |
// file, and then to check for zero (equality). |
// |
if (fp == NULL) { |
fprintf(stderr, "ERR - could not open %s\n", matchfile); |
perror("O/S Err:"); |
printf("FAIL\n"); |
exit(EXIT_FAILURE); |
} |
|
// Quick, look up how long this file is. |
fseek(fp, 0l, SEEK_END); |
flen = ftell(fp); |
fseek(fp, 0l, SEEK_SET); |
|
if (flen <= 0) { |
if (flen == 0) |
fprintf(stderr, "ERR - zero length match file!\n"); |
else { |
fprintf(stderr, "ERR - getting file length\n"); |
perror("O/S Err:"); |
} |
printf("FAIL\n"); |
exit(EXIT_FAILURE); |
} |
|
|
// We are ready to do our forking magic. So, let's allocate |
// pipes for the childs standard input and output streams. |
if ((pipe(childs_stdin)!=0)||(pipe(childs_stdout) != 0)) { |
fprintf(stderr, "ERR setting up child pipes\n"); |
perror("O/S Err:"); |
printf("FAIL\n"); |
exit(EXIT_FAILURE); |
} |
|
|
// |
// FORK !!!!! |
// |
// After this line, there are two threads running--a parent and |
// a child. The childs child_pid will be zero, the parents |
// child_pid will be the pid of the child. |
pid_t child_pid = fork(); |
|
// Make sure the fork worked ... |
if (child_pid < 0) { |
fprintf(stderr, "ERR setting up child process fork\n"); |
perror("O/S Err:"); |
printf("FAIL\n"); |
exit(EXIT_FAILURE); |
} |
|
if (child_pid) { |
int nr = -2, rd, fail; |
|
// We are the parent |
// Adjust our pipe file descriptors so that they are |
// useful. |
close(childs_stdin[ 0]); // Close the read end |
close(childs_stdout[1]); // Close the write end |
|
// Let's allocate some buffers to contain both our |
// match file (string), and what we read from the |
// UART. Nominally, we would only need flen+1 |
// characters, but this number doesn't quite work--since |
// mkspeech turned all of the the LFs into CR/LF pairs. |
// In the worst case, this would double the number of |
// characters we would need. Hence, we read allocate |
// enough for the worst case. |
char *string = (char *)malloc((size_t)(2*flen+2)), |
*rdbuf = (char *)malloc((size_t)(2*flen+2)); |
|
// If this doesn't work, admit to a failure |
if ((string == NULL)||(rdbuf == NULL)) { |
fprintf(stderr, "ERR Malloc failure --- cannot allocate space to read match file\n"); |
perror("O/S Err:"); |
printf("FAIL\n"); |
exit(EXIT_FAILURE); |
} |
|
// Read the string we are going to match against from |
// the matchfile. Expand NLs into CR,NL pairs. Also |
// keep track of the resulting length (in flen), and |
// terminate the string with a null character. |
// |
{ |
// Read string, and expand newlines into |
// CR LF pairs |
char *dp = string; |
int ch; |
while((ch =fgetc(fp))!=EOF) { |
if (ch == '\n') |
*dp++ = '\r'; |
*dp++ = ch; |
} |
*dp++ = '\0'; |
flen = strlen(string); |
} |
|
// |
// Enough setup, let's do our work: Read a character |
// from the pipe and compare it against what we are |
// expecting. Break out on any comparison failure. |
// |
nr = 0; |
rd = 0; |
fail = -1; |
while((nr<flen) |
&&((rd = read(childs_stdout[0], |
&rdbuf[nr], 1))>0)) { |
for(int i=0; i<rd; i++) |
if (rdbuf[nr+i] != string[nr+i]) { |
fail = nr+i; |
break; |
} |
if (fail>=0) |
break; |
rdbuf[rd+nr] = 0; |
nr += rd; |
} |
|
// Tell the user how many (of how many) characters we |
// compared (that matched), for debugging purposes. |
// |
printf("MATCH COMPLETE, nr = %d (/ %ld)\n", nr, flen); |
fflush(stdout); |
|
kill(child_pid, SIGKILL); |
|
free(string); |
free(rdbuf); |
|
// Report on the results, either PASS or FAIL |
if (nr == flen) { |
printf("PASS\n"); |
exit(EXIT_SUCCESS); |
} else { |
printf("%s\n\nDoes not match. MISMATCH: ch[%d]=%c != %c (%02x)\nFAIL\n", rdbuf, fail, rdbuf[fail], string[fail], string[fail]); |
exit(EXIT_FAILURE); |
} |
// |
// At this point, the parent is complete, and can |
// exit. |
} else { |
// |
// If childs_pid == 0, then we are the child |
// |
// The child reports the uart result via stdout, so |
// let's make certain it points to STDOUT_FILENO. |
// |
close(childs_stdin[ 1]); // Close the write end |
close(childs_stdout[0]); // Close the read end |
|
// Now, adjust our stdin/stdout file numbers |
// Stdin first. (Yes, I know we arent use stdin, this |
// is more for form than anything else.) |
close(STDIN_FILENO); |
if (dup(childs_stdin[0]) != STDIN_FILENO) { |
fprintf(stderr, "Could not create childs stdin\n"); |
perror("O/S ERR"); |
exit(EXIT_FAILURE); |
} |
|
// Set up the standard out file descriptor so that it |
// points to our pipe |
close(STDOUT_FILENO); |
if (dup(childs_stdout[1]) != STDOUT_FILENO) { |
fprintf(stderr, "Could not create childs stdout\n"); |
perror("O/S ERR"); |
exit(EXIT_FAILURE); |
} |
|
// Set the UARTSIM up to producing an output to the |
// STDOUT, rather than a TCP/IP port |
uart = new UARTSIM(0); |
// Set up our baud rate, stop bits, parity, etc. |
// properly |
uart->setup(tb.i_setup); |
|
// |
// Now ... we're finally ready to run our simulation. |
// |
// while(testcount < baudclocks * 16 * 2048) |
while(testcount++ < 0x7f000000) { |
// Rising edge of the clock |
tb.i_clk = 1; |
tb.eval(); |
// Negative edge of the clock |
tb.i_clk = 0; |
tb.eval(); |
|
// Advance the UART based upon the output |
// o_uart_tx value |
(*uart)(tb.o_uart_tx); |
} |
|
// We will never get here. If all goes well, we will be |
// killed as soon as we produce the speech.txt file |
// output--many clocks before this. |
|
// |
// If we do get here, something is terribly wrong. |
// |
fprintf(stderr, "Child was never killed, did it produce any output?\n"); |
fprintf(stderr, "FAIL\n"); |
exit(EXIT_FAILURE); |
} |
} |
} |
|
/bench/verilog/Makefile
8,9 → 8,38
## bench test. The result is C++ code (built by Verilator), that |
## is then built (herein) into a library. |
## |
## Targets: The default target, all, builds the target test, which includes |
## the linetest Verilator library necessary for testing. |
## ALTERNATE_PURPOSE: |
## All of the Verilog files within this directory may be made top level |
## files in their own right for the purpose of testing the UART capability |
## of your board. Should you wish to test these as toplevel files, you |
## will need to remove the i_setup from the input, and set it to something |
## like: |
## wire [29:0] i_setup; |
## |
## // If we have a 100MHz clock, then we can set up for a 115,200 |
## // baud clock by setting i_setup to (100MHz / 115200) ~= 868. |
## // The upper bits of this number also set the protocol to |
## // one stop bit, no parity, and 8 data bits. |
## assign i_setup = 30'd868; // 115,200 Baud 8N1 |
## |
## Using this purpose, the UART ports of a new piece of hardware may be |
## proven. To do this, |
## 1. get BLINKY working first--to prove that the clock works like |
## you think it does. Then, once BLINKY is running, |
## 2. get helloworld working. This requires only the clock and |
## the output UART pin to work. |
## (Aside) 3. Once helloworld works, you should be able to get |
## speechfifo to work with no further hassles. |
## 4. After helloworld works, switch to getting linetest running on |
## your hardware. This will prove that you have not only |
## the clock and output UART pin working, but that you also |
## have the input UART pin working as well. |
## |
## Targets: The default target of this makefile, all, builds the target |
## test, which includes the linetest Verilator library, the |
## helloworld Verilator library, and the speechfifo Verilator library--all |
## necessary for bench testing using the C++ files in bench/cpp. |
## |
## Creator: Dan Gisselquist, Ph.D. |
## Gisselquist Technology, LLC |
## |
46,8 → 75,11
VDIRFB:= $(FBDIR)/obj_dir |
RTLDR := ../../rtl |
|
.PHONY: test |
test: $(VDIRFB)/Vlinetest__ALL.a |
.PHONY: test testline testhello speechfifo |
test: testline testhello speechfifo |
testline: $(VDIRFB)/Vlinetest__ALL.a |
testhello: $(VDIRFB)/Vhelloworld__ALL.a |
speechfifo: $(VDIRFB)/Vspeechfifo__ALL.a |
|
$(VDIRFB)/Vlinetest__ALL.a: $(VDIRFB)/Vlinetest.h $(VDIRFB)/Vlinetest.cpp |
$(VDIRFB)/Vlinetest__ALL.a: $(VDIRFB)/Vlinetest.mk |
54,8 → 86,19
$(VDIRFB)/Vlinetest.h $(VDIRFB)/Vlinetest.cpp $(VDIRFB)/Vlinetest.mk: linetest.v |
$(VDIRFB)/Vlinetest.h $(VDIRFB)/Vlinetest.cpp $(VDIRFB)/Vlinetest.mk: $(RTLDR)/rxuart.v $(RTLDR)/txuart.v |
|
$(VDIRFB)/Vhelloworld__ALL.a: $(VDIRFB)/Vhelloworld.h $(VDIRFB)/Vhelloworld.cpp |
$(VDIRFB)/Vhelloworld__ALL.a: $(VDIRFB)/Vhelloworld.mk |
$(VDIRFB)/Vhelloworld.h $(VDIRFB)/Vhelloworld.cpp $(VDIRFB)/Vhelloworld.mk: helloworld.v |
$(VDIRFB)/Vhelloworld.h $(VDIRFB)/Vhelloworld.cpp $(VDIRFB)/Vhelloworld.mk: $(RTLDR)/rxuart.v $(RTLDR)/txuart.v |
|
SPEECHSRCS := $(addprefix $(RTLDR)/,rxuart.v txuart.v ufifo.v wbuart.v) |
SPEECHVFILES:= $(addprefix $(VDIRFB)/,Vspeechfifo.h Vspeechfifo.cpp Vspeechfifo.mk) |
$(VDIRFB)/Vspeechfifo__ALL.a: $(VDIRFB)/Vspeechfifo.h $(VDIRFB)/Vspeechfifo.cpp |
$(VDIRFB)/Vspeechfifo__ALL.a: $(VDIRFB)/Vspeechfifo.mk |
$(SPEECHVFILES): speechfifo.v $(SPEECHSRCS) |
|
$(VDIRFB)/V%.cpp $(VDIRFB)/V%.h $(VDIRFB)/V%.mk: $(FBDIR)/%.v |
verilator -cc -y ../../rtl $*.v |
verilator --trace -cc -y ../../rtl $*.v |
|
$(VDIRFB)/V%__ALL.a: $(VDIRFB)/V%.mk |
cd $(VDIRFB); make -f V$*.mk |
/bench/verilog/README.md
0,0 → 1,14
This directory contains three basic configurations for testing your UART |
and proving that it works: |
- helloworld: Displays the familiar "Hello, World!" message over and over. Tests the transmit UART port. |
- linetest: Reads a line of text, then parrots it back. Tests both receive and transmit UART. |
- speechfifo: Recites the Gettysburg address over and over again. This can be used to test the transmit UART port, and particularly to test receivers to see if they can receive 1400+ characters at full speed without any problems. |
|
Each of these configurations has a commented line defining OPT_STANDALONE within |
it. If you uncomment this line, the configurations may be run as stand alone |
configurations. (You will probably want to adjust the baud clock divider, to |
be specific to the baud rate you wish to generate as well as the clock rate |
you will be generating this from.) |
|
If you leave OPT_STANDALONE commented, these demo programs should work quite |
nicely with a Verilator based simulation. |
/bench/verilog/helloworld.v
0,0 → 1,127
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: helloworld.v |
// |
// Project: wbuart32, a full featured UART with simulator |
// |
// Purpose: To create a *very* simple UART test program, which can be used |
// as the top level design file of any FPGA program. |
// |
// With some modifications (discussed below), this RTL should be able to |
// run as a top-level testing file, requiring only the UART and clock pin |
// to work. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// 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. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
// Uncomment the next line if you want this program to work as a standalone |
// (not verilated) RTL "program" to test your UART. You'll also need to set |
// your setup condition properly, though. I recommend setting it to the |
// ratio of your onboard clock to your desired baud rate. For more information |
// about how to set this, please see the specification. |
// |
//`define OPT_STANDALONE |
// |
module helloworld(i_clk, |
`ifndef OPT_STANDALONE |
i_setup, |
`endif |
o_uart_tx); |
// |
input i_clk; |
output wire o_uart_tx; |
`ifndef OPT_STANDALONE |
input [29:0] i_setup; |
`endif |
|
// If i_setup isnt set up as an input parameter, it needs to be set. |
// We do so here, to a setting appropriate to create a 115200 Baud |
// comms system from a 100MHz clock. This also sets us to an 8-bit |
// data word, 1-stop bit, and no parity. |
`ifdef OPT_STANDALONE |
wire [29:0] i_setup; |
assign i_setup = 30'd868; // 115200 Baud, if clk @ 100MHz |
`endif |
|
reg pwr_reset; |
initial pwr_reset = 1'b1; |
always @(posedge i_clk) |
pwr_reset <= 1'b0; |
|
reg [7:0] message [0:15]; |
|
initial begin |
message[ 0] = "H"; |
message[ 1] = "e"; |
message[ 2] = "l"; |
message[ 3] = "l"; |
message[ 4] = "o"; |
message[ 5] = ","; |
message[ 6] = " "; |
message[ 7] = "W"; |
message[ 8] = "o"; |
message[ 9] = "r"; |
message[10] = "l"; |
message[11] = "d"; |
message[12] = "!"; |
message[13] = " "; |
message[14] = "\r"; |
message[15] = "\n"; |
end |
|
reg [27:0] counter; |
initial counter = 28'hffffff0; |
always @(posedge i_clk) |
counter <= counter + 1'b1; |
|
wire tx_break, tx_busy; |
reg tx_stb; |
reg [3:0] tx_index; |
reg [7:0] tx_data; |
|
assign tx_break = 1'b0; |
|
initial tx_index = 4'h0; |
always @(posedge i_clk) |
if ((tx_stb)&&(!tx_busy)) |
tx_index <= tx_index + 1'b1; |
always @(posedge i_clk) |
tx_data <= message[tx_index]; |
|
initial tx_stb = 1'b0; |
always @(posedge i_clk) |
if (&counter) |
tx_stb <= 1'b1; |
else if ((tx_stb)&&(!tx_busy)&&(tx_index==4'hf)) |
tx_stb <= 1'b0; |
|
txuart transmitter(i_clk, pwr_reset, i_setup, tx_break, |
tx_stb, tx_data, o_uart_tx, tx_busy); |
|
endmodule |
/bench/verilog/linetest.v
8,6 → 8,10
// buffering one line's worth of input, and then piping that line |
// to the transmitter while (possibly) receiving a new line. |
// |
// With some modifications (discussed below), this RTL should be able to |
// run as a top-level testing file, requiring only the transmit and receive |
// UART pins and the clock to work. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
37,31 → 41,85
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module linetest(i_clk, i_setup, i_uart, o_uart); |
// Uncomment the next line if you want this program to work as a standalone |
// (not verilated) RTL "program" to test your UART. You'll also need to set |
// your setup condition properly, though. I recommend setting it to the |
// ratio of your onboard clock to your desired baud rate. For more information |
// about how to set this, please see the specification. |
// |
// `define OPT_STANDALONE |
// |
module linetest(i_clk, |
`ifndef OPT_STANDALONE |
i_setup, |
`endif |
i_uart_rx, o_uart_tx); |
input i_clk; |
`ifndef OPT_STANDALONE |
input [29:0] i_setup; |
input i_uart; |
output wire o_uart; |
`endif |
input i_uart_rx; |
output wire o_uart_tx; |
|
// If i_setup isnt set up as an input parameter, it needs to be set. |
// We do so here, to a setting appropriate to create a 115200 Baud |
// comms system from a 100MHz clock. This also sets us to an 8-bit |
// data word, 1-stop bit, and no parity. |
`ifdef OPT_STANDALONE |
wire [29:0] i_setup; |
assign i_setup = 30'd868; // 115200 Baud, if clk @ 100MHz |
`endif |
|
reg [7:0] buffer [0:255]; |
reg [7:0] head, tail; |
|
// Create a reset line that will always be true on a power on reset |
reg pwr_reset; |
initial pwr_reset = 1'b1; |
always @(posedge i_clk) |
pwr_reset = 1'b0; |
|
|
|
// The UART Receiver |
// |
// This is where everything begins, by reading data from the UART. |
// |
// Data (rx_data) is present when rx_stb is true. Any parity or |
// frame errors will also be valid at that time. Finally, we'll ignore |
// errors, and even the clocked uart input distributed from here. |
wire rx_stb, rx_break, rx_perr, rx_ferr, rx_ignored; |
wire [7:0] rx_data; |
|
rxuart receiver(i_clk, pwr_reset, i_setup, i_uart, rx_stb, rx_data, |
rxuart receiver(i_clk, pwr_reset, i_setup, i_uart_rx, rx_stb, rx_data, |
rx_break, rx_perr, rx_ferr, rx_ignored); |
|
|
// The next step in this process is to dump everything we read into a |
// FIFO. First step: writing into the FIFO. Always write into FIFO |
// memory. (The next step will step the memory address if rx_stb was |
// true ...) |
wire [7:0] nxt_head; |
assign nxt_head = head + 8'h01; |
always @(posedge i_clk) |
buffer[head] <= rx_data; |
|
// Select where in our FIFO memory to write. On reset, we clear the |
// memory. In all other cases/respects, we step the memory forward. |
// |
// However ... we won't step it forward IF ... |
// rx_break - we are in a BREAK condition on the line |
// (i.e. ... it's disconnected) |
// rx_perr - We've seen a parity error |
// rx_ferr - Same thing for a frame error |
// nxt_head != tail - If the FIFO is already full, we'll just drop |
// this new value, rather than dumping random garbage |
// from the FIFO until we go round again ... i.e., we |
// don't write on potential overflow. |
// |
// Adjusting this address will make certain that the next write to the |
// FIFO goes to the next address--since we've already written the FIFO |
// memory at this address. |
initial head= 8'h00; |
always @(posedge i_clk) |
if (pwr_reset) |
73,8 → 131,19
reg [7:0] lineend; |
reg run_tx; |
|
// How much of the FIFO is in use? head - tail. What if they wrap |
// around? Still: head-tail, but this time truncated to the number of |
// bits of interest. It can never be negative ... so ... we're good, |
// this just measures that number. |
assign nused = head-tail; |
|
// Here's the guts of the algorithm--setting run_tx. Once set, the |
// buffer will flush. Here, we set it on one of two conditions: 1) |
// a newline is received, or 2) the line is now longer than 80 |
// characters. |
// |
// Once the line has ben transmitted (separate from emptying the buffer) |
// we stop transmitting. |
initial run_tx = 0; |
initial lineend = 0; |
always @(posedge i_clk) |
82,27 → 151,43
begin |
run_tx <= 1'b0; |
lineend <= 8'h00; |
end else if ((rx_data == 8'h0a)&&(rx_stb)) |
end else if(((rx_data == 8'h0a)||(rx_data == 8'hd))&&(rx_stb)) |
begin |
// Start transmitting once we get to either a newline |
// or a carriage return character |
lineend <= head+8'h1; |
run_tx <= 1'b1; |
end else if ((!run_tx)&&(nused>8'd80)&&(head != tail)) |
end else if ((!run_tx)&&(nused>8'd80)) |
begin |
// Start transmitting once we get to 80 chars |
lineend <= head; |
run_tx <= 1'b1; |
end else if (tail == lineend) |
// Line buffer has been emptied |
run_tx <= 1'b0; |
|
// Now ... let's deal with the transmitter |
wire tx_break, tx_busy; |
assign tx_break = 1'b0; |
reg [7:0] tx_data; |
reg tx_stb; |
|
always @(posedge i_clk) |
tx_data <= buffer[tail]; |
// When do we wish to transmit? |
// |
// Any time run_tx is true--but we'll give it an extra clock. |
initial tx_stb = 1'b0; |
always @(posedge i_clk) |
tx_stb <= run_tx; |
|
// We'll transmit the data from our FIFO from ... wherever our tail |
// is pointed. |
always @(posedge i_clk) |
tx_data <= buffer[tail]; |
|
// We increment the pointer to where we read from any time 1) we are |
// requesting to transmit a character, and 2) the transmitter was not |
// busy and thus accepted our request. At that time, increment the |
// pointer, and we'll be ready for another round. |
initial tail = 8'h00; |
always @(posedge i_clk) |
if(pwr_reset) |
111,6 → 196,6
tail <= tail + 8'h01; |
|
txuart transmitter(i_clk, pwr_reset, i_setup, tx_break, |
tx_stb, tx_data, o_uart, tx_busy); |
tx_stb, tx_data, o_uart_tx, tx_busy); |
|
endmodule |
/bench/verilog/speechfifo.v
0,0 → 1,211
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: speechfifo.v |
// |
// Project: wbuart32, a full featured UART with simulator |
// |
// Purpose: To test/demonstrate/prove the wishbone access to the FIFO'd |
// UART via sending more information than the FIFO can hold, |
// and then verifying that this was the value received. |
// |
// To do this, we "borrow" a copy of Abraham Lincolns Gettysburg address, |
// make that the FIFO isn't large enough to hold it, and then try |
// to send this address every couple of minutes. |
// |
// With some minor modifications (discussed below), this RTL should be |
// able to be run as a top-level testing file, requiring only that the |
// clock and the transmit UART pins be working. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// 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. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
// Uncomment the next line if you want this program to work as a standalone |
// (not verilated) RTL "program" to test your UART. You'll also need to set |
// your i_setup condition properly, though (below). I recommend setting it to |
// the ratio of your onboard clock to your desired baud rate. For more |
// information about how to set this, please see the specification. |
// |
//`define OPT_STANDALONE |
// |
module speechfifo(i_clk, |
`ifndef OPT_STANDALONE |
i_setup, |
`endif |
o_uart_tx); |
input i_clk; |
output wire o_uart_tx; |
|
// The i_setup wires are input when run under Verilator, but need to |
// be set internally if this is going to run as a standalone top level |
// test configuration. |
`ifdef OPT_STANDALONE |
wire [29:0] i_setup; |
|
// Here we set i_setup to something appropriate to create a 115200 Baud |
// UART system from a 100MHz clock. This also sets us to an 8-bit data |
// word, 1-stop bit, and no parity. |
assign i_setup = 30'd868; |
`else |
input [29:0] i_setup; |
`endif |
|
reg wb_stb; |
reg [1:0] wb_addr; |
reg [31:0] wb_data; |
|
wire uart_stall, uart_ack; |
wire [31:0] uart_data; |
|
wire tx_int, txfifo_int; |
|
// The next four lines create a strobe signal that is true on the first |
// clock, but never after. This makes for a decent power-on reset |
// signal. |
reg pwr_reset; |
initial pwr_reset = 1'b1; |
always @(posedge i_clk) |
pwr_reset <= 1'b0; |
|
// The message we wish to transmit is kept in "message". It needs to be |
// set initially. Do so here. |
// |
// Since the message has fewer than 2048 elements in it, we preset every |
// element to a space so that if (for some reason) we broadcast past the |
// end of our message, we'll at least be sending something useful. |
integer i; |
reg [7:0] message [0:2047]; |
initial begin |
for(i=0; i<2048; i=i+1) |
message[i] = 8'h20; |
$readmemh("speech.hex",message); |
end |
|
// Let's keep track of time, and send our message over and over again. |
// To do this, we'll keep track of a restart counter. When this counter |
// rolls over, we restart our message. |
reg [30:0] restart_counter; |
// Since we want to start our message just a couple clocks after power |
// up, we'll set the reset counter just a couple clocks shy of a roll |
// over. |
initial restart_counter = -31'd16; |
always @(posedge i_clk) |
restart_counter <= restart_counter+1'b1; |
|
// Ok, now that we have a counter that tells us when to start over, |
// let's build a set of signals that we can use to get things started |
// again. This will be the restart signal. On this signal, we just |
// restart everything. |
reg restart; |
initial restart = 0; |
always @(posedge i_clk) |
begin |
restart <= (restart_counter == 0); |
end |
|
// Our message index. This is the address of the character we wish to |
// transmit next. Note, there's a clock delay between setting this |
// index and when the wb_data is valid. Hence, we set the index on |
// restart[0] to zero. |
reg [10:0] msg_index; |
initial msg_index = 11'd2040; |
always @(posedge i_clk) |
begin |
if (restart) |
msg_index <= 0; |
else if ((wb_stb)&&(!uart_stall)) |
// We only advance the index if a port operation on the |
// wbuart has taken place. That's what the |
// (wb_stb)&&(!uart_stall) is about. (wb_stb) is the |
// request for a transaction on the bus, uart_stall |
// tells us to wait 'cause the peripheral isn't ready. |
// In our case, it's always ready, uart_stall == 0, but |
// we keep/maintain this logic for good form. |
// |
// Note also, we only advance when restart[0] is zero. |
// This keeps us from advancing prior to the setup |
// word. |
msg_index <= msg_index + 1'b1; |
end |
|
// What data will we be sending to the port? |
always @(posedge i_clk) |
if (restart) |
// The first thing we do is set the baud rate, and |
// serial port configuration parameters. Ideally, |
// we'd only set this once. But rather than complicate |
// the logic, we set it everytime we start over. |
wb_data <= { 2'b00, i_setup }; |
else if ((wb_stb)&&(!uart_stall)) |
// Then, if the last thing was received over the bus, |
// we move to the next data item. |
wb_data <= { 24'h00, message[msg_index] }; |
|
// We send our first value to the SETUP address (all zeros), all other |
// values we send to the transmitters address. We should really be |
// double checking that stall remains low, but its not required here. |
always @(posedge i_clk) |
if (restart) |
wb_addr <= 2'b00; |
else // if (!uart_stall)?? |
wb_addr <= 2'b11; |
|
// The wb_stb signal indicates that we wish to write, using the wishbone |
// to our peripheral. We have two separate types of writes. First, |
// we wish to write our setup. Then we want to drop STB and write |
// our data. Once we've filled half of the FIFO, we wait for the FIFO |
// to empty before issuing a STB again and then fill up half the FIFO |
// again. |
initial wb_stb = 1'b0; |
always @(posedge i_clk) |
if (restart) |
wb_stb <= 1'b1; |
else if (msg_index >= 1481) |
wb_stb <= 1'b0; |
else if (tx_int) |
wb_stb <= 1'b1; |
else if (txfifo_int) |
wb_stb <= wb_stb; |
else |
wb_stb <= 1'b0; |
|
// We aren't using the receive interrupts, so we'll just mark them |
// here as ignored. |
wire ignored_rx_int, ignored_rxfifo_int; |
|
// Finally--the unit under test--now that we've set up all the wires |
// to run/test it. |
wbuart #(30'h868) |
wbuarti(i_clk, pwr_reset, |
wb_stb, wb_stb, 1'b1, wb_addr, wb_data, |
uart_stall, uart_ack, uart_data, |
1'b1, o_uart_tx, |
ignored_rx_int, tx_int, |
ignored_rxfifo_int, txfifo_int); |
|
endmodule |
/doc/gpl-3.0.pdf
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/doc/spec.pdf
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/doc/src/spec.tex
45,13 → 45,13
\project{WBUART32} |
\title{Specification} |
\author{Dan Gisselquist, Ph.D.} |
\email{dgisselq (at) opencores.org} |
\email{dgisselq (at) ieee.org} |
\revision{Rev.~0.1} |
\begin{document} |
\pagestyle{gqtekspecplain} |
\titlepage |
\begin{license} |
Copyright (C) \theyear\today, Gisselquist Technology, LLC. |
Copyright (C) 2016--2017, Gisselquist Technology, LLC. |
|
This project is free software (firmware): you can redistribute it and/or |
modify it under the terms of the GNU General Public License as published |
67,6 → 67,7
with this program. If not, see \texttt{http://www.gnu.org/licenses/} for a copy. |
\end{license} |
\begin{revisionhistory} |
0.2 & 1/03/2017 & D. Gisselquist & Added test-bench information\\\hline |
0.1 & 8/26/2016 & D. Gisselquist & Initial Draft Specification\\\hline |
\end{revisionhistory} |
% Revision History |
79,7 → 80,7
designer. The task is simple, easy, and there's not all that much to it. |
This project comes out of some of my first experiences with Verilog. |
|
Since then, it has been augmented with a very useful capability for |
Since then, it has been augmented with quite a few useful capabilities for |
simulating a UART connection when using Verilator. It is this, perhaps |
unusual, addition to the core set that makes this core worth taking note of. |
|
116,12 → 117,20
CPU, capable of running Linux, to a terminal to verify that yes it can truly |
run Linux--all within Verilator. |
|
As a final addition, there are three files in the test bench section which can |
be used as top--level design files to prove whether or not the serial port on |
a given circuit board works. |
|
\chapter{Architecture}\label{ch:arch} |
|
The HDL portion of the core itself consists of three files: {\tt rxuart.v}, |
{\tt txuart.v}, and {\tt wbuart-insert.v}. These are, respectively, the |
receive UART code, the transmit UART code, and an example of how the receiver |
and transmitter may be connected to a Wishbone bus. |
The HDL portion of the core itself consists of four basic files: {\tt rxuart.v}, |
{\tt txuart.v}, {\tt ufifo.v} and {\tt wbuart.v}. These are, respectively, the |
receive UART code, the transmit UART code, a fairly generic FIFO, and a fully |
wishbone compliant UART peripheral. This latter files demonstrates one example |
of how the receiver, transmitter, and FIFOs may be connected to a Wishbone bus. |
A fifth file, {\tt wbuart-insert.v}, demonstrates how the {\tt rxuart.v} and |
{\tt txuart.v} files may be included into a module implementing a simpler |
interface. |
|
Each of the core files, {\tt rxuart.v} and {\tt txuart.v}, are fully capable. |
They each accept a 29--bit setup value specifying baud rate, the number of bits |
156,22 → 165,24
Connecting to either {\tt txuart.v} or {\tt rxuart.v} is quite simple. Both |
files have a data port and a strobe. To transmit, set the data and strobe |
lines. Drop the strobe line as soon as the strobe is asserted and the busy line |
is not. Likewise, to connect to the {\tt rxuart.v} port, there is a data |
and a strobe. This time, though, these two wires are outputs of the port as |
opposed to inputs. |
is not. |
Likewise, to connect to the {\tt rxuart.v} port, there is a data |
and a strobe. This time, though, these two wires are outputs of the receive |
module as opposed to inputs. |
When the strobe is high, the data is valid. It will only be high for one |
clock period. If you wish to connect this output to a bus, a register will be |
needed to hold the strobe high until the data is read. Also, while the strobe |
is high, the {\tt o\_break} line will indicate whether the receiver is in a |
``break'' state, {\tt o\_frame\_err} will indicate whether or not there was a |
is high, the {\tt o\_frame\_err} will indicate whether or not there was a |
framing error (i.e., no stop bit), and {\tt o\_parity\_err} will indicate |
wheher or not the parity matched. |
whether or not the parity matched. Finally, the {\tt o\_break} line will |
indicate whether the receiver is in a ``break'' state, |
|
The {\tt tx\_busy} line may be inverted and connected to a transmit interrupt |
line. In a similar fashion, the {\tt rx\_stb} line, or the bus equivalent of |
{\tt rx\_ready}, may be used for receive interrupt lines. |
{\tt rx\_ready}, may be used for receive interrupt lines--although it will need |
to be latched as both {\tt wbuart.v} and {\tt wbuart-insert.v} demonstrate. |
|
An example of how to put this configuration together is found in |
An simple example of how to put this configuration together is found in |
{\tt wbuart-insert.v}. In this example given, the {\tt rx\_data} register |
will have only the lower eight bits set if the data is valid, higher bits will |
be set upon error conditions, and cleared automatically upon the next byte read. |
182,14 → 193,34
transmitter is busy (via polling), whether it is currently in a break condition, |
or even what bit is currently being placed to the output port. |
|
A more comprehensive example of how these UART modules may be used together |
can be found in {\tt wbuart.v}. This file provides a full wishbone interface |
allowing interaction with the core using four registers: a setup register, |
receive register and transmit register as before, as well as a FIFO health |
register through which the size and fill of the FIFO can be queried. |
|
The C++ simulation portion of the code revolves around the file |
{\tt bench/cpp/uartsim.cpp} and its associated header. This file defines a |
class, {\tt UARTSIM}, which can be used to connect the UART to a TCP/IP stream. |
When initialized, this class takes, as input, the TCP/IP port number that the |
class is to connect with. Once connected, using this is as simple as |
calculating the receive input bit from the transmit output bit when the |
clock is low, and the core takes care of everything else. |
class is to connect with. Setting the port to zero connects the UART to |
the standard input and output file facilities. Once connected, using this |
simulator is as simple as calculating the receive input bit from the transmit |
output bit when the clock is low, and the core takes care of everything else. |
|
Finally, there are a series of example files found in the bench/verilog |
directory. {\tt helloworld.v} presents an example of a simple UART transmitter |
sending the ``Hello, World \\r\\n'' message over and over again. This example |
uses only the {\tt txuart.v} module, and can be simulated in Verilator. |
A second test file, {\tt linetest.v}, works by waiting for a line of data to be |
received, after which it parrots that line back to the terminal. This tests |
both {\tt txuart.v} and {\tt rxuart.v}. A third test file, {\tt speechfifo.v} |
tests both the wishbone interface as well as the FIFO, by filling the UART, |
10~samples at a time, with text from Abraham Lincoln's Gettysburg address. |
All three of these files have an internal option to define |
{\tt OPT\_STANDALONE}. If and when defined, they may be used as top--level |
files as part of a UART test. |
|
\chapter{Operation}\label{ch:ops} |
|
% This section describes the operation of the core. Specific sequences, such |
198,18 → 229,41
% |
|
To use the core, a couple of steps are required. First, wire it up. The |
{\tt wbuart-insert.v} file should provide a good example of how to wire it up. |
Second, set the UART configuration register. This is ideally set in an |
initial statement within the code somewhere, but can easily be set elsewhere |
by writing to this register from the bus. |
{\tt rxuart.v} and {\tt txuart.v} files may be wired up for use individually, |
or using an example such as {\tt wbuart-insert.v}. Alternatively, the |
{\tt wbuart.v} file may be connected to a straight 32--bit wishbone bus. |
Second, set the UART configuration register. This is ideally set by setting |
the {\tt INITIAL\_SETUP} parameter of {\tt rxuart}, {\tt txuart} or even |
{\tt wbuart.v} Alternatively, you can write to the setup register at a later |
time, as is done with the {\tt speechfifo.v} bench test. |
|
From a simulation standpoint, it will also need to be wired up. Somewhere, |
internal to the top--level Verilator C++ simulation file, you'll want to |
have a line similar to, |
From a simulation standpoint, it will also need to be ``wired'' up in your |
C++ main Verilator file. Somewhere, internal to the top--level Verilator |
C++ simulation file, you'll want to have some setup lines similar to, |
\begin{tabbing} |
\hbox to 3.0in{\tt \#include "uartsim.h"} \= {\em // Tell compiler about UARTSIM}\\ |
\vdots \\ |
{\tt UARTSIM *uartsim;} \> {\em // Declare a variable to hold the simulator}\\ |
{\tt uartsim = new UARTSIM(ip\_port);} \> {\em // Create/initialize it with your TCP/IP port \#} \\ |
{\tt uartsim->setup(setup\_register\_value);} \> {\em // Tell it the line coding to expect}\\ |
\end{tabbing} |
and then another set of lines within your clocked section that look something |
like, |
\begin{tabbing} |
{\tt if (!clk)} \= \\ |
\> {\tt tb->i\_rx} {\tt = } {\tt uartsim(tb->o\_uart, setup);} |
\> {\tt tb->i\_uart\_rx} {\tt = } {\tt uartsim(tb->o\_uart\_tx);} |
\end{tabbing} |
You should be able to find several examples of this in the {\tt helloworld.cpp}, |
{\tt linetest.cpp}, and {\tt speechtest.cpp} files. These C++ implementations, |
though, are also complicated by the need for a self--contained testing program |
to be able to capture and know what was placed onto the standard input and |
output streams, hence many of them fork() into two processes so that one |
process can verify the output of the other. Both {\tt speechtest.cpp} and |
{\tt linetest.cpp} allow a {\em -i} option to run in an interactive mode without |
forking. Either way, forking the simulation program shouldn't be needed for |
normal usages of these techniques, but you may find it helpful to know should |
you examine this code or should you wish to build your own test file that |
proves its own output. |
|
To use the transmitter, set the {\tt i\_stb} and {\tt i\_data} wires. Drop |
the strobe line any time after {\tt (i\_stb)\&\&(!o\_busy)}. |
218,16 → 272,18
|
From the standpoint of the bus, there are two ways to handle receiving and |
transmitting: polling and interrupt based, although both work one character at |
a time. To poll, repeatedly read the receive data register until only no |
bits but the bottom eight are set. This is an indication that the byte is |
a time. To poll, repeatedly read the receive data register until only bits from |
the bottom eight are set. This is an indication that the byte is |
valid. Alternatively, you could wait until the an interrupt line is set and |
then read. In the {\tt wbuart-insert.v} example, the {\tt rx\_int} line will |
be set, and automatically cleared upon any read. To write, one can read from |
the transmit data register until the eighth bit, the {\tt tx\_busy} bit, is |
cleared, and then transmit. Alternatively, this negation of this bit may be |
connected to an interrupt line. Writing to the port while idle will start |
it transmitting. Writing to the port while it is busy will fill a one word |
buffer that will get sent as soon as the port is idle for one clock. |
then read. In the {\tt wbuart-insert.v} example as well as the {\tt wbuart.v} |
implementation, the {\tt o\_uart\_rx\_int} line will be set ({\tt rx\_int} for |
{\tt wbuart-insert.v}), and automatically cleared upon any read. To write, |
one can read from the transmit data register until the eighth bit, the |
{\tt tx\_busy} bit, is cleared, and then transmit. Alternatively, this |
negation of this bit may be connected to an interrupt line, |
{\tt o\_uart\_tx\_int}. Writing to the port while the transmitter is idle will |
start it transmitting. Writing to the port while it is busy will fill a one |
word buffer that will get sent as soon as the port is idle for one clock. |
|
|
\chapter{Registers}\label{ch:registers} |
243,24 → 299,18
% in one and the same document. (Table of bits, vs. byetarray type of |
% description). |
|
The core really only has one register associated with it, which is the setup |
register. The format of this register is important, although not necessarily |
trivial or obvious. We'll cover two other registers here, though, associated |
with the example wishbone connections from {\tt wbuart-insert.v}. All three |
of these registers are shown in Tbl.~\ref{tbl:reglist}. |
\begin{table} |
\begin{center} |
\begin{reglist} |
SETUP & & 30 & R/W & UART configuration/setup register.\\\hline |
RX\_DATA & & 12 & R(/W) & Read data, reads from the UART.\\\hline |
TX\_DATA & & 12 & (R/)W & Transmit data: writes send out the UART.\\\hline |
The {\tt wbuart} core supports four registers, shown in Tbl.~\ref{tbl:reglist}. |
\begin{table}\begin{center}\begin{reglist} |
{\tt SETUP} & 2'b00 & 30 & R/W & UART configuration/setup register.\\\hline |
{\tt FIFO} & 2'b01 & 32 & R & Returns size and status of the FIFOs\\\hline |
{\tt RX\_DATA}& 2'b10 & 13 & R & Read data, reads from the UART.\\\hline |
{\tt TX\_DATA}& 2'b11 & 15 & (R/)W & Transmit data: writes send out the UART. |
\\\hline |
\end{reglist}\caption{UART Registers}\label{tbl:reglist} |
\end{center}\end{table} |
We'll cover the format of all of these registers here, as they are defined by |
{\tt wbuart.v}. |
|
Since the connections presented are only examples, they are listed without |
addresses, as their wishbone bus connectivity will be determined once they |
are connected. |
|
\section{Setup Register} |
The setup register is perhaps the most critical of all the registers. This |
is shown in Fig.\ref{fig:SETUP}. |
278,15 → 328,18
\caption{SETUP Register fields}\label{fig:SETUP} |
\end{center}\end{figure} |
It is designed so that, for any 8N1 protocol (eight data bits, no parity, one |
stop bit), only the number of clocks per baud interval needs to be set. The |
top two bits are unused, making this a 30--bit number. The other fields |
are: $N$ sets the number of bits per word. A value of zero corresponds |
to 8--bit words, a value of one to seven bit words, and so forth up to a value |
of three for five bit words. $S$ determines the number of stop bits. Set this |
to one for two stop bits, or leave it clear for a single stop bit. $P$ |
determines whether or not a parity bit exists (1 for parity, 0 for none), |
while $F$ determines whether or not the parity is fixed. Tbl.~\ref{fig:parity} |
lists out the various values possible here. |
stop bit), all of the upper bits will be set to zero so that only the number of |
clocks per baud interval needs to be set. The top two bits are unused, making |
this a 30--bit number.\footnote{The top two bits are ideally suited for adding |
in a user configurable hardware flow control: one for flow control in use, zero |
otherwise, but this is only a future upgrade possibility as of this writing.} |
The other fields are: $N$ sets the number of bits per word. A value of zero |
corresponds to 8--bit words, a value of one to seven bit words, and so forth up |
to a value of three for five bit words. $S$ determines the number of stop |
bits. Set this to one for two stop bits, or leave it at zero for a single |
stop bit. $P$ determines whether or not a parity bit is used (1~for parity, |
0~for no parity), while $F$ determines whether or not the parity is fixed. |
Tbl.~\ref{tbl:parity} lists out the various values possible here. |
\begin{table}\begin{center} |
\begin{tabular}{ccc|l} |
P&F&T&Setting \\\hline\hline |
298,58 → 351,139
\end{tabular}\caption{Parity setup}\label{tbl:parity} |
\end{center}\end{table} |
|
Changes to this setup register will take place in the transmitter as soon as |
the transmitter is idle and ready to accept another byte. |
|
Changes to this setup register in {\tt rxuart.v} also take place between bytes. |
However, within the {\tt wbuart.v} context, any changes to the setup register |
will also reset the receiver and receive FIFO together. Once reset, the |
receiver will insist on a minimum of sixteen idle baud intervals before |
receiving the next byte. |
|
\section{FIFO Register} |
The FIFO register is a read--only register containing information about the |
status of both receive and transmit FIFOs within it. The transmit FIFO |
information is kept in the upper 16--bits, and the receiver FIFO information |
in the lower 1-bits, as shown in Fig.~\ref{fig:FIFO}. |
\begin{figure}\begin{center} |
\begin{bytefield}[endianness=big]{32} |
\bitheader{0-31}\\ |
\bitbox[rlt]{4}{LGLN} |
\bitbox[rlt]{10}{TX Fill} |
\bitbox[rlt]{1}{H} |
\bitbox[rlt]{1}{Z} |
\bitbox[rlt]{4}{} |
\bitbox[rlt]{10}{} |
\bitbox[rlt]{1}{} |
\bitbox[rlt]{1}{} \\ |
\bitbox[rlb]{4}{} |
\bitbox[rlb]{10}{} |
\bitbox[rlb]{1}{} |
\bitbox[rlb]{1}{} |
\bitbox[rlb]{4}{LGLN} |
\bitbox[rlb]{10}{RX Fill} |
\bitbox[rlb]{1}{H} |
\bitbox[rlb]{1}{Z} \\ |
\end{bytefield} |
\caption{RXDATA Register fields}\label{fig:FIFO} |
\end{center}\end{figure} |
We'll discuss each of these bits individually. |
|
The {\tt LGLN} field indicates the log base two of the FIFO length. Hence an |
{\tt LGLN} field of four would indicate a FIFO length of sixteen values. |
The FIFO fill indicates the current level of fill. The $H$ bit will be true |
if the FIFO is half full, and the $Z$ bit will be true if the FIFO is non-empty. |
|
The $H$ and $Z$ bits also mirror the interrupt bits generated by {\tt wbuart.v}. |
Interrupts will be generated any time the FIFO is half full (on receive), or |
less than half full (on transmit). The same logic applies for the $Z$ bit. |
|
Writes to this register are quietly ignored. |
|
\section{RX\_DATA Register} |
Fig.~\ref{fig:RXDATA} |
\begin{figure}\begin{center} |
\begin{bytefield}[endianness=big]{32} |
\bitheader{0-31}\\ |
\bitbox{20}{20'h00} |
\bitbox{1}{B} |
\bitbox{1}{F} |
\bitbox{1}{P} |
\bitbox{1}{S} |
\bitbox{8}{RWORD} |
\bitbox[rlt]{19}{19'h00} |
\bitbox{1}{E} |
\bitbox[rlt]{1}{B} |
\bitbox[rlt]{1}{F} |
\bitbox[rlt]{1}{P} |
\bitbox[rlt]{1}{S} |
\bitbox[rlt]{8}{RWORD} \\ |
\bitbox[lrb]{19}{} |
\bitbox{1}{-} |
\bitbox[lrb]{1}{} |
\bitbox[lrb]{1}{} |
\bitbox[lrb]{1}{} |
\bitbox[lrb]{1}{} |
\bitbox[lrb]{8}{} |
\end{bytefield} |
\caption{RXDATA Register fields}\label{fig:RXDATA} |
\end{center}\end{figure} |
breaks out the various bit fields of the receive |
data register used in the {\tt wbuart-insert.v} example of connecting it to |
a bus. In particular, the $B$ field indicates that the receive line is in |
a break condition. The $F$ and $P$ fields indicate that a frame error or |
parity error were detected. These are valid like the data word: when the strobe |
line is set. The $S$ field will be false when the {\tt RWORD} is valid. |
Hence, if {\tt (RWORD \& ~0x0ff)} is zero there is a word ready to be received |
without error. |
data register used in {\tt wbuart.v}. In particular, the $B$ field indicates |
that the receive line is in a break condition. The $F$ and $P$ fields indicate |
that a frame error or parity error has been detected. These bits are not self |
clearing, but rather are cleared by writing to 1's to them. The $S$ field will |
be false when the {\tt RWORD} is valid. Hence, if {\tt (RWORD \& ~0x0ff)} is |
zero there is a word ready to be received without error. |
|
The $E$ bit is an error bit. When set, it indicates that the FIFO has |
overflowed sometime since the last reset. This bit is also a reset bit. |
In other words, writing a {\tt 1'b0} to this bit will command a receive |
reset: clearing the FIFO, and waiting for the line to be idle before receiving |
another byte. This bit is not implemented in {\tt wbuart-insert.v}, but |
exists in the {\tt wbuart.v} implementation. |
|
\section{TX\_DATA Register} |
Fig.~\ref{fig:TXDATA} |
\begin{figure}\begin{center} |
\begin{bytefield}[endianness=big]{32} |
\bitheader{0-31}\\ |
\bitbox{20}{2'h00} |
\bitbox{1}{C} |
\bitbox{1}{O} |
\bitbox{1}{B} |
\bitbox{1}{S} |
\bitbox{8}{TWORD} |
\bitbox[lrt]{17}{17'h00} |
\bitbox{1}{H} |
\bitbox{1}{Z} |
\bitbox{1}{E} |
\bitbox[lrt]{1}{C} |
\bitbox[lrt]{1}{O} |
\bitbox[lrt]{1}{B} |
\bitbox[lrt]{1}{S} |
\bitbox[lrt]{8}{TWORD} \\ |
\bitbox[lrb]{17}{} |
\bitbox{3}{3'h0} |
\bitbox[lrb]{1}{} |
\bitbox[lrb]{1}{} |
\bitbox[lrb]{1}{} |
\bitbox[lrb]{1}{} |
\bitbox[lrb]{8}{} |
\end{bytefield} |
\caption{TXDATA Register fields}\label{fig:TXDATA} |
\end{center}\end{figure} |
breaks out the various bit fields of the transmit data register used in |
{\tt wbuart-insert.v}. The $C$ field indicates whether or not the receive |
{\tt wbuart.v}. The $C$ field indicates whether or not the receive |
data line is high or low, the $O$ field indicates the same for the transmit |
line. These aren't particularly useful or valuable, but they don't fit in the |
receive data register since they would violate the error condition detector. |
They're thrown in here for whatever useful purpose one might find. The $B$ |
field, when set, sends a break condition down the wire. Writing to the TXDATA |
register, clearing the $B$ field, will clear the transmitter from the break |
condition without transmitting anything. The $S$ field is similar to the RXDATA |
strobe register. It will be true whenever the transmitter is busy or a byte |
is waiting for it. It will be clear only when the transmitter is idle. |
line. These aren't particularly useful or valuable, but the $C$ bit doesn't |
fit in the receive data register since it would violate the error condition |
detector. These two bits are thrown in here for whatever useful purpose one |
might find. The $B$ field, when set, sends a break condition down the wire. |
Further, writes to the TXDATA register while in a break condition and with the |
$B$ field clear, will clear the transmitter from any break condition without |
transmitting anything. The $S$ field is similar to the RXDATA strobe register. |
It is a read--only bit that will be true any time the transmitter is busy. |
It will be clear only when the transmitter is idle. |
|
To use the transmitter, simply write a byte to the TXDATA register with the |
upper 24--bits clear to transmit. |
The final three bits, $H$, $Z$, and $E$, are present only in {\tt wbuart.v}. |
These bits indicate $H$ if the FIFO is at least half full, $Z$ if the FIFO is |
empty, and $E$ if the FIFO has experienced an overflow condition since the |
last reset. Writing a {\tt 1'b1} to the $E$ bit will reset the transmit FIFO, |
both clearing any error indication in the FIFO as well as clearing the FIFO |
itself. |
|
To use the transmitter, simply write a byte to the TXDATA register |
with the upper 24--bits clear to transmit. |
|
\chapter{Clocks}\label{ch:clocks} |
The UART has been tested with a clock as fast as 200~MHz |
(Tbl.~\ref{tbl:clocks}). |
374,9 → 508,8
\chapter{Wishbone Datasheet}\label{ch:wishbone} |
|
Tbl.~\ref{tbl:wishbone} |
\begin{table}[htbp] |
\begin{center} |
\begin{wishboneds} |
\begin{table}[htbp]\begin{center}\begin{tabular}{|p{2.5in}|p{3.5in}|}\hline |
\rowcolor[gray]{0.85} Description & Specification \\\hline\hline |
Revision level of wishbone & WB B4 spec \\\hline |
Type of interface & Slave, Read/Write, pipeline reads supported \\\hline |
Port size & 32--bit \\\hline |
384,31 → 517,34
Maximum Operand Size & 32--bit \\\hline |
Data transfer ordering & (Irrelevant) \\\hline |
Clock constraints & None.\\\hline |
Signal Names & \begin{tabular}{ll} |
Signal Name & Wishbone Equivalent \\\hline |
{\tt i\_wb\_clk} & {\tt CLK\_I} \\ |
{\tt i\_wb\_cyc} & {\tt CYC\_I} \\ |
{\tt i\_wb\_stb} & {\tt STB\_I} \\ |
{\tt i\_wb\_we} & {\tt WE\_I} \\ |
{\tt i\_wb\_addr} & {\tt ADR\_I} \\ |
{\tt i\_wb\_data} & {\tt DAT\_I} \\ |
{\tt o\_wb\_ack} & {\tt ACK\_O} \\ |
{\tt o\_wb\_stall} & {\tt STALL\_O} \\ |
{\tt o\_wb\_data} & {\tt DAT\_O} |
Signal Names & \begin{tabular}{lll} |
{\tt wbuart.v} & {\tt wbuart-insert.v} & WB Equivalent \\\hline |
{\tt i\_clk} & {\tt i\_wb\_clk} & {\tt CLK\_I} \\ |
{\tt i\_rst} & & {\tt RST\_I} \\ |
{\tt i\_wb\_cyc} & {\tt i\_wb\_cyc} & {\tt CYC\_I} \\ |
{\tt i\_wb\_stb} & {\tt i\_wb\_stb} & {\tt STB\_I} \\ |
{\tt i\_wb\_we} & {\tt i\_wb\_we} & {\tt WE\_I} \\ |
{\tt i\_wb\_addr} & {\tt i\_wb\_addr} & {\tt ADR\_I} \\ |
{\tt i\_wb\_data} & {\tt i\_wb\_data} & {\tt DAT\_I} \\ |
{\tt o\_wb\_ack} & {\tt o\_wb\_ack} & {\tt ACK\_O} \\ |
{\tt o\_wb\_stall} & {\tt o\_wb\_stall} & {\tt STALL\_O} \\ |
{\tt o\_wb\_data} & {\tt o\_wb\_data} & {\tt DAT\_O} |
\end{tabular}\\\hline |
\end{wishboneds} |
\end{tabular} |
\caption{Wishbone Datasheet}\label{tbl:wishbone} |
\end{center}\end{table} |
is required by the wishbone specification in order to declare the core as |
wishbone compliant, and so it is included here. It references the connections |
exemplified by {\tt wbuart-insert.v}. The big thing to notice is that this core |
acts as a wishbone slave, and that all accesses to the core |
registers are 32--bit reads and writes to this interface. |
used in {\tt wbuart.v} as well as those exemplified by {\tt wbuart-insert.v}. |
The big thing to notice is that this core acts as a wishbone slave, and that |
all accesses to the core registers are 32--bit reads and writes to this |
interface---not the 8--bit reads or writes that might otherwise be expected. |
|
What this table doesn't show is that all accesses to the port take a single |
clock. That is, if the {\tt i\_wb\_stb} line is high on one clock, the |
{\tt i\_wb\_ack} line will be high the next. Further, the {\tt o\_wb\_stall} |
line is tied to zero. |
clock for {\tt wbuart-insert.v}, or two clocks for {\tt wbuart.v}. That is, if |
the {\tt i\_wb\_stb} line is high on one clock, the {\tt i\_wb\_ack} line will |
be high the next for single clock access, or the clock after that for two |
clock access. Further, the {\tt o\_wb\_stall} line is tied to zero. |
|
Also, this particular wishbone implementation assumes that if {\tt i\_wb\_stb}, |
then {\tt i\_wb\_cyc} will be high as well. Hence it only checks whether or not |
419,18 → 555,17
\chapter{I/O Ports}\label{ch:ioports} |
% This section specifies all of the core IO ports |
|
In it's simplest form, the UART offers simply two I/O ports: the {\tt i\_rx} |
line to receive, and the {\tt o\_tx} line to transmit. These lines need to be |
brought to the outside of your design. Within verilator, they need to be |
connected inside your verilator test bench, as in: |
In it's simplest form, the UART offers simply two I/O ports: the |
{\tt i\_uart\_rx} line to receive, and the {\tt o\_uart\_tx} line to transmit. |
These lines need to be brought to the outside of your design. Within |
Verilator, they need to be connected inside your Verilator test bench, as in: |
\begin{tabbing} |
{\tt if (!clk)} \= \\ |
\> {\tt tb->i\_rx} {\tt = } {\tt uartsim(tb->o\_uart, setup);} |
\> {\tt tb->i\_uart\_rx} {\tt = } {\tt uartsim(tb->o\_uart\_tx);} |
\end{tabbing} |
|
A more detailed discussion of the connections associated with these modules |
can begin with Tbl.~\ref{tbl:rxports}, detailing the I/O ports of the |
UART receiver, and Tbl.~\ref{tbl:txports}, |
can begin with Tbl.~\ref{tbl:rxports}, |
\begin{table}\begin{center}\begin{portlist} |
{\tt i\_clk} & 1 & Input & The system clock \\\hline |
{\tt i\_reset} & 1 & Input & A positive, synchronous reset \\\hline |
446,7 → 581,7
{\tt o\_ck\_uart} & 1 & Output & A synchronized copy of {\tt i\_uart} \\\hline |
\end{portlist}\caption{RXUART port list}\label{tbl:rxports} |
\end{center}\end{table} |
detailing the I/O ports of the UART transmitter. |
detailing the I/O ports of the UART receiver, Tbl.~\ref{tbl:txports}, |
\begin{table}\begin{center}\begin{portlist} |
{\tt i\_clk} & 1 & Input & The system clock \\\hline |
{\tt i\_reset} & 1 & Input & A positive, synchronous reset \\\hline |
459,10 → 594,19
{\tt o\_busy} & 1 & Output & True if the transmitter is busy, false if it will receive data\\\hline |
\end{portlist}\caption{TXUART port list}\label{tbl:txports} |
\end{center}\end{table} |
detailing the I/O ports of the UART transmitter, and Tbl.~\ref{tbl:wbports} |
\begin{table}\begin{center}\begin{tabular}{|p{1.15in}|p{0.1in}|p{0.75in}|p{3.375in}|} |
\rowcolor[gray]{0.85} Port & W & Direction & Description \\\hline\hline |
{\tt i\_uart\_rx}& 1 & Input & The receive wire coming from the external port\\\hline |
{\tt o\_uart\_tx}& 1 & Output & The transmit wire to be connected to the external port\\\hline |
{\tt o\_uart\_rx\_int} & 1 & Output & True if a byte may be read from the receiver\\\hline |
{\tt o\_uart\_tx\_int} & 1 & Output & True if the transmitter is idle\\\hline |
{\tt o\_uart\_rxfifo\_int}&1& Output & True if the receive FIFO is half full\\\hline |
{\tt o\_uart\_txfifo\_int}&1& Output & True if the transmit FIFO is half empty\\\hline |
\end{tabular}\caption{WBUART port list}\label{tbl:wbports} |
\end{center}\end{table} |
detailing the non--wishbone I/O ports of the wishbone controller. |
|
The ``ports'' associated with the {\tt wbuart-insert.v} example may be |
inferred from the wishbone data sheet. |
|
% Appendices |
% A. May be added to outline different specifications. (??) |
|
/doc/uart.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
doc/uart.png
Property changes :
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: rtl/Makefile
===================================================================
--- rtl/Makefile (revision 4)
+++ rtl/Makefile (revision 5)
@@ -48,6 +48,7 @@
.PHONY: test
test: $(VDIRFB)/Vtxuart__ALL.a
test: $(VDIRFB)/Vrxuart__ALL.a
+test: $(VDIRFB)/Vwbuart__ALL.a
$(VDIRFB)/Vrxuart__ALL.a: $(VDIRFB)/Vrxuart.h $(VDIRFB)/Vrxuart.cpp
$(VDIRFB)/Vrxuart__ALL.a: $(VDIRFB)/Vrxuart.mk
@@ -57,6 +58,10 @@
$(VDIRFB)/Vtxuart__ALL.a: $(VDIRFB)/Vtxuart.mk
$(VDIRFB)/Vtxuart.h $(VDIRFB)/Vtxuart.cpp $(VDIRFB)/Vtxuart.mk: txuart.v
+$(VDIRFB)/Vwbuart__ALL.a: $(VDIRFB)/Vwbuart.h $(VDIRFB)/Vwbuart.cpp
+$(VDIRFB)/Vwbuart__ALL.a: $(VDIRFB)/Vwbuart.mk
+$(VDIRFB)/Vwbuart.h $(VDIRFB)/Vwbuart.cpp $(VDIRFB)/Vwbuart.mk: wbuart.v ufifo.v txuart.v rxuart.v
+
$(VDIRFB)/V%.cpp $(VDIRFB)/V%.h $(VDIRFB)/V%.mk: $(FBDIR)/%.v
verilator -cc $*.v
/rtl/rxuart.v
2,7 → 2,7
// |
// Filename: rxuart.v |
// |
// Project: FPGA library |
// Project: wbuart32, a full featured UART with simulator |
// |
// Purpose: Receive and decode inputs from a single UART line. |
// |
111,15 → 111,13
`define RXU_RESET_IDLE 4'he |
`define RXU_IDLE 4'hf |
|
module rxuart(i_clk, i_reset, i_setup, i_uart, o_wr, o_data, o_break, |
module rxuart(i_clk, i_reset, i_setup, i_uart_rx, o_wr, o_data, o_break, |
o_parity_err, o_frame_err, o_ck_uart); |
// parameter // CLOCKS_PER_BAUD = 25'd004340, |
// BREAK_CONDITION = CLOCKS_PER_BAUD * 12, |
// CLOCKS_PER_HALF_BAUD = CLOCKS_PER_BAUD/2; |
parameter INITIAL_SETUP = 30'd868; |
// 8 data bits, no parity, (at least 1) stop bit |
input i_clk, i_reset; |
input [29:0] i_setup; |
input i_uart; |
input i_uart_rx; |
output reg o_wr; |
output reg [7:0] o_data; |
output reg o_break; |
131,6 → 129,7
wire [1:0] data_bits; |
wire use_parity, parity_even, dblstop, fixd_parity; |
reg [29:0] r_setup; |
|
assign clocks_per_baud = { 4'h0, r_setup[23:0] }; |
assign data_bits = r_setup[29:28]; |
assign dblstop = r_setup[27]; |
138,8 → 137,15
assign fixd_parity = r_setup[25]; |
assign parity_even = r_setup[24]; |
assign break_condition = { r_setup[23:0], 4'h0 }; |
assign half_baud = { 5'h00, r_setup[23:1] }; |
assign half_baud = { 5'h00, r_setup[23:1] }-28'h1; |
reg [27:0] baud_counter; |
reg zero_baud_counter; |
|
|
// Since this is an asynchronous receiver, we need to register our |
// input a couple of clocks over to avoid any problems with |
// metastability. We do that here, and then ignore all but the |
// ck_uart wire. |
reg q_uart, qq_uart, ck_uart; |
initial q_uart = 1'b0; |
initial qq_uart = 1'b0; |
146,12 → 152,19
initial ck_uart = 1'b0; |
always @(posedge i_clk) |
begin |
q_uart <= i_uart; |
q_uart <= i_uart_rx; |
qq_uart <= q_uart; |
ck_uart <= qq_uart; |
end |
|
// In case anyone else wants this clocked, stabilized value, we |
// offer it on our output. |
assign o_ck_uart = ck_uart; |
|
// Keep track of the number of clocks since the last change. |
// |
// This is used to determine if we are in either a break or an idle |
// condition, as discussed further below. |
reg [27:0] chg_counter; |
initial chg_counter = 28'h00; |
always @(posedge i_clk) |
162,76 → 175,100
else if (chg_counter < break_condition) |
chg_counter <= chg_counter + 1; |
|
reg line_synch; |
initial line_synch = 1'b0; |
// Are we in a break condition? |
// |
// A break condition exists if the line is held low for longer than |
// a data word. Hence, we keep track of when the last change occurred. |
// If it was more than break_condition clocks ago, and the current input |
// value is a 0, then we're in a break--and nothing can be read until |
// the line idles again. |
initial o_break = 1'b0; |
always @(posedge i_clk) |
o_break <= ((chg_counter >= break_condition)&&(~ck_uart))? 1'b1:1'b0; |
|
// Are we between characters? |
// |
// The opposite of a break condition is where the line is held high |
// for more clocks than would be in a character. When this happens, |
// we know we have synchronization--otherwise, we might be sampling |
// from within a data word. |
// |
// This logic is used later to hold the RXUART in a reset condition |
// until we know we are between data words. At that point, we should |
// be able to hold on to our synchronization. |
reg line_synch; |
initial line_synch = 1'b0; |
always @(posedge i_clk) |
line_synch <= ((chg_counter >= break_condition)&&(ck_uart)); |
|
// Are we in the middle of a baud iterval? Specifically, are we |
// in the middle of a start bit? Set this to high if so. We'll use |
// this within our state machine to transition out of the IDLE |
// state. |
reg half_baud_time; |
initial half_baud_time = 0; |
always @(posedge i_clk) |
half_baud_time <= (~ck_uart)&&(chg_counter >= half_baud); |
|
|
// Allow our controlling processor to change our setup at any time |
// outside of receiving/processing a character. |
initial r_setup = INITIAL_SETUP; |
always @(posedge i_clk) |
if (state >= `RXU_RESET_IDLE) |
r_setup <= i_setup; |
|
|
// Our monster state machine. YIKES! |
// |
// Yeah, this may be more complicated than it needs to be. The basic |
// progression is: |
// RESET -> RESET_IDLE -> (when line is idle) -> IDLE |
// IDLE -> bit 0 -> bit 1 -> bit_{ndatabits} -> |
// (optional) PARITY -> STOP -> (optional) SECOND_STOP |
// -> IDLE |
// ANY -> (on break) BREAK -> IDLE |
// |
// There are 16 states, although all are not used. These are listed |
// at the top of this file. |
// |
// Logic inputs (12): (I've tried to minimize this number) |
// state (4) |
// i_reset |
// line_synch |
// o_break |
// ckuart |
// half_baud_time |
// zero_baud_counter |
// use_parity |
// dblstop |
// Logic outputs (4): |
// state |
// |
reg [3:0] state; |
reg [27:0] baud_counter; |
reg [7:0] data_reg; |
reg calc_parity, zero_baud_counter, half_baud_time; |
initial o_wr = 1'b0; |
initial state = `RXU_RESET_IDLE; |
initial o_parity_err = 1'b0; |
initial o_frame_err = 1'b0; |
// initial baud_counter = clocks_per_baud; |
always @(posedge i_clk) |
begin |
if (i_reset) |
begin |
o_wr <= 1'b0; |
o_data <= 8'h00; |
state <= `RXU_RESET_IDLE; |
baud_counter <= clocks_per_baud-28'h01;// Set, not reset |
data_reg <= 8'h00; |
calc_parity <= 1'b0; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end else if (state == `RXU_RESET_IDLE) |
else if (state == `RXU_RESET_IDLE) |
begin |
r_setup <= i_setup; |
data_reg <= 8'h00; o_data <= 8'h00; o_wr <= 1'b0; |
baud_counter <= clocks_per_baud-28'h01;// Set, not reset |
if (line_synch) |
// Goto idle state from a reset |
state <= `RXU_IDLE; |
else // Otherwise, stay in this condition 'til reset |
state <= `RXU_RESET_IDLE; |
calc_parity <= 1'b0; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end else if (o_break) |
begin // We are in a break condition |
state <= `RXU_BREAK; |
o_wr <= 1'b0; |
o_data <= 8'h00; |
baud_counter <= clocks_per_baud-28'h01;// Set, not reset |
data_reg <= 8'h00; |
calc_parity <= 1'b0; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
r_setup <= i_setup; |
end else if (state == `RXU_BREAK) |
begin // Goto idle state following return ck_uart going high |
data_reg <= 8'h00; o_data <= 8'h00; o_wr <= 1'b0; |
baud_counter <= clocks_per_baud - 28'h01; |
if (ck_uart) |
state <= `RXU_IDLE; |
else |
state <= `RXU_BREAK; |
calc_parity <= 1'b0; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
r_setup <= i_setup; |
end else if (state == `RXU_IDLE) |
begin // Idle state, independent of baud counter |
r_setup <= i_setup; |
data_reg <= 8'h00; o_data <= 8'h00; o_wr <= 1'b0; |
baud_counter <= clocks_per_baud - 28'h01; |
if ((~ck_uart)&&(half_baud_time)) |
begin |
// We are in the center of a valid start bit |
243,88 → 280,179
endcase |
end else // Otherwise, just stay here in idle |
state <= `RXU_IDLE; |
calc_parity <= 1'b0; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end else if (zero_baud_counter) |
begin |
baud_counter <= clocks_per_baud-28'h1; |
if (state < `RXU_BIT_SEVEN) |
begin |
// Data arrives least significant bit first. |
// By the time this is clocked in, it's what |
// you'll have. |
data_reg <= { ck_uart, data_reg[7:1] }; |
calc_parity <= calc_parity ^ ck_uart; |
o_data <= 8'h00; |
o_wr <= 1'b0; |
state <= state + 1; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end else if (state == `RXU_BIT_SEVEN) |
begin |
data_reg <= { ck_uart, data_reg[7:1] }; |
calc_parity <= calc_parity ^ ck_uart; |
o_data <= 8'h00; |
o_wr <= 1'b0; |
else if (state == `RXU_BIT_SEVEN) |
state <= (use_parity) ? `RXU_PARITY:`RXU_STOP; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end else if (state == `RXU_PARITY) |
begin |
if (fixd_parity) |
o_parity_err <= (ck_uart ^ parity_even); |
else |
o_parity_err <= ((parity_even && (calc_parity != ck_uart)) |
||((~parity_even)&&(calc_parity==ck_uart))); |
else if (state == `RXU_PARITY) |
state <= `RXU_STOP; |
o_frame_err <= 1'b0; |
end else if (state == `RXU_STOP) |
else if (state == `RXU_STOP) |
begin // Stop (or parity) bit(s) |
case (data_bits) |
2'b00: o_data <= data_reg; |
2'b01: o_data <= { 1'b0, data_reg[7:1] }; |
2'b10: o_data <= { 2'b0, data_reg[7:2] }; |
2'b11: o_data <= { 3'b0, data_reg[7:3] }; |
endcase |
o_wr <= 1'b1; // Pulse the write |
o_frame_err <= (~ck_uart); |
if (~ck_uart) |
if (~ck_uart) // On frame error, wait 4 ch idle |
state <= `RXU_RESET_IDLE; |
else if (dblstop) |
state <= `RXU_SECOND_STOP; |
else |
state <= `RXU_IDLE; |
// o_parity_err <= 1'b0; |
end else // state must equal RX_SECOND_STOP |
begin |
if (~ck_uart) |
begin |
o_frame_err <= 1'b1; |
if (~ck_uart) // On frame error, wait 4 ch idle |
state <= `RXU_RESET_IDLE; |
end else begin |
else |
state <= `RXU_IDLE; |
o_frame_err <= 1'b0; |
end |
o_parity_err <= 1'b0; |
end |
end else begin |
o_wr <= 1'b0; // data_reg = data_reg |
baud_counter <= baud_counter - 28'd1; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end |
end |
|
// Data bit capture logic. |
// |
// This is drastically simplified from the state machine above, based |
// upon: 1) it doesn't matter what it is until the end of a captured |
// byte, and 2) the data register will flush itself of any invalid |
// data in all other cases. Hence, let's keep it real simple. |
// The only trick, though, is that if we have parity, then the data |
// register needs to be held through that state without getting |
// updated. |
reg [7:0] data_reg; |
always @(posedge i_clk) |
if ((zero_baud_counter)&&(state != `RXU_PARITY)) |
data_reg <= { ck_uart, data_reg[7:1] }; |
|
// Parity calculation logic |
// |
// As with the data capture logic, all that must be known about this |
// bit is that it is the exclusive-OR of all bits prior. The first |
// of those will follow idle, so we set ourselves to zero on idle. |
// Then, as we walk through the states of a bit, all will adjust this |
// value up until the parity bit, where the value will be read. Setting |
// it then or after will be irrelevant, so ... this should be good |
// and simplified. Note--we don't need to adjust this on reset either, |
// since the reset state will lead to the idle state where we'll be |
// reset before any transmission takes place. |
reg calc_parity; |
always @(posedge i_clk) |
if (state == `RXU_IDLE) |
calc_parity <= 0; |
else if (zero_baud_counter) |
calc_parity <= calc_parity ^ ck_uart; |
|
// Parity error logic |
// |
// Set during the parity bit interval, read during the last stop bit |
// interval, cleared on BREAK, RESET_IDLE, or IDLE states. |
initial o_parity_err = 1'b0; |
always @(posedge i_clk) |
if ((zero_baud_counter)&&(state == `RXU_PARITY)) |
begin |
if (fixd_parity) |
// Fixed parity bit--independent of any dat |
// value. |
o_parity_err <= (ck_uart ^ parity_even); |
else if (parity_even) |
// Parity even: The XOR of all bits including |
// the parity bit must be zero. |
o_parity_err <= (calc_parity != ck_uart); |
else |
// Parity odd: the parity bit must equal the |
// XOR of all the data bits. |
o_parity_err <= (calc_parity == ck_uart); |
end else if (state >= `RXU_BREAK) |
o_parity_err <= 1'b0; |
|
// Frame error determination |
// |
// For the purpose of this controller, a frame error is defined as a |
// stop bit (or second stop bit, if so enabled) not being high midway |
// through the stop baud interval. The frame error value is |
// immediately read, so we can clear it under all other circumstances. |
// Specifically, we want it clear in RXU_BREAK, RXU_RESET_IDLE, and |
// most importantly in RXU_IDLE. |
initial o_frame_err = 1'b0; |
always @(posedge i_clk) |
if ((zero_baud_counter)&&((state == `RXU_STOP) |
||(state == `RXU_SECOND_STOP))) |
o_frame_err <= (o_frame_err)||(~ck_uart); |
else if ((zero_baud_counter)||(state >= `RXU_BREAK)) |
o_frame_err <= 1'b0; |
|
// Our data bit logic doesn't need nearly the complexity of all that |
// work above. Indeed, we only need to know if we are at the end of |
// a stop bit, in which case we copy the data_reg into our output |
// data register, o_data. |
// |
// We would also set o_wr to be true when this is the case, but ... we |
// won't know if there is a frame error on the second stop bit for |
// another baud interval yet. So, instead, we set up the logic so that |
// we know on the next zero baud counter that we can write out. That's |
// the purpose of pre_wr. |
initial o_data = 8'h00; |
reg pre_wr; |
initial pre_wr = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
pre_wr <= 1'b0; |
o_data <= 8'h00; |
end else if ((zero_baud_counter)&&(state == `RXU_STOP)) |
begin |
pre_wr <= 1'b1; |
case (data_bits) |
2'b00: o_data <= data_reg; |
2'b01: o_data <= { 1'b0, data_reg[7:1] }; |
2'b10: o_data <= { 2'b0, data_reg[7:2] }; |
2'b11: o_data <= { 3'b0, data_reg[7:3] }; |
endcase |
end else if ((zero_baud_counter)||(state == `RXU_IDLE)) |
pre_wr <= 1'b0; |
|
// Create an output strobe, true for one clock only, once we know |
// all we need to know. o_data will be set on the last baud interval, |
// o_parity_err on the last parity baud interval (if it existed, |
// cleared otherwise, so ... we should be good to go here.) |
initial o_wr = 1'b0; |
always @(posedge i_clk) |
if ((zero_baud_counter)||(state == `RXU_IDLE)) |
o_wr <= (pre_wr)&&(!i_reset); |
else |
o_wr <= 1'b0; |
|
// The baud counter |
// |
// This is used as a "clock divider" if you will, but the clock needs |
// to be reset before any byte can be decoded. In all other respects, |
// we set ourselves up for clocks_per_baud counts between baud |
// intervals. |
always @(posedge i_clk) |
if (i_reset) |
baud_counter <= clocks_per_baud-28'h01; |
else if (zero_baud_counter) |
baud_counter <= clocks_per_baud-28'h01; |
else case(state) |
`RXU_RESET_IDLE:baud_counter <= clocks_per_baud-28'h01; |
`RXU_BREAK: baud_counter <= clocks_per_baud-28'h01; |
`RXU_IDLE: baud_counter <= clocks_per_baud-28'h01; |
default: baud_counter <= baud_counter-28'h01; |
endcase |
|
// zero_baud_counter |
// |
// Rather than testing whether or not (baud_counter == 0) within our |
// (already too complicated) state transition tables, we use |
// zero_baud_counter to pre-charge that test on the clock |
// before--cleaning up some otherwise difficult timing dependencies. |
initial zero_baud_counter = 1'b0; |
always @(posedge i_clk) |
if (state == `RXU_IDLE) |
zero_baud_counter <= 1'b0; |
else |
zero_baud_counter <= (baud_counter == 28'h01); |
|
initial half_baud_time = 0; |
always @(posedge i_clk) |
half_baud_time <= (~ck_uart)&&(chg_counter >= half_baud); |
|
|
endmodule |
|
|
/rtl/txuart.v
104,13 → 104,14
`define TXU_IDLE 4'hf |
// |
// |
module txuart(i_clk, i_reset, i_setup, i_break, i_wr, i_data, o_uart, o_busy); |
module txuart(i_clk, i_reset, i_setup, i_break, i_wr, i_data,o_uart_tx, o_busy); |
parameter INITIAL_SETUP = 30'd868; |
input i_clk, i_reset; |
input [29:0] i_setup; |
input i_break; |
input i_wr; |
input [7:0] i_data; |
output reg o_uart; |
output reg o_uart_tx; |
output wire o_busy; |
|
wire [27:0] clocks_per_baud, break_condition; |
130,7 → 131,8
reg [7:0] lcl_data; |
reg calc_parity, r_busy, zero_baud_counter; |
|
initial o_uart = 1'b1; |
initial r_setup = INITIAL_SETUP; |
initial o_uart_tx = 1'b1; |
initial r_busy = 1'b1; |
initial state = `TXU_IDLE; |
initial lcl_data= 8'h0; |
140,7 → 142,7
begin |
if (i_reset) |
begin |
o_uart <= 1'b1; |
o_uart_tx <= 1'b1; |
r_busy <= 1'b1; |
state <= `TXU_IDLE; |
lcl_data <= 8'h0; |
147,7 → 149,7
calc_parity <= 1'b0; |
end else if (i_break) |
begin |
o_uart <= 1'b0; |
o_uart_tx <= 1'b0; |
state <= `TXU_BREAK; |
calc_parity <= 1'b0; |
r_busy <= 1'b1; |
158,7 → 160,7
begin |
state <= `TXU_IDLE; |
r_busy <= 1'b1; |
o_uart <= 1'b1; |
o_uart_tx <= 1'b1; |
calc_parity <= 1'b0; |
end else if (state == `TXU_IDLE) // STATE_IDLE |
begin |
167,7 → 169,7
calc_parity <= 1'b0; |
if ((i_wr)&&(~r_busy)) |
begin // Immediately start us off with a start bit |
o_uart <= 1'b0; |
o_uart_tx <= 1'b0; |
r_busy <= 1'b1; |
case(data_bits) |
2'b00: state <= `TXU_BIT_ZERO; |
178,7 → 180,7
lcl_data <= i_data; |
// baud_counter <= clocks_per_baud-28'h01; |
end else begin // Stay in idle |
o_uart <= 1'b1; |
o_uart_tx <= 1'b1; |
r_busy <= 0; |
// lcl_data is irrelevant |
// state <= state; |
189,7 → 191,7
r_busy <= 1'b1; |
if (state[3] == 0) // First 8 bits |
begin |
o_uart <= lcl_data[0]; |
o_uart_tx <= lcl_data[0]; |
calc_parity <= calc_parity ^ lcl_data[0]; |
if (state == `TXU_BIT_SEVEN) |
state <= (use_parity)?`TXU_PARITY:`TXU_STOP; |
200,12 → 202,12
begin |
state <= `TXU_STOP; |
if (fixd_parity) |
o_uart <= parity_even; |
o_uart_tx <= parity_even; |
else |
o_uart <= calc_parity^((parity_even)? 1'b1:1'b0); |
o_uart_tx <= calc_parity^((parity_even)? 1'b1:1'b0); |
end else if (state == `TXU_STOP) |
begin // two stop bit(s) |
o_uart <= 1'b1; |
o_uart_tx <= 1'b1; |
if (dblstop) |
state <= `TXU_SECOND_STOP; |
else |
214,7 → 216,7
end else // `TXU_SECOND_STOP and default: |
begin |
state <= `TXU_IDLE; // Go back to idle |
o_uart <= 1'b1; |
o_uart_tx <= 1'b1; |
// Still r_busy, since we need to wait |
// for the baud clock to finish counting |
// out this last bit. |
231,9 → 233,11
begin |
zero_baud_counter <= (baud_counter == 28'h01); |
if ((i_reset)||(i_break)) |
begin |
// Give ourselves 16 bauds before being ready |
baud_counter <= break_condition; |
else if (~zero_baud_counter) |
zero_baud_counter <= 1'b0; |
end else if (~zero_baud_counter) |
baud_counter <= baud_counter - 28'h01; |
else if (state == `TXU_BREAK) |
// Give us two stop bits before becoming available |
/rtl/ufifo.v
0,0 → 1,189
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: ufifo.v |
// |
// Project: wbuart32, a full featured UART with simulator |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// 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. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module ufifo(i_clk, i_rst, i_wr, i_data, i_rd, o_data, |
o_empty_n, o_half_full, o_status, o_err); |
parameter BW=8, LGFLEN=4; |
input i_clk, i_rst; |
input i_wr; |
input [(BW-1):0] i_data; |
input i_rd; |
output wire [(BW-1):0] o_data; |
output reg o_empty_n; |
output wire o_half_full; |
output wire [15:0] o_status; |
output wire o_err; |
|
localparam FLEN=(1<<LGFLEN); |
|
reg [(BW-1):0] fifo[0:(FLEN-1)]; |
reg [(LGFLEN-1):0] r_first, r_last; |
|
wire [(LGFLEN-1):0] w_first_plus_one, w_first_plus_two, |
w_last_plus_one; |
assign w_first_plus_two = r_first + {{(LGFLEN-2){1'b0}},2'b10}; |
assign w_first_plus_one = r_first + {{(LGFLEN-1){1'b0}},1'b1}; |
assign w_last_plus_one = r_last + {{(LGFLEN-1){1'b0}},1'b1}; |
|
reg will_overflow; |
initial will_overflow = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
will_overflow <= 1'b0; |
else if (i_rd) |
will_overflow <= (will_overflow)&&(i_wr); |
else if (i_wr) |
will_overflow <= (w_first_plus_two == r_last); |
else if (w_first_plus_one == r_last) |
will_overflow <= 1'b1; |
|
// Write |
reg r_ovfl; |
initial r_first = 0; |
initial r_ovfl = 0; |
always @(posedge i_clk) |
if (i_rst) |
begin |
r_ovfl <= 1'b0; |
r_first <= { (LGFLEN){1'b0} }; |
end else if (i_wr) |
begin // Cowardly refuse to overflow |
if ((i_rd)||(!will_overflow)) // (r_first+1 != r_last) |
r_first <= w_first_plus_one; |
else |
r_ovfl <= 1'b1; |
end |
always @(posedge i_clk) |
if (i_wr) // Write our new value regardless--on overflow or not |
fifo[r_first] <= i_data; |
|
// Reads |
// Following a read, the next sample will be available on the |
// next clock |
// Clock ReadCMD ReadAddr Output |
// 0 0 0 fifo[0] |
// 1 1 0 fifo[0] |
// 2 0 1 fifo[1] |
// 3 0 1 fifo[1] |
// 4 1 1 fifo[1] |
// 5 1 2 fifo[2] |
// 6 0 3 fifo[3] |
// 7 0 3 fifo[3] |
reg will_underflow, r_unfl; |
initial will_underflow = 1'b1; |
always @(posedge i_clk) |
if (i_rst) |
will_underflow <= 1'b1; |
else if (i_wr) |
will_underflow <= (will_underflow)&&(i_rd); |
else if (i_rd) |
will_underflow <= (w_last_plus_one == r_first); |
else |
will_underflow <= (r_last == r_first); |
|
initial r_unfl = 1'b0; |
initial r_last = 0; |
always @(posedge i_clk) |
if (i_rst) |
begin |
r_last <= { (LGFLEN){1'b0} }; |
r_unfl <= 1'b0; |
end else if (i_rd) |
begin |
if ((i_wr)||(!will_underflow)) // (r_first != r_last) |
r_last <= w_last_plus_one; |
// Last chases first |
// Need to be prepared for a possible two |
// reads in quick succession |
// o_data <= fifo[r_last+1]; |
else |
r_unfl <= 1'b1; |
end |
|
reg [7:0] fifo_here, fifo_next, r_data; |
always @(posedge i_clk) |
fifo_here <= fifo[r_last]; |
always @(posedge i_clk) |
fifo_next <= fifo[r_last+{{(LGFLEN-1){1'b0}},1'b1}]; |
always @(posedge i_clk) |
r_data <= i_data; |
|
reg [1:0] osrc; |
always @(posedge i_clk) |
if (will_underflow) |
// o_data <= i_data; |
osrc <= 2'b00; |
else if ((i_rd)&&(r_first == w_last_plus_one)) |
osrc <= 2'b01; |
else if (i_rd) |
osrc <= 2'b11; |
else |
osrc <= 2'b10; |
assign o_data = (osrc[1]) ? ((osrc[0])?fifo_next:fifo_here) : r_data; |
|
// wire [(LGFLEN-1):0] current_fill; |
// assign current_fill = (r_first-r_last); |
|
always @(posedge i_clk) |
if (i_rst) |
o_empty_n <= 1'b0; |
else case({i_wr, i_rd}) |
2'b00: o_empty_n <= (r_first != r_last); |
2'b11: o_empty_n <= (r_first != r_last); |
2'b10: o_empty_n <= 1'b1; |
2'b01: o_empty_n <= (r_first != w_last_plus_one); |
endcase |
|
reg [(LGFLEN-1):0] r_fill; |
always @(posedge i_clk) |
if (i_rst) |
r_fill <= 0; |
else if ((i_rd)&&(!i_wr)) |
r_fill <= r_first - r_last - 1'b1; |
else if ((!i_rd)&&(i_wr)) |
r_fill <= r_first - r_last + 1'b1; |
else |
r_fill <= r_first - r_last; |
assign o_half_full = r_fill[(LGFLEN-1)]; |
|
assign o_err = (r_ovfl) || (r_unfl); |
|
wire [3:0] lglen; |
assign lglen = LGFLEN; |
assign o_status = { lglen, {(16-2-4-LGFLEN){1'b0}}, r_fill, o_half_full, o_empty_n }; |
|
endmodule |
/rtl/wbuart-insert.v
62,7 → 62,7
// |
wire rx_stb, rx_break, rx_perr, rx_ferr, ck_uart; |
wire [7:0] rx_data_port; |
rxuart rx(i_clk, 1'b0, uart_setup, i_rx, |
rxuart #(UART_SETUP) rx(i_clk, 1'b0, uart_setup, i_rx, |
rx_stb, rx_data_port, rx_break, |
rx_perr, rx_ferr, ck_uart); |
|
71,18 → 71,24
always @(posedge i_clk) |
if (rx_stb) |
begin |
r_rx_data[11] <= rx_break; |
r_rx_data[10] <= rx_ferr; |
r_rx_data[ 9] <= rx_perr; |
r_rx_data[11] <= (r_rx_data[11])||(rx_break); |
r_rx_data[10] <= (r_rx_data[10])||(rx_ferr); |
r_rx_data[ 9] <= (r_rx_data[ 9])||(rx_perr); |
r_rx_data[7:0]<= rx_data_port; |
end else if ((i_wb_stb)&&(i_wb_we) |
&&(i_wb_addr == `UART_RX_ADDR)) |
begin |
r_rx_data[11] <= (rx_break)&& (!i_wb_data[11]); |
r_rx_data[10] <= (rx_ferr) && (!i_wb_data[10]); |
r_rx_data[ 9] <= (rx_perr) && (!i_wb_data[ 9]); |
end |
always @(posedge i_clk) |
if(((i_wb_stb)&&(~i_wb_we)&&(i_wb_addr == `UART_RX_ADDR)) |
||(rx_stb)) |
r_rx_data[8] <= !rx_stb; |
assign o_cts = rx_stb; |
assign o_cts = !r_rx_data[8]; |
assign rx_data = { 20'h00, r_rx_data }; |
assign rx_int = r_rx_data[8]; |
assign rx_int = !r_rx_data[8]; |
|
// |
// Then the UART transmitter |
91,7 → 97,7
reg [7:0] r_tx_data; |
reg r_tx_stb, r_tx_break; |
wire [31:0] tx_data; |
txuart tx(i_clk, 1'b0, uart_setup, |
txuart #(UART_SETUP) tx(i_clk, 1'b0, uart_setup, |
r_tx_break, r_tx_stb, r_tx_data, |
o_tx, tx_busy); |
always @(posedge i_clk) |
/rtl/wbuart.v
0,0 → 1,354
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbuart.v |
// |
// Project: wbuart32, a full featured UART with simulator |
// |
// Purpose: Unlilke wbuart-insert.v, this is a full blown wishbone core |
// with integrated FIFO support to support the UART transmitter |
// and receiver found within here. As a result, it's usage may be |
// heavier on the bus than the insert, but it may also be more useful. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// 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. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`define UART_SETUP 2'b00 |
`define UART_FIFO 2'b01 |
`define UART_RXREG 2'b10 |
`define UART_TXREG 2'b11 |
module wbuart(i_clk, i_rst, |
// |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, |
o_wb_stall, o_wb_ack, o_wb_data, |
// |
i_uart_rx, o_uart_tx, |
// i_uart_rts, o_uart_cts, i_uart_dtr, o_uart_dts |
// |
o_uart_rx_int, o_uart_tx_int, |
o_uart_rxfifo_int, o_uart_txfifo_int); |
parameter INITIAL_SETUP = 30'd25, // 4MB 8N1, when using 100MHz clock |
LGFLEN = 4; |
// |
input i_clk, i_rst; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [1:0] i_wb_addr; |
input [31:0] i_wb_data; |
output wire o_wb_stall; |
output reg o_wb_ack; |
output reg [31:0] o_wb_data; |
// |
input i_uart_rx; |
output wire o_uart_tx; |
output wire o_uart_rx_int, o_uart_tx_int, |
o_uart_rxfifo_int, o_uart_txfifo_int; |
|
wire tx_busy; |
|
// |
// The UART setup parameters: bits per byte, stop bits, parity, and |
// baud rate are all captured within this uart_setup register. |
// |
reg [29:0] uart_setup; |
initial uart_setup = INITIAL_SETUP; |
always @(posedge i_clk) |
// Under wishbone rules, a write takes place any time i_wb_stb |
// is high. If that's the case, and if the write was to the |
// setup address, then set us up for the new parameters. |
if ((i_wb_stb)&&(i_wb_addr == `UART_SETUP)&&(i_wb_we)) |
uart_setup[29:0] <= i_wb_data[29:0]; |
|
// |
// First the UART receiver |
// |
|
// First the wires/registers this receiver depends upon |
wire rx_stb, rx_break, rx_perr, rx_ferr, ck_uart; |
wire [7:0] rx_uart_data; |
reg rx_uart_reset; |
|
// Here's our UART receiver. Basically, it accepts our setup wires, |
// the UART input, a clock, and a reset line, and produces outputs: |
// a stb (true when new data is ready), an 8-bit data out value |
// valid when stb is high, a break value (true during a break cond.), |
// and parity/framing error flags--also valid when stb is true. |
rxuart #(INITIAL_SETUP) rx(i_clk, (i_rst)||(rx_uart_reset), |
uart_setup, i_uart_rx, |
rx_stb, rx_uart_data, rx_break, |
rx_perr, rx_ferr, ck_uart); |
// The real trick is ... now that we have this data, what do we do |
// with it? |
|
|
// We place it into a receiver FIFO. |
// |
// Here's the declarations for the wires it needs. |
wire rx_empty_n, rx_fifo_err; |
wire [7:0] rxf_wb_data; |
wire [15:0] rxf_status; |
reg rxf_wb_read; |
// |
// And here's the FIFO proper. |
// |
// Note that the FIFO will be cleared upon any reset: either if there's |
// a UART break condition on the line, the receiver is in reset, or an |
// external reset is issued. |
// |
// The FIFO accepts strobe and data from the receiver. |
// We issue another wire to it (rxf_wb_read), true when we wish to read |
// from the FIFO, and we get our data in rxf_wb_data. The FIFO outputs |
// four status-type values: 1) is it non-empty, 2) is the FIFO over half |
// full, 3) a 16-bit status register, containing info regarding how full |
// the FIFO truly is, and 4) an error indicator. |
ufifo #(.LGFLEN(LGFLEN)) |
rxfifo(i_clk, (i_rst)||(rx_break)||(rx_uart_reset), |
rx_stb, rx_uart_data, |
rxf_wb_read, rxf_wb_data, |
(rx_empty_n), (o_uart_rxfifo_int), |
rxf_status, rx_fifo_err); |
|
// We produce four interrupts. One of the receive interrupts indicates |
// whether or not the receive FIFO is non-empty. This should wake up |
// the CPU. |
assign o_uart_rx_int = !rx_empty_n; |
|
// If the bus requests that we read from the receive FIFO, we need to |
// tell this to the receive FIFO. Note that because we are using a |
// clock here, the output from the receive FIFO will necessarily be |
// delayed by an extra clock. |
initial rxf_wb_read = 1'b0; |
always @(posedge i_clk) |
rxf_wb_read <= (i_wb_stb)&&(i_wb_addr[1:0]==`UART_RXREG) |
&&(!i_wb_we); |
|
// Now, let's deal with those RX UART errors: both the parity and frame |
// errors. As you may recall, these are valid only when rx_stb is |
// valid, so we need to hold on to them until the user reads them via |
// a UART read request.. |
reg r_rx_perr, r_rx_ferr; |
initial r_rx_perr = 1'b0; |
initial r_rx_ferr = 1'b0; |
always @(posedge i_clk) |
if ((rx_uart_reset)||(rx_break)) |
begin |
// Clear the error |
r_rx_perr <= 1'b0; |
r_rx_ferr <= 1'b0; |
end else if ((i_wb_stb) |
&&(i_wb_addr[1:0]==`UART_RXREG)&&(i_wb_we)) |
begin |
// Reset the error lines if a '1' is ever written to |
// them, otherwise leave them alone. |
// |
r_rx_perr <= (r_rx_perr)&&(~i_wb_data[9]); |
r_rx_ferr <= (r_rx_ferr)&&(~i_wb_data[10]); |
end else if (rx_stb) |
begin |
// On an rx_stb, capture any parity or framing error |
// indications. These aren't kept with the data rcvd, |
// but rather kept external to the FIFO. As a result, |
// if you get a parity or framing error, you will never |
// know which data byte it was associated with. |
// For now ... that'll work. |
r_rx_perr <= (r_rx_perr)||(rx_perr); |
r_rx_ferr <= (r_rx_ferr)||(rx_ferr); |
end |
|
initial rx_uart_reset = 1'b1; |
always @(posedge i_clk) |
if ((i_rst)||((i_wb_stb)&&(i_wb_addr[1:0]==`UART_SETUP)&&(i_wb_we))) |
// The receiver reset, always set on a master reset |
// request. |
rx_uart_reset <= 1'b1; |
else if ((i_wb_stb)&&(i_wb_addr[1:0]==`UART_RXREG)&&(i_wb_we)) |
// Writes to the receive register will command a receive |
// reset anytime bit[12] is set. |
rx_uart_reset <= i_wb_data[12]; |
else |
rx_uart_reset <= 1'b0; |
|
// Finally, we'll construct a 32-bit value from these various wires, |
// to be returned over the bus on any read. These include the data |
// that would be read from the FIFO, an error indicator set upon |
// reading from an empty FIFO, a break indicator, and the frame and |
// parity error signals. |
wire [31:0] wb_rx_data; |
assign wb_rx_data = { 16'h00, |
3'h0, rx_fifo_err, |
rx_break, rx_ferr, r_rx_perr, !rx_empty_n, |
rxf_wb_data}; |
|
// |
// Then the UART transmitter |
// |
wire tx_empty_n, txf_half_full, txf_err; |
wire [7:0] tx_data; |
wire [15:0] txf_status; |
reg r_tx_break, txf_wb_write, tx_uart_reset; |
reg [7:0] txf_wb_data; |
|
// Unlike the receiver which goes from RXUART -> UFIFO -> WB, the |
// transmitter basically goes WB -> UFIFO -> TXUART. Hence, to build |
// support for the transmitter, we start with the command to write data |
// into the FIFO. In this case, we use the act of writing to the |
// UART_TXREG address as our indication that we wish to write to the |
// FIFO. Here, we create a write command line, and latch the data for |
// the extra clock that it'll take so that the command and data can be |
// both true on the same clock. |
initial txf_wb_write = 1'b0; |
always @(posedge i_clk) |
begin |
txf_wb_write <= (i_wb_stb)&&(i_wb_addr == `UART_TXREG) |
&&(i_wb_we); |
txf_wb_data <= i_wb_data[7:0]; |
end |
|
// Transmit FIFO |
// |
// Most of this is just wire management. The TX FIFO is identical in |
// implementation to the RX FIFO (theyre both UFIFOs), but the TX |
// FIFO is fed from the WB and read by the transmitter. Some key |
// differences to note: we reset the transmitter on any request for a |
// break. We read from the FIFO any time the UART transmitter is idle. |
// and ... we just set the values (above) for controlling writing into |
// this. |
ufifo #(.LGFLEN(LGFLEN)) |
txfifo(i_clk, (r_tx_break)||(tx_uart_reset), |
txf_wb_write, txf_wb_data, |
(~tx_busy)&&(tx_empty_n), tx_data, |
tx_empty_n, txf_half_full, txf_status, txf_err); |
// Let's grab two interrupts from the FIFO for the CPU. |
// The first will be true any time the FIFO is empty. |
assign o_uart_tx_int = !tx_empty_n; |
// The second will be true any time the FIFO is less than half |
// full, allowing us a change to always keep it (near) fully |
// charged. |
assign o_uart_txfifo_int = !txf_half_full; |
|
// Break logic |
// |
// A break in a UART controller is any time the UART holds the line |
// low for an extended period of time. Here, we capture the wb_data[9] |
// wire, on writes, as an indication we wish to break. As long as you |
// write unsigned characters to the interface, this will never be true |
// unless you wish it to be true. Be aware, though, writing a valid |
// value to the interface will bring it out of the break condition. |
initial r_tx_break = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_tx_break <= 1'b0; |
else if ((i_wb_stb)&&(i_wb_addr[1:0]==`UART_TXREG)&&(i_wb_we)) |
r_tx_break <= i_wb_data[9]; |
|
// TX-Reset logic |
// |
// This is nearly identical to the RX reset logic above. Basically, |
// any time someone writes to bit [12] the transmitter will go through |
// a reset cycle. Keep bit [12] low, and everything will proceed as |
// normal. |
initial tx_uart_reset = 1'b1; |
always @(posedge i_clk) |
if((i_rst)||((i_wb_stb)&&(i_wb_addr == `UART_SETUP)&&(i_wb_we))) |
tx_uart_reset <= 1'b1; |
else if ((i_wb_stb)&&(i_wb_addr[1:0]==`UART_TXREG)&&(i_wb_we)) |
tx_uart_reset <= i_wb_data[12]; |
else |
tx_uart_reset <= 1'b0; |
|
// Finally, the UART transmitter module itself. Note that we haven't |
// connected the reset wire. Transmitting is as simple as setting |
// the stb value (here set to tx_empty_n) and the data. When these |
// are both set on the same clock that tx_busy is low, the transmitter |
// will move on to the next data byte. Really, the only thing magical |
// here is that tx_empty_n wire--thus, if there's anything in the FIFO, |
// we read it here. (You might notice above, we register a read any |
// time (tx_empty_n) and (!tx_busy) are both true---the condition for |
// starting to transmit a new byte.) |
txuart #(INITIAL_SETUP) tx(i_clk, 1'b0, uart_setup, |
r_tx_break, (tx_empty_n), tx_data, |
o_uart_tx, tx_busy); |
|
// Now that we are done with the chain, pick some wires for the user |
// to read on any read of the transmit port. |
// |
// This port is different from reading from the receive port, since |
// there are no side effects. (Reading from the receive port advances |
// the receive FIFO, here only writing to the transmit port advances the |
// transmit FIFO--hence the read values are free for ... whatever.) |
// We choose here to provide information about the transmit FIFO |
// (txf_err, txf_half_full, tx_empty_n), information about the current |
// voltage on the line (o_uart_tx)--and even the voltage on the receive |
// line (ck_uart), as well as our current setting of the break and |
// whether or not we are actively transmitting. |
wire [31:0] wb_tx_data; |
assign wb_tx_data = { 16'h00, |
1'h0, txf_half_full, tx_empty_n, txf_err, |
ck_uart, o_uart_tx, r_tx_break, tx_busy, |
txf_wb_data}; |
|
// Each of the FIFO's returns a 16 bit status value. This value tells |
// us both how big the FIFO is, as well as how much of the FIFO is in |
// use. Let's merge those two status words together into a word we |
// can use when reading about the FIFO. |
wire [31:0] wb_fifo_data; |
assign wb_fifo_data = { txf_status, rxf_status }; |
|
// You may recall from above that reads take two clocks. Hence, we |
// need to delay the address decoding for a clock until the data is |
// ready. We do that here. |
reg [1:0] r_wb_addr; |
always @(posedge i_clk) |
r_wb_addr <= i_wb_addr; |
|
// Likewise, the acknowledgement is delayed by one clock. |
reg r_wb_ack; |
always @(posedge i_clk) // We'll ACK in two clocks |
r_wb_ack <= i_wb_stb; |
always @(posedge i_clk) // Okay, time to set the ACK |
o_wb_ack <= r_wb_ack; |
|
// Finally, set the return data. This data must be valid on the same |
// clock o_wb_ack is high. On all other clocks, it is irrelelant--since |
// no one cares, no one is reading it, it gets lost in the mux in the |
// interconnect, etc. For this reason, we can just simplify our logic. |
always @(posedge i_clk) |
casez(r_wb_addr) |
`UART_SETUP: o_wb_data <= { 2'b00, uart_setup }; |
`UART_FIFO: o_wb_data <= wb_fifo_data; |
`UART_RXREG: o_wb_data <= wb_rx_data; |
`UART_TXREG: o_wb_data <= wb_tx_data; |
endcase |
|
// This device never stalls. Sure, it takes two clocks, but they are |
// pipelined, and nothing stalls that pipeline. (Creates FIFO errors, |
// perhaps, but doesn't stall the pipeline.) Hence, we can just |
// set this value to zero. |
assign o_wb_stall = 1'b0; |
|
endmodule |
/wbuart32.core
0,0 → 1,27
CAPI=1 |
[main] |
description = A full featured UART with Simulator |
simulators = verilator |
|
[fileset rtl] |
files = |
rtl/rxuart.v |
rtl/txuart.v |
rtl/ufifo.v |
rtl/wbuart.v |
file_type = verilogSource |
|
[verilator] |
verilator_options = |
tb_toplevel = bench/cpp/linetest.cpp |
top_module = linetest |
source_type = CPP |
src_files = bench/cpp/linetest.cpp bench/cpp/uartsim.cpp |
include_files = bench/cpp/uartsim.h |
|
[fileset tb_files] |
files = bench/verilog/linetest.v |
usage = verilator |
file_type = verilogSource |
scope = private |
|