OpenCores
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
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.